blob: 69d19231e01483f4c1f5af59bb5bd46aa1717152 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1999-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.misc;
27
28import java.io.*;
29import java.util.*;
30import java.util.jar.*;
31import java.util.zip.*;
32
33/**
34 * This class is used to maintain mappings from packages, classes
35 * and resources to their enclosing JAR files. Mappings are kept
36 * at the package level except for class or resource files that
37 * are located at the root directory. URLClassLoader uses the mapping
38 * information to determine where to fetch an extension class or
39 * resource from.
40 *
41 * @author Zhenghua Li
42 * @since 1.3
43 */
44
45public class JarIndex {
46
47 /**
48 * The hash map that maintains mappings from
49 * package/classe/resource to jar file list(s)
50 */
51 private HashMap indexMap;
52
53 /**
54 * The hash map that maintains mappings from
55 * jar file to package/class/resource lists
56 */
57 private HashMap jarMap;
58
59 /*
60 * An ordered list of jar file names.
61 */
62 private String[] jarFiles;
63
64 /**
65 * The index file name.
66 */
67 public static final String INDEX_NAME = "META-INF/INDEX.LIST";
68
69 /**
70 * Constructs a new, empty jar index.
71 */
72 public JarIndex() {
73 indexMap = new HashMap();
74 jarMap = new HashMap();
75 }
76
77 /**
78 * Constructs a new index from the specified input stream.
79 *
80 * @param is the input stream containing the index data
81 */
82 public JarIndex(InputStream is) throws IOException {
83 this();
84 read(is);
85 }
86
87 /**
88 * Constructs a new index for the specified list of jar files.
89 *
90 * @param files the list of jar files to construct the index from.
91 */
92 public JarIndex(String[] files) throws IOException {
93 this();
94 this.jarFiles = files;
95 parseJars(files);
96 }
97
98 /**
99 * Returns the jar index, or <code>null</code> if none.
100 *
101 * @param jar the JAR file to get the index from.
102 * @exception IOException if an I/O error has occurred.
103 */
104 public static JarIndex getJarIndex(JarFile jar, MetaIndex metaIndex) throws IOException {
105 JarIndex index = null;
106 /* If metaIndex is not null, check the meta index to see
107 if META-INF/INDEX.LIST is contained in jar file or not.
108 */
109 if (metaIndex != null &&
110 !metaIndex.mayContain(INDEX_NAME)) {
111 return null;
112 }
113 JarEntry e = jar.getJarEntry(INDEX_NAME);
114 // if found, then load the index
115 if (e != null) {
116 index = new JarIndex(jar.getInputStream(e));
117 }
118 return index;
119 }
120
121 /**
122 * Returns the jar files that are defined in this index.
123 */
124 public String[] getJarFiles() {
125 return jarFiles;
126 }
127
128 /*
129 * Add the key, value pair to the hashmap, the value will
130 * be put in a linked list which is created if necessary.
131 */
132 private void addToList(String key, String value, HashMap t) {
133 LinkedList list = (LinkedList)t.get(key);
134 if (list == null) {
135 list = new LinkedList();
136 list.add(value);
137 t.put(key, list);
138 } else if (!list.contains(value)) {
139 list.add(value);
140 }
141 }
142
143 /**
144 * Returns the list of jar files that are mapped to the file.
145 *
146 * @param fileName the key of the mapping
147 */
148 public LinkedList get(String fileName) {
149 LinkedList jarFiles = null;
150 if ((jarFiles = (LinkedList)indexMap.get(fileName)) == null) {
151 /* try the package name again */
152 int pos;
153 if((pos = fileName.lastIndexOf("/")) != -1) {
154 jarFiles = (LinkedList)indexMap.get(fileName.substring(0, pos));
155 }
156 }
157 return jarFiles;
158 }
159
160 /**
161 * Add the mapping from the specified file to the specified
162 * jar file. If there were no mapping for the package of the
163 * specified file before, a new linked list will be created,
164 * the jar file is added to the list and a new mapping from
165 * the package to the jar file list is added to the hashmap.
166 * Otherwise, the jar file will be added to the end of the
167 * existing list.
168 *
169 * @param fileName the file name
170 * @param jarName the jar file that the file is mapped to
171 *
172 */
173 public void add(String fileName, String jarName) {
174 String packageName;
175 int pos;
176 if((pos = fileName.lastIndexOf("/")) != -1) {
177 packageName = fileName.substring(0, pos);
178 } else {
179 packageName = fileName;
180 }
181
182 // add the mapping to indexMap
183 addToList(packageName, jarName, indexMap);
184
185 // add the mapping to jarMap
186 addToList(jarName, packageName, jarMap);
187 }
188
189 /**
190 * Go through all the jar files and construct the
191 * index table.
192 */
193 private void parseJars(String[] files) throws IOException {
194 if (files == null) {
195 return;
196 }
197
198 String currentJar = null;
199
200 for (int i = 0; i < files.length; i++) {
201 currentJar = files[i];
202 ZipFile zrf = new ZipFile(currentJar.replace
203 ('/', File.separatorChar));
204
205 Enumeration entries = zrf.entries();
206 while(entries.hasMoreElements()) {
207 String fileName = ((ZipEntry)(entries.nextElement())).getName();
208 // Index the META-INF directory, but not the index or manifest.
209 if (!fileName.startsWith("META-INF/") ||
210 !(fileName.equals("META-INF/") ||
211 fileName.equals(INDEX_NAME) ||
212 fileName.equals(JarFile.MANIFEST_NAME))) {
213 add(fileName, currentJar);
214 }
215 }
216 zrf.close();
217 }
218 }
219
220 /**
221 * Writes the index to the specified OutputStream
222 *
223 * @param out the output stream
224 * @exception IOException if an I/O error has occurred
225 */
226 public void write(OutputStream out) throws IOException {
227 BufferedWriter bw = new BufferedWriter
228 (new OutputStreamWriter(out, "UTF8"));
229 bw.write("JarIndex-Version: 1.0\n\n");
230
231 if (jarFiles != null) {
232 for (int i = 0; i < jarFiles.length; i++) {
233 /* print out the jar file name */
234 String jar = jarFiles[i];
235 bw.write(jar + "\n");
236 LinkedList jarlist = (LinkedList)jarMap.get(jar);
237 if (jarlist != null) {
238 Iterator listitr = jarlist.iterator();
239 while(listitr.hasNext()) {
240 bw.write((String)(listitr.next()) + "\n");
241 }
242 }
243 bw.write("\n");
244 }
245 bw.flush();
246 }
247 }
248
249
250 /**
251 * Reads the index from the specified InputStream.
252 *
253 * @param is the input stream
254 * @exception IOException if an I/O error has occurred
255 */
256 public void read(InputStream is) throws IOException {
257 BufferedReader br = new BufferedReader
258 (new InputStreamReader(is, "UTF8"));
259 String line = null;
260 String currentJar = null;
261
262 /* an ordered list of jar file names */
263 Vector jars = new Vector();
264
265 /* read until we see a .jar line */
266 while((line = br.readLine()) != null && !line.endsWith(".jar"));
267
268 for(;line != null; line = br.readLine()) {
269 if (line.length() == 0)
270 continue;
271
272 if (line.endsWith(".jar")) {
273 currentJar = line;
274 jars.add(currentJar);
275 } else {
276 String name = line;
277 addToList(name, currentJar, indexMap);
278 addToList(currentJar, name, jarMap);
279 }
280 }
281
282 jarFiles = (String[])jars.toArray(new String[jars.size()]);
283 }
284
285 /**
286 * Merges the current index into another index, taking into account
287 * the relative path of the current index.
288 *
289 * @param toIndex The destination index which the current index will
290 * merge into.
291 * @param path The relative path of the this index to the destination
292 * index.
293 *
294 */
295 public void merge(JarIndex toIndex, String path) {
296 Iterator itr = indexMap.entrySet().iterator();
297 while(itr.hasNext()) {
298 Map.Entry e = (Map.Entry)itr.next();
299 String packageName = (String)e.getKey();
300 LinkedList from_list = (LinkedList)e.getValue();
301 Iterator listItr = from_list.iterator();
302 while(listItr.hasNext()) {
303 String jarName = (String)listItr.next();
304 if (path != null) {
305 jarName = path.concat(jarName);
306 }
307 toIndex.add(packageName, jarName);
308 }
309 }
310 }
311}