Statistics
| Revision:

root / import / ext3D / trunk / install-extension3d / IzPack / src / lib / com / izforge / izpack / installer / Unpacker.java @ 15280

History | View | Annotate | Download (31.2 KB)

1
/*
2
 *  $Id: Unpacker.java,v 1.1 2006/06/14 07:29:07 cesar Exp $
3
 *  IzPack
4
 *  Copyright (C) 2001-2004 Julien Ponge
5
 *
6
 *  File :               Unpacker.java
7
 *  Description :        The unpacker class.
8
 *  Author's email :     julien@izforge.com
9
 *  Author's Website :   http://www.izforge.com
10
 *
11
 *  Portions are Copyright (c) 2001 Johannes Lehtinen
12
 *  johannes.lehtinen@iki.fi
13
 *  http://www.iki.fi/jle/
14
 *
15
 *  This program is free software; you can redistribute it and/or
16
 *  modify it under the terms of the GNU General Public License
17
 *  as published by the Free Software Foundation; either version 2
18
 *  of the License, or any later version.
19
 *
20
 *  This program is distributed in the hope that it will be useful,
21
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
22
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23
 *  GNU General Public License for more details.
24
 *
25
 *  You should have received a copy of the GNU General Public License
26
 *  along with this program; if not, write to the Free Software
27
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28
 */
29
package com.izforge.izpack.installer;
30

    
31
import java.io.File;
32
import java.io.FileInputStream;
33
import java.io.FileNotFoundException;
34
import java.io.FileOutputStream;
35
import java.io.IOException;
36
import java.io.InputStream;
37
import java.io.ObjectInputStream;
38
import java.net.URL;
39
import java.util.ArrayList;
40
import java.util.HashSet;
41
import java.util.Iterator;
42
import java.util.List;
43
import java.util.Stack;
44
import java.util.TreeSet;
45
import java.util.zip.ZipEntry;
46
import java.util.zip.ZipInputStream;
47
import java.util.zip.ZipOutputStream;
48

    
49
import org.apache.regexp.RE;
50
import org.apache.regexp.RECompiler;
51
import org.apache.regexp.RESyntaxException;
52

    
53
import com.izforge.izpack.ExecutableFile;
54
import com.izforge.izpack.LocaleDatabase;
55
import com.izforge.izpack.Pack;
56
import com.izforge.izpack.PackFile;
57
import com.izforge.izpack.ParsableFile;
58
import com.izforge.izpack.UpdateCheck;
59
import com.izforge.izpack.event.InstallerListener;
60
import com.izforge.izpack.util.AbstractUIHandler;
61
import com.izforge.izpack.util.AbstractUIProgressHandler;
62
import com.izforge.izpack.util.FileExecutor;
63
import com.izforge.izpack.util.IoHelper;
64
import com.izforge.izpack.util.OsConstraint;
65

    
66
/**
67
 *  Unpacker class.
68
 *
69
 * @author     Julien Ponge
70
 * @author     Johannes Lehtinen
71
 */
