root / trunk / build / distribution / IzPack / src / lib / com / izforge / izpack / installer / Unpacker.java @ 21757
History | View | Annotate | Download (31.1 KB)
1 |
/*
|
---|---|
2 |
* $Id: Unpacker.java 5819 2006-06-14 07:29:09Z cesar $
|
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 |
} |