KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > izforge > izpack > compiler > CompilerConfig


1 /*
2  * $Id: CompilerConfig.java 1708 2007-01-13 18:31:26Z jponge $
3  * IzPack - Copyright 2001-2007 Julien Ponge, All Rights Reserved.
4  *
5  * http://www.izforge.com/izpack/
6  * http://developer.berlios.de/projects/izpack/
7  *
8  * Copyright 2001 Johannes Lehtinen
9  * Copyright 2002 Paul Wilkinson
10  * Copyright 2004 Gaganis Giorgos
11  *
12  *
13  * Licensed under the Apache License, Version 2.0 (the "License");
14  * you may not use this file except in compliance with the License.
15  * You may obtain a copy of the License at
16  *
17  * http://www.apache.org/licenses/LICENSE-2.0
18  *
19  * Unless required by applicable law or agreed to in writing, software
20  * distributed under the License is distributed on an "AS IS" BASIS,
21  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22  * See the License for the specific language governing permissions and
23  * limitations under the License.
24  */

25
26 package com.izforge.izpack.compiler;
27
28 import java.io.BufferedInputStream JavaDoc;
29 import java.io.BufferedOutputStream JavaDoc;
30 import java.io.File JavaDoc;
31 import java.io.FileNotFoundException JavaDoc;
32 import java.io.FileInputStream JavaDoc;
33 import java.io.FileOutputStream JavaDoc;
34 import java.io.IOException JavaDoc;
35 import java.io.InputStream JavaDoc;
36 import java.net.MalformedURLException JavaDoc;
37 import java.net.URI JavaDoc;
38 import java.net.URL JavaDoc;
39 import java.net.URLClassLoader JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.Date JavaDoc;
42 import java.util.Enumeration JavaDoc;
43 import java.util.HashMap JavaDoc;
44 import java.util.Iterator JavaDoc;
45 import java.util.List JavaDoc;
46 import java.util.Map JavaDoc;
47 import java.util.Properties JavaDoc;
48 import java.util.Set JavaDoc;
49 import java.util.StringTokenizer JavaDoc;
50 import java.util.TreeMap JavaDoc;
51 import java.util.Vector JavaDoc;
52 import java.util.jar.JarInputStream JavaDoc;
53 import java.util.zip.ZipEntry JavaDoc;
54 import java.util.zip.ZipInputStream JavaDoc;
55
56 import org.apache.tools.ant.DirectoryScanner;
57
58 import com.izforge.izpack.CustomData;
59 import com.izforge.izpack.ExecutableFile;
60 import com.izforge.izpack.GUIPrefs;
61 import com.izforge.izpack.Info;
62 import com.izforge.izpack.PackFile;
63 import com.izforge.izpack.Panel;
64 import com.izforge.izpack.ParsableFile;
65 import com.izforge.izpack.UpdateCheck;
66 import com.izforge.izpack.compiler.Compiler.CmdlinePackagerListener;
67 import com.izforge.izpack.event.CompilerListener;
68 import com.izforge.izpack.util.Debug;
69 import com.izforge.izpack.util.OsConstraint;
70 import com.izforge.izpack.util.VariableSubstitutor;
71
72 import net.n3.nanoxml.IXMLReader;
73 import net.n3.nanoxml.NonValidator;
74 import net.n3.nanoxml.StdXMLBuilder;
75 import net.n3.nanoxml.StdXMLParser;
76 import net.n3.nanoxml.StdXMLReader;
77 import net.n3.nanoxml.XMLElement;
78
79 /**
80  * A parser for the installer xml configuration. This parses a document
81  * conforming to the installation.dtd and populates a Compiler instance to
82  * perform the install compilation.
83  *
84  * @author Scott Stark
85  * @version $Revision: 1708 $
86  */