72
public class Unpacker extends Thread
73
{
74
  /**  The installdata. */
75
  private AutomatedInstallData idata;
76

    
77
  /**  The installer listener. */
78
  private AbstractUIProgressHandler handler;
79

    
80
  /**  The uninstallation data. */
81
  private UninstallData udata;
82

    
83
  /**  The variables substitutor. */
84
  private VariableSubstitutor vs;
85

    
86
  /**  The instances of the unpacker objects. */
87
  private static ArrayList instances = new ArrayList();
88

    
89
  /**  The absolute path of the installation. (NOT the canonical!) */
90
  private File absolute_installpath;
91

    
92
  /** The packs locale database. */
93
  private LocaleDatabase langpack = null;
94

    
95
  /** The name of the XML file that specifies the panel langpack */
96
  private static final String LANG_FILE_NAME = "packsLang.xml";
97

    
98
  /**
99
   *  The constructor.
100
   *
101
   * @param  idata     The installation data.
102
   * @param  handler   The installation progress handler.
103
   */
104
  public Unpacker(
105
    AutomatedInstallData idata,
106
    AbstractUIProgressHandler handler)
107
  {
108
    super("IzPack - Unpacker thread");
109
    try
110
    {
111
      String resource = LANG_FILE_NAME + "_" + idata.localeISO3;
112
      this.langpack = new LocaleDatabase(ResourceManager.getInstance().getInputStream(resource));
113
    }
114
    catch (Throwable exception)
115
    {}
116

    
117
    this.idata = idata;
118
    this.handler = handler;
119

    
120
    // Initialize the variable substitutor
121
    vs = new VariableSubstitutor(idata.getVariables());
122
  }
123

    
124
  /**
125
   *  Returns the active unpacker instances.
126
   *
127
   * @return    The active unpacker instances.
128
   */
129
  public static ArrayList getRunningInstances()
130
  {
131
    return instances;
132
  }
133

    
134
  /**  The run method.  */
135
  public void run()
136
  {
137
    instances.add(this);
138
    try
139
    {
140
      //
141
      // Initialisations
142
      FileOutputStream out = null;
143
      ArrayList parsables = new ArrayList();
144
      ArrayList executables = new ArrayList();
145
      ArrayList updatechecks = new ArrayList();
146
      List packs = idata.selectedPacks;
147
      int npacks = packs.size();
148
      handler.startAction("Unpacking", npacks);
149
      udata = UninstallData.getInstance();
150
      // Custom action listener stuff --- load listeners ----
151
      List [] customActions = getCustomActions();
152
      // Custom action listener stuff --- beforePacks ----
153
      informListeners(customActions, InstallerListener.BEFORE_PACKS,idata, 
154
        new Integer(npacks), handler);
155

    
156
      // We unpack the selected packs
157
      for (int i = 0; i < npacks; i++)
158
      {
159
        // We get the pack stream
160
        int n = idata.allPacks.indexOf(packs.get(i));
161

    
162
        // Custom action listener stuff --- beforePack ----
163
        informListeners(customActions, InstallerListener.BEFORE_PACK,packs.get(i),
164
          new Integer(npacks), handler);
165
        ObjectInputStream objIn = new ObjectInputStream(getPackAsStream(n));
166

    
167
        // We unpack the files
168
        int nfiles = objIn.readInt();
169

    
170
          //We get the internationalized name of the pack
171
          final Pack pack = ((Pack) packs.get(i));
172
          String stepname = pack.name;//the message to be passed to the installpanel
173
          if(langpack !=null && !(pack.id == null || pack.id.equals("")) )
174
          {
175

    
176
            final String name = langpack.getString(pack.id);
177
            if(name !=null && !name.equals(""))
178
            {
179
              stepname = name;
180
            }
181
          }
182
          handler.nextStep(stepname, i + 1, nfiles);
183
        for (int j = 0; j < nfiles; j++)
184
        {
185
          // We read the header
186
          PackFile pf = (PackFile) objIn.readObject();
187

    
188
          if (OsConstraint.oneMatchesCurrentSystem(pf.osConstraints()))
189
          {
190
            // We translate & build the path
191
            String path = IoHelper.translatePath(pf.getTargetPath(), vs);
192
            File pathFile = new File(path);
193
            File dest = pathFile;
194
            if (! pf.isDirectory())
195
              dest = pathFile.getParentFile();
196

    
197
            if (!dest.exists())
198
            {
199
              // If there are custom actions which would be called at
200
              // creating a directory, create it recursively.
201
              List fileListeners = customActions[customActions.length - 1];
202
              if( fileListeners != null && fileListeners.size() > 0 )
203
                mkDirsWithEnhancement(dest, pf, customActions );
204
              else  // Create it in on step.
205
              {
206
                if (!dest.mkdirs())
207
                {
208
                  handler.emitError(
209
                    "Error creating directories",
210
                    "Could not create directory\n" + dest.getPath());
211
                  handler.stopAction();
212
                  return;
213
                }
214
              }
215
            }
216

    
217
            if (pf.isDirectory())
218
              continue;
219

    
220
            // Custom action listener stuff --- beforeFile ----
221
            informListeners(customActions, InstallerListener.BEFORE_FILE,pathFile,
222
              pf, null);
223
            // We add the path to the log,
224
            udata.addFile(path);
225

    
226
            handler.progress(j, path);
227

    
228
            //if this file exists and should not be overwritten, check
229
            //what to do
230
            if ((pathFile.exists()) && (pf.override() != PackFile.OVERRIDE_TRUE))
231
            {
232
              boolean overwritefile = false;
233

    
234
              // don't overwrite file if the user said so
235
              if (pf.override() != PackFile.OVERRIDE_FALSE)
236
              {
237
                if (pf.override() == PackFile.OVERRIDE_TRUE)
238
                {
239
                  overwritefile = true;
240
                } else if (pf.override() == PackFile.OVERRIDE_UPDATE)
241
                {
242
                  // check mtime of involved files
243
                  // (this is not 100% perfect, because the already existing file might
244
                  // still be modified but the new installed is just a bit newer; we would
245
                  // need the creation time of the existing file or record with which mtime
246
                  // it was installed...)
247
                  overwritefile = (pathFile.lastModified() < pf.lastModified());
248
                } else
249
                {
250
                  int def_choice = -1;
251

    
252
                  if (pf.override() == PackFile.OVERRIDE_ASK_FALSE)
253
                    def_choice = AbstractUIHandler.ANSWER_NO;
254
                  if (pf.override() == PackFile.OVERRIDE_ASK_TRUE)
255
                    def_choice = AbstractUIHandler.ANSWER_YES;
256

    
257
                  int answer =
258
                    handler.askQuestion(
259
                      idata.langpack.getString("InstallPanel.overwrite.title")
260
                        + pathFile.getName(),
261
                      idata.langpack.getString(
262
                        "InstallPanel.overwrite.question")
263
                        + pathFile.getAbsolutePath(),
264
                      AbstractUIHandler.CHOICES_YES_NO,
265
                      def_choice);
266

    
267
                  overwritefile = (answer == AbstractUIHandler.ANSWER_YES);
268
                }
269

    
270
              }
271

    
272
              if (!overwritefile)
273
              {
274
                if (!pf.isBackReference() && !((Pack)packs.get(i)).loose)
275
                  objIn.skip(pf.length());
276
                continue;
277
              }
278

    
279
            }
280

    
281
            // We copy the file
282
            out = new FileOutputStream(pathFile);
283
            byte[] buffer = new byte[5120];
284
            long bytesCopied = 0;
285
            InputStream pis = objIn;
286
            if (pf.isBackReference())
287
            {
288
              InputStream is = getPackAsStream(pf.previousPackNumber);
289
              pis = new ObjectInputStream(is);
290
              //must wrap for blockdata use by objectstream (otherwise strange result)
291
              //skip on underlaying stream (for some reason not possible on ObjectStream)
292
              is.skip(pf.offsetInPreviousPack - 4);
293
              //but the stream header is now already read (== 4 bytes)
294
            }
295
            else if(((Pack)packs.get(i)).loose)
296
            {
297
              pis = new FileInputStream(pf.sourcePath);
298
            }
299
            while (bytesCopied < pf.length())
300
            {
301
              int maxBytes = (int)Math.min(pf.length() - bytesCopied, buffer.length);
302
              int bytesInBuffer = pis.read(buffer, 0, maxBytes);
303
              if (bytesInBuffer == -1)
304
                throw new IOException("Unexpected end of stream");
305

    
306
              out.write(buffer, 0, bytesInBuffer);
307

    
308
              bytesCopied += bytesInBuffer;
309
            }
310
            // Cleanings
311
            out.close();
312
            if (pis != objIn)
313
              pis.close();
314

    
315
            // Set file modification time if specified
316
            if (pf.lastModified() >= 0)
317
              pathFile.setLastModified(pf.lastModified());
318
            // Custom action listener stuff --- afterFile ----
319
            informListeners(customActions, InstallerListener.AFTER_FILE,pathFile,
320
              pf, null);
321

    
322

    
323
          } else
324
          {
325
            if (!pf.isBackReference())
326
              objIn.skip(pf.length());
327
          }
328
        }
329

    
330
        // Load information about parsable files
331
        int numParsables = objIn.readInt();
332
        for (int k = 0; k < numParsables; k++)
333
        {
334
          ParsableFile pf = (ParsableFile) objIn.readObject();
335
          pf.path = IoHelper.translatePath(pf.path, vs);
336
          parsables.add(pf);
337
        }
338

    
339
        // Load information about executable files
340
        int numExecutables = objIn.readInt();
341
        for (int k = 0; k < numExecutables; k++)
342
        {
343
          ExecutableFile ef = (ExecutableFile) objIn.readObject();
344
          ef.path = IoHelper.translatePath(ef.path, vs);
345
          if (null != ef.argList && !ef.argList.isEmpty())
346
          {
347
            String arg = null;
348
            for (int j = 0; j < ef.argList.size(); j++)
349
            {
350
              arg = (String) ef.argList.get(j);
351
              arg = IoHelper.translatePath(arg, vs);
352
              ef.argList.set(j, arg);
353
            }
354
          }
355
          executables.add(ef);
356
          if (ef.executionStage == ExecutableFile.UNINSTALL)
357
          {
358
            udata.addExecutable(ef);
359
          }
360
        }
361
        // Custom action listener stuff --- uninstall data ----
362
        handleAdditionalUninstallData(udata, customActions);
363

    
364
        // Load information about updatechecks
365
        int numUpdateChecks = objIn.readInt();
366

    
367
        for (int k = 0; k < numUpdateChecks; k++)
368
        {
369
          UpdateCheck uc = (UpdateCheck) objIn.readObject();
370

    
371
          updatechecks.add(uc);
372
        }
373

    
374
        objIn.close();
375
        // Custom action listener stuff --- afterPack ----
376
        informListeners(customActions, InstallerListener.AFTER_PACK,
377
          packs.get(i), new Integer(i), handler);
378
      }
379

    
380
      // We use the scripts parser
381
      ScriptParser parser = new ScriptParser(parsables, vs);
382
      parser.parseFiles();
383

    
384
      // We use the file executor
385
      FileExecutor executor = new FileExecutor(executables);
386
      if (executor.executeFiles(ExecutableFile.POSTINSTALL, handler) != 0)
387
        handler.emitError(
388
          "File execution failed",
389
          "The installation was not completed");
390

    
391
      // We put the uninstaller (it's not yet complete...)
392
      putUninstaller();
393

    
394
      // update checks _after_ uninstaller was put, so we don't delete it
395
      performUpdateChecks(updatechecks);
396

    
397
      // Custom action listener stuff --- afterPacks ----
398
      informListeners(customActions, InstallerListener.AFTER_PACKS,idata, 
399
        handler, null);
400

    
401
      // The end :-)
402
      handler.stopAction();
403
    } catch (Exception err)
404
    {
405
      // TODO: finer grained error handling with useful error messages
406
      handler.stopAction();
407
      handler.emitError("An error occured", err.toString());
408
      err.printStackTrace();
409
    } finally
410
    {
411
      instances.remove(instances.indexOf(this));
412
    }
413
  }
414

    
415
  /**
416
   * @param updatechecks
417
   */
418
  private void performUpdateChecks(ArrayList updatechecks)
419
  {
420
    ArrayList include_patterns = new ArrayList();
421
    ArrayList exclude_patterns = new ArrayList();
422

    
423
    RECompiler recompiler = new RECompiler();
424

    
425
    this.absolute_installpath =
426
      new File(idata.getInstallPath()).getAbsoluteFile();
427

    
428
    // at first, collect all patterns
429
    for (Iterator iter = updatechecks.iterator(); iter.hasNext();)
430
    {
431
      UpdateCheck uc = (UpdateCheck) iter.next();
432

    
433
      if (uc.includesList != null)
434
        include_patterns.addAll(preparePatterns(uc.includesList, recompiler));
435

    
436
      if (uc.excludesList != null)
437
        exclude_patterns.addAll(preparePatterns(uc.excludesList, recompiler));
438
    }
439

    
440
    // do nothing if no update checks were specified
441
    if (include_patterns.size() == 0)
442
      return;
443

    
444
    // now collect all files in the installation directory and figure
445
    // out files to check for deletion
446

    
447
    // use a treeset for fast access
448
    TreeSet installed_files = new TreeSet();
449

    
450
    for (Iterator if_it = this.udata.getFilesList().iterator();
451
      if_it.hasNext();
452
      )
453
    {
454
      String fname = (String) if_it.next();
455

    
456
      File f = new File(fname);
457

    
458
      if (!f.isAbsolute())
459
      {
460
        f = new File(this.absolute_installpath, fname);
461
      }
462

    
463
      installed_files.add(f.getAbsolutePath());
464
    }
465

    
466
    // now scan installation directory (breadth first), contains Files of directories to scan
467
    // (note: we'll recurse infinitely if there are circular links or similar nasty things)
468
    Stack scanstack = new Stack();
469

    
470
    // contains File objects determined for deletion
471
    ArrayList files_to_delete = new ArrayList();
472

    
473
    try
474
    {
475
      scanstack.add(absolute_installpath);
476

    
477
      while (!scanstack.empty())
478
      {
479
        File f = (File) scanstack.pop();
480

    
481
        File[] files = f.listFiles();
482

    
483
        if (files == null)
484
        {
485
          throw new IOException(f.getPath() + "is not a directory!");
486
        }
487

    
488
        for (int i = 0; i < files.length; i++)
489
        {
490
          File newf = files[i];
491

    
492
          String newfname = newf.getPath();
493

    
494
          // skip files we just installed
495
          if (installed_files.contains(newfname))
496
            continue;
497

    
498
          if (fileMatchesOnePattern(newfname, include_patterns)
499
            && (!fileMatchesOnePattern(newfname, exclude_patterns)))
500
          {
501
            files_to_delete.add(newf);
502
          }
503

    
504
          if (newf.isDirectory())
505
          {
506
            scanstack.push(newf);
507
          }
508

    
509
        }
510
      }
511
    } catch (IOException e)
512
    {
513
      this.handler.emitError(
514
        "error while performing update checks",
515
        e.toString());
516
    }
517

    
518
    for (Iterator f_it = files_to_delete.iterator(); f_it.hasNext();)
519
    {
520
      File f = (File) f_it.next();
521

    
522
      if (!f.isDirectory())
523
        // skip directories - they cannot be removed safely yet
524
      {
525
        this.handler.emitNotification("deleting " + f.getPath());
526
        f.delete();
527
      }
528

    
529
    }
530

    
531
  }
532

    
533
  /**
534
   * @param filename
535
   * @param patterns
536
   * 
537
   * @return true if the file matched one pattern, false if it did not
538
   */
539
  private boolean fileMatchesOnePattern(String filename, ArrayList patterns)
540
  {
541
    // first check whether any include matches
542
    for (Iterator inc_it = patterns.iterator(); inc_it.hasNext();)
543
    {
544
      RE pattern = (RE) inc_it.next();
545

    
546
      if (pattern.match(filename))
547
      {
548
        return true;
549
      }
550
    }
551

    
552
    return false;
553
  }
554

    
555
  /**
556
   * @param list A list of file name patterns (in ant fileset syntax)
557
   * @param recompiler The regular expression compiler (used to speed up RE compiling).
558
   * 
559
   * @return List of org.apache.regexp.RE
560
   */
561
  private List preparePatterns(ArrayList list, RECompiler recompiler)
562
  {
563
    ArrayList result = new ArrayList();
564

    
565
    for (Iterator iter = list.iterator(); iter.hasNext();)
566
    {
567
      String element = (String) iter.next();
568

    
569
      if ((element != null) && (element.length() > 0))
570
      {
571
        // substitute variables in the pattern
572
        element = this.vs.substitute(element, "plain");
573

    
574
        // check whether the pattern is absolute or relative
575
        File f = new File(element);
576

    
577
        // if it is relative, make it absolute and prepend the installation path
578
        // (this is a bit dangerous...)
579
        if (!f.isAbsolute())
580
        {
581
          element = new File(this.absolute_installpath, element).toString();
582
        }
583

    
584
        // now parse the element and construct a regular expression from it
585
        // (we have to parse it one character after the next because every
586
        // character should only be processed once - it's not possible to get this
587
        // correct using regular expression replacing)
588
        StringBuffer element_re = new StringBuffer();
589

    
590
        int lookahead = -1;
591

    
592
        int pos = 0;
593

    
594
        while (pos < element.length())
595
        {
596
          char c;
597

    
598
          if (lookahead != -1)
599
          {
600
            c = (char) lookahead;
601
            lookahead = -1;
602
          } else
603
            c = element.charAt(pos++);
604

    
605
          switch (c)
606
          {
607
            case '/' :
608
              {
609
                element_re.append(File.separator);
610
                break;
611
              }
612
              // escape backslash and dot
613
            case '\\' :
614
            case '.' :
615
              {
616
                element_re.append("\\");
617
                element_re.append(c);
618
                break;
619
              }
620
            case '*' :
621
              {
622
                if (pos == element.length())
623
                {
624
                  element_re.append("[^" + File.separator + "]*");
625
                  break;
626
                }
627

    
628
                lookahead = element.charAt(pos++);
629

    
630
                // check for "**"
631
                if (lookahead == '*')
632
                {
633
                  element_re.append(".*");
634
                  // consume second star
635
                  lookahead = -1;
636
                } else
637
                {
638
                  element_re.append("[^" + File.separator + "]*");
639
                  // lookahead stays there
640
                }
641
                break;
642
              }
643
            default :
644
              {
645
                element_re.append(c);
646
                break;
647
              }
648
          } // switch
649

    
650
        }
651

    
652
        // make sure that the whole expression is matched
653
        element_re.append('$');
654

    
655
        // replace \ by \\ and create a RE from the result
656
        try
657
        {
658
          result.add(new RE(recompiler.compile(element_re.toString())));
659
        } catch (RESyntaxException e)
660
        {
661
          this.handler.emitNotification(
662
            "internal error: pattern \""
663
              + element
664
              + "\" produced invalid RE \""
665
              + f.getPath()
666
              + "\"");
667
        }
668

    
669
      }
670
    }
671

    
672
    return result;
673
  }
674

    
675
  /**
676
   *  Puts the uninstaller.
677
   *
678
   * @exception  Exception  Description of the Exception
679
   */
680
  private void putUninstaller() throws Exception
681
  {
682
    // get the uninstaller base, returning if not found so that
683
    // idata.uninstallOutJar remains null
684
    InputStream [] in = new InputStream[2];
685
    in[0] = Unpacker.class.getResourceAsStream("/res/IzPack.uninstaller");
686
    if (in[0] == null)
687
      return;
688
    // The uninstaller extension is facultative; it will be exist only
689
    // if a native library was marked for uninstallation.
690
    in[1] = Unpacker.class.getResourceAsStream("/res/IzPack.uninstaller-ext");
691

    
692
    // Me make the .uninstaller directory
693
    String dest =
694
      IoHelper.translatePath("$INSTALL_PATH", vs) + File.separator + "Uninstaller";
695
    String jar = dest + File.separator + "uninstaller.jar";
696
    File pathMaker = new File(dest);
697
    pathMaker.mkdirs();
698

    
699
    // We log the uninstaller deletion information
700
    udata.setUninstallerJarFilename(jar);
701
    udata.setUninstallerPath(dest);
702

    
703
    // We open our final jar file
704
    FileOutputStream out = new FileOutputStream(jar);
705
    ZipOutputStream outJar = new ZipOutputStream(out);
706
    idata.uninstallOutJar = outJar;
707
    outJar.setLevel(9);
708
    udata.addFile(jar);
709

    
710
    // We copy the uninstallers
711
    HashSet doubles = new HashSet();
712
        
713
    for( int i = 0; i < in.length; ++i )
714
    {
715
      if( in[i] == null )
716
        continue;
717
      ZipInputStream inRes = new ZipInputStream(in[i]);
718
      ZipEntry zentry = inRes.getNextEntry();
719
      while (zentry != null)
720
      {
721
        // Puts a new entry, but not twice like META-INF
722
        if( ! doubles.contains(zentry.getName()))
723
        {
724
          doubles.add(zentry.getName());
725
          outJar.putNextEntry(new ZipEntry(zentry.getName()));
726

    
727
          // Byte to byte copy
728
          int unc = inRes.read();
729
          while (unc != -1)
730
          {
731
            outJar.write(unc);
732
            unc = inRes.read();
733
          }
734

    
735
          // Next one please
736
          inRes.closeEntry();
737
          outJar.closeEntry();
738
        }
739
        zentry = inRes.getNextEntry();
740
      }
741
      inRes.close();
742
    }
743

    
744
    // We put the langpack
745
    InputStream in2 =
746
    Unpacker.class.getResourceAsStream("/langpacks/" + idata.localeISO3 + ".xml");
747
    outJar.putNextEntry(new ZipEntry("langpack.xml"));
748
    int read = in2.read();
749
    while (read != -1)
750
    {
751
      outJar.write(read);
752
      read = in2.read();
753
    }
754
    outJar.closeEntry();
755
  }
756

    
757
  /**
758
   *  Returns a stream to a pack, location depending on if it's web based.
759
   *
760
   * @param  n              The pack number.
761
   * @return                The stream or null if it could not be found.
762
   * @exception  Exception  Description of the Exception
763
   */
764
  private InputStream getPackAsStream(int n) throws Exception
765
  {
766
    InputStream in = null;
767

    
768
    String webDirURL = idata.info.getWebDirURL();
769
    
770
    if (webDirURL == null) // local
771
    {
772
      in = Unpacker.class.getResourceAsStream("/packs/pack" + n);
773
    }
774
    else // web based
775
    {
776
      // TODO: Look first in same directory as primary jar
777
      //       This may include prompting for changing of media
778
      // TODO: download and cache them all before starting copy process
779
      
780
      // See compiler.Packager#getJarOutputStream for the counterpart
781
      String baseName = idata.info.getInstallerBase();
782
      String packURL = webDirURL + "/" + baseName + ".pack" + n + ".jar";
783
      URL url = new URL("jar:" + packURL + "!/packs/pack" + n);
784
      //JarURLConnection jarConnection = (JarURLConnection) url.openConnection();
785
      // TODO: what happens when using an automated installer?
786
      in = new WebAccessor(null).openInputStream(url);
787
      // TODO: Fails miserably when pack jars are not found, so this is temporary
788
      if (in == null)
789
        throw new FileNotFoundException(url.toString());
790
    }
791
    return in;
792
  }
793

    
794
  // CUSTOM ACTION STUFF -------------- start -----------------
795

    
796
  /**
797
   * Informs all listeners which would be informed at the given
798
   * action type.
799
   * @param customActions array of lists with the custom action objects
800
   * @param action identifier for which callback should be called
801
   * @param firstParam first parameter for the call
802
   * @param secondParam second parameter for the call
803
   * @param thirdParam third parameter for the call
804
   */
805
  private void informListeners(List[] customActions, int action, 
806
    Object firstParam, Object secondParam, Object thirdParam)
807
    throws Exception
808
  {
809
    List listener = null;
810
    // select the right action list.
811
    switch( action )
812
    {
813
      case InstallerListener.BEFORE_FILE:
814
      case InstallerListener.AFTER_FILE:
815
      case InstallerListener.BEFORE_DIR:
816
      case InstallerListener.AFTER_DIR:
817
        listener = customActions[customActions.length - 1];  
818
        break;
819
      default:
820
        listener = customActions[0];   
821
        break;
822
    }
823
    if( listener == null )
824
      return;
825
    // Iterate the action list.
826
    Iterator iter = listener.iterator();
827
    while( iter.hasNext())
828
    {
829
      InstallerListener il = (InstallerListener) iter.next();
830
      switch( action )
831
      {
832
        case InstallerListener.BEFORE_FILE:
833
          il.beforeFile(  (File) firstParam, (PackFile) secondParam );
834
          break;
835
        case InstallerListener.AFTER_FILE:
836
          il.afterFile(  (File) firstParam, (PackFile) secondParam );
837
          break;
838
        case InstallerListener.BEFORE_DIR:
839
          il.beforeDir( (File) firstParam, (PackFile) secondParam );
840
          break;
841
        case InstallerListener.AFTER_DIR:
842
          il.afterDir( (File) firstParam, (PackFile) secondParam );
843
          break;
844
        case InstallerListener.BEFORE_PACK:
845
          il.beforePack((Pack) firstParam, (Integer) secondParam, 
846
            (AbstractUIProgressHandler) thirdParam);
847
          break;
848
        case InstallerListener.AFTER_PACK:
849
          il.afterPack((Pack) firstParam, (Integer) secondParam, 
850
            (AbstractUIProgressHandler) thirdParam);
851
          break;
852
        case InstallerListener.BEFORE_PACKS:
853
          il.beforePacks((AutomatedInstallData) firstParam, 
854
            (Integer) secondParam, (AbstractUIProgressHandler) thirdParam);
855
          break;
856
        case InstallerListener.AFTER_PACKS:
857
          il.afterPacks((AutomatedInstallData) firstParam,
858
            (AbstractUIProgressHandler) secondParam );
859
          break;
860
       
861
      }
862
    }
863
  }
864

    
865
  /**
866
   * Returns the defined custom actions split into types including
867
   * a constructed type for the file related installer listeners. 
868
   * @return array of lists of custom action data like listeners
869
   */
870
  private List [] getCustomActions()
871
  {
872
    String [] listenerNames = AutomatedInstallData.CUSTOM_ACTION_TYPES;
873
    List [] retval = new List[listenerNames.length + 1];
874
    int i;
875
    for( i = 0; i < listenerNames.length; ++i)
876
    {
877
      retval[i] = (List) idata.customData.get(listenerNames[i]);
878
      if( retval[i] == null)
879
        //    Make a dummy list, then iterator is ever callable.
880
        retval[i] = new ArrayList(); 
881
    }
882
    if( retval[AutomatedInstallData.INSTALLER_LISTENER_INDEX].size() > 0 )
883
    { // Installer listeners exist
884
      // Create file related installer listener list in the last
885
      //element of custom action array.
886
      i = retval.length - 1; // Should be so, but safe is safe ...
887
      retval[i] = new ArrayList(); 
888
      Iterator iter = ((List)retval[
889
        AutomatedInstallData.INSTALLER_LISTENER_INDEX]).iterator();
890
      while( iter.hasNext())
891
      {
892
        // If we get a class cast exception many is wrong and
893
        // we must fix it.
894
        InstallerListener li = (InstallerListener) iter.next();
895
        if( li.isFileListener())
896
          retval[i].add(li);
897
      }
898
       
899
    }
900
    return(retval);
901
  }
902

    
903
  /**
904
   * Adds additional unistall data to the uninstall data object.
905
   * @param udata unistall data 
906
   * @param customActions array of lists of custom action data like uninstaller listeners
907
   */
908
  private void handleAdditionalUninstallData(UninstallData udata, List[] customData)
909
  {
910
    // Handle uninstall libs
911
    udata.addAdditionalData("__uninstallLibs__",
912
      customData[AutomatedInstallData.UNINSTALLER_LIBS_INDEX]);
913
    // Handle uninstaller listeners
914
    udata.addAdditionalData("uninstallerListeners",
915
      customData[AutomatedInstallData.UNINSTALLER_LISTENER_INDEX]);
916
    // Handle uninstaller jars
917
    udata.addAdditionalData("uninstallerJars",
918
      customData[AutomatedInstallData.UNINSTALLER_JARS_INDEX]);
919
  }
920

    
921
  
922
  // This method is only used if a file related custom action exist.
923
  /**
924
   * Creates the given directory recursive and calls the
925
   * method "afterDir" of each listener with the current file
926
   * object and the pack file object.  On error an exception
927
   * is raised.
928
   * @param dest the directory which should be created
929
   * @param pf current pack file object
930
   * @param customActions all defined custom actions
931
   * @return false on error, true else
932
   * @throws Exception
933
   */
934

    
935
  private boolean mkDirsWithEnhancement(File dest, PackFile pf, 
936
    List [] customActions  ) throws Exception
937
  {
938
    String path = "unknown";
939
    if( dest != null )
940
      path = dest.getAbsolutePath();
941
    if( dest != null && !dest.exists() && dest.getParentFile() != null)
942
    {
943
      if( dest.getParentFile().exists() )
944
        informListeners(customActions, InstallerListener.BEFORE_DIR,
945
          dest, pf, null);
946
      if( ! dest.mkdir() )
947
      {
948
        mkDirsWithEnhancement( dest.getParentFile(), pf, customActions );
949
        if( ! dest.mkdir() )
950
          dest = null;
951
      }
952
      informListeners(customActions, InstallerListener.AFTER_DIR,
953
        dest, pf, null);
954
    }
955
    if (dest == null)
956
    {
957
      handler.emitError(
958
      "Error creating directories",
959
      "Could not create directory\n" + path);
960
      handler.stopAction();
961
      return(false);
962
    }
963
    return(true);
964
  }
965
  // CUSTOM ACTION STUFF -------------- end -----------------
966

    
967
}