1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package net.sf.firemox.xml;
20
21 import java.io.IOException;
22 import java.io.OutputStream;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.List;
26 import java.util.Map;
27
28 import net.sf.firemox.clickable.ability.Optimization;
29 import net.sf.firemox.clickable.ability.Priority;
30 import net.sf.firemox.expression.intlist.ListType;
31 import net.sf.firemox.operation.IdOperation;
32 import net.sf.firemox.test.TestOn;
33 import net.sf.firemox.token.AbstractValue;
34 import net.sf.firemox.token.IdAbilities;
35 import net.sf.firemox.token.IdCardColors;
36 import net.sf.firemox.token.IdConst;
37 import net.sf.firemox.token.IdMessageBox;
38 import net.sf.firemox.token.IdPositions;
39 import net.sf.firemox.token.IdTargets;
40 import net.sf.firemox.token.IdTokens;
41 import net.sf.firemox.token.IdZones;
42 import net.sf.firemox.token.Register;
43 import net.sf.firemox.tools.MToolKit;
44 import net.sf.firemox.xml.XmlParser.Node;
45
46 import org.apache.commons.lang.StringUtils;
47
48 /***
49 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
50 */
51 public final class XmlTools {
52
53 private XmlTools() {
54 super();
55 }
56
57 private static Map<String, Integer> registerIndexName = new HashMap<String, Integer>();
58
59 private static Map<String, Integer> targetMode = new HashMap<String, Integer>();
60
61 private static Map<String, Integer> position = new HashMap<String, Integer>();
62
63 /***
64 * The available zones.
65 *
66 * @see net.sf.firemox.xml.tbs.Tbs#buildMdb(Node, OutputStream)
67 */
68 public static Map<String, Integer> zones = new HashMap<String, Integer>();
69
70 /***
71 * The available abilities
72 *
73 * @see net.sf.firemox.xml.tbs.Tbs#buildMdb(Node, OutputStream)
74 */
75 public static Map<String, Integer> abilities = new HashMap<String, Integer>();
76
77 private static Map<String, Integer> definedValuesName = new HashMap<String, Integer>();
78
79 private static Map<String, Integer> cardColorsName = new HashMap<String, Integer>();
80
81 /***
82 * If there is neither attribute neither node with the given name, '-1' is
83 * written instead of throwing an exception.
84 *
85 * @param node
86 * the parent node.
87 * @param attribute
88 * the attribute/node name to write.
89 * @param out
90 * the output stream.
91 * @throws IOException
92 * error while writing.
93 */
94 public static void tryWriteExpression(XmlParser.Node node, String attribute,
95 OutputStream out) throws IOException {
96 if (node.getAttribute(attribute) == null && node.get(attribute) == null) {
97
98 XmlTools.writeConstant(out, -1);
99 } else if ("-1".equals(node.getAttribute(attribute))) {
100
101 XmlTools.writeConstant(out, IdConst.ALL);
102 } else {
103
104 XmlTools.writeAttrOptions(node, attribute, out);
105 }
106 }
107
108 /***
109 * Fill the referenced HashMaps
110 */
111 public static void initHashMaps() {
112 clean();
113 for (int i = IdTokens.REGISTER_INDEX_NAMES.length; i-- > 0;) {
114 registerIndexName.put(IdTokens.REGISTER_INDEX_NAMES[i],
115 IdTokens.REGISTER_INDEX_VALUES[i]);
116 }
117 for (int i = IdTargets.MODE_NAMES.length; i-- > 0;) {
118 targetMode.put(IdTargets.MODE_NAMES[i], IdTargets.MODE_VALUES[i]);
119 }
120 for (int i = IdPositions.POSITION_NAMES.length; i-- > 0;) {
121 position.put(IdPositions.POSITION_NAMES[i],
122 IdPositions.POSITION_VALUES[i]);
123 }
124 for (int i = IdZones.ZONE_NAMES.length; i-- > 0;) {
125 zones.put(IdZones.ZONE_NAMES[i], IdZones.ZONE_VALUES[i]);
126 }
127 for (int i = IdAbilities.ABILITIES_NAMES.length; i-- > 0;) {
128 abilities.put(IdAbilities.ABILITIES_NAMES[i],
129 IdAbilities.ABILITIES_VALUES[i]);
130 }
131 for (int i = IdConst.VALUES_NAME.length; i-- > 0;) {
132 definedValuesName.put(IdConst.VALUES_NAME[i], IdConst.VALUES[i]);
133 }
134 for (int i = IdCardColors.CARD_COLOR_NAMES.length; i-- > 1;) {
135 cardColorsName.put(IdCardColors.CARD_COLOR_NAMES[i],
136 IdCardColors.CARD_COLOR_VALUES[i]);
137 }
138 }
139
140 /***
141 * Clean tables.
142 */
143 public static void clean() {
144 registerIndexName.clear();
145 targetMode.clear();
146 position.clear();
147 zones.clear();
148 abilities.clear();
149 definedValuesName.clear();
150 cardColorsName.clear();
151 aliasMap = null;
152 XmlTest.clean();
153 XmlAction.clean();
154 XmlExpression.clean();
155 XmlTbs.clean();
156 XmlModifier.clean();
157 }
158
159 /***
160 * Write the TestOn instance corresponding to the given XSD attribute name.
161 *
162 * @param out
163 * the stream this enumeration would be written.
164 * @param xsdName
165 * the XSD name of this TestOn.
166 * @throws IOException
167 * error while writing.
168 */
169 public static void writeTestOn(OutputStream out, String xsdName)
170 throws IOException {
171 if (xsdName != null) {
172 try {
173 TestOn.serialize(out, xsdName);
174 } catch (IllegalArgumentException e) {
175 XmlConfiguration.error(e.getMessage());
176 TestOn.THIS.serialize(out);
177 }
178 } else if (defaultOnMeTag)
179 TestOn.THIS.serialize(out);
180 else
181 TestOn.TESTED.serialize(out);
182 }
183
184 /***
185 * @param attribute
186 * the optimization name.
187 * @return the optimization id.
188 */
189 public static Optimization getOptimization(String attribute) {
190 if (attribute == null)
191 return net.sf.firemox.clickable.ability.Optimization.none;
192 return Optimization.valueOf(attribute);
193 }
194
195 /***
196 * Return the operation code
197 *
198 * @param operation
199 * the alias
200 * @return the operation code
201 * @see net.sf.firemox.action.ModifyRegister
202 */
203 public static IdOperation getOperation(String operation) {
204 if (operation == null) {
205 return IdOperation.ANY;
206 }
207 final IdOperation op = IdOperation.valueOfXsd(operation);
208 if (op != null) {
209 return op;
210 }
211 XmlConfiguration.error("Unknown operation : '" + operation + "'");
212 return IdOperation.ANY;
213 }
214
215 /***
216 * Return the resolution code
217 *
218 * @param resolution
219 * the alias
220 * @return the resolution code
221 */
222 public static Priority getPriority(String resolution) {
223 if (resolution == null)
224 return net.sf.firemox.clickable.ability.Priority.normal;
225 return net.sf.firemox.clickable.ability.Priority.valueOf(resolution);
226 }
227
228 /***
229 * Return the corresponding code to the specified target mode.
230 *
231 * @param modeAlias
232 * the alias of mode
233 * @return the corresponding code to the specified target mode.
234 * @see IdTargets#MODE_NAMES
235 */
236 public static int getTargetMode(String modeAlias) {
237 if (modeAlias == null) {
238 return IdTargets.CHOOSE;
239 }
240 final Integer obj = XmlTools.targetMode.get(modeAlias);
241 if (obj != null) {
242 return obj.intValue();
243 }
244 XmlConfiguration.error("Unknown target mode : '" + modeAlias + "'");
245 return 0;
246 }
247
248 /***
249 * Return the corresponding code to the specified position name.
250 *
251 * @param positionAlias
252 * the alias of place
253 * @return the corresponding code to the specified position name.
254 * @see IdPositions#POSITION_NAMES
255 */
256 public static Integer getPosition(String positionAlias) {
257 if (positionAlias == null) {
258 return IdPositions.ON_THE_TOP;
259 }
260 return XmlTools.position.get(positionAlias);
261 }
262
263 /***
264 * Return the corresponding code to the specified zone name.
265 *
266 * @param zoneAlias
267 * the alias of place
268 * @return the corresponding code to the specified zone name.
269 * @see IdZones#ZONE_NAMES
270 */
271 public static int getZone(String zoneAlias) {
272 final Integer obj = XmlTools.zones.get(zoneAlias);
273 if (obj != null) {
274 return obj.intValue();
275 }
276 XmlConfiguration.error("Unknown zone : '" + zoneAlias + "'");
277 return 0;
278 }
279
280 /***
281 * Return the corresponding code to the specified ability name.
282 *
283 * @param abilityAlias
284 * the alias of place
285 * @return the corresponding code to the specified ability name.
286 * @see IdAbilities#ABILITIES_NAMES
287 */
288 public static int getAbility(String abilityAlias) {
289 final Integer obj = XmlTools.abilities.get(abilityAlias);
290 if (obj != null) {
291 return obj.intValue();
292 }
293 XmlConfiguration.error("Unknown ability : '" + abilityAlias + "'");
294 return 0;
295 }
296
297 /***
298 * Return the corresponding codeto the specified string register index.
299 *
300 * @param alias
301 * the alias of value in it's string form
302 * @return the corresponding code to the specified string register index.
303 */
304 public static int getValue(String alias) {
305 final Integer value = getIntPriv(alias);
306 if (value == null) {
307 Integer obj = XmlTools.definedValuesName.get(alias);
308 if (obj != null) {
309 return obj.intValue();
310 }
311 obj = XmlTools.registerIndexName.get(alias);
312 if (obj != null) {
313 return obj.intValue();
314 }
315
316 XmlConfiguration.error("Unknown alias : '" + alias + "'");
317 return 0;
318 }
319 return value;
320 }
321
322 /***
323 * Return the corresponding code to the specified alias.
324 *
325 * @param alias
326 * the alias of value in it's string form
327 * @return the corresponding code to the specified alias.
328 */
329 private static Integer getIntPriv(String alias) {
330 if (alias == null) {
331 XmlConfiguration.error("Null register/index.", true);
332 return 0;
333 }
334
335 if (alias.length() > 0) {
336 switch (alias.charAt(0)) {
337 case '-':
338 final int valueNeg = MToolKit.parseInt(alias);
339 if (valueNeg == Integer.MIN_VALUE) {
340
341 return null;
342 }
343 return getNegativeConstant(valueNeg);
344 case '1':
345 case '2':
346 case '3':
347 case '4':
348 case '5':
349 case '6':
350 case '7':
351 case '8':
352 case '9':
353 case '0':
354 final int value = MToolKit.parseInt(alias);
355 if (value == Integer.MIN_VALUE) {
356
357 return null;
358 }
359 return value;
360 default:
361
362 }
363 }
364
365 final Integer value = aliasMap.get(alias);
366 if (value != null)
367 return getNegativeConstant(value);
368 return XmlTools.registerIndexName.get(alias);
369 }
370
371 private static int getNegativeConstant(int value) {
372 if (value < 0) {
373 if (value < -127) {
374 throw new InternalError("Negative number cannot exceed -127 : " + value);
375 }
376 return IdConst.NEGATIVE_NUMBER_MASK | -value;
377 }
378 return value;
379 }
380
381 /***
382 * Return the corresponding code to the specified alias.
383 *
384 * @param alias
385 * the alias of value in it's string form
386 * @return the corresponding code to the specified alias.
387 */
388 public static int getInt(String alias) {
389 Integer value = getValue(alias);
390 if (value == null) {
391 XmlConfiguration.error("Unknown integer value : " + alias);
392 return 0;
393 }
394 return value;
395 }
396
397 /***
398 * Return the corresponding code to the card color name
399 *
400 * @param alias
401 * the alias name.
402 * @return the alias value
403 */
404 public static int getAliasValue(String alias) {
405 int value = aliasMap.get(alias);
406 if (value == Integer.MIN_VALUE) {
407
408 value = 0;
409 XmlConfiguration.error("Unknown alias value '" + alias + "'");
410 }
411 return value;
412 }
413
414 /***
415 * Return the corresponding code to the card color name
416 *
417 * @param colorName
418 * the color alias
419 * @return the corresponding code to the card color name
420 */
421 public static int getColor(String colorName) {
422 final Integer obj = XmlTools.cardColorsName.get(colorName);
423 if (obj != null) {
424 return obj.intValue();
425 }
426 XmlConfiguration.error("Unknown color : '" + colorName + "'");
427 return 0;
428 }
429
430 /***
431 * Return the corresponding code to the card type name
432 *
433 * @param idCard
434 * the card type name
435 * @return the corresponding code to the card type name
436 */
437 public static int getIdCard(String idCard) {
438 final Integer value = aliasMap.get(idCard);
439 if (value == null || value == Integer.MIN_VALUE) {
440
441 XmlConfiguration.error("Unknown idcard : '" + idCard + "'");
442 return 0;
443 }
444 return value;
445 }
446
447 /***
448 * Return the corresponding code to the list of card type name
449 *
450 * @param list
451 * is the list of card type name
452 * @return the corresponding code to the list of card type name.
453 */
454 public static int getIdCards(String list) {
455
456 int idCard = 0;
457 final String[] arrayid = list.split(" ");
458 for (String id : arrayid) {
459 idCard |= XmlTools.getIdCard(id);
460 }
461 return idCard;
462 }
463
464 /***
465 * Write to the specified output stream the values defined as linear list ' '
466 * separated values, or 'value' elements.
467 *
468 * @param out
469 * output stream where the card structure will be saved
470 * @param node
471 * the XML test container structure
472 * @param tagAttr
473 * @throws IOException
474 * error while writing.
475 */
476 public static void writeList(OutputStream out, XmlParser.Node node,
477 String tagAttr) throws IOException {
478 if (node == null) {
479 out.write(ListType.COLLECTION.ordinal());
480 out.write(0);
481 return;
482 }
483 final String listName;
484 if (tagAttr.endsWith("y")) {
485 listName = tagAttr + "ies";
486 } else {
487 listName = tagAttr + "s";
488 }
489 final String nodeAttr = node.getAttribute(listName);
490 if (nodeAttr != null) {
491
492
493
494 final String[] values;
495 if (nodeAttr.indexOf("..") != -1) {
496 out.write(ListType.RANGE.ordinal());
497 values = nodeAttr.split("//.//.");
498 } else {
499 out.write(ListType.COLLECTION.ordinal());
500 values = nodeAttr.split(" ");
501 }
502 out.write(values.length);
503 for (String value : values) {
504 writeSimpleValue(out, value);
505 }
506 } else {
507
508 out.write(ListType.COLLECTION.ordinal());
509 Node valuesNode = node.get(listName);
510 if (valuesNode == null) {
511 out.write(0);
512 } else {
513 final List<Node> values = valuesNode.getNodes("value");
514 out.write(values.size());
515 for (XmlParser.Node value : values) {
516 writeComplexValue(out, value);
517 }
518 }
519 }
520 }
521
522 /***
523 * Write to the specified output stream the 16bits integer value.
524 *
525 * @param out
526 * output stream where the card structure will be saved
527 * @param value
528 * the simple value to write.
529 * @throws IOException
530 * error while writing.
531 */
532 public static void writeSimpleValue(OutputStream out, String value)
533 throws IOException {
534 final AbstractValue abstractValue = AbstractValue.valueOfXsd(value);
535 if (abstractValue != null) {
536 IdOperation.ABSTRACT_VALUE.serialize(out);
537 abstractValue.serialize(out);
538 } else {
539 TestOn testOn = TestOn.valueOfXsd(value);
540 if (testOn != null) {
541 IdOperation.TEST_ON.serialize(out);
542 testOn.serialize(out);
543 } else {
544 Integer intValue = getIntPriv(value);
545 if (intValue == null) {
546 XmlConfiguration.error("Unknown alias : '" + value + "'");
547 writeConstant(out, 0);
548 } else {
549 writeConstant(out, intValue);
550 }
551 }
552 }
553 }
554
555 /***
556 * Write to the specified output stream the 16bits integer value.
557 *
558 * @param out
559 * output stream where the card structure will be saved
560 * @param value
561 * the simple value to write.
562 * @throws IOException
563 * error while writing.
564 */
565 public static void writeConstant(OutputStream out, int value)
566 throws IOException {
567 IdOperation.INT_VALUE.serialize(out);
568 MToolKit.writeInt16(out, getNegativeConstant(value));
569 }
570
571 /***
572 * @param out
573 * output stream where the card structure will be saved
574 * @param expr
575 * complex expression node to write
576 * @throws IOException
577 * error while writing.
578 */
579 public static void writeComplexValue(OutputStream out, XmlParser.Node expr)
580 throws IOException {
581
582 String register = expr.getAttribute("register");
583 if (register != null) {
584 if ("true".equals(expr.getAttribute("base"))) {
585 IdOperation.BASE_REGISTER_INT_VALUE.serialize(out);
586 } else {
587 IdOperation.REGISTER_ACCESS.serialize(out);
588 }
589 Register.valueOfXsd(register).serialize(out);
590 writeAttrOptions(expr, "index", out);
591 } else {
592
593 final Iterator<?> it = expr.iterator();
594 while (it.hasNext()) {
595 Object obj = it.next();
596 if (obj instanceof XmlParser.Node) {
597 XmlExpression.getExpression(((XmlParser.Node) obj).getTag())
598 .buildMdb((XmlParser.Node) obj, out);
599 return;
600 }
601 }
602 XmlConfiguration
603 .error("Element '"
604 + expr.getParent().getTag()
605 + "' must contain a complex operation, or have value defined as attribute. Context="
606 + expr.getParent());
607 }
608
609 }
610
611 /***
612 * @param node
613 * the XML test container structure
614 * @param tagAttr
615 * @param out
616 * output stream where the card structure will be saved
617 * @throws IOException
618 * error while writing.
619 */
620 public static void writeAttrOptions(XmlParser.Node node, String tagAttr,
621 OutputStream out) throws IOException {
622 writeAttrOptionsDefault(node, tagAttr, out, null);
623 }
624
625 /***
626 * @param node
627 * the XML test container structure
628 * @param tagAttr
629 * @param out
630 * output stream where the card structure will be saved
631 * @param defaultValue
632 * the default value to use if neither attribute, neither element has
633 * been found.
634 * @throws IOException
635 * error while writing.
636 */
637 public static void writeAttrOptionsDefault(XmlParser.Node node,
638 String tagAttr, OutputStream out, String defaultValue) throws IOException {
639 if (defaultValue != null && node.getAttribute(tagAttr) == null
640 && node.get(tagAttr) == null) {
641 node.addAttribute(new XmlParser.Attribute(tagAttr, defaultValue));
642 }
643 final String nodeAttr = node.getAttribute(tagAttr);
644 if (nodeAttr != null) {
645
646 if (node.getAttribute(tagAttr + "-class") != null
647 && !int.class.getName().equals(node.getAttribute(tagAttr + "-class"))
648 && !Integer.class.getName().equals(
649 node.getAttribute(tagAttr + "-class"))) {
650
651 IdOperation.OBJECT_VALUE.serialize(out);
652 MToolKit.writeString(out, node.getAttribute(tagAttr + "-class"));
653 MToolKit.writeString(out, node.getAttribute(tagAttr));
654 } else if (nodeAttr.startsWith("%")) {
655
656 IdOperation.REF_VALUE.serialize(out);
657 MToolKit.writeString(out, nodeAttr);
658 } else {
659 writeSimpleValue(out, nodeAttr);
660 }
661 } else {
662
663 final XmlParser.Node expr = node.get(tagAttr);
664 if (expr == null) {
665 XmlConfiguration.error("Neither element '" + tagAttr
666 + "' neither attribute '" + tagAttr
667 + "' have been found in element '" + node.getTag()
668 + "'.\n\t Context:\n" + node);
669 return;
670 }
671 writeComplexValue(out, expr);
672 }
673 }
674
675 /***
676 * @param node
677 * @param tagAttr
678 * @param out
679 * @param nameSpace
680 * @throws IOException
681 * error while writing.
682 */
683 public static void writeAttrOptions(XmlParser.Node node, String tagAttr,
684 OutputStream out, String nameSpace) throws IOException {
685 final String colorAttr = node.getAttribute(tagAttr);
686 if (colorAttr != null) {
687 final String method = "get" + StringUtils.capitalize(nameSpace);
688 try {
689 final Integer retVal = (Integer) XmlTools.class.getMethod(method,
690 String.class).invoke(null, colorAttr);
691 if (retVal == null) {
692 XmlConfiguration.error("Unknown " + nameSpace + " : '" + colorAttr
693 + "'");
694 writeConstant(out, 0);
695 } else {
696 writeConstant(out, retVal);
697 }
698 } catch (Throwable e2) {
699 XmlConfiguration.error("Error found in 'get" + nameSpace + "' : "
700 + e2.getMessage());
701 writeConstant(out, 0);
702 }
703 } else {
704 final XmlParser.Node expr = node.get(tagAttr);
705 if (expr == null) {
706 XmlConfiguration.error("Neither element '" + tagAttr
707 + "' neither attribute '" + tagAttr
708 + "' have been found in element '" + node.getTag() + "'. Context="
709 + node);
710 return;
711 }
712 writeComplexValue(out, expr);
713 }
714 }
715
716 /***
717 * @param attribute
718 * the message name.
719 * @return the message id.
720 */
721 public static IdMessageBox getMessageType(String attribute) {
722 if (attribute == null) {
723 return IdMessageBox.ok;
724 }
725 IdMessageBox messageBox = IdMessageBox.valueOf(attribute);
726 if (messageBox == null) {
727 return IdMessageBox.ok;
728 }
729 return messageBox;
730 }
731
732 /***
733 * The defined user alias
734 */
735 public static Map<String, Integer> aliasMap = null;
736
737 /***
738 * This field must be set by activated ability, triggered, and counters to
739 * known the default value for the 'on' attribute
740 */
741 public static boolean defaultOnMeTag;
742
743 /***
744 * This field must be set by actions needing some pre-check test such as
745 * target action. When is <code>true</code>, the last build test does not
746 * refer to a ability's runtime register such as 'stack' register.
747 */
748 public static boolean testCanBePreempted;
749
750 /***
751 * Return the named node from the given node. This node may be defined in an
752 * external xml file.
753 *
754 * @param node
755 * the parent node.
756 * @param nodeName
757 * the node name.
758 * @return the named node from the given node.
759 */
760 public static Node getExternalizableNode(Node node, String nodeName) {
761 final Node references = node.get(nodeName);
762 if (references.getAttribute("file") != null) {
763
764 try {
765 return new XmlParser(XmlConfiguration.getOptions().isXsdValidation())
766 .parse(MToolKit.getUrl(references.getAttribute("file")).getPath());
767 } catch (Exception e) {
768 throw new RuntimeException(e);
769 }
770 }
771 return references;
772 }
773 }