Gridarta Editor
GameObjectMatcherParser.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.model.match;
21 
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Locale;
27 import nu.xom.Element;
28 import nu.xom.Node;
29 import nu.xom.Nodes;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32 
38 
43  @NotNull
44  private static final String XML_ATTRIB_ATTRIBUTE_NAME = "name";
45 
50  @NotNull
51  private static final String XML_ATTRIB_ATTRIBUTE_OP = "op";
52 
57  @NotNull
58  private static final String XML_ATTRIB_ATTRIBUTE_TYPE = "type";
59 
64  @NotNull
65  private static final String XML_ATTRIB_ATTRIBUTE_VALUE = "value";
66 
71  @NotNull
72  private static final String XML_ATTRIB_ATTRIBUTE_USEARCHETYPE = "useArchetype";
73 
77  @NotNull
78  private static final String XML_MATCHER_ATTRIBUTE_ID = "id";
79 
83  @NotNull
84  private static final String XML_MATCHER_ATTRIBUTE_SYSTEM = "system";
85 
89  @NotNull
90  private static final String XML_MATCHER_ELEMENT_ENV = "Env";
91 
95  @NotNull
96  private static final String XML_ELEMENT_TYPENRS = "TypeNrs";
97 
101  @NotNull
102  private static final String XML_ELEMENT_ATTRIB = "Attrib";
103 
107  @NotNull
108  private static final String XML_ELEMENT_OR = "Or";
109 
113  @NotNull
114  private static final String XML_ELEMENT_AND = "And";
115 
120  @NotNull
121  private static final String XML_TYPENRS_ATTRIBUTE_NUMBERS = "numbers";
122 
127  }
128 
136  @NotNull
137  public static NamedGameObjectMatcher parseMatcher(@NotNull final Element el, final int editType) throws ParsingException {
138  final String currentLanguage = Locale.getDefault().getLanguage();
139  final String localName = el.getLocalName();
140  if (localName == null || !localName.equals("GameObjectMatcher")) {
141  throw new ParsingException("wrong local element name: expected \"GameObjectMatcher\" but got \"" + localName + "\"");
142  }
143  final String id = el.getAttribute(XML_MATCHER_ATTRIBUTE_ID).getValue();
144  String title = getLanguageTitle(el, currentLanguage);
145  if (title == null || title.isEmpty()) {
146  title = getLanguageTitle(el, "en");
147  }
148  if (title == null || title.isEmpty()) {
149  title = id;
150  }
151  final boolean systemMatcher = el.getAttribute(XML_MATCHER_ATTRIBUTE_SYSTEM).getValue().equals("true");
152  final Node content = xpathEvaluate(el, "*[last()]");
153  assert content != null;
154 
155  final Node env = xpathEvaluate(el, XML_MATCHER_ELEMENT_ENV);
156  @Nullable final GameObjectMatcher envGameObjectMatcher;
157  if (env != null && env instanceof Element) {
158  final Node envContent = xpathEvaluate(env, "*[1]");
159  envGameObjectMatcher = envContent == null ? new AndGameObjectMatcher(Collections.emptyList()) : createMatcher((Element) envContent);
160  } else {
161  envGameObjectMatcher = null;
162  }
163 
164  //XXX:assert content.getNodeType() == Node.ELEMENT_NODE;
165  final GameObjectMatcher matcher = createMatcher((Element) content);
166  return new NamedGameObjectMatcher(systemMatcher ? 0 : editType, id, title, systemMatcher, envGameObjectMatcher, matcher);
167  }
168 
175  @Nullable
176  private static String getLanguageTitle(@NotNull final Node el, @NotNull final String language) {
177  final Nodes nodes = el.query("title[lang('" + language + "')]");
178  return nodes.size() == 0 ? null : nodes.get(0).getValue();
179  }
180 
187  @NotNull
188  private static GameObjectMatcher createMatcher(@NotNull final Element el) throws ParsingException {
189  final String localName = el.getLocalName();
190  if (localName == null) {
191  // ignore
192  } else if (localName.equals(XML_ELEMENT_TYPENRS)) {
194  } else if (localName.equals(XML_ELEMENT_ATTRIB)) {
196  } else if (localName.equals(XML_ELEMENT_OR)) {
197  return createOrMatcher(el);
198  } else if (localName.equals(XML_ELEMENT_AND)) {
199  return createAndMatcher(el);
200  }
201  throw new ParsingException("expected element node name to be one of \"" + XML_ELEMENT_TYPENRS + "\", \"" + XML_ELEMENT_ATTRIB + "\", \"" + XML_ELEMENT_OR + "\", \"" + XML_ELEMENT_AND + "\".");
202  }
203 
210  @NotNull
211  private static GameObjectMatcher createAndMatcher(@NotNull final Node el) throws ParsingException {
212  return new AndGameObjectMatcher(getChildMatchers(el));
213  }
214 
221  @NotNull
222  private static GameObjectMatcher createOrMatcher(@NotNull final Node el) throws ParsingException {
223  return new OrGameObjectMatcher(getChildMatchers(el));
224  }
225 
235  @NotNull
236  private static Collection<GameObjectMatcher> getChildMatchers(@NotNull final Node el) throws ParsingException {
237  final Collection<GameObjectMatcher> childMatchers = new ArrayList<>();
238  for (int i = 0; i < el.getChildCount(); i++) {
239  final Node childNode = el.getChild(i);
240  if (childNode instanceof Element) {
241  childMatchers.add(createMatcher((Element) childNode));
242  }
243  }
244  return childMatchers;
245  }
246 
253  @NotNull
254  private static GameObjectMatcher createAttributeArchObjectMatcher(@NotNull final Element el) throws ParsingException {
255  final String type = el.getAttribute(XML_ATTRIB_ATTRIBUTE_TYPE).getValue();
256  if (type.equals("string")) {
257  return new StringAttributeGameObjectMatcher(el.getAttribute(XML_ATTRIB_ATTRIBUTE_NAME).getValue(), Enum.valueOf(Operation.class, el.getAttribute(XML_ATTRIB_ATTRIBUTE_OP).getValue()), el.getAttribute(XML_ATTRIB_ATTRIBUTE_VALUE).getValue(), el.getAttribute(XML_ATTRIB_ATTRIBUTE_USEARCHETYPE).getValue().equals("true"));
258  } else if (type.equals("int")) {
259  return new IntAttributeGameObjectMatcher(el.getAttribute(XML_ATTRIB_ATTRIBUTE_NAME).getValue(), Enum.valueOf(Operation.class, el.getAttribute(XML_ATTRIB_ATTRIBUTE_OP).getValue()), el.getAttribute(XML_ATTRIB_ATTRIBUTE_VALUE).getValue(), el.getAttribute(XML_ATTRIB_ATTRIBUTE_USEARCHETYPE).getValue().equals("true"));
260  } else {
261  throw new AssertionError("impossible value '" + type + "' for XML_ATTRIB_ATTRIBUTE_TYPE");
262  }
263  }
264 
271  @NotNull
272  private static GameObjectMatcher createTypeNrsArchObjectMatcher(@NotNull final Element el) throws ParsingException {
273  final CharSequence numbers = el.getAttribute(XML_TYPENRS_ATTRIBUTE_NUMBERS).getValue();
274  final String[] typeNrs = StringUtils.PATTERN_WHITESPACE.split(numbers, 0);
275  final int[] types = new int[typeNrs.length];
276  for (int i = 0; i < types.length; i++) {
277  try {
278  types[i] = Integer.parseInt(typeNrs[i]);
279  } catch (final NumberFormatException ignored) {
280  //noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException
281  throw new ParsingException("invalid number: " + typeNrs[i]);
282  }
283  }
284  return new TypeNrsGameObjectMatcher(types);
285  }
286 
293  @Nullable
294  private static Node xpathEvaluate(@NotNull final Node el, @NotNull final String xpathExpression) {
295  final Nodes nodes = el.query(xpathExpression);
296  assert nodes.size() <= 1;
297  return nodes.size() == 0 ? null : nodes.get(0);
298  }
299 
300 }
static final String XML_TYPENRS_ATTRIBUTE_NUMBERS
The name of the "numbers" attribute within XML_ELEMENT_TYPENRS elements.
static GameObjectMatcher createAttributeArchObjectMatcher(@NotNull final Element el)
Creates an AttributeGameObjectMatcher.
Utility class for string manipulation.
Interface for classes that match GameObjects.
GameObjectMatcherParser()
Private constructor to prevent instantiation.
static final String XML_ATTRIB_ATTRIBUTE_TYPE
The name of the "type" attribute within XML_ELEMENT_ATTRIB elements.
Thrown when a parsing error occurs.
static final String XML_ATTRIB_ATTRIBUTE_VALUE
The name of the "value" attribute within XML_ELEMENT_ATTRIB elements.
A GameObjectMatcher that And-combines other.
Class for some default GameObjectMatchers.
static GameObjectMatcher createTypeNrsArchObjectMatcher(@NotNull final Element el)
Creates a TypeNrsGameObjectMatcher.
static Node xpathEvaluate(@NotNull final Node el, @NotNull final String xpathExpression)
Evaluates an XPath expression and returns the result Node.
Decorates an arbitrary GameObjectMatcher with a localized name that is suitable for the user interfac...
static final String XML_ATTRIB_ATTRIBUTE_USEARCHETYPE
The name of the "useArchetype" attribute within XML_ELEMENT_ATTRIB elements.
Base package of all Gridarta classes.
static NamedGameObjectMatcher parseMatcher(@NotNull final Element el, final int editType)
Creates a NamedGameObjectMatcher from XML.
static final String XML_ATTRIB_ATTRIBUTE_OP
The name of the "op" attribute within XML_ELEMENT_ATTRIB elements.
A AttributeGameObjectMatcher that compares attributes values using "string" type. ...
static final String XML_ELEMENT_ATTRIB
The name of the "Attrib" element.
static final String XML_MATCHER_ATTRIBUTE_SYSTEM
The name of the "system" attribute within matcher elements.
static Collection< GameObjectMatcher > getChildMatchers(@NotNull final Node el)
Gets the matchers that are found as children of an XML element.
static final String XML_ELEMENT_OR
The name of the "Or" element.
static final Pattern PATTERN_WHITESPACE
Pattern to match whitespace excluding NL and CR.
static final String XML_ELEMENT_TYPENRS
The name of the "TypeNrs" element.
static GameObjectMatcher createOrMatcher(@NotNull final Node el)
Creates an OrGameObjectMatcher.
static GameObjectMatcher createMatcher(@NotNull final Element el)
Creates a GameObjectMatcher from XML.
static final String XML_MATCHER_ATTRIBUTE_ID
The name of the "id" attribute within matcher elements.
An GameObjectMatcher matching certain archetype types.
static String getLanguageTitle(@NotNull final Node el, @NotNull final String language)
Returns the title for a given language.
static final String XML_MATCHER_ELEMENT_ENV
The name of the "Env" element within matcher elements.
static final String XML_ATTRIB_ATTRIBUTE_NAME
The name of the "name" attribute within XML_ELEMENT_ATTRIB elements.
A GameObjectMatcher that Or-combines other.
static GameObjectMatcher createAndMatcher(@NotNull final Node el)
Creates an AndGameObjectMatcher.
static final String XML_ELEMENT_AND
The name of the "And" element.
A AttributeGameObjectMatcher that compares attributes values using "int" type.