001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.classfile;
042:
043: import org.netbeans.lib.profiler.utils.MiscUtils;
044: import java.io.File;
045: import java.io.IOException;
046: import java.util.*;
047: import java.util.zip.ZipEntry;
048: import java.util.zip.ZipFile;
049:
050: /**
051: * Class path, that can be set containing both directories and .jar files, and then used to read a .class (.java)
052: * file with a specified fully qualified name.
053: *
054: * @author Misha Dmitirev
055: */
056: public class ClassPath {
057: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
058:
059: private abstract static class PathEntry {
060: //~ Instance fields ------------------------------------------------------------------------------------------------------
061:
062: protected HashSet entries;
063: protected int hits; // This is done to avoid indexing of the JAR files too early and all at once
064: protected int threshHits; // This is done to avoid indexing of the JAR files too early and all at once
065:
066: //~ Methods --------------------------------------------------------------------------------------------------------------
067:
068: abstract String getLocationForClassFile(String fileName);
069: }
070:
071: private static class Dir extends PathEntry {
072: //~ Instance fields ------------------------------------------------------------------------------------------------------
073:
074: private File dir;
075:
076: //~ Constructors ---------------------------------------------------------------------------------------------------------
077:
078: Dir(File dirF) {
079: dir = dirF;
080: threshHits = 100 + (int) (40 * Math.random());
081: }
082:
083: //~ Methods --------------------------------------------------------------------------------------------------------------
084:
085: String getLocationForClassFile(String fileName) {
086: if (entries != null) {
087: if (entries.contains(fileName)) {
088: return dir.getAbsolutePath();
089: } else {
090: return null;
091: }
092: } else {
093: if (++hits >= threshHits) {
094: entries = new HashSet();
095: MiscUtils.getAllClassesInDir(dir.getAbsolutePath(),
096: "", false, entries); // NOI18N
097:
098: return getLocationForClassFile(fileName);
099: } else {
100: File file = new File(dir, fileName);
101:
102: //System.err.println("*** Trying file " + file.getAbsolutePath() + " in PathEntry = " + dir.getAbsolutePath());
103: if (file.exists()) {
104: return dir.getAbsolutePath();
105: } else {
106: return null;
107: }
108: }
109: }
110: }
111: }
112:
113: private static class Zip extends PathEntry {
114: //~ Instance fields ------------------------------------------------------------------------------------------------------
115:
116: private ZipFile zip;
117:
118: //~ Constructors ---------------------------------------------------------------------------------------------------------
119:
120: Zip(ZipFile z) {
121: zip = z;
122: threshHits = 50 + (int) (20 * Math.random());
123: }
124:
125: //~ Methods --------------------------------------------------------------------------------------------------------------
126:
127: String getLocationForClassFile(String fileName) {
128: if (entries != null) {
129: if (entries.contains(fileName)) {
130: return zip.getName();
131: } else {
132: return null;
133: }
134: } else {
135: if (++hits >= threshHits) {
136: entries = new HashSet();
137: MiscUtils.getAllClassesInJar(zip.getName(), false,
138: entries);
139:
140: Enumeration e = zip.entries();
141:
142: return getLocationForClassFile(fileName);
143: } else {
144: ZipEntry entry = zip.getEntry(fileName);
145:
146: if (entry != null) {
147: return zip.getName();
148: } else {
149: return null;
150: }
151: }
152: }
153: }
154: }
155:
156: //~ Instance fields ----------------------------------------------------------------------------------------------------------
157:
158: private Hashtable zipFileNameToFile;
159: private PathEntry[] paths;
160: private boolean isCP; // True for a class path, false for a source path
161:
162: //~ Constructors -------------------------------------------------------------------------------------------------------------
163:
164: public ClassPath(String classPath, boolean isCP) {
165: this .isCP = isCP;
166:
167: ArrayList vec = new ArrayList();
168: zipFileNameToFile = new Hashtable();
169:
170: for (StringTokenizer tok = new StringTokenizer(classPath,
171: File.pathSeparator); tok.hasMoreTokens();) {
172: String path = tok.nextToken();
173:
174: if (!path.equals("")) { // NOI18N
175:
176: File file = new File(path);
177:
178: try {
179: if (file.exists()) {
180: if (file.isDirectory()) {
181: vec.add(new Dir(file));
182: } else {
183: ZipFile zipFile = new ZipFile(file);
184: vec.add(new Zip(zipFile));
185: zipFileNameToFile.put(path, zipFile);
186: }
187: }
188: } catch (IOException e) {
189: System.err.println("Warning: CLASSPATH component "
190: + file + ": " + e); // NOI18N
191: }
192: }
193: }
194:
195: paths = new PathEntry[vec.size()];
196: vec.toArray(paths);
197: }
198:
199: //~ Methods ------------------------------------------------------------------------------------------------------------------
200:
201: /**
202: * Searches for the class on this class path, reads it if found, and returns the DynamicClassInfo for it.
203: * If class is not found, returns null. Exceptions are thrown if class file is found but something goes wrong when reading it.
204: */
205: public DynamicClassInfo getClassInfoForClass(String className,
206: int classLoaderId) throws IOException, ClassFormatError {
207: String slashedClassName = className.replace('.', '/'); // NOI18N
208: //System.err.println("*** Requested " + slashedClassName);
209:
210: String dirOrJar = getLocationForClass(slashedClassName);
211:
212: //if (dirOrJar == null) System.err.println("*** Unsuccessful for " + slashedClassName);
213: if (dirOrJar == null) {
214: return null;
215: }
216:
217: return new DynamicClassInfo(slashedClassName, classLoaderId,
218: dirOrJar);
219: }
220:
221: /** Requires "slashed" class name. Returns the directory or .jar name where this class is located, or null if not found. */
222: public String getLocationForClass(String slashedClassName) {
223: String fileName = slashedClassName
224: + (isCP ? ".class" : ".java"); // NOI18N
225:
226: for (int i = 0; i < paths.length; i++) {
227: String location = paths[i]
228: .getLocationForClassFile(fileName);
229:
230: if (location != null) {
231: return location;
232: }
233: }
234:
235: return null;
236: }
237:
238: /** This is used to avoid repetitive creation of ZipFiles in the code that reads files from JARs given just the name of the latter */
239: public ZipFile getZipFileForName(String zipFileName) {
240: return (ZipFile) zipFileNameToFile.get(zipFileName);
241: }
242:
243: public void close() {
244: // close all ZipFiles in ClassPath, the files on disk would otherwise be locked
245: // this is a bugfix for http://profiler.netbeans.org/issues/show_bug.cgi?id=61849
246: for (Iterator it = zipFileNameToFile.values().iterator(); it
247: .hasNext();) {
248: try {
249: ((ZipFile) it.next()).close();
250: } catch (IOException e) {
251: // ignore
252: }
253: }
254: }
255:
256: //------------------------------------------ Debugging -----------------------------------------
257: public String toString() {
258: StringBuffer buf = new StringBuffer();
259:
260: for (int i = 0; i < paths.length; i++) {
261: buf
262: .append((paths[i] instanceof Dir) ? ((Dir) paths[i]).dir
263: .getAbsolutePath()
264: : ((Zip) paths[i]).zip.getName());
265: buf.append(File.pathSeparatorChar);
266: }
267:
268: return buf.toString();
269: }
270: }
|