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.File;
22 import java.io.FileOutputStream;
23 import java.io.FilenameFilter;
24 import java.io.IOException;
25 import java.io.OutputStream;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import net.sf.firemox.annotation.XmlTestElement;
32 import net.sf.firemox.token.IdConst;
33 import net.sf.firemox.tools.MToolKit;
34 import net.sf.firemox.tools.PairStringInt;
35 import net.sf.firemox.xml.XmlParser.Node;
36
37 import org.apache.commons.io.FilenameUtils;
38 import org.apache.commons.io.filefilter.FileFilterUtils;
39 import org.apache.commons.lang.StringUtils;
40 import org.apache.commons.lang.WordUtils;
41 import org.kohsuke.args4j.CmdLineException;
42 import org.kohsuke.args4j.CmdLineParser;
43 import org.xml.sax.SAXException;
44 import org.xml.sax.SAXParseException;
45
46 /***
47 * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
48 */
49 public class XmlConfiguration {
50
51 /***
52 * Constructor.
53 *
54 * @param xmlFile
55 * the XML source file.
56 * @param workingDir
57 * the working directory.
58 * @param output
59 * the output stream of MDB file.
60 * @exception SAXException
61 * parsing error.
62 * @exception IOException
63 * writing error.
64 */
65 private XmlConfiguration(String xmlFile, String workingDir,
66 OutputStream output) throws SAXException, IOException {
67 initParser();
68 synchronized (parser) {
69 config = parser.parse(MToolKit.getResourceAsStream(xmlFile));
70 }
71 try {
72 if (XmlTools.aliasMap == null) {
73 XmlTools.aliasMap = new HashMap<String, Integer>(newIncludeInstance());
74 }
75 String tagName = config.getTag();
76 config.addAttribute(new XmlParser.Attribute("workingDir", workingDir));
77 config.addAttribute(new XmlParser.Attribute("xmlFile", xmlFile.replace(
78 '//', '/')));
79 XmlTbs.getTbsComponent(tagName).buildMdb(config, output);
80 } catch (Exception e) {
81 error("Component error with '" + config.getTag() + "'\n" + config);
82 e.printStackTrace();
83 }
84 }
85
86 /***
87 * Build and return a parsed instance of given rules file.
88 *
89 * @param rulesFile
90 * the XML card file.
91 * @param workingDir
92 * the working directory.
93 * @param output
94 * the output stream of MD file.
95 * @return a parsed instance of given rules file.
96 * @exception SAXException
97 * parsing error.
98 * @exception IOException
99 * writing error.
100 */
101 public static XmlConfiguration parseRules(String rulesFile,
102 String workingDir, OutputStream output) throws SAXException, IOException {
103 currentCard = null;
104 return new XmlConfiguration(rulesFile, workingDir, output);
105 }
106
107 /***
108 * Build and return a parsed instance of given card file.
109 *
110 * @param cardFile
111 * the XML card file.
112 * @param workingDir
113 * the working directory.
114 * @param output
115 * the output stream of MDB file.
116 * @return a parsed instance of given card file.
117 * @exception SAXException
118 * parsing error.
119 * @exception IOException
120 * writing error.
121 */
122 public static XmlConfiguration parseCard(String cardFile, String workingDir,
123 OutputStream output) throws SAXException, IOException {
124 currentCard = FilenameUtils.getName(cardFile);
125 return new XmlConfiguration(cardFile, workingDir, output);
126 }
127
128 /***
129 * @throws SAXException
130 */
131 private static synchronized void initParser() throws SAXException {
132 if (parser != null) {
133 return;
134 }
135 parser = new XmlParser(XmlConfiguration.getOptions().isXsdValidation());
136 }
137
138 /***
139 * Add an error.
140 *
141 * @param message
142 * error message.
143 */
144 public static void error(String message) {
145 error(message, false);
146 }
147
148 /***
149 * Add an error.
150 *
151 * @param message
152 * error message.
153 * @param dump
154 * if <code>true</code>, the stack trace is printed.
155 */
156 public static void error(String message, boolean dump) {
157 if (currentCard != null) {
158 System.out.println(currentCard);
159 currentCard = null;
160 }
161 XmlConfiguration.error++;
162 System.out.print("\tERROR ");
163 System.out.println(message);
164 if (dump) {
165 new RuntimeException().printStackTrace(System.out);
166 }
167 }
168
169 /***
170 * Add an error.
171 *
172 * @param message
173 * error message.
174 */
175 public static void info(String message) {
176 System.out.println(message);
177 }
178
179 /***
180 * Add an error.
181 *
182 * @param message
183 * error message.
184 */
185 public static void warning(String message) {
186 if (currentCard != null) {
187 System.out.println(currentCard);
188 currentCard = null;
189 }
190 XmlConfiguration.warning++;
191 System.out.print("\tWARNING ");
192 System.out.println(message);
193 }
194
195 /***
196 * Add an uncompleted counter.
197 */
198 public static void uncompleted() {
199 if (currentCard != null) {
200 System.out.println(currentCard);
201 currentCard = null;
202 }
203 XmlConfiguration.uncompleted++;
204 System.out.println("\t... uncompleted card");
205 }
206
207 /***
208 * Add a fatal error message.
209 *
210 * @param message
211 * error message.
212 */
213 public static void fatal(String message) {
214 XmlConfiguration.error++;
215 throw new RuntimeException(message);
216 }
217
218 /***
219 * The current parsed card.
220 */
221 private static String currentCard;
222
223 private static Options options;
224
225 /***
226 * Return the method name corresponding to the specified TAG.
227 *
228 * @param tagName
229 * @return the method name corresponding to the specified TAG.
230 */
231 static XmlToMDB getXmlClass(String tagName, Map<String, XmlToMDB> instances,
232 Class<?> nameSpaceCall) {
233 if (!nameSpaceCall.getSimpleName().startsWith("Xml"))
234 throw new InternalError("Caller should be an Xml class : "
235 + nameSpaceCall);
236
237 XmlToMDB nodeClass = instances.get(tagName);
238 if (nodeClass != null) {
239 return nodeClass;
240 }
241
242 String simpleClassName = StringUtils
243 .capitalize(tagName.replaceAll("-", ""));
244 String packageName = nameSpaceCall.getPackage().getName();
245 String namespace = nameSpaceCall.getSimpleName().substring(3).toLowerCase();
246 String className = packageName + "." + namespace + "." + simpleClassName;
247 XmlToMDB result;
248 try {
249 result = (XmlToMDB) Class.forName(className).newInstance();
250 } catch (Throwable e) {
251 Class<?> mdbClass = null;
252 simpleClassName = WordUtils.capitalize(tagName.replaceAll("-", " "))
253 .replaceAll(" ", "");
254 try {
255 result = (XmlToMDB) Class.forName(
256 packageName + "." + namespace + "." + simpleClassName)
257 .newInstance();
258 } catch (Throwable e1) {
259 try {
260 className = StringUtils.chomp(packageName, ".xml") + "." + namespace
261 + "." + simpleClassName;
262 mdbClass = Class.forName(className);
263 if (!mdbClass.isAnnotationPresent(XmlTestElement.class)) {
264 result = (XmlToMDB) mdbClass.newInstance();
265 } else {
266 result = getAnnotedBuilder(mdbClass, tagName, packageName,
267 namespace);
268 }
269 } catch (Throwable ei2) {
270 error("Unsupported " + namespace + " '" + tagName + "'");
271 result = DummyBuilder.instance();
272 }
273 }
274 }
275 instances.put(tagName, result);
276 return result;
277 }
278
279 /***
280 * @param mdbClass
281 * @throws IllegalAccessException
282 * @throws InstantiationException
283 * @throws ClassNotFoundException
284 */
285 private static XmlToMDB getAnnotedBuilder(Class<?> mdbClass, String tagName,
286 String packageName, String namespace) throws InstantiationException,
287 IllegalAccessException, ClassNotFoundException {
288 if (!mdbClass.isAnnotationPresent(XmlTestElement.class)) {
289 error("Unsupported annoted " + namespace + " '" + tagName + "'");
290 return DummyBuilder.instance();
291 }
292 XmlTestElement xmlElement = mdbClass.getAnnotation(XmlTestElement.class);
293 XmlAnnoted annotedInstance = (XmlAnnoted) Class.forName(
294 packageName + "." + namespace + ".XmlAnnoted").newInstance();
295 annotedInstance.setXmlElement(xmlElement);
296 annotedInstance.setAnnotedClass(mdbClass);
297 return annotedInstance;
298 }
299
300 /***
301 * Create a new object and configure it. A new object is created and
302 * configured.
303 *
304 * @return The newly created configured object.
305 */
306 private Map<String, Integer> newIncludeInstance() {
307
308 EXPORTED_PROPERTIES.clear();
309 EXPORTED_TYPES.clear();
310 EXPORTED_PHASES.clear();
311 PROPERTY_PICTURES.clear();
312
313 Node aliases = XmlTools.getExternalizableNode(config, "aliases");
314 Map<String, Integer> aliasMap = new HashMap<String, Integer>();
315
316 aliasMap.put("nocare", IdConst.NO_CARE);
317 for (Node alias : aliases.getNodes("alias")) {
318 aliasMap.put(alias.getAttribute("name"), Integer.parseInt(alias
319 .getAttribute("value")));
320 final String exportation = alias.getAttribute("export");
321 if ("properties".equals(exportation)) {
322 EXPORTED_PROPERTIES.add(new PairStringInt(alias.getAttribute("name"),
323 Integer.parseInt(alias.getAttribute("value"))));
324
325 final String picture = alias.getAttribute("picture");
326 if (picture != null) {
327 PROPERTY_PICTURES.put(Integer.parseInt(alias.getAttribute("value")),
328 picture);
329 }
330 } else if ("damage-types".equals(exportation)) {
331 EXPORTED_DAMAGE_TYPES.add(new PairStringInt(alias.getAttribute("name"),
332 Integer.parseInt(alias.getAttribute("value"))));
333 } else if ("types".equals(exportation)) {
334 EXPORTED_TYPES.add(new PairStringInt(alias.getAttribute("name"),
335 Integer.parseInt(alias.getAttribute("value"))));
336 } else if ("phases".equals(exportation)) {
337 EXPORTED_PHASES.add(new PairStringInt(alias.getAttribute("name"),
338 Integer.parseInt(alias.getAttribute("value"))));
339 }
340 }
341 return aliasMap;
342 }
343
344 /***
345 * <ul>
346 * 2 modes:
347 * <li>Update the a MDB for specified TBS against the XML files (main file,
348 * cards and fragments). Arguments are : TBS_NAME</li>
349 * <li>Rebuild completely the MDB for specified TBS. Arguments are : -full
350 * TBS_NAME</li>
351 * </ul>
352 *
353 * @param args
354 * main arguments.
355 */
356 public static void main(String... args) {
357 options = new Options();
358 final CmdLineParser parser = new CmdLineParser(options);
359 try {
360 parser.parseArgument(args);
361 } catch (CmdLineException e) {
362
363 info(e.getMessage());
364 parser.setUsageWidth(80);
365 parser.printUsage(System.out);
366 System.exit(-1);
367 return;
368 }
369
370 if (options.isVersion()) {
371
372 info("Version is " + IdConst.VERSION);
373 System.exit(-1);
374 return;
375 }
376
377 if (options.isHelp()) {
378
379 parser.setUsageWidth(80);
380 parser.printUsage(System.out);
381 System.exit(-1);
382 return;
383 }
384
385 warning = 0;
386 uncompleted = 0;
387 error = 0;
388 long start = System.currentTimeMillis();
389 XmlTools.initHashMaps();
390 MToolKit.tbsName = options.getMdb();
391 String xmlFile = MToolKit.getFile(
392 IdConst.TBS_DIR + "/" + MToolKit.tbsName + ".xml", false)
393 .getAbsolutePath();
394 try {
395 if (options.isForce()) {
396 final File recycledDir = MToolKit.getTbsFile("recycled");
397 if (!recycledDir.exists() || !recycledDir.isDirectory()) {
398 recycledDir.mkdir();
399 }
400
401 parseRules(xmlFile, MToolKit.getTbsFile("recycled").getAbsolutePath(),
402 new FileOutputStream(MToolKit.getTbsFile(MToolKit.tbsName + ".mdb",
403 false)));
404 } else {
405
406 final File file = MToolKit.getFile(IdConst.TBS_DIR + "/"
407 + MToolKit.tbsName + "/" + MToolKit.tbsName + ".mdb");
408 final long lastModifiedMdb;
409 if (file == null) {
410 lastModifiedMdb = 0;
411 } else {
412 lastModifiedMdb = file.lastModified();
413 }
414 boolean update = false;
415
416 if (MToolKit.getFile(xmlFile).lastModified() > lastModifiedMdb) {
417
418 System.out.println("MDB is out of date, " + xmlFile + " is newer");
419 update = true;
420 } else {
421 final File fragmentDir = MToolKit.getTbsFile("");
422 for (File frament : fragmentDir
423 .listFiles((FilenameFilter) FileFilterUtils.andFileFilter(
424 FileFilterUtils.suffixFileFilter("xml"), FileFilterUtils
425 .prefixFileFilter("fragment-")))) {
426 if (frament.lastModified() > lastModifiedMdb) {
427
428 System.out
429 .println("MDB is out of date, at least one fragment found : "
430 + frament.getName());
431 update = true;
432 break;
433 }
434 }
435 if (!update) {
436
437 final File recycledDir = MToolKit.getTbsFile("recycled");
438 if (!recycledDir.exists() || !recycledDir.isDirectory()) {
439 recycledDir.mkdir();
440 }
441 if (recycledDir.lastModified() > lastModifiedMdb) {
442
443 System.out
444 .println("MDB is out of date, the recycled directory is new");
445 update = true;
446 } else {
447 for (File card : recycledDir
448 .listFiles((FilenameFilter) FileFilterUtils.andFileFilter(
449 FileFilterUtils.suffixFileFilter("xml"), FileFilterUtils
450 .notFileFilter(FileFilterUtils
451 .suffixFileFilter(IdConst.FILE_DATABASE_SAVED))))) {
452 if (card.lastModified() > lastModifiedMdb) {
453
454 System.out
455 .println("MDB is out of date, at least one new card found : "
456 + card);
457 update = true;
458 break;
459 }
460 }
461 }
462 }
463 }
464 if (!update) {
465 return;
466 }
467
468 parseRules(xmlFile, MToolKit.getTbsFile("recycled").getAbsolutePath(),
469 new FileOutputStream(MToolKit.getTbsFile(MToolKit.tbsName + ".mdb",
470 false)));
471 }
472 } catch (SAXParseException e) {
473
474 } catch (Exception e) {
475 e.printStackTrace();
476 }
477 if (warning > 0) {
478 System.out
479 .println("\t" + warning + " warning" + (warning > 1 ? "s" : ""));
480 }
481 if (error > 0) {
482 System.out.println("\t" + error + " error" + (error > 1 ? "s" : ""));
483 System.out.println("Some cards have not been built correctly. Fix them.");
484 } else {
485 System.out.println("\tSuccessfull build");
486 }
487 System.out.println("\tTime : " + (System.currentTimeMillis() - start)
488 / 1000 + " s");
489 }
490
491 /***
492 *
493 */
494 private static XmlParser parser;
495
496 /***
497 *
498 */
499 private XmlParser.Node config;
500
501 /***
502 * Exported properties
503 */
504 public static final List<PairStringInt> EXPORTED_PROPERTIES = new ArrayList<PairStringInt>();
505
506 /***
507 * Exported properties
508 */
509 public static final Map<Integer, String> PROPERTY_PICTURES = new HashMap<Integer, String>();
510
511 /***
512 * Exported types
513 */
514 public static final List<PairStringInt> EXPORTED_TYPES = new ArrayList<PairStringInt>();
515
516 /***
517 * Exported phases
518 */
519 public static final List<PairStringInt> EXPORTED_PHASES = new ArrayList<PairStringInt>();
520
521 /***
522 * Exported damage types
523 */
524 public static final List<PairStringInt> EXPORTED_DAMAGE_TYPES = new ArrayList<PairStringInt>();
525
526 /***
527 * Found errors
528 */
529 private static int error;
530
531 /***
532 * Found warnings
533 */
534 private static int warning;
535
536 /***
537 * Found uncompleted cards
538 */
539 static int uncompleted;
540
541 /***
542 * Indicates the debug data are saved in the MDB.
543 *
544 * @return true if the debug data are saved in the MDB.
545 */
546 public static boolean isDebugEnable() {
547 return true;
548 }
549
550 /***
551 * Return <code>true</code> if there is one or more errors.
552 *
553 * @return <code>true</code> if there is one or more errors.
554 */
555 public static boolean hasError() {
556 return error != 0;
557 }
558
559 /***
560 * Return options of builder.
561 *
562 * @return options.
563 */
564 public static Options getOptions() {
565 return options;
566 }
567
568 /***
569 * Return the no-Pay-Mana option.
570 *
571 * @return the no-Pay-Mana option.
572 */
573 public static boolean isNoPayMana() {
574 return options.isNoPayMana();
575 }
576 }