1f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved.
2f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project *
3f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project * This program and the accompanying materials are made available under
4f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project * the terms of the Common Public License v1.0 which accompanies this distribution,
5f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project * and is available at http://www.eclipse.org/legal/cpl-v10.html
6f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project *
7f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project * $Id: IPathEnumerator.java,v 1.1.1.1.2.1 2004/07/16 23:32:04 vlad_r Exp $
8f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project */
9f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectpackage com.vladium.util;
10f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
11f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.io.BufferedInputStream;
12f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.io.File;
13f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.io.FileInputStream;
14f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.io.FileNotFoundException;
15f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.io.IOException;
16f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.ArrayList;
17f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.HashSet;
18f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.Set;
19f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.StringTokenizer;
20f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.jar.Attributes;
21f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.jar.JarFile;
22f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.jar.JarInputStream;
23f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.jar.Manifest;
24f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport java.util.zip.ZipEntry;
25f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
26f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport com.vladium.logging.Logger;
27f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectimport com.vladium.util.asserts.$assert;
28f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
29f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project// ----------------------------------------------------------------------------
30f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project/**
31f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project * @author Vlad Roubtsov, (C) 2003
32f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project */
33f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectpublic
34f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Projectinterface IPathEnumerator
35f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project{
36f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    // public: ................................................................
37f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
38f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    // TODO: archives inside archives? (.war ?)
39f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
40f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    public static interface IPathHandler
41f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    {
42f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        void handleDirStart (File pathDir, File dir); // not generated for path dirs themselves
43f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        void handleFile (File pathDir, File file);
44f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        void handleDirEnd (File pathDir, File dir);
45f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
46f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        /**
47f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project         * Called just after the enumerator's zip input stream for this archive
48f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project         * is opened and the manifest entry is read.
49f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project         */
50f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        void handleArchiveStart (File parentDir, File archive, Manifest manifest);
51f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
52f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        void handleArchiveEntry (JarInputStream in, ZipEntry entry);
53f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
54f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        /**
55f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project         * Called after the enumerator's zip input stream for this archive
56f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project         * has been closed.
57f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project         */
58f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        void handleArchiveEnd (File parentDir, File archive);
59f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
60f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    } // end of nested interface
61f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
62f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
63f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    void enumerate () throws IOException;
64f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
65f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
66f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    public static abstract class Factory
67f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    {
68f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        public static IPathEnumerator create (final File [] path, final boolean canonical, final IPathHandler handler)
69f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        {
70f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            return new PathEnumerator (path, canonical, handler);
71f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        }
72f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
73f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        private static final class PathEnumerator implements IPathEnumerator
74f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        {
75f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            public void enumerate () throws IOException
76f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            {
77f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final IPathHandler handler = m_handler;
78f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
79f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                for (m_pathIndex = 0; m_pathIndex < m_path.size (); ++ m_pathIndex) // important not to cache m_path.size()
80f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
81f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    final File f = (File) m_path.get (m_pathIndex);
82f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
83f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (! f.exists ())
84f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
85f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (IGNORE_INVALID_ENTRIES)
86f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            continue;
87f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        else
88f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            throw new IllegalArgumentException ("path entry does not exist: [" + f + "]");
89f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
90f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
91f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
92f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (f.isDirectory ())
93f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
94f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (m_verbose) m_log.verbose ("processing dir path entry [" + f.getAbsolutePath () + "] ...");
95f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
96f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        m_currentPathDir = f;
97f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        enumeratePathDir (null);
98f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
99f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    else
100f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
101f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        final String name = f.getName ();
102f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        final String lcName = name.toLowerCase ();
103f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
104f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (lcName.endsWith (".zip") || lcName.endsWith (".jar"))
105f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        {
106f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            if (m_verbose) m_log.verbose ("processing archive path entry [" + f.getAbsolutePath () + "] ...");
107f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
108f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            final File parent = f.getParentFile (); // could be null
109f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            final File archive = new File (name);
110f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            m_currentPathDir = parent;
111f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
112f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            // move to enumeratePathArchive(): handler.handleArchiveStart (parent, archive);
113f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            enumeratePathArchive (name);
114f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            handler.handleArchiveEnd (parent, archive); // note: it is important that this is called after the zip stream has been closed
115f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        }
116f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        else if (! IGNORE_INVALID_ENTRIES)
117f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        {
118f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            throw new IllegalArgumentException ("path entry is not a directory or an archive: [" + f + "]");
119f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        }
120f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
121f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
122f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            }
123f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
124f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            PathEnumerator (final File [] path, final boolean canonical, final IPathHandler handler)
125f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            {
126f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_path = new ArrayList (path.length);
127f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                for (int p = 0; p < path.length; ++ p) m_path.add (path [p]);
128f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
129f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_canonical = canonical;
130f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
131f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                if (handler == null) throw new IllegalArgumentException ("null input: handler");
132f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_handler = handler;
133f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
134f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_processManifest = true; // TODO
135f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
136f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                if (m_processManifest)
137f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
138f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    m_pathSet = new HashSet (path.length);
139f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    for (int p = 0; p < path.length; ++ p)
140f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
141f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        m_pathSet.add (path [p].getPath ()); // set of [possibly canonical] paths
142f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
143f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
144f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                else
145f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
146f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    m_pathSet = null;
147f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
148f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
149f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_log = Logger.getLogger (); // each path enumerator caches its logger at creation time
150f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_verbose = m_log.atVERBOSE ();
151f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                m_trace1 = m_log.atTRACE1 ();
152f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            }
153f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
154f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
155f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private void enumeratePathDir (final String dir)
156f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                throws IOException
157f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            {
158f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final boolean trace1 = m_trace1;
159f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
160f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final File currentPathDir = m_currentPathDir;
161f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final File fullDir = dir != null ? new File (currentPathDir, dir) : currentPathDir;
162f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
163f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final String [] children = fullDir.list ();
164f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final IPathHandler handler = m_handler;
165f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
166f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                for (int c = 0, cLimit = children.length; c < cLimit; ++ c)
167f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
168f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    final String childName = children [c];
169f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
170f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    final File child = dir != null ? new File (dir, childName) : new File (childName);
171f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    final File fullChild = new File (fullDir, childName);
172f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
173f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (fullChild.isDirectory ())
174f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
175f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        handler.handleDirStart (currentPathDir, child);
176f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (trace1) m_log.trace1 ("enumeratePathDir", "recursing into [" + child.getName () + "] ...");
177f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        enumeratePathDir (child.getPath ());
178f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        handler.handleDirEnd (currentPathDir, child);
179f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
180f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    else
181f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
182f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                        final String lcName = childName.toLowerCase ();
183f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//
184f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                        if (lcName.endsWith (".zip") || lcName.endsWith (".jar"))
185f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                        {
186f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                            handler.handleArchiveStart (currentPathDir, child);
187f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                            enumeratePathArchive (child.getPath ());
188f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                            handler.handleArchiveEnd (currentPathDir, child);
189f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                        }
190f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project//                        else
191f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        {
192f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            if (trace1) m_log.trace1 ("enumeratePathDir", "processing file [" + child.getName () + "] ...");
193f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            handler.handleFile (currentPathDir, child);
194f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        }
195f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
196f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
197f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            }
198f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
199f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private void enumeratePathArchive (final String archive)
200f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                throws IOException
201f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            {
202f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final boolean trace1 = m_trace1;
203f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
204f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                final File fullArchive = new File (m_currentPathDir, archive);
205f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
206f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                JarInputStream in = null;
207f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                try
208f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
209f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // note: Sun's JarFile uses native code and has been known to
210f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // crash the JVM in some builds; however, it uses random file
211f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // access and can find "bad" manifests that are not the first
212f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // entries in their archives (which JarInputStream can't do);
213f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // [bugs: 4263225, 4696354, 4338238]
214f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    //
215f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // there is really no good solution here but as a compromise
216f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // I try to read the manifest again via a JarFile if the stream
217f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // returns null for it:
218f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
219f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    in = new JarInputStream (new BufferedInputStream (new FileInputStream (fullArchive), 32 * 1024));
220f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
221f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    final IPathHandler handler = m_handler;
222f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
223f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    Manifest manifest = in.getManifest (); // can be null
224f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (manifest == null) manifest = readManifestViaJarFile (fullArchive); // can be null
225f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
226f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    handler.handleArchiveStart (m_currentPathDir, new File (archive), manifest);
227f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
228f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // note: this loop does not skip over the manifest-related
229f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // entries [the handler needs to be smart about that]
230f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    for (ZipEntry entry; (entry = in.getNextEntry ()) != null; )
231f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
232f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        // TODO: handle nested archives
233f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
234f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (trace1) m_log.trace1 ("enumeratePathArchive", "processing archive entry [" + entry.getName () + "] ...");
235f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        handler.handleArchiveEntry (in, entry);
236f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        in.closeEntry ();
237f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
238f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
239f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
240f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    // TODO: this needs major testing
241f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (m_processManifest)
242f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    {
243f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        // note: JarInputStream only reads the manifest if it the
244f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        // first jar entry
245f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (manifest == null) manifest = in.getManifest ();
246f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        if (manifest != null)
247f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        {
248f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            final Attributes attributes = manifest.getMainAttributes ();
249f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            if (attributes != null)
250f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            {
251f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                // note: Sun's documentation says that multiple Class-Path:
252f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                // entries are merged sequentially (http://java.sun.com/products/jdk/1.2/docs/guide/extensions/spec.html)
253f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                // however, their own code does not implement this
254f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                final String jarClassPath = attributes.getValue (Attributes.Name.CLASS_PATH);
255f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                if (jarClassPath != null)
256f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                {
257f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                    final StringTokenizer tokenizer = new StringTokenizer (jarClassPath);
258f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                    for (int p = 1; tokenizer.hasMoreTokens (); )
259f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                    {
260f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        final String relPath = tokenizer.nextToken ();
261f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
262f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        final File archiveParent = fullArchive.getParentFile ();
263f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        final File path = archiveParent != null ? new File (archiveParent, relPath) : new File (relPath);
264f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
265f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        final String fullPath = m_canonical ? Files.canonicalizePathname (path.getPath ()) : path.getPath ();
266f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
267f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        if (m_pathSet.add (fullPath))
268f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        {
269f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                            if (m_verbose) m_log.verbose ("  added manifest Class-Path entry [" + path + "]");
270f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                            m_path.add (m_pathIndex + (p ++), path); // insert after the current m_path entry
271f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                        }
272f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                    }
273f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                                }
274f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                            }
275f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                        }
276f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    }
277f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
278f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                catch (FileNotFoundException fnfe) // ignore: this should not happen
279f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
280f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if ($assert.ENABLED) throw fnfe;
281f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
282f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                finally
283f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
284f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (in != null) try { in.close (); } catch (Exception ignore) {}
285f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
286f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            }
287f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
288f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
289f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            // see comments at the start of enumeratePathArchive()
290f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
291f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private static Manifest readManifestViaJarFile (final File archive)
292f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            {
293f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                Manifest result = null;
294f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
295f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                JarFile jarfile = null;
296f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                try
297f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
298f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    jarfile = new JarFile (archive, false); // 3-arg constructor is not in J2SE 1.2
299f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    result = jarfile.getManifest ();
300f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
301f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                catch (IOException ignore)
302f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
303f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
304f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                finally
305f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                {
306f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                    if (jarfile != null) try { jarfile.close (); } catch (IOException ignore) {}
307f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                }
308f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
309f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project                return result;
310f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            }
311f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
312f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
313f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private final ArrayList /* File */ m_path;
314f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private final boolean m_canonical;
315f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private final Set /* String */ m_pathSet;
316f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private final IPathHandler m_handler;
317f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private final boolean m_processManifest;
318f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
319f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private final Logger m_log;
320f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private boolean m_verbose, m_trace1;
321f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
322f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private int m_pathIndex;
323f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private File m_currentPathDir;
324f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
325f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            // if 'true', non-existent or non-archive or non-directory path entries
326f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            // will be silently ignored:
327f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project            private static final boolean IGNORE_INVALID_ENTRIES = true; // this is consistent with the normal JVM behavior
328f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
329f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project        } // end of nested class
330f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
331f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project    } // end of nested class
332f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project
333f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project} // end of interface
334f6fe897e173f4e4bda72a7dddb091b667066764aThe Android Open Source Project// ----------------------------------------------------------------------------