87 public class CompilerConfig extends Thread JavaDoc
88 {
89     /** The compiler version. */
90     public final static String JavaDoc VERSION = "1.0";
91
92     /** Standard installer. */
93     public final static String JavaDoc STANDARD = "standard";
94
95     /** Web installer. */
96     public final static String JavaDoc WEB = "web";
97
98     /** Constant for checking attributes. */
99     private static boolean YES = true;
100
101     /** Constant for checking attributes. */
102     private static boolean NO = false;
103
104     private final static String JavaDoc IZ_TEST_FILE = "ShellLink.dll";
105
106     private final static String JavaDoc IZ_TEST_SUBDIR = "bin" + File.separator + "native" + File.separator
107             + "izpack";
108
109     /** The xml install file */
110     private String JavaDoc filename;
111     /** The xml install configuration text */
112     private String JavaDoc installText;
113     /** The base directory. */
114     protected String JavaDoc basedir;
115
116     /** The installer packager compiler */
117     private Compiler JavaDoc compiler;
118
119     /**
120      * List of CompilerListeners which should be called at packaging
121      */

122     protected List JavaDoc compilerListeners = new ArrayList JavaDoc();
123
124     /**
125      * Set the IzPack home directory
126      * @param izHome - the izpack home directory
127      */

128     public static void setIzpackHome(String JavaDoc izHome)
129     {
130         Compiler.setIzpackHome(izHome);
131     }
132
133     /**
134      * The constructor.
135      *
136      * @param filename The XML filename.
137      * @param basedir The base directory.
138      * @param kind The installer kind.
139      * @param output The installer filename.
140      * @throws CompilerException
141      */

142     public CompilerConfig(String JavaDoc filename, String JavaDoc basedir, String JavaDoc kind, String JavaDoc output) throws CompilerException
143     {
144         this(filename, basedir, kind, output, (PackagerListener) null);
145     }
146     /**
147      * The constructor.
148      *
149      * @param filename The XML filename.
150      * @param basedir The base directory.
151      * @param kind The installer kind.
152      * @param output The installer filename.
153      * @param listener The PackagerListener.
154      * @throws CompilerException
155      */

156     public CompilerConfig(String JavaDoc filename, String JavaDoc basedir, String JavaDoc kind, String JavaDoc output, PackagerListener listener)
157     throws CompilerException
158     {
159         this(filename,basedir,kind,output, "default", listener);
160     }
161
162     /**
163      * @param filename The XML filename.
164      * @param kind The installer kind.
165      * @param output The installer filename.
166      * @param compr_format The compression format to be used for packs.
167      * @param listener The PackagerListener.
168      * @throws CompilerException
169      */

170     public CompilerConfig(String JavaDoc filename, String JavaDoc base, String JavaDoc kind, String JavaDoc output, String JavaDoc compr_format,
171             PackagerListener listener) throws CompilerException
172     {
173         this(filename,base,kind,output, compr_format,listener, (String JavaDoc) null);
174     }
175
176     /**
177      *
178      * @param basedir The base directory.
179      * @param kind The installer kind.
180      * @param output The installer filename.
181      * @param listener The PackagerListener.
182      * @param installText The install xml configuration text
183      * @throws CompilerException
184      */

185     public CompilerConfig(String JavaDoc basedir, String JavaDoc kind, String JavaDoc output, PackagerListener listener,
186             String JavaDoc installText) throws CompilerException
187     {
188         this((String JavaDoc) null, basedir, kind, output, "default", listener, installText);
189     }
190     /**
191      *
192      * @param filename The XML filename.
193      * @param basedir The base directory.
194      * @param kind The installer kind.
195      * @param output The installer filename.
196      * @param compr_format The compression format to be used for packs.
197      * @param listener The PackagerListener.
198      * @param installText The install xml configuration text
199      * @throws CompilerException
200      */

201     public CompilerConfig(String JavaDoc filename, String JavaDoc basedir, String JavaDoc kind, String JavaDoc output, String JavaDoc compr_format,
202             PackagerListener listener, String JavaDoc installText) throws CompilerException
203     {
204         this( filename, basedir, kind, output, compr_format, -1, listener, installText);
205     }
206
207     /**
208      * @param filename The XML filename.
209      * @param basedir The base directory.
210      * @param kind The installer kind.
211      * @param output The installer filename.
212      * @param compr_format The compression format to be used for packs.
213      * @param compr_level Compression level to be used if supported.
214      * @param listener The PackagerListener.
215      * @param installText The install xml configuration text
216     * @throws CompilerException
217      */

218     public CompilerConfig(String JavaDoc filename, String JavaDoc basedir, String JavaDoc kind, String JavaDoc output, String JavaDoc compr_format,
219             int compr_level, PackagerListener listener, String JavaDoc installText) throws CompilerException
220     {
221         this.filename = filename;
222         this.installText = installText;
223         this.basedir = basedir;
224         this.compiler = new Compiler JavaDoc(basedir, kind, output, compr_format, compr_level);
225         compiler.setPackagerListener(listener);
226     }
227
228
229
230     /**
231      * Add a name value pair to the project property set. It is <i>not</i>
232      * replaced it is already in the set of properties.
233      *
234      * @param name the name of the property
235      * @param value the value to set
236      * @return true if the property was not already set
237      */

238     public boolean addProperty(String JavaDoc name, String JavaDoc value)
239     {
240         return compiler.addProperty(name, value);
241     }
242
243     /**
244      * Access the install compiler
245      * @return the install compiler
246      */

247     public Compiler JavaDoc getCompiler()
248     {
249         return compiler;
250     }
251
252     /**
253      * Retrieves the packager listener
254      */

255     public PackagerListener getPackagerListener()
256     {
257         return compiler.getPackagerListener();
258     }
259
260     /** Compile the installation */
261     public void compile()
262     {
263         start();
264     }
265
266     /** The run() method. */
267     public void run()
268     {
269         try
270         {
271             executeCompiler();
272         }
273         catch (CompilerException ce)
274         {
275             System.out.println(ce.getMessage() + "\n");
276         }
277         catch (Exception JavaDoc e)
278         {
279             if (Debug.stackTracing())
280             {
281                 e.printStackTrace();
282             }
283             else
284             {
285                 System.out.println("ERROR: " + e.getMessage());
286             }
287         }
288     }
289
290     /**
291      * Compiles the installation.
292      *
293      * @exception Exception Description of the Exception
294      */

295     public void executeCompiler() throws Exception JavaDoc
296     {
297         // normalize and test: TODO: may allow failure if we require write
298
// access
299
File JavaDoc base = new File JavaDoc(basedir).getAbsoluteFile();
300         if (!base.canRead() || !base.isDirectory())
301             throw new CompilerException("Invalid base directory: " + base);
302
303         // add izpack built in property
304
compiler.setProperty("basedir", base.toString());
305
306         // We get the XML data tree
307
XMLElement data = getXMLTree();
308         // loads the specified packager
309
loadPackagingInformation(data);
310         
311         // Listeners to various events
312
addCustomListeners(data);
313
314         // Read the properties and perform replacement on the rest of the tree
315
substituteProperties(data);
316
317         // We add all the information
318
addVariables(data);
319         addInfo(data);
320         addGUIPrefs(data);
321         addLangpacks(data);
322         addResources(data);
323         addNativeLibraries(data);
324         addJars(data);
325         addPanels(data);
326         addPacks(data);
327
328         // We ask the packager to create the installer
329
compiler.createInstaller();
330     }
331
332     private void loadPackagingInformation(XMLElement data) throws CompilerException
333     {
334         notifyCompilerListener("loadPackager", CompilerListener.BEGIN, data);
335         // Initialisation
336
XMLElement root = data.getFirstChildNamed("packaging");
337         String JavaDoc packagerclassname = "com.izforge.izpack.compiler.Packager";
338         String JavaDoc unpackerclassname = "com.izforge.izpack.installer.Unpacker";
339         XMLElement packager = null;
340         if (root != null){
341             packager = root.getFirstChildNamed("packager");
342             
343             if (packager != null){
344                 packagerclassname = requireAttribute(packager, "class");
345             }
346             
347             XMLElement unpacker = root.getFirstChildNamed("unpacker");
348             
349             if (unpacker != null){
350                 unpackerclassname = requireAttribute(unpacker, "class");
351             }
352         }
353         compiler.initPackager(packagerclassname);
354         if (packager != null){
355             XMLElement options = packager.getFirstChildNamed("options");
356             if (options != null){
357                 compiler.getPackager().addConfigurationInformation(options);
358             }
359         }
360         compiler.addProperty("UNPACKER_CLASS", unpackerclassname);
361         notifyCompilerListener("loadPackager", CompilerListener.END, data);
362     }
363
364     public boolean wasSuccessful()
365     {
366         return compiler.wasSuccessful();
367     }
368
369     /**
370      * Returns the GUIPrefs.
371      *
372      * @param data The XML data.
373      * @exception CompilerException Description of the Exception
374      */

375     protected void addGUIPrefs(XMLElement data) throws CompilerException
376     {
377         notifyCompilerListener("addGUIPrefs", CompilerListener.BEGIN, data);
378         // We get the XMLElement & the attributes
379
XMLElement gp = data.getFirstChildNamed("guiprefs");
380         GUIPrefs prefs = new GUIPrefs();
381         if (gp != null)
382         {
383             prefs.resizable = requireYesNoAttribute(gp, "resizable");
384             prefs.width = requireIntAttribute(gp, "width");
385             prefs.height = requireIntAttribute(gp, "height");
386
387             // Look and feel mappings
388
Iterator JavaDoc it = gp.getChildrenNamed("laf").iterator();
389             while (it.hasNext())
390             {
391                 XMLElement laf = (XMLElement) it.next();
392                 String JavaDoc lafName = requireAttribute(laf, "name");
393                 requireChildNamed(laf, "os");
394
395                 Iterator JavaDoc oit = laf.getChildrenNamed("os").iterator();
396                 while (oit.hasNext())
397                 {
398                     XMLElement os = (XMLElement) oit.next();
399                     String JavaDoc osName = requireAttribute(os, "family");
400                     prefs.lookAndFeelMapping.put(osName, lafName);
401                 }
402
403                 Iterator JavaDoc pit = laf.getChildrenNamed("param").iterator();
404                 Map JavaDoc params = new TreeMap JavaDoc();
405                 while (pit.hasNext())
406                 {
407                     XMLElement param = (XMLElement) pit.next();
408                     String JavaDoc name = requireAttribute(param, "name");
409                     String JavaDoc value = requireAttribute(param, "value");
410                     params.put(name, value);
411                 }
412                 prefs.lookAndFeelParams.put(lafName, params);
413             }
414             // Load modifier
415
it = gp.getChildrenNamed("modifier").iterator();
416             while (it.hasNext())
417             {
418                 XMLElement curentModifier = (XMLElement) it.next();
419                 String JavaDoc key = requireAttribute(curentModifier, "key");
420                 String JavaDoc value = requireAttribute(curentModifier, "value");
421                 prefs.modifier.put(key, value);
422
423             }
424             // make sure jar contents of each are available in installer
425
// map is easier to read/modify than if tree
426
HashMap JavaDoc lafMap = new HashMap JavaDoc();
427             lafMap.put("liquid", "liquidlnf.jar");
428             lafMap.put("kunststoff", "kunststoff.jar");
429             lafMap.put("metouia", "metouia.jar");
430             lafMap.put("looks", "looks.jar");
431
432             // is this really what we want? a double loop? needed, since above,
433
// it's
434
// the /last/ lnf for an os which is used, so can't add during
435
// initial
436
// loop
437
Iterator JavaDoc kit = prefs.lookAndFeelMapping.keySet().iterator();
438             while (kit.hasNext())
439             {
440                 String JavaDoc lafName = (String JavaDoc) prefs.lookAndFeelMapping.get(kit.next());
441                 String JavaDoc lafJarName = (String JavaDoc) lafMap.get(lafName);
442                 if (lafJarName == null) parseError(gp, "Unrecognized Look and Feel: " + lafName);
443
444                 URL JavaDoc lafJarURL = findIzPackResource("lib/" + lafJarName, "Look and Feel Jar file",
445                         gp);
446                 compiler.addJarContent(lafJarURL);
447             }
448         }
449         compiler.setGUIPrefs(prefs);
450         notifyCompilerListener("addGUIPrefs", CompilerListener.END, data);
451     }
452
453     /**
454      * Add project specific external jar files to the installer.
455      *
456      * @param data The XML data.
457      */

458     protected void addJars(XMLElement data) throws Exception JavaDoc
459     {
460         notifyCompilerListener("addJars", CompilerListener.BEGIN, data);
461         Iterator JavaDoc iter = data.getChildrenNamed("jar").iterator();
462         while (iter.hasNext())
463         {
464             XMLElement el = (XMLElement) iter.next();
465             String JavaDoc src = requireAttribute(el, "src");
466             URL JavaDoc url = findProjectResource(src, "Jar file", el);
467             compiler.addJarContent(url);
468             // Additionals for mark a jar file also used in the uninstaller.
469
// The contained files will be copied from the installer into the
470
// uninstaller if needed.
471
// Therefore the contained files of the jar should be in the
472
// installer also
473
// they are used only from the uninstaller. This is the reason why
474
// the stage
475
// wiil be only observed for the uninstaller.
476
String JavaDoc stage = el.getAttribute("stage");
477             if (stage != null
478                     && ("both".equalsIgnoreCase(stage) || "uninstall".equalsIgnoreCase(stage)))
479             {
480                 CustomData ca = new CustomData(null, getContainedFilePaths(url), null,
481                         CustomData.UNINSTALLER_JAR);
482                 compiler.addCustomJar(ca, url);
483             }
484         }
485         notifyCompilerListener("addJars", CompilerListener.END, data);
486     }
487
488     /**
489      * Add native libraries to the installer.
490      *
491      * @param data The XML data.
492      */

493     protected void addNativeLibraries(XMLElement data) throws Exception JavaDoc
494     {
495         boolean needAddOns = false;
496         notifyCompilerListener("addNativeLibraries", CompilerListener.BEGIN, data);
497         Iterator JavaDoc iter = data.getChildrenNamed("native").iterator();
498         while (iter.hasNext())
499         {
500             XMLElement el = (XMLElement) iter.next();
501             String JavaDoc type = requireAttribute(el, "type");
502             String JavaDoc name = requireAttribute(el, "name");
503             String JavaDoc path = "bin/native/" + type + "/" + name;
504             URL JavaDoc url = findIzPackResource(path, "Native Library", el);
505             compiler.addNativeLibrary(name, url);
506             // Additionals for mark a native lib also used in the uninstaller
507
// The lib will be copied from the installer into the uninstaller if
508
// needed.
509
// Therefore the lib should be in the installer also it is used only
510
// from
511
// the uninstaller. This is the reason why the stage wiil be only
512
// observed
513
// for the uninstaller.
514
String JavaDoc stage = el.getAttribute("stage");
515             List JavaDoc constraints = OsConstraint.getOsList(el);
516             if (stage != null
517                     && ("both".equalsIgnoreCase(stage) || "uninstall".equalsIgnoreCase(stage)))
518             {
519                 ArrayList JavaDoc al = new ArrayList JavaDoc();
520                 al.add(name);
521                 CustomData cad = new CustomData(null, al, constraints, CustomData.UNINSTALLER_LIB);
522                 compiler.addNativeUninstallerLibrary(cad);
523                 needAddOns = true;
524             }
525
526         }
527         if (needAddOns)
528         {
529             // Add the uninstaller extensions as a resource if specified
530
XMLElement root = requireChildNamed(data, "info");
531             XMLElement uninstallInfo = root.getFirstChildNamed("uninstaller");
532             if (validateYesNoAttribute(uninstallInfo, "write", YES))
533             {
534                 URL JavaDoc url = findIzPackResource("lib/uninstaller-ext.jar", "Uninstaller extensions",
535                         root);
536                 compiler.addResource("IzPack.uninstaller-ext", url);
537             }
538
539         }
540         notifyCompilerListener("addNativeLibraries", CompilerListener.END, data);
541     }
542
543     /**
544      * Add packs and their contents to the installer.
545      *
546      * @param data The XML data.
547      */

548     protected void addPacks(XMLElement data) throws CompilerException
549     {
550         notifyCompilerListener("addPacks", CompilerListener.BEGIN, data);
551         // Initialisation
552
XMLElement root = requireChildNamed(data, "packs");
553
554         // at least one pack is required
555
Vector JavaDoc packElements = root.getChildrenNamed("pack");
556         if (packElements.isEmpty()) parseError(root, "<packs> requires a <pack>");
557
558         File JavaDoc baseDir = new File JavaDoc(basedir);
559         
560         Iterator JavaDoc packIter = packElements.iterator();
561         while (packIter.hasNext())
562         {
563             XMLElement el = (XMLElement) packIter.next();
564
565             // Trivial initialisations
566
String JavaDoc name = requireAttribute(el, "name");
567             String JavaDoc id = el.getAttribute("id");
568             
569             boolean loose = "true".equalsIgnoreCase(el.getAttribute("loose", "false"));
570             String JavaDoc description = requireChildNamed(el, "description").getContent();
571             boolean required = requireYesNoAttribute(el, "required");
572             String JavaDoc group = el.getAttribute("group");
573             String JavaDoc installGroups = el.getAttribute("installGroups");
574             String JavaDoc excludeGroup = el.getAttribute("excludeGroup");
575             
576             if(required && excludeGroup != null)
577             {
578                 parseError(el, "Pack, which has excludeGroup can not be required.",
579                     new Exception JavaDoc("Pack, which has excludeGroup can not be required."));
580             }
581             
582             PackInfo pack = new PackInfo(name, id, description, required, loose, excludeGroup);
583             pack.setOsConstraints(OsConstraint.getOsList(el)); // TODO:
584

585             // unverified
586
// if the pack belongs to an excludeGroup it's not preselected by default
587
if(excludeGroup == null)
588                 pack.setPreselected(validateYesNoAttribute(el, "preselected", YES));
589             else
590                 pack.setPreselected(validateYesNoAttribute(el, "preselected", NO));
591             
592             // Set the pack group if specified
593
if (group != null)
594                 pack.setGroup(group);
595             // Set the pack install groups if specified
596
if (installGroups != null)
597             {
598                 StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(installGroups, ",");
599                 while (st.hasMoreTokens())
600                 {
601                     String JavaDoc igroup = st.nextToken();
602                     pack.addInstallGroup(igroup);
603                 }
604             }
605
606             // We get the parsables list
607
Iterator JavaDoc iter = el.getChildrenNamed("parsable").iterator();
608             while (iter.hasNext())
609             {
610                 XMLElement p = (XMLElement) iter.next();
611                 String JavaDoc target = requireAttribute(p, "targetfile");
612                 String JavaDoc type = p.getAttribute("type", "plain");
613                 String JavaDoc encoding = p.getAttribute("encoding", null);
614                 List JavaDoc osList = OsConstraint.getOsList(p); // TODO: unverified
615

616                 pack.addParsable(new ParsableFile(target, type, encoding, osList));
617             }
618
619             // We get the executables list
620
iter = el.getChildrenNamed("executable").iterator();
621             while (iter.hasNext())
622             {
623                 XMLElement e = (XMLElement) iter.next();
624                 ExecutableFile executable = new ExecutableFile();
625                 String JavaDoc val; // temp value
626

627                 executable.path = requireAttribute(e, "targetfile");
628
629                 // when to execute this executable
630
val = e.getAttribute("stage", "never");
631                 if ("postinstall".equalsIgnoreCase(val))
632                     executable.executionStage = ExecutableFile.POSTINSTALL;
633                 else if ("uninstall".equalsIgnoreCase(val))
634                     executable.executionStage = ExecutableFile.UNINSTALL;
635
636                 // type of this executable
637
val = e.getAttribute("type", "bin");
638                 if ("jar".equalsIgnoreCase(val))
639                 {
640                     executable.type = ExecutableFile.JAR;
641                     executable.mainClass = e.getAttribute("class"); // executable
642
// class
643
}
644
645                 // what to do if execution fails
646
val = e.getAttribute("failure", "ask");
647                 if ("abort".equalsIgnoreCase(val))
648                     executable.onFailure = ExecutableFile.ABORT;
649                 else if ("warn".equalsIgnoreCase(val)) executable.onFailure = ExecutableFile.WARN;
650
651                 // whether to keep the executable after executing it
652
val = e.getAttribute("keep");
653                 executable.keepFile = "true".equalsIgnoreCase(val);
654
655                 // get arguments for this executable
656
XMLElement args = e.getFirstChildNamed("args");
657                 if (null != args)
658                 {
659                     Iterator JavaDoc argIterator = args.getChildrenNamed("arg").iterator();
660                     while (argIterator.hasNext())
661                     {
662                         XMLElement arg = (XMLElement) argIterator.next();
663                         executable.argList.add(requireAttribute(arg, "value"));
664                     }
665                 }
666
667                 executable.osList = OsConstraint.getOsList(e); // TODO:
668
// unverified
669

670                 pack.addExecutable(executable);
671             }
672
673             // We get the files list
674
iter = el.getChildrenNamed("file").iterator();
675             while (iter.hasNext())
676             {
677                 XMLElement f = (XMLElement) iter.next();
678                 String JavaDoc src = requireAttribute(f, "src");
679                 String JavaDoc targetdir = requireAttribute(f, "targetdir");
680                 List JavaDoc osList = OsConstraint.getOsList(f); // TODO: unverified
681
int override = getOverrideValue(f);
682                 Map JavaDoc additionals = getAdditionals(f);
683                 boolean unpack = src.endsWith(".zip") && "true".equalsIgnoreCase(f.getAttribute("unpack"));
684
685                 File JavaDoc file = new File JavaDoc(src);
686                 if (!file.isAbsolute()) file = new File JavaDoc(basedir, src);
687
688                 try
689                 {
690                     if (unpack)
691                         addArchiveContent(baseDir, file, targetdir, osList, override, pack, additionals);
692                     else
693                         addRecursively(baseDir, file, targetdir, osList, override, pack, additionals);
694                 }
695                 catch (Exception JavaDoc x)
696                 {
697                     parseError(f, x.getMessage(), x);
698                 }
699             }
700
701             // We get the singlefiles list
702
iter = el.getChildrenNamed("singlefile").iterator();
703             while (iter.hasNext())
704             {
705                 XMLElement f = (XMLElement) iter.next();
706                 String JavaDoc src = requireAttribute(f, "src");
707                 String JavaDoc target = requireAttribute(f, "target");
708                 List JavaDoc osList = OsConstraint.getOsList(f); // TODO: unverified
709
int override = getOverrideValue(f);
710                 Map JavaDoc additionals = getAdditionals(f);
711
712                 File JavaDoc file = new File JavaDoc(src);
713                 if (!file.isAbsolute()) file = new File JavaDoc(basedir, src);
714
715                 try
716                 {
717                     pack.addFile(baseDir, file, target, osList, override, additionals);
718                 }
719                 catch (FileNotFoundException JavaDoc x)
720                 {
721                     parseError(f, x.getMessage(), x);
722                 }
723             }
724
725             // We get the fileset list
726
iter = el.getChildrenNamed("fileset").iterator();
727             while (iter.hasNext())
728             {
729                 XMLElement f = (XMLElement) iter.next();
730                 String JavaDoc dir_attr = requireAttribute(f, "dir");
731
732                 File JavaDoc dir = new File JavaDoc(dir_attr);
733                 if (!dir.isAbsolute()) dir = new File JavaDoc(basedir, dir_attr);
734                 if (!dir.isDirectory()) // also tests '.exists()'
735
parseError(f, "Invalid directory 'dir': " + dir_attr);
736
737                 boolean casesensitive = validateYesNoAttribute(f, "casesensitive", YES);
738                 boolean defexcludes = validateYesNoAttribute(f, "defaultexcludes", YES);
739                 String JavaDoc targetdir = requireAttribute(f, "targetdir");
740                 List JavaDoc osList = OsConstraint.getOsList(f); // TODO: unverified
741
int override = getOverrideValue(f);
742                 Map JavaDoc additionals = getAdditionals(f);
743
744                 // get includes and excludes
745
Vector JavaDoc xcludesList = null;
746                 String JavaDoc[] includes = null;
747                 xcludesList = f.getChildrenNamed("include");
748                 if (!xcludesList.isEmpty())
749                 {
750                     includes = new String JavaDoc[xcludesList.size()];
751                     for (int j = 0; j < xcludesList.size(); j++)
752                     {
753                         XMLElement xclude = (XMLElement) xcludesList.get(j);
754                         includes[j] = requireAttribute(xclude, "name");
755                     }
756                 }
757                 String JavaDoc[] excludes = null;
758                 xcludesList = f.getChildrenNamed("exclude");
759                 if (!xcludesList.isEmpty())
760                 {
761                     excludes = new String JavaDoc[xcludesList.size()];
762                     for (int j = 0; j < xcludesList.size(); j++)
763                     {
764                         XMLElement xclude = (XMLElement) xcludesList.get(j);
765                         excludes[j] = requireAttribute(xclude, "name");
766                     }
767                 }
768
769                 // parse additional fileset attributes "includes" and "excludes"
770
String JavaDoc[] toDo = new String JavaDoc[] { "includes", "excludes"};
771                 // use the existing containers filled from include and exclude
772
// and add the includes and excludes to it
773
String JavaDoc[][] containers = new String JavaDoc[][] { includes, excludes};
774                 for (int j = 0; j < toDo.length; ++j)
775                 {
776                     String JavaDoc inex = f.getAttribute(toDo[j]);
777                     if (inex != null && inex.length() > 0)
778                     { // This is the same "splitting" as ant PatternSet do ...
779
StringTokenizer JavaDoc tok = new StringTokenizer JavaDoc(inex, ", ", false);
780                         int newSize = tok.countTokens();
781                         int k = 0;
782                         String JavaDoc[] nCont = null;
783                         if (containers[j] != null && containers[j].length > 0)
784                         { // old container exist; create a new which can hold
785
// all values
786
// and copy the old stuff to the front
787
newSize += containers[j].length;
788                             nCont = new String JavaDoc[newSize];
789                             for (; k < containers[j].length; ++k)
790                                 nCont[k] = containers[j][k];
791                         }
792                         if (nCont == null) // No container for old values
793
// created,
794
// create a new one.
795
nCont = new String JavaDoc[newSize];
796                         for (; k < newSize; ++k)
797                             // Fill the new one or expand the existent container
798
nCont[k] = tok.nextToken();
799                         containers[j] = nCont;
800                     }
801                 }
802                 includes = containers[0]; // push the new includes to the
803
// local var
804
excludes = containers[1]; // push the new excludes to the
805
// local var
806

807                 // scan and add fileset
808
DirectoryScanner ds = new DirectoryScanner();
809                 ds.setIncludes(includes);
810                 ds.setExcludes(excludes);
811                 if (defexcludes) ds.addDefaultExcludes();
812                 ds.setBasedir(dir);
813                 ds.setCaseSensitive(casesensitive);
814                 ds.scan();
815
816                 String JavaDoc[] files = ds.getIncludedFiles();
817                 String JavaDoc[] dirs = ds.getIncludedDirectories();
818
819                 // Directory scanner has done recursion, add files and
820
// directories
821
for (int i = 0; i < files.length; ++i)
822                 {
823                     try
824                     {
825                         String JavaDoc target = new File JavaDoc(targetdir, files[i]).getPath();
826                         pack
827                                 .addFile(baseDir, new File JavaDoc(dir, files[i]), target, osList, override,
828                                         additionals);
829                     }
830                     catch (FileNotFoundException JavaDoc x)
831                     {
832                         parseError(f, x.getMessage(), x);
833                     }
834                 }
835                 for (int i = 0; i < dirs.length; ++i)
836                 {
837                     try
838                     {
839                         String JavaDoc target = new File JavaDoc(targetdir, dirs[i]).getPath();
840                         pack.addFile(baseDir, new File JavaDoc(dir, dirs[i]), target, osList, override, additionals);
841                     }
842                     catch (FileNotFoundException JavaDoc x)
843                     {
844                         parseError(f, x.getMessage(), x);
845                     }
846                 }
847             }
848
849             // get the updatechecks list
850
iter = el.getChildrenNamed("updatecheck").iterator();
851             while (iter.hasNext())
852             {
853                 XMLElement f = (XMLElement) iter.next();
854
855                 String JavaDoc casesensitive = f.getAttribute("casesensitive");
856
857                 // get includes and excludes
858
ArrayList JavaDoc includesList = new ArrayList JavaDoc();
859                 ArrayList JavaDoc excludesList = new ArrayList JavaDoc();
860
861                 // get includes and excludes
862
Iterator JavaDoc include_it = f.getChildrenNamed("include").iterator();
863                 while (include_it.hasNext())
864                 {
865                     XMLElement inc_el = (XMLElement) include_it.next();
866                     includesList.add(requireAttribute(inc_el, "name"));
867                 }
868
869                 Iterator JavaDoc exclude_it = f.getChildrenNamed("exclude").iterator();
870                 while (exclude_it.hasNext())
871                 {
872                     XMLElement excl_el = (XMLElement) exclude_it.next();
873                     excludesList.add(requireAttribute(excl_el, "name"));
874                 }
875
876                 pack.addUpdateCheck(new UpdateCheck(includesList, excludesList, casesensitive));
877             }
878             // We get the dependencies
879
iter = el.getChildrenNamed("depends").iterator();
880             while (iter.hasNext())
881             {
882                 XMLElement dep = (XMLElement) iter.next();
883                 String JavaDoc depName = requireAttribute(dep, "packname");
884                 pack.addDependency(depName);
885
886             }
887             // We add the pack
888
compiler.addPack(pack);
889         }
890         
891         compiler.checkDependencies();
892         compiler.checkExcludes();
893
894         notifyCompilerListener("addPacks", CompilerListener.END, data);
895     }
896
897     /**
898      * Checks whether the dependencies stated in the configuration file are correct. Specifically it
899      * checks that no pack point to a non existent pack and also that there are no circular
900      * dependencies in the packs.
901      */

902     public void checkDependencies(List JavaDoc packs) throws CompilerException
903     {
904         // Because we use package names in the configuration file we assosiate
905
// the names with the objects
906
Map JavaDoc names = new HashMap JavaDoc();
907         for (int i = 0; i < packs.size(); i++)
908         {
909             PackInfo pack = (PackInfo) packs.get(i);
910             names.put(pack.getPack().name, pack);
911         }
912         int result = dfs(packs, names);
913         // @todo More informative messages to include the source of the error
914
if (result == -2)
915             parseError("Circular dependency detected");
916         else if (result == -1) parseError("A dependency doesn't exist");
917     }
918
919     /**
920      * We use the dfs graph search algorithm to check whether the graph is acyclic as described in:
921      * Thomas H. Cormen, Charles Leiserson, Ronald Rivest and Clifford Stein. Introduction to
922      * algorithms 2nd Edition 540-549,MIT Press, 2001
923      *
924      * @param packs The graph
925      * @param names The name map
926      */

927     private int dfs(List JavaDoc packs, Map JavaDoc names)
928     {
929         Map JavaDoc edges = new HashMap JavaDoc();
930         for (int i = 0; i < packs.size(); i++)
931         {
932             PackInfo pack = (PackInfo) packs.get(i);
933             if (pack.colour == PackInfo.WHITE)
934             {
935                 if (dfsVisit(pack, names, edges) != 0) return -1;
936             }
937
938         }
939         return checkBackEdges(edges);
940     }
941
942     /**
943      * This function checks for the existence of back edges.
944      */

945     private int checkBackEdges(Map JavaDoc edges)
946     {
947         Set JavaDoc keys = edges.keySet();
948         for (Iterator JavaDoc iterator = keys.iterator(); iterator.hasNext();)
949         {
950             final Object JavaDoc key = iterator.next();
951             int color = ((Integer JavaDoc) edges.get(key)).intValue();
952             if (color == PackInfo.GREY) { return -2; }
953         }
954         return 0;
955
956     }
957
958     /**
959      * This class is used for the classification of the edges
960      */

961     private class Edge
962     {
963
964         PackInfo u;
965
966         PackInfo v;
967
968         Edge(PackInfo u, PackInfo v)
969         {
970             this.u = u;
971             this.v = v;
972         }
973     }
974
975     private int dfsVisit(PackInfo u, Map JavaDoc names, Map JavaDoc edges)
976     {
977         u.colour = PackInfo.GREY;
978         List JavaDoc deps = u.getDependencies();
979         if (deps != null)
980         {
981             for (int i = 0; i < deps.size(); i++)
982             {
983                 String JavaDoc name = (String JavaDoc) deps.get(i);
984                 PackInfo v = (PackInfo) names.get(name);
985                 if (v == null)
986                 {
987                     System.out.println("Failed to find dependency: "+name);
988                     return -1;
989                 }
990                 Edge edge = new Edge(u, v);
991                 if (edges.get(edge) == null) edges.put(edge, new Integer JavaDoc(v.colour));
992
993                 if (v.colour == PackInfo.WHITE)
994                 {
995
996                     final int result = dfsVisit(v, names, edges);
997                     if (result != 0) return result;
998                 }
999             }
1000        }
1001        u.colour = PackInfo.BLACK;
1002        return 0;
1003    }
1004
1005    /**
1006     * Add files in an archive to a pack
1007     * @param archive the archive file to unpack
1008     * @parm targetdir the target directory where the content of the archive will be installed
1009     * @param osList The target OS constraints.
1010     * @param override Overriding behaviour.
1011     * @param pack Pack to be packed into
1012     * @param additionals Map which contains additional data
1013     */

1014    protected void addArchiveContent(File JavaDoc baseDir, File JavaDoc archive, String JavaDoc targetdir, List JavaDoc osList, int override, PackInfo pack, Map JavaDoc additionals) throws IOException JavaDoc {
1015      
1016      FileInputStream JavaDoc fin = new FileInputStream JavaDoc(archive);
1017      ZipInputStream JavaDoc zin = new ZipInputStream JavaDoc(fin);
1018      while (true) {
1019        ZipEntry JavaDoc zentry = zin.getNextEntry();
1020        if (zentry==null) break;
1021        if (zentry.isDirectory()) continue;
1022        
1023        try {
1024            File JavaDoc temp = File.createTempFile("izpack", null);
1025            temp.deleteOnExit();
1026            
1027            FileOutputStream JavaDoc out = new FileOutputStream JavaDoc(temp);
1028            PackagerHelper.copyStream(zin, out);
1029            out.close();
1030        
1031            pack.addFile(baseDir, temp, targetdir + "/" + zentry.getName(), osList, override, additionals);
1032        } catch (IOException JavaDoc e) {
1033            throw new IOException JavaDoc("Couldn't create temporary file for "+zentry.getName()+" in archive "+archive+" ("+e.getMessage()+")");
1034        }
1035        
1036      }
1037      fin.close();
1038    }
1039    
1040    /**
1041     * Recursive method to add files in a pack.
1042     *
1043     * @param file The file to add.
1044     * @param targetdir The relative path to the parent.
1045     * @param osList The target OS constraints.
1046     * @param override Overriding behaviour.
1047     * @param pack Pack to be packed into
1048     * @param additionals Map which contains additional data
1049     * @exception FileNotFoundException if the file does not exist
1050     */

1051    protected void addRecursively(File JavaDoc baseDir, File JavaDoc file, String JavaDoc targetdir, List JavaDoc osList, int override,
1052            PackInfo pack, Map JavaDoc additionals) throws IOException JavaDoc
1053    {
1054        String JavaDoc targetfile = targetdir + "/" + file.getName();
1055        if (!file.isDirectory())
1056            pack.addFile(baseDir, file, targetfile, osList, override, additionals);
1057        else
1058        {
1059            File JavaDoc[] files = file.listFiles();
1060            if (files.length == 0) // The directory is empty so must be added
1061
pack.addFile(baseDir, file, targetfile, osList, override, additionals);
1062            else
1063            {
1064                // new targetdir = targetfile;
1065
for (int i = 0; i < files.length; i++)
1066                    addRecursively(baseDir, files[i], targetfile, osList, override, pack, additionals);
1067            }
1068        }
1069    }
1070
1071    /**
1072     * Parse panels and their paramters, locate the panels resources and add to the Packager.
1073     *
1074     * @param data The XML data.
1075     * @exception CompilerException Description of the Exception
1076     */

1077    protected void addPanels(XMLElement data) throws CompilerException
1078    {
1079        notifyCompilerListener("addPanels", CompilerListener.BEGIN, data);
1080        XMLElement root = requireChildNamed(data, "panels");
1081
1082        // at least one panel is required
1083
Vector JavaDoc panels = root.getChildrenNamed("panel");
1084        if (panels.isEmpty()) parseError(root, "<panels> requires a <panel>");
1085
1086        // We process each panel markup
1087
Iterator JavaDoc iter = panels.iterator();
1088        while (iter.hasNext())
1089        {
1090            XMLElement xmlPanel = (XMLElement) iter.next();
1091
1092            // create the serialized Panel data
1093
Panel panel = new Panel();
1094            panel.osConstraints = OsConstraint.getOsList(xmlPanel);
1095            String JavaDoc className = xmlPanel.getAttribute("classname");
1096            // add an id
1097
String JavaDoc panelid = xmlPanel.getAttribute("id");
1098            panel.setPanelid(panelid);
1099            // Panel files come in jars packaged w/ IzPack
1100
String JavaDoc jarPath = "bin/panels/" + className + ".jar";
1101            URL JavaDoc url = findIzPackResource(jarPath, "Panel jar file", xmlPanel);
1102            String JavaDoc fullClassName = null;
1103            try
1104            {
1105                fullClassName = getFullClassName(url, className);
1106            }
1107            catch (IOException JavaDoc e)
1108            {
1109            }
1110            if (fullClassName != null)
1111                panel.className = fullClassName;
1112            else
1113                panel.className = className;
1114            // insert into the packager
1115
compiler.addPanelJar(panel, url);
1116        }
1117        notifyCompilerListener("addPanels", CompilerListener.END, data);
1118    }
1119
1120    /**
1121     * Adds the resources.
1122     *
1123     * @param data The XML data.
1124     * @exception CompilerException Description of the Exception
1125     */

1126    protected void addResources(XMLElement data) throws CompilerException
1127    {
1128        notifyCompilerListener("addResources", CompilerListener.BEGIN, data);
1129        XMLElement root = data.getFirstChildNamed("resources");
1130        if (root == null) return;
1131
1132        // We process each res markup
1133
Iterator JavaDoc iter = root.getChildrenNamed("res").iterator();
1134        while (iter.hasNext())
1135        {
1136            XMLElement res = (XMLElement) iter.next();
1137            String JavaDoc id = requireAttribute(res, "id");
1138            String JavaDoc src = requireAttribute(res, "src");
1139            boolean parse = validateYesNoAttribute(res, "parse", NO);
1140
1141            // basedir is not prepended if src is already an absolute path
1142
URL JavaDoc url = findProjectResource(src, "Resource", res);
1143
1144            // substitute variable values in the resource if parsed
1145
if (parse)
1146            {
1147                if (compiler.getVariables().isEmpty())
1148                {
1149                    parseWarn(res, "No variables defined. " + url.getPath() + " not parsed.");
1150                }
1151                else
1152                {
1153                    String JavaDoc type = res.getAttribute("type");
1154                    String JavaDoc encoding = res.getAttribute("encoding");
1155                    File JavaDoc parsedFile = null;
1156
1157                    try
1158                    {
1159                        // make the substitutions into a temp file
1160
InputStream JavaDoc bin = new BufferedInputStream JavaDoc(url.openStream());
1161
1162                        parsedFile = File.createTempFile("izpp", null);
1163                        parsedFile.deleteOnExit();
1164                        FileOutputStream JavaDoc outFile = new FileOutputStream JavaDoc(parsedFile);
1165                        BufferedOutputStream JavaDoc bout = new BufferedOutputStream JavaDoc(outFile);
1166
1167                        VariableSubstitutor vs = new VariableSubstitutor(compiler.getVariables());
1168                        vs.substitute(bin, bout, type, encoding);
1169                        bin.close();
1170                        bout.close();
1171
1172                        // and specify the substituted file to be added to the
1173
// packager
1174
url = parsedFile.toURL();
1175                    }
1176                    catch (IOException JavaDoc x)
1177                    {
1178                        parseError(res, x.getMessage(), x);
1179                    }
1180                }
1181            }
1182
1183            compiler.addResource(id, url);
1184        }
1185        notifyCompilerListener("addResources", CompilerListener.END, data);
1186    }
1187
1188    /**
1189     * Adds the ISO3 codes of the langpacks and associated resources.
1190     *
1191     * @param data The XML data.
1192     * @exception CompilerException Description of the Exception
1193     */

1194    protected void addLangpacks(XMLElement data) throws CompilerException
1195    {
1196        notifyCompilerListener("addLangpacks", CompilerListener.BEGIN, data);
1197        XMLElement root = requireChildNamed(data, "locale");
1198
1199        // at least one langpack is required
1200
Vector JavaDoc locals = root.getChildrenNamed("langpack");
1201        if (locals.isEmpty()) parseError(root, "<locale> requires a <langpack>");
1202
1203        // We process each langpack markup
1204
Iterator JavaDoc iter = locals.iterator();
1205        while (iter.hasNext())
1206        {
1207            XMLElement el = (XMLElement) iter.next();
1208            String JavaDoc iso3 = requireAttribute(el, "iso3");
1209            String JavaDoc path;
1210
1211            path = "bin/langpacks/installer/" + iso3 + ".xml";
1212            URL JavaDoc iso3xmlURL = findIzPackResource(path, "ISO3 file", el);
1213
1214            path = "bin/langpacks/flags/" + iso3 + ".gif";
1215            URL JavaDoc iso3FlagURL = findIzPackResource(path, "ISO3 flag image", el);
1216
1217            compiler.addLangPack(iso3, iso3xmlURL, iso3FlagURL);
1218        }
1219        notifyCompilerListener("addLangpacks", CompilerListener.END, data);
1220    }
1221
1222    /**
1223     * Builds the Info class from the XML tree.
1224     *
1225     * @param data The XML data. return The Info.
1226     * @exception Exception Description of the Exception
1227     */

1228    protected void addInfo(XMLElement data) throws Exception JavaDoc
1229    {
1230        notifyCompilerListener("addInfo", CompilerListener.BEGIN, data);
1231        // Initialisation
1232
XMLElement root = requireChildNamed(data, "info");
1233
1234        Info info = new Info();
1235        info.setAppName(requireContent(requireChildNamed(root, "appname")));
1236        info.setAppVersion(requireContent(requireChildNamed(root, "appversion")));
1237        // We get the installation subpath
1238
XMLElement subpath = root.getFirstChildNamed("appsubpath");
1239        if (subpath != null)
1240        {
1241            info.setInstallationSubPath(requireContent(subpath));
1242        }
1243
1244        // validate and insert app URL
1245
final XMLElement URLElem = root.getFirstChildNamed("url");
1246        if (URLElem != null)
1247        {
1248            URL JavaDoc appURL = requireURLContent(URLElem);
1249            info.setAppURL(appURL.toString());
1250        }
1251
1252        // We get the authors list
1253
XMLElement authors = root.getFirstChildNamed("authors");
1254        if (authors != null)
1255        {
1256            Iterator JavaDoc iter = authors.getChildrenNamed("author").iterator();
1257            while (iter.hasNext())
1258            {
1259                XMLElement author = (XMLElement) iter.next();
1260                String JavaDoc name = requireAttribute(author, "name");
1261                String JavaDoc email = requireAttribute(author, "email");
1262                info.addAuthor(new Info.Author(name, email));
1263            }
1264        }
1265
1266        // We get the java version required
1267
XMLElement javaVersion = root.getFirstChildNamed("javaversion");
1268        if (javaVersion != null) info.setJavaVersion(requireContent(javaVersion));
1269
1270        // validate and insert (and require if -web kind) web dir
1271
XMLElement webDirURL = root.getFirstChildNamed("webdir");
1272        if (webDirURL != null) info.setWebDirURL(requireURLContent(webDirURL).toString());
1273        String JavaDoc kind = compiler.getKind();
1274        if (kind != null)
1275        {
1276            if (kind.equalsIgnoreCase(WEB) && webDirURL == null)
1277            {
1278                parseError(root, "<webdir> required when \"WEB\" installer requested");
1279            }
1280            else if (kind.equalsIgnoreCase(STANDARD) && webDirURL != null)
1281            {
1282                // Need a Warning? parseWarn(webDirURL, "Not creating web
1283
// installer.");
1284
info.setWebDirURL(null);
1285            }
1286        }
1287
1288        // Add the uninstaller as a resource if specified
1289
XMLElement uninstallInfo = root.getFirstChildNamed("uninstaller");
1290        if (validateYesNoAttribute(uninstallInfo, "write", YES))
1291        {
1292            URL JavaDoc url = findIzPackResource("lib/uninstaller.jar", "Uninstaller", root);
1293            compiler.addResource("IzPack.uninstaller", url);
1294
1295            if (uninstallInfo != null)
1296            {
1297                String JavaDoc uninstallerName = uninstallInfo.getAttribute("name");
1298                if (uninstallerName != null && uninstallerName.endsWith(".jar")
1299                        && uninstallerName.length() > ".jar".length())
1300                    info.setUninstallerName(uninstallerName);
1301            }
1302        }
1303        // Add the path for the summary log file if specified
1304
XMLElement slfPath = root.getFirstChildNamed("summarylogfilepath");
1305        if (slfPath != null) info.setSummaryLogFilePath(requireContent(slfPath));
1306
1307        // look for an unpacker class
1308
String JavaDoc unpackerclass = compiler.getProperty("UNPACKER_CLASS");
1309        info.setUnpackerClassName(unpackerclass);
1310        compiler.setInfo(info);
1311        notifyCompilerListener("addInfo", CompilerListener.END, data);
1312    }
1313
1314    /**
1315     * Variable declaration is a fragment of the xml file. For example:
1316     *
1317     * <pre>
1318     *
1319     *
1320     *
1321     *
1322     * &lt;variables&gt;
1323     * &lt;variable name=&quot;nom&quot; value=&quot;value&quot;/&gt;
1324     * &lt;variable name=&quot;foo&quot; value=&quot;pippo&quot;/&gt;
1325     * &lt;/variables&gt;
1326     *
1327     *
1328     *
1329     *
1330     * </pre>
1331     *
1332     * variable declared in this can be referred to in parsable files.
1333     *
1334     * @param data The XML data.
1335     * @exception CompilerException Description of the Exception
1336     */

1337    protected void addVariables(XMLElement data) throws CompilerException
1338    {
1339        notifyCompilerListener("addVariables", CompilerListener.BEGIN, data);
1340        // We get the varible list
1341
XMLElement root = data.getFirstChildNamed("variables");
1342        if (root == null) return;
1343
1344        Properties JavaDoc variables = compiler.getVariables();
1345
1346        Iterator JavaDoc iter = root.getChildrenNamed("variable").iterator();
1347        while (iter.hasNext())
1348        {
1349            XMLElement var = (XMLElement) iter.next();
1350            String JavaDoc name = requireAttribute(var, "name");
1351            String JavaDoc value = requireAttribute(var, "value");
1352            if (variables.contains(name))
1353                parseWarn(var, "Variable '" + name + "' being overwritten");
1354            variables.setProperty(name, value);
1355        }
1356        notifyCompilerListener("addVariables", CompilerListener.END, data);
1357    }
1358
1359    /**
1360     * Properties declaration is a fragment of the xml file. For example:
1361     *
1362     * <pre>
1363     *
1364     *
1365     *
1366     *
1367     * &lt;properties&gt;
1368     * &lt;property name=&quot;app.name&quot; value=&quot;Property Laden Installer&quot;/&gt;
1369     * &lt;!-- Ant styles 'location' and 'refid' are not yet supported --&gt;
1370     * &lt;property file=&quot;filename-relative-to-install?&quot;/&gt;
1371     * &lt;property file=&quot;filename-relative-to-install?&quot; prefix=&quot;prefix&quot;/&gt;
1372     * &lt;!-- Ant style 'url' and 'resource' are not yet supported --&gt;
1373     * &lt;property environment=&quot;prefix&quot;/&gt;
1374     * &lt;/properties&gt;
1375     *
1376     *
1377     *
1378     *
1379     * </pre>
1380     *
1381     * variable declared in this can be referred to in parsable files.
1382     *
1383     * @param data The XML data.
1384     * @exception CompilerException Description of the Exception
1385     */

1386    protected void substituteProperties(XMLElement data) throws CompilerException
1387    {
1388        notifyCompilerListener("substituteProperties", CompilerListener.BEGIN, data);
1389
1390        XMLElement root = data.getFirstChildNamed("properties");
1391        if (root != null)
1392        {
1393            // add individual properties
1394
Iterator JavaDoc iter = root.getChildrenNamed("property").iterator();
1395            while (iter.hasNext())
1396            {
1397                XMLElement prop = (XMLElement) iter.next();
1398                Property property = new Property(prop, this);
1399                property.execute();
1400            }
1401        }
1402
1403        // temporarily remove the 'properties' branch, replace all properties in
1404
// the remaining DOM, and replace properties branch.
1405
// TODO: enhance XMLElement with an "indexOf(XMLElement)" method
1406
// and addChild(XMLElement, int) so returns to the same place.
1407
if (root != null) data.removeChild(root);
1408
1409        substituteAllProperties(data);
1410        if (root != null) data.addChild(root);
1411
1412        notifyCompilerListener("substituteProperties", CompilerListener.END, data);
1413    }
1414
1415    /**
1416     * Perform recursive substitution on all properties
1417     */

1418    protected void substituteAllProperties(XMLElement element) throws CompilerException
1419    {
1420        Enumeration JavaDoc attributes = element.enumerateAttributeNames();
1421        while (attributes.hasMoreElements())
1422        {
1423            String JavaDoc name = (String JavaDoc) attributes.nextElement();
1424            String JavaDoc value = compiler.replaceProperties(element.getAttribute(name));
1425            element.setAttribute(name, value);
1426        }
1427
1428        String JavaDoc content = element.getContent();
1429        if (content != null)
1430        {
1431            element.setContent(compiler.replaceProperties(content));
1432        }
1433
1434        Enumeration JavaDoc children = element.enumerateChildren();
1435        while (children.hasMoreElements())
1436        {
1437            XMLElement child = (XMLElement) children.nextElement();
1438            substituteAllProperties(child);
1439        }
1440    }
1441
1442    /**
1443     * Returns the XMLElement representing the installation XML file.
1444     *
1445     * @return The XML tree.
1446     * @exception CompilerException For problems with the installation file
1447     * @exception IOException for errors reading the installation file
1448     */

1449    protected XMLElement getXMLTree() throws CompilerException, IOException JavaDoc
1450    {
1451        // Initialises the parser
1452
IXMLReader reader = null;
1453        if( filename != null )
1454        {
1455            File JavaDoc file = new File JavaDoc(filename).getAbsoluteFile();
1456            if (!file.canRead()) throw new CompilerException("Invalid file: " + file);
1457            reader = new StdXMLReader(new FileInputStream JavaDoc(filename));
1458            // add izpack built in property
1459
compiler.setProperty("izpack.file", file.toString());
1460        }
1461        else if( installText != null )
1462        {
1463            reader = StdXMLReader.stringReader(installText);
1464        }
1465        else
1466        {
1467            throw new CompilerException("Neither install file or text specified");
1468        }
1469
1470        StdXMLParser parser = new StdXMLParser();
1471        parser.setBuilder(new StdXMLBuilder());
1472        parser.setReader(reader);
1473        parser.setValidator(new NonValidator());
1474
1475        // We get it
1476
XMLElement data = null;
1477        try
1478        {
1479            data = (XMLElement) parser.parse();
1480        }
1481        catch (Exception JavaDoc x)
1482        {
1483            throw new CompilerException("Error parsing installation file", x);
1484        }
1485
1486        // We check it
1487
if (!"installation".equalsIgnoreCase(data.getName()))
1488            parseError(data, "this is not an IzPack XML installation file");
1489        if (!requireAttribute(data, "version").equalsIgnoreCase(VERSION))
1490            parseError(data, "the file version is different from the compiler version");
1491
1492        // We finally return the tree
1493
return data;
1494    }
1495
1496    protected int getOverrideValue(XMLElement f) throws CompilerException
1497    {
1498        int override = PackFile.OVERRIDE_UPDATE;
1499
1500        String JavaDoc override_val = f.getAttribute("override");
1501        if (override_val != null)
1502        {
1503            if ("true".equalsIgnoreCase(override_val))
1504            {
1505                override = PackFile.OVERRIDE_TRUE;
1506            }
1507            else if ("false".equalsIgnoreCase(override_val))
1508            {
1509                override = PackFile.OVERRIDE_FALSE;
1510            }
1511            else if ("asktrue".equalsIgnoreCase(override_val))
1512            {
1513                override = PackFile.OVERRIDE_ASK_TRUE;
1514            }
1515            else if ("askfalse".equalsIgnoreCase(override_val))
1516            {
1517                override = PackFile.OVERRIDE_ASK_FALSE;
1518            }
1519            else if ("update".equalsIgnoreCase(override_val))
1520            {
1521                override = PackFile.OVERRIDE_UPDATE;
1522            }
1523            else
1524                parseError(f, "invalid value for attribute \"override\"");
1525        }
1526
1527        return override;
1528    }
1529
1530    /**
1531     * Look for a project specified resources, which, if not absolute, are sought relative to the
1532     * projects basedir. The path should use '/' as the fileSeparator. If the resource is not found,
1533     * a CompilerException is thrown indicating fault in the parent element.
1534     *
1535     * @param path the relative path (using '/' as separator) to the resource.
1536     * @param desc the description of the resource used to report errors
1537     * @param parent the XMLElement the resource is specified in, used to report errors
1538     * @return a URL to the resource.
1539     */

1540    private URL JavaDoc findProjectResource(String JavaDoc path, String JavaDoc desc, XMLElement parent)
1541            throws CompilerException
1542    {
1543        URL JavaDoc url = null;
1544        File JavaDoc resource = new File JavaDoc(path);
1545        if (!resource.isAbsolute()) resource = new File JavaDoc(basedir, path);
1546
1547        if (!resource.exists()) // fatal
1548
parseError(parent, desc + " not found: " + resource);
1549
1550        try
1551        {
1552            url = resource.toURL();
1553        }
1554        catch (MalformedURLException JavaDoc how)
1555        {
1556            parseError(parent, desc + "(" + resource + ")", how);
1557        }
1558
1559        return url;
1560    }
1561
1562    /**
1563     * Look for an IzPack resource either in the compiler jar, or within IZPACK_HOME. The path must
1564     * not be absolute. The path must use '/' as the fileSeparator (it's used to access the jar
1565     * file). If the resource is not found, a CompilerException is thrown indicating fault in the
1566     * parent element.
1567     *
1568     * @param path the relative path (using '/' as separator) to the resource.
1569     * @param desc the description of the resource used to report errors
1570     * @param parent the XMLElement the resource is specified in, used to report errors
1571     * @return a URL to the resource.
1572     */

1573    private URL JavaDoc findIzPackResource(String JavaDoc path, String JavaDoc desc, XMLElement parent)
1574            throws CompilerException
1575    {
1576        URL JavaDoc url = getClass().getResource("/" + path);
1577        if (url == null)
1578        {
1579            File JavaDoc resource = new File JavaDoc(path);
1580
1581            if (!resource.isAbsolute()) resource = new File JavaDoc(Compiler.IZPACK_HOME, path);
1582
1583            if (!resource.exists()) // fatal
1584
parseError(parent, desc + " not found: " + resource);
1585
1586            try
1587            {
1588                url = resource.toURL();
1589            }
1590            catch (MalformedURLException JavaDoc how)
1591            {
1592                parseError(parent, desc + "(" + resource + ")", how);
1593            }
1594        }
1595
1596        return url;
1597    }
1598
1599    /**
1600     * Create parse error with consistent messages. Includes file name. For use When parent is
1601     * unknown.
1602     *
1603     * @param message Brief message explaining error
1604     */

1605    protected void parseError(String JavaDoc message) throws CompilerException
1606    {
1607        throw new CompilerException(filename + ":" + message);
1608    }
1609
1610    /**
1611     * Create parse error with consistent messages. Includes file name and line # of parent. It is
1612     * an error for 'parent' to be null.
1613     *
1614     * @param parent The element in which the error occured
1615     * @param message Brief message explaining error
1616     */

1617    protected void parseError(XMLElement parent, String JavaDoc message) throws CompilerException
1618    {
1619        throw new CompilerException(filename + ":" + parent.getLineNr() + ": " + message);
1620    }
1621
1622    /**
1623     * Create a chained parse error with consistent messages. Includes file name and line # of
1624     * parent. It is an error for 'parent' to be null.
1625     *
1626     * @param parent The element in which the error occured
1627     * @param message Brief message explaining error
1628     */

1629    protected void parseError(XMLElement parent, String JavaDoc message, Throwable JavaDoc cause)
1630            throws CompilerException
1631    {
1632        throw new CompilerException(filename + ":" + parent.getLineNr() + ": " + message, cause);
1633    }
1634
1635    /**
1636     * Create a parse warning with consistent messages. Includes file name and line # of parent. It
1637     * is an error for 'parent' to be null.
1638     *
1639     * @param parent The element in which the warning occured
1640     * @param message Warning message
1641     */

1642    protected void parseWarn(XMLElement parent, String JavaDoc message)
1643    {
1644        System.out.println(filename + ":" + parent.getLineNr() + ": " + message);
1645    }
1646
1647    /**
1648     * Call getFirstChildNamed on the parent, producing a meaningful error message on failure. It is
1649     * an error for 'parent' to be null.
1650     *
1651     * @param parent The element to search for a child
1652     * @param name Name of the child element to get
1653     */

1654    protected XMLElement requireChildNamed(XMLElement parent, String JavaDoc name) throws CompilerException
1655    {
1656        XMLElement child = parent.getFirstChildNamed(name);
1657        if (child == null)
1658            parseError(parent, "<" + parent.getName() + "> requires child <" + name + ">");
1659        return child;
1660    }
1661
1662    /**
1663     * Call getContent on an element, producing a meaningful error message if not present, or empty,
1664     * or a valid URL. It is an error for 'element' to be null.
1665     *
1666     * @param element The element to get content of
1667     */

1668    protected URL JavaDoc requireURLContent(XMLElement element) throws CompilerException
1669    {
1670        URL JavaDoc url = null;
1671        try
1672        {
1673            url = new URL JavaDoc(requireContent(element));
1674        }
1675        catch (MalformedURLException JavaDoc x)
1676        {
1677            parseError(element, "<" + element.getName() + "> requires valid URL", x);
1678        }
1679        return url;
1680    }
1681
1682    /**
1683     * Call getContent on an element, producing a meaningful error message if not present, or empty.
1684     * It is an error for 'element' to be null.
1685     *
1686     * @param element The element to get content of
1687     */

1688    protected String JavaDoc requireContent(XMLElement element) throws CompilerException
1689    {
1690        String JavaDoc content = element.getContent();
1691        if (content == null || content.length() == 0)
1692            parseError(element, "<" + element.getName() + "> requires content");
1693        return content;
1694    }
1695
1696    /**
1697     * Call getAttribute on an element, producing a meaningful error message if not present, or
1698     * empty. It is an error for 'element' or 'attribute' to be null.
1699     *
1700     * @param element The element to get the attribute value of
1701     * @param attribute The name of the attribute to get
1702     */

1703    protected String JavaDoc requireAttribute(XMLElement element, String JavaDoc attribute)
1704            throws CompilerException
1705    {
1706        String JavaDoc value = element.getAttribute(attribute);
1707        if (value == null)
1708            parseError(element, "<" + element.getName() + "> requires attribute '" + attribute
1709                    + "'");
1710        return value;
1711    }
1712
1713    /**
1714     * Get a required attribute of an element, ensuring it is an integer. A meaningful error message
1715     * is generated as a CompilerException if not present or parseable as an int. It is an error for
1716     * 'element' or 'attribute' to be null.
1717     *
1718     * @param element The element to get the attribute value of
1719     * @param attribute The name of the attribute to get
1720     */

1721    protected int requireIntAttribute(XMLElement element, String JavaDoc attribute)
1722            throws CompilerException
1723    {
1724        String JavaDoc value = element.getAttribute(attribute);
1725        if (value == null || value.length() == 0)
1726            parseError(element, "<" + element.getName() + "> requires attribute '" + attribute
1727                    + "'");
1728        try
1729        {
1730            return Integer.parseInt(value);
1731        }
1732        catch (NumberFormatException JavaDoc x)
1733        {
1734            parseError(element, "'" + attribute + "' must be an integer");
1735        }
1736        return 0; // never happens
1737
}
1738
1739    /**
1740     * Call getAttribute on an element, producing a meaningful error message if not present, or one
1741     * of "yes" or "no". It is an error for 'element' or 'attribute' to be null.
1742     *
1743     * @param element The element to get the attribute value of
1744     * @param attribute The name of the attribute to get
1745     */

1746    protected boolean requireYesNoAttribute(XMLElement element, String JavaDoc attribute)
1747            throws CompilerException
1748    {
1749        String JavaDoc value = requireAttribute(element, attribute);
1750        if ("yes".equalsIgnoreCase(value)) return true;
1751        if ("no".equalsIgnoreCase(value)) return false;
1752
1753        parseError(element, "<" + element.getName() + "> invalid attribute '" + attribute
1754                + "': Expected (yes|no)");
1755
1756        return false; // never happens
1757
}
1758
1759    /**
1760     * Call getAttribute on an element, producing a meaningful warning if not "yes" or "no". If the
1761     * 'element' or 'attribute' are null, the default value is returned.
1762     *
1763     * @param element The element to get the attribute value of
1764     * @param attribute The name of the attribute to get
1765     * @param defaultValue Value returned if attribute not present or invalid
1766     */

1767    protected boolean validateYesNoAttribute(XMLElement element, String JavaDoc attribute,
1768            boolean defaultValue)
1769    {
1770        if (element == null) return defaultValue;
1771
1772        String JavaDoc value = element.getAttribute(attribute, (defaultValue ? "yes" : "no"));
1773        if ("yes".equalsIgnoreCase(value)) return true;
1774        if ("no".equalsIgnoreCase(value)) return false;
1775
1776        // TODO: should this be an error if it's present but "none of the
1777
// above"?
1778
parseWarn(element, "<" + element.getName() + "> invalid attribute '" + attribute
1779                + "': Expected (yes|no) if present");
1780
1781        return defaultValue;
1782    }
1783
1784    /**
1785     * The main method if the compiler is invoked by a command-line call.
1786     *
1787     * @param args The arguments passed on the command-line.
1788     */

1789    public static void main(String JavaDoc[] args)
1790    {
1791        // Outputs some informations
1792
System.out.println("");
1793        System.out.println(".:: IzPack - Version " + Compiler.IZPACK_VERSION + " ::.");
1794        System.out.println("");
1795        System.out.println("< compiler specifications version: " + VERSION + " >");
1796        System.out.println("");
1797        System.out.println("- Copyright (C) 2001-2007 Julien Ponge");
1798        System.out.println("- Visit http://www.izforge.com/izpack/ for the latest releases");
1799        System.out.println("- Released under the terms of the Apache Software License version 2.0.");
1800        System.out.println("");
1801
1802        // exit code 1 means: error
1803
int exitCode = 1;
1804        String JavaDoc home = ".";
1805        // We get the IzPack home directory
1806
String JavaDoc izHome = System.getProperty("IZPACK_HOME");
1807        if (izHome != null) home = izHome;
1808                    
1809        // We analyse the command line parameters
1810
try
1811        {
1812            // Our arguments
1813
String JavaDoc filename;
1814            String JavaDoc base = ".";
1815            String JavaDoc kind = "standard";
1816            String JavaDoc output;
1817            String JavaDoc compr_format = "default";
1818            int compr_level = -1;
1819
1820            // First check
1821
int nArgs = args.length;
1822            if (nArgs < 1) throw new Exception JavaDoc("no arguments given");
1823
1824            // The users wants to know the command line parameters
1825
if ("-?".equalsIgnoreCase(args[0]))
1826            {
1827                System.out.println("-> Command line parameters are : (xml file) [args]");
1828                System.out.println(" (xml file): the xml file describing the installation");
1829                System.out.println(" -h (IzPack home) : the root path of IzPack. This will be needed");
1830                System.out.println(" if the compiler is not called in the root directory of IzPack.");
1831                System.out.println(" Do not forget quotations if there are blanks in the path.");
1832                         System.out
1833                          .println(" -b (base) : indicates the base path that the compiler will use for filenames");
1834                System.out.println(" of sources. Default is the current path. Attend to -h.");
1835                System.out.println(" -k (kind) : indicates the kind of installer to generate");
1836                System.out.println(" default is standard");
1837                System.out.println(" -o (out) : indicates the output file name");
1838                System.out.println(" default is the xml file name\n");
1839                System.out
1840                        .println(" -c (compression) : indicates the compression format to be used for packs");
1841                System.out.println(" default is the internal deflate compression\n");
1842                System.out
1843                        .println(" -l (compression-level) : indicates the level for the used compression format");
1844                System.out.println(" if supported. Only integer are valid\n");
1845
1846                System.out
1847                        .println(" When using vm option -DSTACKTRACE=true there is all kind of debug info ");
1848                System.out.println("");
1849                exitCode = 0;
1850            }
1851            else
1852            {
1853                // We can parse the other parameters & try to compile the
1854
// installation
1855

1856                // We get the input file name and we initialize the output file
1857
// name
1858
filename = args[0];
1859                // default jar files names are based on input file name
1860
output = filename.substring(0, filename.length() - 3) + "jar";
1861
1862                // We parse the other ones
1863
int pos = 1;
1864                while (pos < nArgs)
1865                    if ((args[pos].startsWith("-")) && (args[pos].length() == 2))
1866                    {
1867                        switch (args[pos].toLowerCase().charAt(1))
1868                        {
1869                        case 'b':
1870                            if ((pos + 1) < nArgs)
1871                            {
1872                                pos++;
1873                                base = args[pos];
1874                            }
1875                            else
1876                                throw new Exception JavaDoc("base argument missing");
1877                            break;
1878                        case 'k':
1879                            if ((pos + 1) < nArgs)
1880                            {
1881                                pos++;
1882                                kind = args[pos];
1883                            }
1884                            else
1885                                throw new Exception JavaDoc("kind argument missing");
1886                            break;
1887                        case 'o':
1888                            if ((pos + 1) < nArgs)
1889                            {
1890                                pos++;
1891                                output = args[pos];
1892                            }
1893                            else
1894                                throw new Exception JavaDoc("output argument missing");
1895                            break;
1896                        case 'c':
1897                            if ((pos + 1) < nArgs)
1898                            {
1899                                pos++;
1900                                compr_format = args[pos];
1901                            }
1902                            else
1903                                throw new Exception JavaDoc("compression format argument missing");
1904                            break;
1905                        case 'l':
1906                            if ((pos + 1) < nArgs)
1907                            {
1908                                pos++;
1909                                compr_level = Integer.parseInt(args[pos]);
1910                            }
1911                            else
1912                                throw new Exception JavaDoc("compression level argument missing");
1913                            break;
1914                        case 'h':
1915                            if ((pos + 1) < nArgs)
1916                            {
1917                                pos++;
1918                                home = args[pos];
1919                            }
1920                            else
1921                                throw new Exception JavaDoc("IzPack home path argument missing");
1922                            break;
1923                        default:
1924                            throw new Exception JavaDoc("unknown argument");
1925                        }
1926                        pos++;
1927                    }
1928                    else
1929                        throw new Exception JavaDoc("bad argument");
1930
1931                home = resolveIzPackHome(home);
1932                // Outputs what we are going to do
1933
System.out.println("-> Processing : " + filename);
1934                System.out.println("-> Output : " + output);
1935                System.out.println("-> Base path : " + base);
1936                System.out.println("-> Kind : " + kind);
1937                System.out.println("-> Compression : " + compr_format);
1938                System.out.println("-> Compr. level: " + compr_level);
1939                System.out.println("-> IzPack home : " + home);
1940                System.out.println("");
1941
1942                
1943                Compiler.setIzpackHome(home);
1944
1945
1946                // Calls the compiler
1947
CmdlinePackagerListener listener = new CmdlinePackagerListener();
1948                CompilerConfig compiler = new CompilerConfig(filename, base, kind, output,
1949                        compr_format, compr_level, listener, (String JavaDoc) null);
1950                compiler.executeCompiler();
1951
1952                // Waits
1953
while (compiler.isAlive())
1954                    Thread.sleep(100);
1955
1956                if (compiler.wasSuccessful()) exitCode = 0;
1957
1958                System.out.println("Build time: " + new Date JavaDoc());
1959            }
1960        }
1961        catch (Exception JavaDoc err)
1962        {
1963            // Something bad has happened
1964
System.err.println("-> Fatal error :");
1965            System.err.println(" " + err.getMessage());
1966            err.printStackTrace();
1967            System.err.println("");
1968            System.err.println("(tip : use -? to get the commmand line parameters)");
1969        }
1970
1971        // Closes the JVM
1972
System.exit(exitCode);
1973    }
1974
1975    private static String JavaDoc resolveIzPackHome(String JavaDoc home)
1976    {
1977        File JavaDoc test = new File JavaDoc(home, IZ_TEST_SUBDIR + File.separator + IZ_TEST_FILE);
1978        if( test != null && test.exists())
1979            return( home);
1980        // Try to resolve the path using compiler.jar which also should be under
1981
// IZPACK_HOME.
1982
String JavaDoc self = Compiler JavaDoc.class.getName();
1983        self = self.replace('.', '/');
1984        self = "/" + self + ".class";
1985        URL JavaDoc url = Compiler JavaDoc.class.getResource(self);
1986        String JavaDoc np = url.getFile();
1987        int start = np.indexOf(self);
1988        np = np.substring(0, start);
1989        if( np.endsWith("!"))
1990        { // Where shut IZPACK_HOME at the standalone-compiler be??
1991
// No idea.
1992
if(np.endsWith("standalone-compiler.jar!"))
1993                return(".");
1994            np = np.substring(0, np.length() - 1);
1995        }
1996        File JavaDoc root = null;
1997        if( URI.create(np).isAbsolute())
1998            root = new File JavaDoc(URI.create(np));
1999        else
2000            root = new File JavaDoc(np);
2001        while(true)
2002        {
2003            if( root == null )
2004                throw new IllegalArgumentException JavaDoc("No valid IzPack home directory found");
2005            test = new File JavaDoc(root, IZ_TEST_SUBDIR + File.separator + IZ_TEST_FILE);
2006            if( test.exists())
2007                return(root.getAbsolutePath());
2008            root = root.getParentFile();
2009        }
2010    }
2011
2012    // -------------------------------------------------------------------------
2013
// ------------- Listener stuff ------------------------- START ------------
2014

2015    /**
2016     * This method parses install.xml for defined listeners and put them in the right position. If
2017     * posible, the listeners will be validated. Listener declaration is a fragmention in
2018     * install.xml like : <listeners> <listener compiler="PermissionCompilerListener"
2019     * installer="PermissionInstallerListener"/> </<listeners>
2020     *
2021     * @param data the XML data
2022     * @exception Exception Description of the Exception
2023     */

2024    private void addCustomListeners(XMLElement data) throws Exception JavaDoc
2025    {
2026        // We get the listeners
2027
XMLElement root = data.getFirstChildNamed("listeners");
2028        if (root == null) return;
2029        Iterator JavaDoc iter = root.getChildrenNamed("listener").iterator();
2030        while (iter.hasNext())
2031        {
2032            XMLElement xmlAction = (XMLElement) iter.next();
2033            Object JavaDoc[] listener = getCompilerListenerInstance(xmlAction);
2034            if (listener != null)
2035                addCompilerListener((CompilerListener) listener[0]);
2036            String JavaDoc[] typeNames = new String JavaDoc[] { "installer", "uninstaller"};
2037            int[] types = new int[] { CustomData.INSTALLER_LISTENER,
2038                    CustomData.UNINSTALLER_LISTENER};
2039            for (int i = 0; i < typeNames.length; ++i)
2040            {
2041                String JavaDoc className = xmlAction.getAttribute(typeNames[i]);
2042                if (className != null)
2043                {
2044                    // Check for a jar attribute on the listener
2045
String JavaDoc jarPath = xmlAction.getAttribute("jar");
2046                    jarPath = compiler.replaceProperties(jarPath);
2047                    if( jarPath == null )
2048                        jarPath = "bin/customActions/" + className + ".jar";
2049                    List JavaDoc constraints = OsConstraint.getOsList(xmlAction);
2050                    compiler.addCustomListener(types[i], className, jarPath, constraints);
2051                }
2052            }
2053        }
2054
2055    }
2056
2057    /**
2058     * Returns a list which contains the pathes of all files which are included in the given url.
2059     * This method expects as the url param a jar.
2060     *
2061     * @param url url of the jar file
2062     * @return full qualified paths of the contained files
2063     * @throws Exception
2064     */

2065    private List JavaDoc getContainedFilePaths(URL JavaDoc url) throws Exception JavaDoc
2066    {
2067        JarInputStream JavaDoc jis = new JarInputStream JavaDoc(url.openStream());
2068        ZipEntry JavaDoc zentry = null;
2069        ArrayList JavaDoc fullNames = new ArrayList JavaDoc();
2070        while ((zentry = jis.getNextEntry()) != null)
2071        {
2072            String JavaDoc name = zentry.getName();
2073            // Add only files, no directory entries.
2074
if (!zentry.isDirectory()) fullNames.add(name);
2075        }
2076        jis.close();
2077        return (fullNames);
2078    }
2079
2080    /**
2081     * Returns the qualified class name for the given class. This method expects as the url param a
2082     * jar file which contains the given class. It scans the zip entries of the jar file.
2083     *
2084     * @param url url of the jar file which contains the class
2085     * @param className short name of the class for which the full name should be resolved
2086     * @return full qualified class name
2087     * @throws IOException
2088     */

2089    private String JavaDoc getFullClassName(URL JavaDoc url, String JavaDoc className) throws IOException JavaDoc //throws Exception
2090
{
2091        JarInputStream JavaDoc jis = new JarInputStream JavaDoc(url.openStream());
2092        ZipEntry JavaDoc zentry = null;
2093        while ((zentry = jis.getNextEntry()) != null)
2094        {
2095            String JavaDoc name = zentry.getName();
2096            int lastPos = name.lastIndexOf(".class");
2097            if (lastPos < 0)
2098            {
2099                continue; // No class file.
2100
}
2101            name = name.replace('/', '.');
2102            int pos = -1;
2103            int nonCasePos = -1;
2104            if (className != null)
2105            {
2106                pos = name.indexOf(className);
2107                nonCasePos = name.toLowerCase().indexOf(className.toLowerCase());
2108            }
2109            if (pos != -1 && name.length() == pos + className.length() + 6) // "Main" class found
2110
{
2111                jis.close();
2112                return (name.substring(0, lastPos));
2113            }
2114            
2115            if (nonCasePos != -1 && name.length() == nonCasePos + className.length() + 6)
2116                // "Main" class with different case found
2117
throw new IllegalArgumentException JavaDoc("Fatal error! The declared panel name in the xml file ("
2118                        + className + ") differs in case to the founded class file (" + name + ").");
2119        }
2120        jis.close();
2121        return (null);
2122    }
2123
2124    /**
2125     * Returns the compiler listener which is defined in the xml element. As
2126     * xml element a "listner" node will be expected. Additional it is expected,
2127     * that either "findIzPackResource" returns an url based on
2128     * "bin/customActions/[className].jar", or that the listener element has
2129     * a jar attribute specifying the listener jar path. The class will be
2130     * loaded via an URLClassLoader.
2131     *
2132     * @param var the xml element of the "listener" node
2133     * @return instance of the defined compiler listener
2134     * @throws Exception
2135     */

2136    private Object JavaDoc[] getCompilerListenerInstance(XMLElement var) throws Exception JavaDoc
2137    {
2138        String JavaDoc className = var.getAttribute("compiler");
2139        Class JavaDoc listener = null;
2140        Object JavaDoc instance = null;
2141        if (className == null) return (null);
2142
2143        // CustomAction files come in jars packaged IzPack, or they can be
2144
// specified via a jar attribute on the listener
2145
String JavaDoc jarPath = var.getAttribute("jar");
2146        jarPath = compiler.replaceProperties(jarPath);
2147        if( jarPath == null )
2148            jarPath = "bin/customActions/" + className + ".jar";
2149        URL JavaDoc url = findIzPackResource(jarPath, "CustomAction jar file", var);
2150        String JavaDoc fullName = getFullClassName(url, className);
2151        if (fullName == null){
2152            // class not found
2153
return null;
2154        }
2155        if (url != null)
2156        {
2157            if (getClass().getResource("/" + jarPath) != null)
2158            { // Oops, standalone, URLClassLoader will not work ...
2159
// Write the jar to a temp file.
2160
InputStream JavaDoc in = null;
2161                FileOutputStream JavaDoc outFile = null;
2162                byte[] buffer = new byte[5120];
2163                File JavaDoc tf = null;
2164                try
2165                {
2166                    tf = File.createTempFile("izpj", ".jar");
2167                    tf.deleteOnExit();
2168                    outFile = new FileOutputStream JavaDoc(tf);
2169                    in = getClass().getResourceAsStream("/" + jarPath);
2170                    long bytesCopied = 0;
2171                    int bytesInBuffer;
2172                    while ((bytesInBuffer = in.read(buffer)) != -1)
2173                    {
2174                        outFile.write(buffer, 0, bytesInBuffer);
2175                        bytesCopied += bytesInBuffer;
2176                    }
2177                }
2178                finally
2179                {
2180                    if (in != null) in.close();
2181                    if (outFile != null) outFile.close();
2182                }
2183                url = tf.toURL();
2184
2185            }
2186            // Use the class loader of the interface as parent, else
2187
// compile will fail at using it via an Ant task.
2188
URLClassLoader JavaDoc ucl = new URLClassLoader JavaDoc(new URL JavaDoc[] { url}, CompilerListener.class
2189                    .getClassLoader());
2190            listener = ucl.loadClass(fullName);
2191        }
2192        if (listener != null)
2193            instance = listener.newInstance();
2194        else
2195            parseError(var, "Cannot find defined compiler listener " + className);
2196        if (!CompilerListener.class.isInstance(instance))
2197            parseError(var, "'" + className + "' must be implemented "
2198                    + CompilerListener.class.toString());
2199        List JavaDoc constraints = OsConstraint.getOsList(var);
2200        return (new Object JavaDoc[] { instance, className, constraints});
2201    }
2202
2203    /**
2204     * Add a CompilerListener. A registered CompilerListener will be called at every enhancmend
2205     * point of compiling.
2206     *
2207     * @param pe CompilerListener which should be added
2208     */

2209    private void addCompilerListener(CompilerListener pe)
2210    {
2211        compilerListeners.add(pe);
2212    }
2213
2214    /**
2215     * Calls all defined compile listeners notify method with the given data
2216     *
2217     * @param callerName name of the calling method as string
2218     * @param state CompileListener.BEGIN or END
2219     * @param data current install data
2220     * @throws CompilerException
2221     */

2222    private void notifyCompilerListener(String JavaDoc callerName, int state, XMLElement data)
2223            throws CompilerException
2224    {
2225        Iterator JavaDoc i = compilerListeners.iterator();
2226        IPackager packager = compiler.getPackager();
2227        while (i != null && i.hasNext())
2228        {
2229            CompilerListener listener = (CompilerListener) i.next();
2230            listener.notify(callerName, state, data, packager);
2231        }
2232
2233    }
2234
2235    /**
2236     * Calls the reviseAdditionalDataMap method of all registered CompilerListener's.
2237     *
2238     * @param f file releated XML node
2239     * @return a map with the additional attributes
2240     */

2241    private Map JavaDoc getAdditionals(XMLElement f) throws CompilerException
2242    {
2243        Iterator JavaDoc i = compilerListeners.iterator();
2244        Map JavaDoc retval = null;
2245        try
2246        {
2247            while (i != null && i.hasNext())
2248            {
2249                retval = ((CompilerListener) i.next()).reviseAdditionalDataMap(retval, f);
2250            }
2251        }
2252        catch (CompilerException ce)
2253        {
2254            parseError(f, ce.getMessage());
2255        }
2256        return (retval);
2257    }
2258}
2259
Popular Tags