OSDN Git Service

libjava/classpath/ChangeLog.gcj:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / net / loader / JarURLLoader.java
1 package gnu.java.net.loader;
2
3 import gnu.java.net.IndexListParser;
4
5 import java.io.IOException;
6 import java.net.JarURLConnection;
7 import java.net.MalformedURLException;
8 import java.net.URL;
9 import java.net.URLClassLoader;
10 import java.net.URLStreamHandlerFactory;
11 import java.util.ArrayList;
12 import java.util.Iterator;
13 import java.util.LinkedHashMap;
14 import java.util.Map;
15 import java.util.Set;
16 import java.util.StringTokenizer;
17 import java.util.jar.Attributes;
18 import java.util.jar.JarEntry;
19 import java.util.jar.JarFile;
20 import java.util.jar.Manifest;
21
22 /**
23  * A <code>JarURLLoader</code> is a type of <code>URLLoader</code>
24  * only loading from jar url.
25  */
26 public final class JarURLLoader extends URLLoader
27 {
28   // True if we've initialized -- i.e., tried open the jar file.
29   boolean initialized;
30   // The jar file for this url.
31   JarFile jarfile;
32   // Base jar: url for all resources loaded from jar.
33   final URL baseJarURL;
34   // The "Class-Path" attribute of this Jar's manifest.
35   ArrayList<URLLoader> classPath;
36   // If not null, a mapping from INDEX.LIST for this jar only.
37   // This is a set of all prefixes and top-level files that
38   // ought to be available in this jar.
39   Set indexSet;
40
41   // This constructor is used internally.  It purposely does not open
42   // the jar file -- it defers this until later.  This allows us to
43   // implement INDEX.LIST lazy-loading semantics.
44   private JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache,
45                        URLStreamHandlerFactory factory,
46                        URL baseURL, URL absoluteUrl,
47                        Set indexSet)
48   {
49     super(classloader, cache, factory, baseURL, absoluteUrl);
50
51     URL newBaseURL = null;
52     try
53       {
54         // Cache url prefix for all resources in this jar url.
55         String base = baseURL.toExternalForm() + "!/";
56         newBaseURL = new URL("jar", "", -1, base, cache.get(factory, "jar"));
57       }
58     catch (MalformedURLException ignore)
59       {
60         // Ignore.
61       }
62     this.baseJarURL = newBaseURL;
63     this.classPath = null;
64     this.indexSet = indexSet;
65   }
66
67   // This constructor is used by URLClassLoader.  It will immediately
68   // try to read the jar file, in case we've found an index or a class-path
69   // setting.  FIXME: it would be nice to defer this as well, but URLClassLoader
70   // makes this hard.
71   public JarURLLoader(URLClassLoader classloader, URLStreamHandlerCache cache,
72                       URLStreamHandlerFactory factory,
73                       URL baseURL, URL absoluteUrl)
74   {
75     this(classloader, cache, factory, baseURL, absoluteUrl, null);
76     initialize();
77   }
78
79   private void initialize()
80   {
81     JarFile jarfile = null;
82     try
83       {
84         jarfile =
85           ((JarURLConnection) baseJarURL.openConnection()).getJarFile();
86         
87         Manifest manifest;
88         Attributes attributes;
89         String classPathString;
90
91         IndexListParser parser = new IndexListParser(jarfile, baseJarURL,
92                                                      baseURL);
93         LinkedHashMap<URL, Set<String>> indexMap = parser.getHeaders();
94         if (indexMap != null)
95           {
96             // Note that the index also computes
97             // the resulting Class-Path -- there are jars out there
98             // where the index lists some required jars which do
99             // not appear in the Class-Path attribute in the manifest.
100             this.classPath = new ArrayList<URLLoader>();
101             Iterator<Map.Entry<URL, Set<String>>> it = indexMap.entrySet().iterator();
102             while (it.hasNext())
103               {
104                 Map.Entry<URL, Set<String>> entry = it.next();
105                 URL subURL = entry.getKey();
106                 Set<String> prefixes = entry.getValue();
107                 if (subURL.equals(baseURL))
108                   this.indexSet = prefixes;
109                 else
110                   {
111                     JarURLLoader subLoader = new JarURLLoader(classloader,
112                                                               cache,
113                                                               factory, subURL,
114                                                               subURL,
115                                                               prefixes);
116                     // Note that we don't care if the sub-loader itself has an
117                     // index or a class-path -- only the top-level jar
118                     // file gets this treatment; its index should cover
119                     // everything.
120                     this.classPath.add(subLoader);
121                   }
122               }
123           }
124         else if ((manifest = jarfile.getManifest()) != null
125                  && (attributes = manifest.getMainAttributes()) != null
126                  && ((classPathString
127                       = attributes.getValue(Attributes.Name.CLASS_PATH)) 
128                      != null))
129           {
130             this.classPath = new ArrayList<URLLoader>();
131             StringTokenizer st = new StringTokenizer(classPathString, " ");
132             while (st.hasMoreElements ()) 
133               {
134                 String e = st.nextToken ();
135                 try
136                   {
137                     URL subURL = new URL(baseURL, e);
138                     // We've seen at least one jar file whose Class-Path
139                     // attribute includes the original jar.  If we process
140                     // that normally we end up with infinite recursion.
141                     if (subURL.equals(baseURL))
142                       continue;
143                     JarURLLoader subLoader = new JarURLLoader(classloader,
144                                                               cache, factory,
145                                                               subURL, subURL);
146                     this.classPath.add(subLoader);
147                     ArrayList<URLLoader> extra = subLoader.getClassPath();
148                     if (extra != null)
149                       this.classPath.addAll(extra);
150                   }
151                 catch (java.net.MalformedURLException xx)
152                   {
153                     // Give up
154                   }
155               }
156           }
157       }
158     catch (IOException ioe)
159       {
160         /* ignored */
161       }
162
163     this.jarfile = jarfile;
164     this.initialized = true;
165   }
166
167   /** get resource with the name "name" in the jar url */
168   public Resource getResource(String name)
169   {
170     if (name.startsWith("/"))
171       name = name.substring(1);
172     if (indexSet != null)
173       {
174         // Trust the index.
175         String basename = name;
176         int offset = basename.lastIndexOf('/');
177         if (offset != -1)
178           basename = basename.substring(0, offset);
179         if (! indexSet.contains(basename))
180           return null;
181         // FIXME: if the index claim to hold the resource, and jar file
182         // doesn't have it, we're supposed to throw an exception.  However,
183         // in our model this is tricky to implement, as another URLLoader from
184         // the same top-level jar may have an overlapping index entry.
185       }
186
187     if (! initialized)
188       initialize();
189     if (jarfile == null)
190       return null;
191
192     JarEntry je = jarfile.getJarEntry(name);
193     if (je != null)
194       return new JarURLResource(this, name, je);
195     else
196       return null;
197   }
198
199   public Manifest getManifest()
200   {
201     try
202       {
203         return (jarfile == null) ? null : jarfile.getManifest();
204       }
205     catch (IOException ioe)
206       {
207         return null;
208       }
209   }
210
211   public ArrayList<URLLoader> getClassPath()
212   {
213     return classPath;
214   }
215 }