source: release-kits/lirk3/resources/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/taskdefs/Jar.java@ 14982

Last change on this file since 14982 was 14982, checked in by oranfry, 16 years ago

initial import of LiRK3

File size: 30.7 KB
Line 
1/*
2 * Copyright 2000-2005 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant.taskdefs;
19
20import java.io.ByteArrayInputStream;
21import java.io.ByteArrayOutputStream;
22import java.io.File;
23import java.io.FileOutputStream;
24import java.io.FileInputStream;
25import java.io.IOException;
26import java.io.InputStream;
27import java.io.UnsupportedEncodingException;
28import java.io.InputStreamReader;
29import java.io.OutputStreamWriter;
30import java.io.PrintWriter;
31import java.io.Reader;
32import java.util.ArrayList;
33import java.util.Collections;
34import java.util.Comparator;
35import java.util.Enumeration;
36import java.util.HashSet;
37import java.util.Iterator;
38import java.util.List;
39import java.util.StringTokenizer;
40import java.util.TreeMap;
41import java.util.Vector;
42import java.util.zip.ZipEntry;
43import java.util.zip.ZipFile;
44import org.apache.tools.ant.BuildException;
45import org.apache.tools.ant.Project;
46import org.apache.tools.ant.types.EnumeratedAttribute;
47import org.apache.tools.ant.types.FileSet;
48import org.apache.tools.ant.types.Path;
49import org.apache.tools.ant.types.ZipFileSet;
50import org.apache.tools.zip.JarMarker;
51import org.apache.tools.zip.ZipExtraField;
52import org.apache.tools.zip.ZipOutputStream;
53
54/**
55 * Creates a JAR archive.
56 *
57 * @since Ant 1.1
58 *
59 * @ant.task category="packaging"
60 */
61public class Jar extends Zip {
62 /** The index file name. */
63 private static final String INDEX_NAME = "META-INF/INDEX.LIST";
64
65 /** The manifest file name. */
66 private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
67
68 /** merged manifests added through addConfiguredManifest */
69 private Manifest configuredManifest;
70 /** shadow of the above if upToDate check alters the value */
71 private Manifest savedConfiguredManifest;
72
73 /** merged manifests added through filesets */
74 private Manifest filesetManifest;
75
76 /**
77 * Manifest of original archive, will be set to null if not in
78 * update mode.
79 */
80 private Manifest originalManifest;
81
82 /**
83 * whether to merge fileset manifests;
84 * value is true if filesetmanifest is 'merge' or 'mergewithoutmain'
85 */
86 private FilesetManifestConfig filesetManifestConfig;
87
88 /**
89 * whether to merge the main section of fileset manifests;
90 * value is true if filesetmanifest is 'merge'
91 */
92 private boolean mergeManifestsMain = true;
93
94 /** the manifest specified by the 'manifest' attribute **/
95 private Manifest manifest;
96
97 /** The encoding to use when reading in a manifest file */
98 private String manifestEncoding;
99
100 /**
101 * The file found from the 'manifest' attribute. This can be
102 * either the location of a manifest, or the name of a jar added
103 * through a fileset. If its the name of an added jar, the
104 * manifest is looked for in META-INF/MANIFEST.MF
105 */
106 private File manifestFile;
107
108 /** jar index is JDK 1.3+ only */
109 private boolean index = false;
110
111 /**
112 * whether to really create the archive in createEmptyZip, will
113 * get set in getResourcesToAdd.
114 */
115 private boolean createEmpty = false;
116
117 /**
118 * Stores all files that are in the root of the archive (i.e. that
119 * have a name that doesn't contain a slash) so they can get
120 * listed in the index.
121 *
122 * Will not be filled unless the user has asked for an index.
123 *
124 * @since Ant 1.6
125 */
126 private Vector rootEntries;
127
128 /**
129 * Path containing jars that shall be indexed in addition to this archive.
130 *
131 * @since Ant 1.6.2
132 */
133 private Path indexJars;
134
135 /**
136 * Extra fields needed to make Solaris recognize the archive as a jar file.
137 *
138 * @since Ant 1.6.3
139 */
140 private ZipExtraField[] JAR_MARKER = new ZipExtraField[] {
141 JarMarker.getInstance()
142 };
143
144 /** constructor */
145 public Jar() {
146 super();
147 archiveType = "jar";
148 emptyBehavior = "create";
149 setEncoding("UTF8");
150 rootEntries = new Vector();
151 }
152
153 /**
154 * @ant.attribute ignore="true"
155 */
156 public void setWhenempty(WhenEmpty we) {
157 log("JARs are never empty, they contain at least a manifest file",
158 Project.MSG_WARN);
159 }
160
161 /**
162 * @deprecated Use setDestFile(File) instead
163 */
164 public void setJarfile(File jarFile) {
165 setDestFile(jarFile);
166 }
167
168 /**
169 * Set whether or not to create an index list for classes.
170 * This may speed up classloading in some cases.
171 */
172 public void setIndex(boolean flag) {
173 index = flag;
174 }
175
176 /**
177 * Set whether or not to create an index list for classes.
178 * This may speed up classloading in some cases.
179 */
180 public void setManifestEncoding(String manifestEncoding) {
181 this.manifestEncoding = manifestEncoding;
182 }
183
184 /**
185 * Allows the manifest for the archive file to be provided inline
186 * in the build file rather than in an external file.
187 *
188 * @param newManifest
189 * @throws ManifestException
190 */
191 public void addConfiguredManifest(Manifest newManifest)
192 throws ManifestException {
193 if (configuredManifest == null) {
194 configuredManifest = newManifest;
195 } else {
196 configuredManifest.merge(newManifest);
197 }
198 savedConfiguredManifest = configuredManifest;
199 }
200
201 /**
202 * The manifest file to use. This can be either the location of a manifest,
203 * or the name of a jar added through a fileset. If its the name of an added
204 * jar, the task expects the manifest to be in the jar at META-INF/MANIFEST.MF.
205 *
206 * @param manifestFile the manifest file to use.
207 */
208 public void setManifest(File manifestFile) {
209 if (!manifestFile.exists()) {
210 throw new BuildException("Manifest file: " + manifestFile
211 + " does not exist.", getLocation());
212 }
213
214 this.manifestFile = manifestFile;
215 }
216
217 private Manifest getManifest(File manifestFile) {
218
219 Manifest newManifest = null;
220 FileInputStream fis = null;
221 InputStreamReader isr = null;
222 try {
223 fis = new FileInputStream(manifestFile);
224 if (manifestEncoding == null) {
225 isr = new InputStreamReader(fis);
226 } else {
227 isr = new InputStreamReader(fis, manifestEncoding);
228 }
229 newManifest = getManifest(isr);
230 } catch (UnsupportedEncodingException e) {
231 throw new BuildException("Unsupported encoding while reading manifest: "
232 + e.getMessage(), e);
233 } catch (IOException e) {
234 throw new BuildException("Unable to read manifest file: "
235 + manifestFile
236 + " (" + e.getMessage() + ")", e);
237 } finally {
238 if (isr != null) {
239 try {
240 isr.close();
241 } catch (IOException e) {
242 // do nothing
243 }
244 }
245 }
246 return newManifest;
247 }
248
249 /**
250 * @return null if jarFile doesn't contain a manifest, the
251 * manifest otherwise.
252 * @since Ant 1.5.2
253 */
254 private Manifest getManifestFromJar(File jarFile) throws IOException {
255 ZipFile zf = null;
256 try {
257 zf = new ZipFile(jarFile);
258
259 // must not use getEntry as "well behaving" applications
260 // must accept the manifest in any capitalization
261 Enumeration e = zf.entries();
262 while (e.hasMoreElements()) {
263 ZipEntry ze = (ZipEntry) e.nextElement();
264 if (ze.getName().equalsIgnoreCase(MANIFEST_NAME)) {
265 InputStreamReader isr =
266 new InputStreamReader(zf.getInputStream(ze), "UTF-8");
267 return getManifest(isr);
268 }
269 }
270 return null;
271 } finally {
272 if (zf != null) {
273 try {
274 zf.close();
275 } catch (IOException e) {
276 // XXX - log an error? throw an exception?
277 }
278 }
279 }
280 }
281
282 private Manifest getManifest(Reader r) {
283
284 Manifest newManifest = null;
285 try {
286 newManifest = new Manifest(r);
287 } catch (ManifestException e) {
288 log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
289 throw new BuildException("Invalid Manifest: " + manifestFile,
290 e, getLocation());
291 } catch (IOException e) {
292 throw new BuildException("Unable to read manifest file"
293 + " (" + e.getMessage() + ")", e);
294 }
295 return newManifest;
296 }
297
298 /**
299 * Behavior when a Manifest is found in a zipfileset or zipgroupfileset file.
300 * Valid values are "skip", "merge", and "mergewithoutmain".
301 * "merge" will merge all of manifests together, and merge this into any
302 * other specified manifests.
303 * "mergewithoutmain" merges everything but the Main section of the manifests.
304 * Default value is "skip".
305 *
306 * Note: if this attribute's value is not "skip", the created jar will not
307 * be readable by using java.util.jar.JarInputStream
308 *
309 * @param config setting for found manifest behavior.
310 */
311 public void setFilesetmanifest(FilesetManifestConfig config) {
312 filesetManifestConfig = config;
313 mergeManifestsMain = "merge".equals(config.getValue());
314
315 if (filesetManifestConfig != null
316 && !filesetManifestConfig.getValue().equals("skip")) {
317
318 doubleFilePass = true;
319 }
320 }
321
322 /**
323 * Adds a zipfileset to include in the META-INF directory.
324 *
325 * @param fs zipfileset to add
326 */
327 public void addMetainf(ZipFileSet fs) {
328 // We just set the prefix for this fileset, and pass it up.
329 fs.setPrefix("META-INF/");
330 super.addFileset(fs);
331 }
332
333 /**
334 * @since Ant 1.6.2
335 */
336 public void addConfiguredIndexJars(Path p) {
337 if (indexJars == null) {
338 indexJars = new Path(getProject());
339 }
340 indexJars.append(p);
341 }
342
343 protected void initZipOutputStream(ZipOutputStream zOut)
344 throws IOException, BuildException {
345
346 if (!skipWriting) {
347 Manifest jarManifest = createManifest();
348 writeManifest(zOut, jarManifest);
349 }
350 }
351
352 private Manifest createManifest()
353 throws BuildException {
354 try {
355 Manifest finalManifest = Manifest.getDefaultManifest();
356
357 if (manifest == null) {
358 if (manifestFile != null) {
359 // if we haven't got the manifest yet, attempt to
360 // get it now and have manifest be the final merge
361 manifest = getManifest(manifestFile);
362 }
363 }
364
365 /*
366 * Precedence: manifestFile wins over inline manifest,
367 * over manifests read from the filesets over the original
368 * manifest.
369 *
370 * merge with null argument is a no-op
371 */
372
373 if (isInUpdateMode()) {
374 finalManifest.merge(originalManifest);
375 }
376 finalManifest.merge(filesetManifest);
377 finalManifest.merge(configuredManifest);
378 finalManifest.merge(manifest, !mergeManifestsMain);
379
380 return finalManifest;
381
382 } catch (ManifestException e) {
383 log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
384 throw new BuildException("Invalid Manifest", e, getLocation());
385 }
386 }
387
388 private void writeManifest(ZipOutputStream zOut, Manifest manifest)
389 throws IOException {
390 for (Enumeration e = manifest.getWarnings();
391 e.hasMoreElements();) {
392 log("Manifest warning: " + (String) e.nextElement(),
393 Project.MSG_WARN);
394 }
395
396 zipDir(null, zOut, "META-INF/", ZipFileSet.DEFAULT_DIR_MODE,
397 JAR_MARKER);
398 // time to write the manifest
399 ByteArrayOutputStream baos = new ByteArrayOutputStream();
400 OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");
401 PrintWriter writer = new PrintWriter(osw);
402 manifest.write(writer);
403 writer.flush();
404
405 ByteArrayInputStream bais =
406 new ByteArrayInputStream(baos.toByteArray());
407 super.zipFile(bais, zOut, MANIFEST_NAME,
408 System.currentTimeMillis(), null,
409 ZipFileSet.DEFAULT_FILE_MODE);
410 super.initZipOutputStream(zOut);
411 }
412
413 protected void finalizeZipOutputStream(ZipOutputStream zOut)
414 throws IOException, BuildException {
415
416 if (index) {
417 createIndexList(zOut);
418 }
419 }
420
421 /**
422 * Create the index list to speed up classloading.
423 * This is a JDK 1.3+ specific feature and is enabled by default. See
424 * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index">
425 * the JAR index specification</a> for more details.
426 *
427 * @param zOut the zip stream representing the jar being built.
428 * @throws IOException thrown if there is an error while creating the
429 * index and adding it to the zip stream.
430 */
431 private void createIndexList(ZipOutputStream zOut) throws IOException {
432 ByteArrayOutputStream baos = new ByteArrayOutputStream();
433 // encoding must be UTF8 as specified in the specs.
434 PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos,
435 "UTF8"));
436
437 // version-info blankline
438 writer.println("JarIndex-Version: 1.0");
439 writer.println();
440
441 // header newline
442 writer.println(zipFile.getName());
443
444 writeIndexLikeList(new ArrayList(addedDirs.keySet()),
445 rootEntries, writer);
446 writer.println();
447
448 if (indexJars != null) {
449 Manifest mf = createManifest();
450 Manifest.Attribute classpath =
451 mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH);
452 String[] cpEntries = null;
453 if (classpath != null) {
454 StringTokenizer tok = new StringTokenizer(classpath.getValue(),
455 " ");
456 cpEntries = new String[tok.countTokens()];
457 int c = 0;
458 while (tok.hasMoreTokens()) {
459 cpEntries[c++] = tok.nextToken();
460 }
461 }
462 String[] indexJarEntries = indexJars.list();
463 for (int i = 0; i < indexJarEntries.length; i++) {
464 String name = findJarName(indexJarEntries[i], cpEntries);
465 if (name != null) {
466 ArrayList dirs = new ArrayList();
467 ArrayList files = new ArrayList();
468 grabFilesAndDirs(indexJarEntries[i], dirs, files);
469 if (dirs.size() + files.size() > 0) {
470 writer.println(name);
471 writeIndexLikeList(dirs, files, writer);
472 writer.println();
473 }
474 }
475 }
476 }
477
478 writer.flush();
479 ByteArrayInputStream bais =
480 new ByteArrayInputStream(baos.toByteArray());
481 super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(), null,
482 ZipFileSet.DEFAULT_FILE_MODE);
483 }
484
485 /**
486 * Overridden from Zip class to deal with manifests and index lists.
487 */
488 protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath,
489 long lastModified, File fromArchive, int mode)
490 throws IOException {
491 if (MANIFEST_NAME.equalsIgnoreCase(vPath)) {
492 if (!doubleFilePass || (doubleFilePass && skipWriting)) {
493 filesetManifest(fromArchive, is);
494 }
495 } else if (INDEX_NAME.equalsIgnoreCase(vPath) && index) {
496 log("Warning: selected " + archiveType
497 + " files include a META-INF/INDEX.LIST which will"
498 + " be replaced by a newly generated one.", Project.MSG_WARN);
499 } else {
500 if (index && vPath.indexOf("/") == -1) {
501 rootEntries.addElement(vPath);
502 }
503 super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
504 }
505 }
506
507 private void filesetManifest(File file, InputStream is) throws IOException {
508 if (manifestFile != null && manifestFile.equals(file)) {
509 // If this is the same name specified in 'manifest', this
510 // is the manifest to use
511 log("Found manifest " + file, Project.MSG_VERBOSE);
512 try {
513 if (is != null) {
514 InputStreamReader isr;
515 if (manifestEncoding == null) {
516 isr = new InputStreamReader(is);
517 } else {
518 isr = new InputStreamReader(is, manifestEncoding);
519 }
520 manifest = getManifest(isr);
521 } else {
522 manifest = getManifest(file);
523 }
524 } catch (UnsupportedEncodingException e) {
525 throw new BuildException("Unsupported encoding while reading "
526 + "manifest: " + e.getMessage(), e);
527 }
528 } else if (filesetManifestConfig != null
529 && !filesetManifestConfig.getValue().equals("skip")) {
530 // we add this to our group of fileset manifests
531 log("Found manifest to merge in file " + file,
532 Project.MSG_VERBOSE);
533
534 try {
535 Manifest newManifest = null;
536 if (is != null) {
537 InputStreamReader isr;
538 if (manifestEncoding == null) {
539 isr = new InputStreamReader(is);
540 } else {
541 isr = new InputStreamReader(is, manifestEncoding);
542 }
543 newManifest = getManifest(isr);
544 } else {
545 newManifest = getManifest(file);
546 }
547
548 if (filesetManifest == null) {
549 filesetManifest = newManifest;
550 } else {
551 filesetManifest.merge(newManifest);
552 }
553 } catch (UnsupportedEncodingException e) {
554 throw new BuildException("Unsupported encoding while reading "
555 + "manifest: " + e.getMessage(), e);
556 } catch (ManifestException e) {
557 log("Manifest in file " + file + " is invalid: "
558 + e.getMessage(), Project.MSG_ERR);
559 throw new BuildException("Invalid Manifest", e, getLocation());
560 }
561 } else {
562 // assuming 'skip' otherwise
563 // don't warn if skip has been requested explicitly, warn if user
564 // didn't set the attribute
565
566 // Hide warning also as it makes no sense since
567 // the filesetmanifest attribute itself has been
568 // hidden
569
570 //int logLevel = filesetManifestConfig == null ?
571 // Project.MSG_WARN : Project.MSG_VERBOSE;
572 //log("File " + file
573 // + " includes a META-INF/MANIFEST.MF which will be ignored. "
574 // + "To include this file, set filesetManifest to a value other "
575 // + "than 'skip'.", logLevel);
576 }
577 }
578
579 /**
580 * Collect the resources that are newer than the corresponding
581 * entries (or missing) in the original archive.
582 *
583 * <p>If we are going to recreate the archive instead of updating
584 * it, all resources should be considered as new, if a single one
585 * is. Because of this, subclasses overriding this method must
586 * call <code>super.getResourcesToAdd</code> and indicate with the
587 * third arg if they already know that the archive is
588 * out-of-date.</p>
589 *
590 * @param filesets The filesets to grab resources from
591 * @param zipFile intended archive file (may or may not exist)
592 * @param needsUpdate whether we already know that the archive is
593 * out-of-date. Subclasses overriding this method are supposed to
594 * set this value correctly in their call to
595 * super.getResourcesToAdd.
596 * @return an array of resources to add for each fileset passed in as well
597 * as a flag that indicates whether the archive is uptodate.
598 *
599 * @exception BuildException if it likes
600 */
601 protected ArchiveState getResourcesToAdd(FileSet[] filesets,
602 File zipFile,
603 boolean needsUpdate)
604 throws BuildException {
605
606 // need to handle manifest as a special check
607 if (zipFile.exists()) {
608 // if it doesn't exist, it will get created anyway, don't
609 // bother with any up-to-date checks.
610
611 try {
612 originalManifest = getManifestFromJar(zipFile);
613 if (originalManifest == null) {
614 log("Updating jar since the current jar has no manifest",
615 Project.MSG_VERBOSE);
616 needsUpdate = true;
617 } else {
618 Manifest mf = createManifest();
619 if (!mf.equals(originalManifest)) {
620 log("Updating jar since jar manifest has changed",
621 Project.MSG_VERBOSE);
622 needsUpdate = true;
623 }
624 }
625 } catch (Throwable t) {
626 log("error while reading original manifest: " + t.getMessage(),
627 Project.MSG_WARN);
628 needsUpdate = true;
629 }
630
631 } else {
632 // no existing archive
633 needsUpdate = true;
634 }
635
636 createEmpty = needsUpdate;
637 return super.getResourcesToAdd(filesets, zipFile, needsUpdate);
638 }
639
640 protected boolean createEmptyZip(File zipFile) throws BuildException {
641 if (!createEmpty) {
642 return true;
643 }
644
645 ZipOutputStream zOut = null;
646 try {
647 log("Building MANIFEST-only jar: "
648 + getDestFile().getAbsolutePath());
649 zOut = new ZipOutputStream(new FileOutputStream(getDestFile()));
650
651 zOut.setEncoding(getEncoding());
652 if (isCompress()) {
653 zOut.setMethod(ZipOutputStream.DEFLATED);
654 } else {
655 zOut.setMethod(ZipOutputStream.STORED);
656 }
657 initZipOutputStream(zOut);
658 finalizeZipOutputStream(zOut);
659 } catch (IOException ioe) {
660 throw new BuildException("Could not create almost empty JAR archive"
661 + " (" + ioe.getMessage() + ")", ioe,
662 getLocation());
663 } finally {
664 // Close the output stream.
665 try {
666 if (zOut != null) {
667 zOut.close();
668 }
669 } catch (IOException ex) {
670 }
671 createEmpty = false;
672 }
673 return true;
674 }
675
676 /**
677 * Make sure we don't think we already have a MANIFEST next time this task
678 * gets executed.
679 *
680 * @see Zip#cleanUp
681 */
682 protected void cleanUp() {
683 super.cleanUp();
684
685 // we want to save this info if we are going to make another pass
686 if (!doubleFilePass || (doubleFilePass && !skipWriting)) {
687 manifest = null;
688 configuredManifest = savedConfiguredManifest;
689 filesetManifest = null;
690 originalManifest = null;
691 }
692 rootEntries.removeAllElements();
693 }
694
695 /**
696 * reset to default values.
697 *
698 * @see Zip#reset
699 *
700 * @since 1.44, Ant 1.5
701 */
702 public void reset() {
703 super.reset();
704 configuredManifest = null;
705 filesetManifestConfig = null;
706 mergeManifestsMain = false;
707 manifestFile = null;
708 index = false;
709 }
710
711 public static class FilesetManifestConfig extends EnumeratedAttribute {
712 public String[] getValues() {
713 return new String[] {"skip", "merge", "mergewithoutmain"};
714 }
715 }
716
717 /**
718 * Writes the directory entries from the first and the filenames
719 * from the second list to the given writer, one entry per line.
720 *
721 * @since Ant 1.6.2
722 */
723 protected final void writeIndexLikeList(List dirs, List files,
724 PrintWriter writer)
725 throws IOException {
726 // JarIndex is sorting the directories by ascending order.
727 // it has no value but cosmetic since it will be read into a
728 // hashtable by the classloader, but we'll do so anyway.
729 Collections.sort(dirs);
730 Collections.sort(files);
731 Iterator iter = dirs.iterator();
732 while (iter.hasNext()) {
733 String dir = (String) iter.next();
734
735 // try to be smart, not to be fooled by a weird directory name
736 dir = dir.replace('\\', '/');
737 if (dir.startsWith("./")) {
738 dir = dir.substring(2);
739 }
740 while (dir.startsWith("/")) {
741 dir = dir.substring(1);
742 }
743 int pos = dir.lastIndexOf('/');
744 if (pos != -1) {
745 dir = dir.substring(0, pos);
746 }
747
748 // looks like nothing from META-INF should be added
749 // and the check is not case insensitive.
750 // see sun.misc.JarIndex
751 if (dir.startsWith("META-INF")) {
752 continue;
753 }
754 // name newline
755 writer.println(dir);
756 }
757
758 iter = files.iterator();
759 while (iter.hasNext()) {
760 writer.println(iter.next());
761 }
762 }
763
764 /**
765 * try to guess the name of the given file.
766 *
767 * <p>If this jar has a classpath attribute in its manifest, we
768 * can assume that it will only require an index of jars listed
769 * there. try to find which classpath entry is most likely the
770 * one the given file name points to.</p>
771 *
772 * <p>In the absence of a classpath attribute, assume the other
773 * files will be placed inside the same directory as this jar and
774 * use their basename.</p>
775 *
776 * <p>if there is a classpath and the given file doesn't match any
777 * of its entries, return null.</p>
778 *
779 * @since Ant 1.7
780 */
781 protected static final String findJarName(String fileName,
782 String[] classpath) {
783 if (classpath == null) {
784 return (new File(fileName)).getName();
785 }
786 fileName = fileName.replace(File.separatorChar, '/');
787 TreeMap matches = new TreeMap(new Comparator() {
788 // longest match comes first
789 public int compare(Object o1, Object o2) {
790 if (o1 instanceof String && o2 instanceof String) {
791 return ((String) o2).length()
792 - ((String) o1).length();
793 }
794 return 0;
795 }
796 });
797
798 for (int i = 0; i < classpath.length; i++) {
799 if (fileName.endsWith(classpath[i])) {
800 matches.put(classpath[i], classpath[i]);
801 } else {
802 int slash = classpath[i].indexOf("/");
803 String candidate = classpath[i];
804 while (slash > -1) {
805 candidate = candidate.substring(slash + 1);
806 if (fileName.endsWith(candidate)) {
807 matches.put(candidate, classpath[i]);
808 break;
809 }
810 slash = candidate.indexOf("/");
811 }
812 }
813 }
814
815 return matches.size() == 0
816 ? null : (String) matches.get(matches.firstKey());
817 }
818
819 /**
820 * Grab lists of all root-level files and all directories
821 * contained in the given archive.
822 *
823 * @since Ant 1.7
824 */
825 protected static final void grabFilesAndDirs(String file, List dirs,
826 List files)
827 throws IOException {
828 org.apache.tools.zip.ZipFile zf = null;
829 try {
830 zf = new org.apache.tools.zip.ZipFile(file, "utf-8");
831 Enumeration entries = zf.getEntries();
832 HashSet dirSet = new HashSet();
833 while (entries.hasMoreElements()) {
834 org.apache.tools.zip.ZipEntry ze =
835 (org.apache.tools.zip.ZipEntry) entries.nextElement();
836 String name = ze.getName();
837 // META-INF would be skipped anyway, avoid index for
838 // manifest-only jars.
839 if (!name.startsWith("META-INF/")) {
840 if (ze.isDirectory()) {
841 dirSet.add(name);
842 } else if (name.indexOf("/") == -1) {
843 files.add(name);
844 } else {
845 // a file, not in the root
846 // since the jar may be one without directory
847 // entries, add the parent dir of this file as
848 // well.
849 dirSet.add(name.substring(0,
850 name.lastIndexOf("/") + 1));
851 }
852 }
853 }
854 dirs.addAll(dirSet);
855 } finally {
856 if (zf != null) {
857 zf.close();
858 }
859 }
860 }
861}
Note: See TracBrowser for help on using the repository browser.