PageRenderTime 100ms CodeModel.GetById 30ms RepoModel.GetById 1ms app.codeStats 0ms

/build/distribution/IzPack/src/lib/com/izforge/izpack/compiler/Compiler.java

https://bitbucket.org/jorgenio/gvsig
Java | 1475 lines | 956 code | 151 blank | 368 comment | 168 complexity | ed57d4079f7e53151215d9fd977487f8 MD5 | raw file
Possible License(s): GPL-2.0, MPL-2.0-no-copyleft-exception
  1. /*
  2. * $Id: Compiler.java 5819 2006-06-14 07:29:09Z cesar $
  3. * IzPack
  4. * Copyright (C) 2001-2004 Julien Ponge
  5. *
  6. * File : Compiler.java
  7. * Description : The IzPack compiler.
  8. * Author's email : julien@izforge.com
  9. * Author's Website : http://www.izforge.com
  10. *
  11. * Portions are Copyright (c) 2001 Johannes Lehtinen
  12. * johannes.lehtinen@iki.fi
  13. * http://www.iki.fi/jle/
  14. *
  15. * Portions are Copyright (c) 2002 Paul Wilkinson
  16. * paulw@wilko.com
  17. *
  18. * Portions are Copyright (C) 2004 Gaganis Giorgos (geogka@it.teithe.gr)
  19. *
  20. * This program is free software; you can redistribute it and/or
  21. * modify it under the terms of the GNU General Public License
  22. * as published by the Free Software Foundation; either version 2
  23. * of the License, or any later version.
  24. *
  25. * This program is distributed in the hope that it will be useful,
  26. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. * GNU General Public License for more details.
  29. *
  30. * You should have received a copy of the GNU General Public License
  31. * along with this program; if not, write to the Free Software
  32. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  33. */
  34. package com.izforge.izpack.compiler;
  35. import java.io.BufferedInputStream;
  36. import java.io.BufferedOutputStream;
  37. import java.io.File;
  38. import java.io.FileInputStream;
  39. import java.io.FileNotFoundException;
  40. import java.io.FileOutputStream;
  41. import java.io.IOException;
  42. import java.io.InputStream;
  43. import java.net.MalformedURLException;
  44. import java.net.URL;
  45. import java.net.URLClassLoader;
  46. import java.util.*;
  47. import java.util.jar.JarInputStream;
  48. import java.util.zip.ZipEntry;
  49. import net.n3.nanoxml.NonValidator;
  50. import net.n3.nanoxml.StdXMLBuilder;
  51. import net.n3.nanoxml.StdXMLParser;
  52. import net.n3.nanoxml.StdXMLReader;
  53. import net.n3.nanoxml.XMLElement;
  54. import org.apache.tools.ant.DirectoryScanner;
  55. import com.izforge.izpack.CustomData;
  56. import com.izforge.izpack.ExecutableFile;
  57. import com.izforge.izpack.GUIPrefs;
  58. import com.izforge.izpack.Info;
  59. import com.izforge.izpack.PackFile;
  60. import com.izforge.izpack.Panel;
  61. import com.izforge.izpack.ParsableFile;
  62. import com.izforge.izpack.UpdateCheck;
  63. import com.izforge.izpack.event.CompilerListener;
  64. import com.izforge.izpack.installer.VariableSubstitutor;
  65. import com.izforge.izpack.util.Debug;
  66. import com.izforge.izpack.util.OsConstraint;
  67. /**
  68. * The IzPack compiler class.
  69. *
  70. * @author Julien Ponge
  71. * @author Tino Schwarze
  72. * @author Chadwick McHenry
  73. */
  74. public class Compiler extends Thread
  75. {
  76. /** The compiler version. */
  77. public final static String VERSION = "1.0";
  78. /** The IzPack version. */
  79. public final static String IZPACK_VERSION = "3.7.2 (build 2005.04.22)";
  80. /** Standard installer. */
  81. public final static String STANDARD = "standard";
  82. /** Web installer. */
  83. public final static String WEB = "web";
  84. /** The IzPack home directory. */
  85. public static String IZPACK_HOME = ".";
  86. /** Constant for checking attributes. */
  87. private static boolean YES = true;
  88. /** Constant for checking attributes. */
  89. private static boolean NO = false;
  90. /** The IzPack home directory specified or found on startup. */
  91. private static File home = new File (IZPACK_HOME);
  92. /** The XML filename. */
  93. protected String filename;
  94. /** The base directory. */
  95. protected String basedir;
  96. /** The installer kind. */
  97. protected String kind;
  98. /** The output jar filename. */
  99. protected String output;
  100. /** The packager listener. */
  101. protected PackagerListener packagerListener;
  102. /** List of CompilerListeners which should be called
  103. * at packaging */
  104. protected List compilerListeners;
  105. /** Collects and packs files into installation jars, as told. */
  106. private Packager packager = null;
  107. /** Error code, set to true if compilation succeeded. */
  108. private boolean compileFailed = true;
  109. /**
  110. * The constructor.
  111. *
  112. * @param filename The XML filename.
  113. * @param basedir The base directory.
  114. * @param kind The installer kind.
  115. * @param output The installer filename.
  116. */
  117. public Compiler(String filename, String basedir, String kind, String output)
  118. {
  119. // Default initialisation
  120. this.filename = filename;
  121. this.basedir = basedir;
  122. this.kind = kind;
  123. this.output = output;
  124. }
  125. /**
  126. * Sets the packager listener.
  127. *
  128. * @param listener The listener.
  129. */
  130. public void setPackagerListener(PackagerListener listener)
  131. {
  132. packagerListener = listener;
  133. }
  134. /** Compiles. */
  135. public void compile()
  136. {
  137. start();
  138. }
  139. /** The run() method. */
  140. public void run()
  141. {
  142. try
  143. {
  144. executeCompiler(); // Execute the compiler - may send info to System.out
  145. } catch (CompilerException ce)
  146. {
  147. System.out.println(ce.getMessage() + "\n");
  148. } catch (Exception e)
  149. {
  150. if (Debug.stackTracing())
  151. {
  152. e.printStackTrace();
  153. } else
  154. {
  155. System.out.println("ERROR: " + e.getMessage());
  156. }
  157. }
  158. }
  159. /**
  160. * Compiles the installation.
  161. *
  162. * @exception Exception Description of the Exception
  163. */
  164. public void executeCompiler() throws Exception
  165. {
  166. // normalize and test: TODO: may allow failure if we require write access
  167. File base = new File(basedir).getAbsoluteFile();
  168. if (!base.canRead() || !base.isDirectory())
  169. throw new CompilerException("Invalid base directory: " + base);
  170. // We get the XML data tree
  171. XMLElement data = getXMLTree();
  172. // We create the Packager
  173. packager = new Packager();
  174. packager.setPackagerListener(packagerListener);
  175. // We add all the information
  176. addCustomListeners(data);
  177. addVariables(data);
  178. addInfo(data);
  179. addGUIPrefs(data);
  180. addLangpacks(data);
  181. addResources(data);
  182. addNativeLibraries(data);
  183. addJars(data);
  184. addPanels(data);
  185. addPacks(data);
  186. // We ask the packager to create the installer
  187. packager.createInstaller(new File(output));
  188. this.compileFailed = false;
  189. }
  190. public boolean wasSuccessful()
  191. {
  192. return !this.compileFailed;
  193. }
  194. /**
  195. * Returns the GUIPrefs.
  196. *
  197. * @param data The XML data.
  198. * return The GUIPrefs.
  199. * @exception CompilerException Description of the Exception
  200. */
  201. protected void addGUIPrefs(XMLElement data) throws CompilerException
  202. {
  203. notifyCompilerListener("addGUIPrefs", CompilerListener.BEGIN, data);
  204. // We get the XMLElement & the attributes
  205. XMLElement gp = data.getFirstChildNamed("guiprefs");
  206. GUIPrefs prefs = new GUIPrefs();
  207. if (gp != null)
  208. {
  209. prefs.resizable = requireYesNoAttribute(gp, "resizable");
  210. prefs.width = requireIntAttribute(gp, "width");
  211. prefs.height = requireIntAttribute(gp, "height");
  212. // Look and feel mappings
  213. Iterator it = gp.getChildrenNamed("laf").iterator();
  214. while (it.hasNext())
  215. {
  216. XMLElement laf = (XMLElement)it.next();
  217. String lafName = requireAttribute(laf, "name");
  218. requireChildNamed(laf, "os");
  219. Iterator oit = laf.getChildrenNamed("os").iterator();
  220. while (oit.hasNext())
  221. {
  222. XMLElement os = (XMLElement)oit.next();
  223. String osName = requireAttribute(os, "family");
  224. prefs.lookAndFeelMapping.put(osName, lafName);
  225. }
  226. Iterator pit = laf.getChildrenNamed("param").iterator();
  227. Map params = new TreeMap();
  228. while (pit.hasNext())
  229. {
  230. XMLElement param = (XMLElement)pit.next();
  231. String name = requireAttribute(param, "name");
  232. String value = requireAttribute(param, "value");
  233. params.put(name, value);
  234. }
  235. prefs.lookAndFeelParams.put(lafName, params);
  236. }
  237. // Load modifier
  238. it = gp.getChildrenNamed("modifier").iterator();
  239. while (it.hasNext())
  240. {
  241. XMLElement curentModifier = (XMLElement)it.next();
  242. String key = requireAttribute(curentModifier, "key");
  243. String value = requireAttribute(curentModifier, "value");
  244. prefs.modifier.put(key, value);
  245. }
  246. // make sure jar contents of each are available in installer
  247. // map is easier to read/modify than if tree
  248. HashMap lafMap = new HashMap();
  249. lafMap.put("liquid", "liquidlnf.jar");
  250. lafMap.put("kunststoff","kunststoff.jar");
  251. lafMap.put("metouia", "metouia.jar");
  252. lafMap.put("looks", "looks.jar");
  253. // is this really what we want? a double loop? needed, since above, it's
  254. // the /last/ lnf for an os which is used, so can't add during initial
  255. // loop
  256. Iterator kit = prefs.lookAndFeelMapping.keySet().iterator();
  257. while (kit.hasNext())
  258. {
  259. String lafName = (String)prefs.lookAndFeelMapping.get(kit.next());
  260. String lafJarName = (String) lafMap.get(lafName);
  261. if (lafJarName == null)
  262. parseError(gp, "Unrecognized Look and Feel: " + lafName);
  263. URL lafJarURL = findIzPackResource("lib/" + lafJarName,
  264. "Look and Feel Jar file", gp);
  265. packager.addJarContent(lafJarURL);
  266. }
  267. }
  268. packager.setGUIPrefs(prefs);
  269. notifyCompilerListener("addGUIPrefs", CompilerListener.END, data);
  270. }
  271. /**
  272. * Add project specific external jar files to the installer.
  273. *
  274. * @param data The XML data.
  275. */
  276. protected void addJars(XMLElement data) throws Exception
  277. {
  278. notifyCompilerListener("addJars", CompilerListener.BEGIN, data);
  279. Iterator iter = data.getChildrenNamed("jar").iterator();
  280. while (iter.hasNext())
  281. {
  282. XMLElement el = (XMLElement) iter.next();
  283. String src = requireAttribute(el, "src");
  284. URL url = findProjectResource(src, "Jar file", el);
  285. packager.addJarContent(url);
  286. // Additionals for mark a jar file also used in the uninstaller.
  287. // The contained files will be copied from the installer into the
  288. // uninstaller if needed.
  289. // Therefore the contained files of the jar should be in the installer also
  290. // they are used only from the uninstaller. This is the reason why the stage
  291. // wiil be only observed for the uninstaller.
  292. String stage = el.getAttribute("stage");
  293. if( stage != null &&
  294. ( stage.equalsIgnoreCase("both") || stage.equalsIgnoreCase("uninstall")))
  295. {
  296. CustomData ca = new CustomData( null,
  297. getContainedFilePaths(url), null, CustomData.UNINSTALLER_JAR );
  298. packager.addCustomJar( ca, url);
  299. }
  300. }
  301. notifyCompilerListener("addJars", CompilerListener.END, data);
  302. }
  303. /**
  304. * Add native libraries to the installer.
  305. *
  306. * @param data The XML data.
  307. */
  308. protected void addNativeLibraries(XMLElement data) throws Exception
  309. {
  310. boolean needAddOns = false;
  311. notifyCompilerListener("addNativeLibraries", CompilerListener.BEGIN, data);
  312. Iterator iter = data.getChildrenNamed("native").iterator();
  313. while (iter.hasNext())
  314. {
  315. XMLElement el = (XMLElement) iter.next();
  316. String type = requireAttribute(el, "type");
  317. String name = requireAttribute(el, "name");
  318. String path = "bin/native/" + type + "/" + name;
  319. URL url = findIzPackResource(path, "Native Library", el);
  320. packager.addNativeLibrary(name, url);
  321. // Additionals for mark a native lib also used in the uninstaller
  322. // The lib will be copied from the installer into the uninstaller if needed.
  323. // Therefore the lib should be in the installer also it is used only from
  324. // the uninstaller. This is the reason why the stage wiil be only observed
  325. // for the uninstaller.
  326. String stage = el.getAttribute("stage");
  327. List constraints = OsConstraint.getOsList(el);
  328. if( stage != null &&
  329. ( stage.equalsIgnoreCase("both") || stage.equalsIgnoreCase("uninstall")))
  330. {
  331. ArrayList al = new ArrayList();
  332. al.add(name);
  333. CustomData cad = new CustomData(null,al, constraints, CustomData.UNINSTALLER_LIB);
  334. packager.addNativeUninstallerLibrary(cad);
  335. needAddOns = true;
  336. }
  337. }
  338. if( needAddOns )
  339. {
  340. // Add the uninstaller extensions as a resource if specified
  341. XMLElement root = requireChildNamed(data, "info");
  342. XMLElement uninstallInfo = root.getFirstChildNamed("uninstaller");
  343. if (validateYesNoAttribute(uninstallInfo, "write", YES))
  344. {
  345. URL url = findIzPackResource("lib/uninstaller-ext.jar", "Uninstaller extensions", root);
  346. packager.addResource("IzPack.uninstaller-ext", url);
  347. }
  348. }
  349. notifyCompilerListener("addNativeLibraries", CompilerListener.END, data);
  350. }
  351. /**
  352. * Add packs and their contents to the installer.
  353. *
  354. * @param data The XML data.
  355. */
  356. protected void addPacks(XMLElement data) throws CompilerException
  357. {
  358. notifyCompilerListener("addPacks", CompilerListener.BEGIN, data);
  359. // Initialisation
  360. XMLElement root = requireChildNamed(data, "packs");
  361. // at least one pack is required
  362. Vector packElements = root.getChildrenNamed("pack");
  363. if (packElements.isEmpty())
  364. parseError(root, "<packs> requires a <pack>");
  365. Iterator packIter = packElements.iterator();
  366. while (packIter.hasNext())
  367. {
  368. XMLElement el = (XMLElement) packIter.next();
  369. // Trivial initialisations
  370. String name = requireAttribute(el, "name");
  371. String id = el.getAttribute("id");
  372. boolean loose = "true".equalsIgnoreCase(el.getAttribute("loose", "false"));
  373. String description = requireChildNamed(el, "description").getContent();
  374. boolean required = requireYesNoAttribute(el, "required");
  375. PackInfo pack = new PackInfo(name, id, description, required, loose);
  376. pack.setOsConstraints(OsConstraint.getOsList(el)); // TODO: unverified
  377. pack.setPreselected(validateYesNoAttribute(el, "preselected", YES));
  378. // We get the parsables list
  379. Iterator iter = el.getChildrenNamed("parsable").iterator();
  380. while (iter.hasNext())
  381. {
  382. XMLElement p = (XMLElement) iter.next();
  383. String target = requireAttribute(p, "targetfile");
  384. String type = p.getAttribute("type", "plain");
  385. String encoding = p.getAttribute("encoding", null);
  386. List osList = OsConstraint.getOsList(p); // TODO: unverified
  387. pack.addParsable(new ParsableFile(target, type, encoding, osList));
  388. }
  389. // We get the executables list
  390. iter = el.getChildrenNamed("executable").iterator();
  391. while (iter.hasNext())
  392. {
  393. XMLElement e = (XMLElement) iter.next();
  394. ExecutableFile executable = new ExecutableFile();
  395. String val; // temp value
  396. executable.path = requireAttribute(e, "targetfile");
  397. // when to execute this executable
  398. val = e.getAttribute("stage", "never");
  399. if ("postinstall".equalsIgnoreCase(val))
  400. executable.executionStage = ExecutableFile.POSTINSTALL;
  401. else if ("uninstall".equalsIgnoreCase(val))
  402. executable.executionStage = ExecutableFile.UNINSTALL;
  403. // type of this executable
  404. val = e.getAttribute("type", "bin");
  405. if ("jar".equalsIgnoreCase(val))
  406. {
  407. executable.type = ExecutableFile.JAR;
  408. executable.mainClass = e.getAttribute("class"); // executable class
  409. }
  410. // what to do if execution fails
  411. val = e.getAttribute("failure", "ask");
  412. if ("abort".equalsIgnoreCase(val))
  413. executable.onFailure = ExecutableFile.ABORT;
  414. else if ("warn".equalsIgnoreCase(val))
  415. executable.onFailure = ExecutableFile.WARN;
  416. // whether to keep the executable after executing it
  417. val = e.getAttribute("keep");
  418. executable.keepFile = "true".equalsIgnoreCase(val);
  419. // get arguments for this executable
  420. XMLElement args = e.getFirstChildNamed("args");
  421. if (null != args)
  422. {
  423. Iterator argIterator = args.getChildrenNamed("arg").iterator();
  424. while (argIterator.hasNext())
  425. {
  426. XMLElement arg = (XMLElement) argIterator.next();
  427. executable.argList.add(requireAttribute(arg, "value"));
  428. }
  429. }
  430. executable.osList = OsConstraint.getOsList(e); // TODO: unverified
  431. pack.addExecutable(executable);
  432. }
  433. // We get the files list
  434. iter = el.getChildrenNamed("file").iterator();
  435. while (iter.hasNext())
  436. {
  437. XMLElement f = (XMLElement) iter.next();
  438. String src = requireAttribute(f, "src");
  439. String targetdir = requireAttribute(f, "targetdir");
  440. List osList = OsConstraint.getOsList(f); // TODO: unverified
  441. int override = getOverrideValue(f);
  442. Map additionals = getAdditionals(f);
  443. File file = new File(src);
  444. if (! file.isAbsolute())
  445. file = new File(basedir, src);
  446. try
  447. {
  448. addRecursively(file, targetdir, osList, override, pack, additionals);
  449. } catch (Exception x)
  450. {
  451. parseError(f, x.getMessage(), x);
  452. }
  453. }
  454. // We get the singlefiles list
  455. iter = el.getChildrenNamed("singlefile").iterator();
  456. while (iter.hasNext())
  457. {
  458. XMLElement f = (XMLElement) iter.next();
  459. String src = requireAttribute(f, "src");
  460. String target = requireAttribute(f, "target");
  461. List osList = OsConstraint.getOsList(f); // TODO: unverified
  462. int override = getOverrideValue(f);
  463. Map additionals = getAdditionals(f);
  464. File file = new File(src);
  465. if (! file.isAbsolute())
  466. file = new File(basedir, src);
  467. try
  468. {
  469. pack.addFile(file, target, osList, override, additionals);
  470. } catch (FileNotFoundException x)
  471. {
  472. parseError(f, x.getMessage(), x);
  473. }
  474. }
  475. // We get the fileset list
  476. iter = el.getChildrenNamed("fileset").iterator();
  477. while (iter.hasNext())
  478. {
  479. XMLElement f = (XMLElement) iter.next();
  480. String dir_attr = requireAttribute(f, "dir");
  481. File dir = new File(dir_attr);
  482. if (! dir.isAbsolute())
  483. dir = new File(basedir, dir_attr);
  484. if (! dir.isDirectory()) // also tests '.exists()'
  485. parseError(f, "Invalid directory 'dir': " + dir_attr);
  486. boolean casesensitive = validateYesNoAttribute(f, "casesensitive", YES);
  487. boolean defexcludes = validateYesNoAttribute(f, "defaultexcludes", YES);
  488. String targetdir = requireAttribute(f, "targetdir");
  489. List osList = OsConstraint.getOsList(f); // TODO: unverified
  490. int override = getOverrideValue(f);
  491. Map additionals = getAdditionals(f);
  492. // get includes and excludes
  493. Vector xcludesList = null;
  494. String[] includes = null;
  495. xcludesList = f.getChildrenNamed("include");
  496. if (! xcludesList.isEmpty())
  497. {
  498. includes = new String[xcludesList.size()];
  499. for (int j = 0; j < xcludesList.size(); j++)
  500. {
  501. XMLElement xclude = (XMLElement) xcludesList.get(j);
  502. includes[j] = requireAttribute(xclude, "name");
  503. }
  504. }
  505. String[] excludes = null;
  506. xcludesList = f.getChildrenNamed("exclude");
  507. if (! xcludesList.isEmpty())
  508. {
  509. excludes = new String[xcludesList.size()];
  510. for (int j = 0; j < xcludesList.size(); j++)
  511. {
  512. XMLElement xclude = (XMLElement) xcludesList.get(j);
  513. excludes[j] = requireAttribute(xclude, "name");
  514. }
  515. }
  516. // parse additional fileset attributes "includes" and "excludes"
  517. String [] toDo = new String[] {"includes", "excludes" };
  518. // use the existing containers filled from include and exclude
  519. // and add the includes and excludes to it
  520. String [][] containers = new String[][] { includes, excludes };
  521. for( int j = 0; j < toDo.length; ++j )
  522. {
  523. String inex = f.getAttribute(toDo[j]);
  524. if( inex != null && inex.length() > 0 )
  525. { // This is the same "splitting" as ant PatternSet do ...
  526. StringTokenizer tok = new StringTokenizer(inex, ", ", false);
  527. int newSize = tok.countTokens();
  528. int k = 0;
  529. String [] nCont = null;
  530. if(containers[j] != null && containers[j].length > 0 )
  531. { // old container exist; create a new which can hold all values
  532. // and copy the old stuff to the front
  533. newSize += containers[j].length;
  534. nCont = new String[newSize];
  535. for(; k < containers[j].length; ++k)
  536. nCont[k] = containers[j][k];
  537. }
  538. if( nCont == null ) // No container for old values created,
  539. // create a new one.
  540. nCont = new String[newSize];
  541. for( ; k < newSize; ++k)
  542. // Fill the new one or expand the existent container
  543. nCont[k] = tok.nextToken();
  544. containers[j] = nCont;
  545. }
  546. }
  547. includes = containers[0]; // push the new includes to the local var
  548. excludes = containers[1]; // push the new excludes to the local var
  549. // scan and add fileset
  550. DirectoryScanner ds = new DirectoryScanner();
  551. ds.setIncludes(includes);
  552. ds.setExcludes(excludes);
  553. if (defexcludes)
  554. ds.addDefaultExcludes();
  555. ds.setBasedir(dir);
  556. ds.setCaseSensitive(casesensitive);
  557. ds.scan();
  558. String[] files = ds.getIncludedFiles();
  559. String[] dirs = ds.getIncludedDirectories();
  560. // Directory scanner has done recursion, add files and directories
  561. for (int i = 0; i < files.length; ++i)
  562. {
  563. try
  564. {
  565. String target = new File(targetdir, files[i]).getPath();
  566. pack.addFile(new File(dir, files[i]), target, osList,
  567. override, additionals);
  568. } catch (FileNotFoundException x)
  569. {
  570. parseError(f, x.getMessage(), x);
  571. }
  572. }
  573. for (int i = 0; i < dirs.length; ++i)
  574. {
  575. try
  576. {
  577. String target = new File(targetdir, dirs[i]).getPath();
  578. pack.addFile(new File(dir, dirs[i]), target, osList,
  579. override, additionals);
  580. } catch (FileNotFoundException x)
  581. {
  582. parseError(f, x.getMessage(), x);
  583. }
  584. }
  585. }
  586. // get the updatechecks list
  587. iter = el.getChildrenNamed("updatecheck").iterator();
  588. while (iter.hasNext())
  589. {
  590. XMLElement f = (XMLElement) iter.next();
  591. String casesensitive = f.getAttribute("casesensitive");
  592. // get includes and excludes
  593. ArrayList includesList = new ArrayList();
  594. ArrayList excludesList = new ArrayList();
  595. // get includes and excludes
  596. Iterator include_it = f.getChildrenNamed("include").iterator();
  597. while (include_it.hasNext())
  598. {
  599. XMLElement inc_el = (XMLElement) include_it.next();
  600. includesList.add(requireAttribute(inc_el, "name"));
  601. }
  602. Iterator exclude_it = f.getChildrenNamed("exclude").iterator();
  603. while (exclude_it.hasNext())
  604. {
  605. XMLElement excl_el = (XMLElement) exclude_it.next();
  606. excludesList.add(requireAttribute(excl_el, "name"));
  607. }
  608. pack.addUpdateCheck(
  609. new UpdateCheck(includesList, excludesList, casesensitive));
  610. }
  611. //We get the dependencies
  612. iter = el.getChildrenNamed("depends").iterator();
  613. while (iter.hasNext())
  614. {
  615. XMLElement dep = (XMLElement) iter.next();
  616. String depName = requireAttribute(dep,"packname");
  617. pack.addDependency(depName);
  618. }
  619. // We add the pack
  620. packager.addPack(pack);
  621. }
  622. checkDependencies(packager.getPacksList());
  623. notifyCompilerListener("addPacks", CompilerListener.END, data);
  624. }
  625. /**
  626. * Checks whether the dependencies stated in the configuration file are
  627. * correct. Specifically it checks that no pack point to a non existent
  628. * pack and also that there are no circular dependencies in the packs.
  629. */
  630. public void checkDependencies(List packs) throws CompilerException
  631. {
  632. // Because we use package names in the configuration file we assosiate
  633. // the names with the objects
  634. Map names = new HashMap();
  635. for (int i = 0; i < packs.size(); i++)
  636. {
  637. PackInfo pack = (PackInfo) packs.get(i);
  638. names.put(pack.getPack().name,pack);
  639. }
  640. int result = dfs(packs,names);
  641. //@todo More informative messages to include the source of the error
  642. if(result == -2)
  643. parseError("Circular dependency detected");
  644. else if(result == -1)
  645. parseError("A dependency doesn't exist");
  646. }
  647. /** We use the dfs graph search algorithm to check whether the graph
  648. * is acyclic as described in:
  649. * Thomas H. Cormen, Charles Leiserson, Ronald Rivest and Clifford Stein. Introduction
  650. * to algorithms 2nd Edition 540-549,MIT Press, 2001
  651. * @param packs The graph
  652. * @param names The name map
  653. * @return
  654. */
  655. private int dfs(List packs,Map names)
  656. {
  657. Map edges = new HashMap();
  658. for (int i = 0; i < packs.size(); i++)
  659. {
  660. PackInfo pack = (PackInfo) packs.get(i);
  661. if(pack.colour == PackInfo.WHITE)
  662. {
  663. if(dfsVisit(pack,names,edges)!=0)
  664. return -1;
  665. }
  666. }
  667. return checkBackEdges(edges);
  668. }
  669. /**
  670. * This function checks for the existence of back edges.
  671. */
  672. private int checkBackEdges(Map edges)
  673. {
  674. Set keys = edges.keySet();
  675. for (Iterator iterator = keys.iterator(); iterator.hasNext();)
  676. {
  677. final Object key = iterator.next();
  678. int color = ((Integer) edges.get(key)).intValue();
  679. if(color == PackInfo.GREY)
  680. {
  681. return -2;
  682. }
  683. }
  684. return 0;
  685. }
  686. /**
  687. * This class is used for the classification of the edges
  688. */
  689. private class Edge
  690. {
  691. PackInfo u;
  692. PackInfo v;
  693. Edge(PackInfo u,PackInfo v)
  694. {
  695. this.u = u;
  696. this.v = v;
  697. }
  698. }
  699. private int dfsVisit(PackInfo u,Map names,Map edges)
  700. {
  701. u.colour = PackInfo.GREY;
  702. List deps = u.getDependencies();
  703. if (deps != null)
  704. {
  705. for (int i = 0; i < deps.size(); i++)
  706. {
  707. String name = (String) deps.get(i);
  708. PackInfo v = (PackInfo)names.get(name);
  709. if(v == null)
  710. return -1;
  711. Edge edge = new Edge(u,v);
  712. if(edges.get(edge) == null)
  713. edges.put(edge,new Integer(v.colour));
  714. if(v.colour == PackInfo.WHITE)
  715. {
  716. final int result = dfsVisit(v,names,edges);
  717. if(result != 0)
  718. return result;
  719. }
  720. }
  721. }
  722. u.colour = PackInfo.BLACK;
  723. return 0;
  724. }
  725. /**
  726. * Recursive method to add files in a pack.
  727. *
  728. * @param file The file to add.
  729. * @param targetdir The relative path to the parent.
  730. * @param osList The target OS constraints.
  731. * @param override Overriding behaviour.
  732. * @param pack Pack to be packed into
  733. * @param additionals Map which contains additional data
  734. * @exception FileNotFoundException if the file does not exist
  735. */
  736. protected void addRecursively(File file, String targetdir,
  737. List osList, int override,
  738. PackInfo pack, Map additionals)
  739. throws IOException
  740. {
  741. String targetfile = targetdir + "/" + file.getName();
  742. if (! file.isDirectory())
  743. pack.addFile(file, targetfile, osList, override, additionals);
  744. else
  745. {
  746. File[] files = file.listFiles();
  747. if (files.length == 0) // The directory is empty so must be added
  748. pack.addFile(file, targetfile, osList, override, additionals);
  749. else
  750. {
  751. // new targetdir = targetfile;
  752. for (int i = 0; i < files.length; i++)
  753. addRecursively(files[i], targetfile, osList, override,
  754. pack, additionals);
  755. }
  756. }
  757. }
  758. /**
  759. * Parse panels and their paramters, locate the panels resources and add to
  760. * the Packager.
  761. *
  762. * @param data The XML data.
  763. * @exception CompilerException Description of the Exception
  764. */
  765. protected void addPanels(XMLElement data) throws CompilerException
  766. {
  767. notifyCompilerListener("addPanels", CompilerListener.BEGIN, data);
  768. XMLElement root = requireChildNamed(data, "panels");
  769. // at least one panel is required
  770. Vector panels = root.getChildrenNamed("panel");
  771. if (panels.isEmpty())
  772. parseError(root, "<panels> requires a <panel>");
  773. // We process each panel markup
  774. Iterator iter = panels.iterator();
  775. while (iter.hasNext())
  776. {
  777. XMLElement xmlPanel = (XMLElement) iter.next();
  778. // create the serialized Panel data
  779. Panel panel = new Panel();
  780. panel.osConstraints = OsConstraint.getOsList(xmlPanel);
  781. String className = xmlPanel.getAttribute("classname");
  782. // Panel files come in jars packaged w/ IzPack
  783. String jarPath = "bin/panels/" + className + ".jar";
  784. URL url = findIzPackResource(jarPath, "Panel jar file", xmlPanel);
  785. String fullClassName = null;
  786. try
  787. {
  788. fullClassName = getFullClassName(url, className);
  789. }
  790. catch (Exception e)
  791. {
  792. ;
  793. }
  794. if( fullClassName != null)
  795. panel.className = fullClassName;
  796. else
  797. panel.className = className;
  798. // insert into the packager
  799. packager.addPanelJar(panel, url);
  800. }
  801. notifyCompilerListener("addPanels", CompilerListener.END, data);
  802. }
  803. /**
  804. * Adds the resources.
  805. *
  806. * @param data The XML data.
  807. * @exception CompilerException Description of the Exception
  808. */
  809. protected void addResources(XMLElement data) throws CompilerException
  810. {
  811. notifyCompilerListener("addResources", CompilerListener.BEGIN, data);
  812. XMLElement root = data.getFirstChildNamed("resources");
  813. if (root == null)
  814. return;
  815. // We process each res markup
  816. Iterator iter = root.getChildrenNamed("res").iterator();
  817. while (iter.hasNext())
  818. {
  819. XMLElement res = (XMLElement) iter.next();
  820. String id = requireAttribute(res, "id");
  821. String src = requireAttribute(res, "src");
  822. boolean parse = validateYesNoAttribute(res, "parse", NO);
  823. // basedir is not prepended if src is already an absolute path
  824. URL url = findProjectResource(src, "Resource", res);
  825. // substitute variable values in the resource if parsed
  826. if (parse)
  827. {
  828. if (packager.getVariables().isEmpty())
  829. {
  830. parseWarn(res, "No variables defined. " +
  831. url.getPath() + " not parsed.");
  832. } else
  833. {
  834. String type = res.getAttribute("type");
  835. String encoding = res.getAttribute("encoding");
  836. File parsedFile = null;
  837. try
  838. {
  839. // make the substitutions into a temp file
  840. InputStream bin = new BufferedInputStream(url.openStream());
  841. parsedFile = File.createTempFile("izpp", null);
  842. parsedFile.deleteOnExit();
  843. FileOutputStream outFile = new FileOutputStream(parsedFile);
  844. BufferedOutputStream bout = new BufferedOutputStream(outFile);
  845. VariableSubstitutor vs = new VariableSubstitutor(packager.getVariables());
  846. vs.substitute(bin, bout, type, encoding);
  847. bin.close();
  848. bout.close();
  849. // and specify the substituted file to be added to the packager
  850. url = parsedFile.toURL();
  851. } catch (IOException x)
  852. {
  853. parseError(res, x.getMessage(), x);
  854. }
  855. }
  856. }
  857. packager.addResource(id, url);
  858. }
  859. notifyCompilerListener("addResources", CompilerListener.END, data);
  860. }
  861. /**
  862. * Adds the ISO3 codes of the langpacks and associated resources.
  863. *
  864. * @param data The XML data.
  865. * @exception CompilerException Description of the Exception
  866. */
  867. protected void addLangpacks(XMLElement data)
  868. throws CompilerException
  869. {
  870. notifyCompilerListener("addLangpacks", CompilerListener.BEGIN, data);
  871. XMLElement root = requireChildNamed(data, "locale");
  872. // at least one langpack is required
  873. Vector locals = root.getChildrenNamed("langpack");
  874. if (locals.isEmpty())
  875. parseError(root, "<locale> requires a <langpack>");
  876. // We process each langpack markup
  877. Iterator iter = locals.iterator();
  878. while (iter.hasNext())
  879. {
  880. XMLElement el = (XMLElement) iter.next();
  881. String iso3 = requireAttribute(el, "iso3");
  882. String path;
  883. path = "bin/langpacks/installer/" + iso3 + ".xml";
  884. URL iso3xmlURL = findIzPackResource(path, "ISO3 file", el);
  885. path = "bin/langpacks/flags/" + iso3 + ".gif";
  886. URL iso3FlagURL = findIzPackResource(path, "ISO3 flag image", el);
  887. packager.addLangPack(iso3, iso3xmlURL, iso3FlagURL);
  888. }
  889. notifyCompilerListener("addLangpacks", CompilerListener.END, data);
  890. }
  891. /**
  892. * Builds the Info class from the XML tree.
  893. *
  894. * @param data The XML data.
  895. * return The Info.
  896. * @exception Exception Description of the Exception
  897. */
  898. protected void addInfo(XMLElement data) throws Exception
  899. {
  900. notifyCompilerListener("addInfo", CompilerListener.BEGIN, data);
  901. // Initialisation
  902. XMLElement root = requireChildNamed(data, "info");
  903. Info info = new Info();
  904. String temp = null;
  905. info.setAppName(requireContent(requireChildNamed(root, "appname")));
  906. info.setAppVersion(requireContent(requireChildNamed(root, "appversion")));
  907. // We get the installation subpath
  908. XMLElement subpath = root.getFirstChildNamed("appsubpath");
  909. if(subpath != null)
  910. {
  911. info.setInstallationSubPath(requireContent(subpath));
  912. }
  913. // validate and insert app URL
  914. final XMLElement URLElem = root.getFirstChildNamed("url");
  915. if(URLElem != null)
  916. {
  917. URL appURL = requireURLContent(URLElem);
  918. info.setAppURL(appURL.toString());
  919. }
  920. // We get the authors list
  921. XMLElement authors = root.getFirstChildNamed("authors");
  922. if (authors != null)
  923. {
  924. Iterator iter = authors.getChildrenNamed("author").iterator();
  925. while (iter.hasNext())
  926. {
  927. XMLElement author = (XMLElement) iter.next();
  928. String name = requireAttribute(author, "name");
  929. String email = requireAttribute(author, "email");
  930. info.addAuthor(new Info.Author(name, email));
  931. }
  932. }
  933. // We get the java version required
  934. XMLElement javaVersion = root.getFirstChildNamed("javaversion");
  935. if (javaVersion != null)
  936. info.setJavaVersion(requireContent(javaVersion));
  937. // validate and insert (and require if -web kind) web dir
  938. XMLElement webDirURL = root.getFirstChildNamed("webdir");
  939. if (webDirURL != null)
  940. info.setWebDirURL(requireURLContent(webDirURL).toString());
  941. if (kind != null)
  942. {
  943. if (kind.equalsIgnoreCase(WEB) && webDirURL == null)
  944. {
  945. parseError(root, "<webdir> required when \"WEB\" installer requested");
  946. }
  947. else if (kind.equalsIgnoreCase(STANDARD) && webDirURL != null)
  948. {
  949. // Need a Warning? parseWarn(webDirURL, "Not creating web installer.");
  950. info.setWebDirURL(null);
  951. }
  952. }
  953. // Add the uninstaller as a resource if specified
  954. XMLElement uninstallInfo = root.getFirstChildNamed("uninstaller");
  955. if (validateYesNoAttribute(uninstallInfo, "write", YES))
  956. {
  957. URL url = findIzPackResource("lib/uninstaller.jar", "Uninstaller", root);
  958. packager.addResource("IzPack.uninstaller", url);
  959. }
  960. packager.setInfo(info);
  961. notifyCompilerListener("addInfo", CompilerListener.END, data);
  962. }
  963. /**
  964. * Variable declaration is a fragment of the xml file. For example:
  965. * <pre>
  966. * &lt;variables&gt;
  967. * &lt;variable name="nom" value="value"/&gt;
  968. * &lt;variable name="foo" value="pippo"/&gt;
  969. * &lt;/variables&gt;
  970. * </pre>
  971. * variable declared in this can be referred to in parsable files.
  972. *
  973. * @param data The XML data.
  974. * @exception CompilerException Description of the Exception
  975. */
  976. protected void addVariables(XMLElement data) throws CompilerException
  977. {
  978. notifyCompilerListener("addVariables", CompilerListener.BEGIN, data);
  979. // We get the varible list
  980. XMLElement root = data.getFirstChildNamed("variables");
  981. if (root == null)
  982. return;
  983. Properties variables = packager.getVariables();
  984. Iterator iter = root.getChildrenNamed("variable").iterator();
  985. while (iter.hasNext())
  986. {
  987. XMLElement var = (XMLElement) iter.next();
  988. String name = requireAttribute(var, "name");
  989. String value = requireAttribute(var, "value");
  990. if (variables.contains(name))
  991. parseWarn(var, "Variable '" + name + "' being overwritten");
  992. variables.setProperty(name, value);
  993. }
  994. notifyCompilerListener("addVariables", CompilerListener.END, data);
  995. }
  996. /**
  997. * Returns the XMLElement representing the installation XML file.
  998. *
  999. * @return The XML tree.
  1000. * @exception CompilerException For problems with the installation file
  1001. * @exception IOException for errors reading the installation file
  1002. */
  1003. protected XMLElement getXMLTree() throws CompilerException, IOException
  1004. {
  1005. // Initialises the parser
  1006. StdXMLParser parser = new StdXMLParser();
  1007. parser.setBuilder(new StdXMLBuilder());
  1008. parser.setReader(new StdXMLReader(new FileInputStream(filename)));
  1009. parser.setValidator(new NonValidator());
  1010. // We get it
  1011. XMLElement data = null;
  1012. try
  1013. {
  1014. data = (XMLElement) parser.parse();
  1015. } catch (Exception x)
  1016. {
  1017. throw new CompilerException("Error parsing installation file", x);
  1018. }
  1019. // We check it
  1020. if (!"installation".equalsIgnoreCase(data.getName()))
  1021. parseError(data, "this is not an IzPack XML installation file");
  1022. if (!requireAttribute(data, "version").equalsIgnoreCase(VERSION))
  1023. parseError(
  1024. data,
  1025. "the file version is different from the compiler version");
  1026. // We finally return the tree
  1027. return data;
  1028. }
  1029. protected int getOverrideValue(XMLElement f)
  1030. throws CompilerException
  1031. {
  1032. int override = PackFile.OVERRIDE_UPDATE;
  1033. String override_val = f.getAttribute("override");
  1034. if (override_val != null)
  1035. {
  1036. if (override_val.equalsIgnoreCase("true"))
  1037. {
  1038. override = PackFile.OVERRIDE_TRUE;
  1039. } else if (override_val.equalsIgnoreCase("false"))
  1040. {
  1041. override = PackFile.OVERRIDE_FALSE;
  1042. } else if (override_val.equalsIgnoreCase("asktrue"))
  1043. {
  1044. override = PackFile.OVERRIDE_ASK_TRUE;
  1045. } else if (override_val.equalsIgnoreCase("askfalse"))
  1046. {
  1047. override = PackFile.OVERRIDE_ASK_FALSE;
  1048. } else if (override_val.equalsIgnoreCase("update"))
  1049. {
  1050. override = PackFile.OVERRIDE_UPDATE;
  1051. }
  1052. else
  1053. parseError(f, "invalid value for attribute \"override\"");
  1054. }
  1055. return override;
  1056. }
  1057. /**
  1058. * Look for a project specified resources, which, if not absolute, are sought
  1059. * relative to the projects basedir. The path should use '/' as the
  1060. * fileSeparator. If the resource is not found, a CompilerException is thrown
  1061. * indicating fault in the parent element.
  1062. *
  1063. * @param path the relative path (using '/' as separator) to the resource.
  1064. * @param desc the description of the resource used to report errors
  1065. * @param parent the XMLElement the resource is specified in, used to
  1066. * report errors
  1067. * @return a URL to the resource.
  1068. */
  1069. private URL findProjectResource(String path, String desc, XMLElement parent)
  1070. throws CompilerException
  1071. {
  1072. URL url = null;
  1073. File resource = new File(path);
  1074. if (! resource.isAbsolute())
  1075. resource = new File(basedir, path);
  1076. if (! resource.exists()) // fatal
  1077. parseError(parent, desc + " not found: " + resource);
  1078. try
  1079. {
  1080. url = resource.toURL();
  1081. } catch(MalformedURLException how)
  1082. {
  1083. parseError(parent, desc + "(" + resource + ")", how);
  1084. }
  1085. return url;
  1086. }
  1087. /**
  1088. * Look for an IzPack resource either in the compiler jar, or within
  1089. * IZPACK_HOME. The path must not be absolute. The path must use '/' as the
  1090. * fileSeparator (it's used to access the jar file). If the resource is not
  1091. * found, a CompilerException is thrown indicating fault in the parent
  1092. * element.
  1093. *
  1094. * @param path the relative path (using '/' as separator) to the resource.
  1095. * @param desc the description of the resource used to report errors
  1096. * @param parent the XMLElement the resource is specified in, used to
  1097. * report errors
  1098. * @return a URL to the resource.
  1099. */
  1100. private URL findIzPackResource(String path, String desc, XMLElement parent)
  1101. throws CompilerException
  1102. {
  1103. URL url = getClass().getResource("/" + path);
  1104. if (url == null)
  1105. {
  1106. File resource = new File(path);
  1107. if (! resource.isAbsolute())
  1108. resource = new File(IZPACK_HOME, path);
  1109. if (! resource.exists()) // fatal
  1110. parseError(parent, desc + " not found: " + resource);
  1111. try
  1112. {
  1113. url = resource.toURL();
  1114. } catch(MalformedURLException how)
  1115. {
  1116. parseError(parent, desc + "(" + resource + ")", how);
  1117. }
  1118. }
  1119. return url;
  1120. }
  1121. /**
  1122. * Create parse error with consistent messages. Includes file name. For use
  1123. * When parent is unknown.
  1124. *
  1125. * @param message Brief message explaining error
  1126. */
  1127. protected void parseError(String message)
  1128. throws CompilerException
  1129. {
  1130. this.compileFailed = true;
  1131. throw new CompilerException(
  1132. filename + ":" + message);
  1133. }
  1134. /**
  1135. * Create parse error with consistent messages. Includes file name and line #
  1136. * of parent. It is an error for 'parent' to be null.
  1137. *
  1138. * @param parent The element in which the error occured
  1139. * @param message Brief message explaining error
  1140. */
  1141. protected void parseError(XMLElement parent, String message)
  1142. throws CompilerException
  1143. {
  1144. this.compileFailed = true;
  1145. throw new CompilerException(
  1146. filename + ":" + parent.getLineNr() + ": " + message);
  1147. }
  1148. /**
  1149. * Create a chained parse error with consistent messages. Includes file name
  1150. * and line # of parent. It is an error for 'parent' to be null.
  1151. *
  1152. * @param parent The element in which the error occured
  1153. * @param message Brief message explaining error
  1154. */
  1155. protected void parseError(XMLElement parent, String message, Throwable cause)
  1156. throws CompilerException
  1157. {
  1158. this.compileFailed = true;
  1159. throw new CompilerException(
  1160. filename + ":" + parent.getLineNr() + ": " + message,
  1161. cause);
  1162. }
  1163. /**
  1164. * Create a parse warning with consistent messages. Includes file name
  1165. * and line # of parent. It is an error for 'parent' to be null.
  1166. *
  1167. * @param parent The element in which the warning occured
  1168. * @param message Warning message
  1169. */
  1170. protected void parseWarn(XMLElement parent, String message)
  1171. {
  1172. System.out.println(filename + ":" + parent.getLineNr() + ": " + message);
  1173. }
  1174. /**
  1175. * Call getFirstChildNamed on the parent, producing a meaningful error
  1176. * message on failure. It is an error for 'parent' to be null.
  1177. *
  1178. * @param parent The element to search for a child
  1179. * @param name Name of the child element to get
  1180. */
  1181. protected XMLElement requireChildNamed(XMLElement parent, String name)
  1182. throws CompilerException
  1183. {
  1184. XMLElement child = parent.getFirstChildNamed(name);
  1185. if (child == null)
  1186. parseError(
  1187. parent,
  1188. "<" + parent.getName() + "> requires child <" + name + ">");
  1189. return child;
  1190. }
  1191. /**
  1192. * Call getContent on an element, producing a meaningful error message if not
  1193. * present, or empty, or a valid URL. It is an error for 'element' to be
  1194. * null.
  1195. *
  1196. * @param element The element to get content of
  1197. */
  1198. protected URL requireURLContent(XMLElement element)
  1199. throws CompilerException
  1200. {
  1201. URL url = null;
  1202. try
  1203. {
  1204. url = new URL(requireContent(element));
  1205. }
  1206. catch (MalformedURLException x)
  1207. {
  1208. parseError(element, "<" + element.getName() + "> requires valid URL", x);
  1209. }
  1210. return url;
  1211. }
  1212. /**
  1213. * Call getContent on an element, producing a meaningful error message if not
  1214. * present, or empty. It is an error for 'element' to be null.
  1215. *
  1216. * @param element The element to get content of
  1217. */
  1218. protected String requireContent(XMLElement element) throws CompilerException
  1219. {
  1220. String content = element.getContent();
  1221. if (content == null || content.length() == 0)
  1222. parseError(element, "<" + element.getName() + "> requires content");
  1223. return content;
  1224. }
  1225. /**
  1226. * Call getAttribute on an element, producing a meaningful error message if
  1227. * not present, or empty. It is an error for 'element' or 'attribute' to be null.
  1228. *
  1229. * @param element The element to get the attribute value of
  1230. * @param attribute The name of the attribute to get
  1231. */
  1232. protected String requireAttribute(XMLElement element, String attribute)
  1233. throws CompilerException
  1234. {
  1235. String value = element.getAttribute(attribute);
  1236. if (value == null)
  1237. parseError(
  1238. element,
  1239. "<" + element.getName() + "> requires attribute '" + attribute + "'");
  1240. return value;
  1241. }
  1242. /**
  1243. * Get a required attribute of an element, ensuring it is an integer. A
  1244. * meaningful error message is generated as a CompilerException if not
  1245. * present or parseable as an int. It is an error for 'element' or
  1246. * 'attribute' to be null.
  1247. *
  1248. * @param element The element to get the attribute value of
  1249. * @param attribute The name of the attribute to get
  1250. */
  1251. protected int requireIntAttribute(XMLElement element, String attribute)
  1252. throws CompilerException
  1253. {
  1254. String value = element.getAttribute(attribute);
  1255. if (value == null || value.length() == 0)
  1256. parseError(
  1257. element,
  1258. "<" + element.getName() + "> requires attribute '" + attribute + "'");
  1259. try
  1260. {
  1261. return Integer.parseInt(value);
  1262. } catch (NumberFormatException x)
  1263. {
  1264. parseError(element, "'" + attribute + "' must be an integer");
  1265. }
  1266. return 0; // never happens
  1267. }
  1268. /**
  1269. * Call getAttribute on an element, producing a meaningful error message if
  1270. * not present, or one of "yes" or "no". It is an error for 'element' or
  1271. * 'attribute' to be null.
  1272. *
  1273. * @param element The element to get the attribute value of
  1274. * @param attribute The name of the attribute to get
  1275. */
  1276. protected boolean requireYesNoAttribute(XMLElement element, String attribute)
  1277. throws CompilerException
  1278. {
  1279. String value = requireAttribute(element, attribute);
  1280. if (value.equalsIgnoreCase("yes"))
  1281. return true;
  1282. if (value.equalsIgnoreCase("no"))
  1283. return false;
  1284. parseError(
  1285. element,
  1286. "<" + element.getName() + "> invalid attribute '"
  1287. + attribute + "': Expected (yes|no)");
  1288. return false; // never happens
  1289. }
  1290. /**
  1291. * Call getAttribute on an element, producing a meaningful warning if not
  1292. * "yes" or "no". If the 'element' or 'attribute' are null, the default value
  1293. * is returned.
  1294. *
  1295. * @param element The element to get the attribute value of
  1296. * @param attribute The name of the attribute to get
  1297. * @param defaultValue Value returned if attribute not present or invalid
  1298. */
  1299. protected boolean validateYesNoAttribute(
  1300. XMLElement element,
  1301. String attribute,
  1302. boolean defaultValue)
  1303. {
  1304. if (element == null)
  1305. return defaultValue;
  1306. String value =
  1307. element.getAttribute(attribute, (defaultValue ? "yes" : "no"));
  1308. if (value.equalsIgnoreCase("yes"))
  1309. return true;
  1310. if (value.equalsIgnoreCase("no"))
  1311. return false;
  1312. // TODO: should this be an error if it's present but "none of the above"?
  1313. parseWarn(
  1314. element,
  1315. "<" + element.getName() + "> invalid attribute '"
  1316. + attribute + "': Expected (yes|no) if present");
  1317. return defaultValue;
  1318. }
  1319. /**
  1320. * The main method if the compiler is invoked by a command-line call.
  1321. *
  1322. * @param args The arguments passed on the command-line.
  1323. */
  1324. public static void main(String[] args)