001: /*
002:
003: ============================================================================
004: The Apache Software License, Version 1.1
005: ============================================================================
006:
007: Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
008:
009: Redistribution and use in source and binary forms, with or without modifica-
010: tion, are permitted provided that the following conditions are met:
011:
012: 1. Redistributions of source code must retain the above copyright notice,
013: this list of conditions and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright notice,
016: this list of conditions and the following disclaimer in the documentation
017: and/or other materials provided with the distribution.
018:
019: 3. The end-user documentation included with the redistribution, if any, must
020: include the following acknowledgment: "This product includes software
021: developed by the Apache Software Foundation (http://www.apache.org/)."
022: Alternately, this acknowledgment may appear in the software itself, if
023: and wherever such third-party acknowledgments normally appear.
024:
025: 4. The names "Batik" and "Apache Software Foundation" must not be
026: used to endorse or promote products derived from this software without
027: prior written permission. For written permission, please contact
028: apache@apache.org.
029:
030: 5. Products derived from this software may not be called "Apache", nor may
031: "Apache" appear in their name, without prior written permission of the
032: Apache Software Foundation.
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
035: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
036: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
037: APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
039: DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
040: OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
041: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: This software consists of voluntary contributions made by many individuals
046: on behalf of the Apache Software Foundation. For more information on the
047: Apache Software Foundation, please see <http://www.apache.org/>.
048:
049: */
050:
051: package org.apache.batik.util;
052:
053: import java.io.DataInputStream;
054: import java.io.File;
055: import java.io.FileInputStream;
056: import java.io.IOException;
057: import java.io.InputStream;
058: import java.util.HashSet;
059: import java.util.Iterator;
060: import java.util.Set;
061:
062: /**
063: * This class contains utility methods to manipulate Java classes.
064: *
065: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
066: * @version $Id$
067: */
068: public class ClassFileUtilities {
069:
070: // Constant pool info tags
071: public final static byte CONSTANT_UTF8_INFO = 1;
072: public final static byte CONSTANT_INTEGER_INFO = 3;
073: public final static byte CONSTANT_FLOAT_INFO = 4;
074: public final static byte CONSTANT_LONG_INFO = 5;
075: public final static byte CONSTANT_DOUBLE_INFO = 6;
076: public final static byte CONSTANT_CLASS_INFO = 7;
077: public final static byte CONSTANT_STRING_INFO = 8;
078: public final static byte CONSTANT_FIELDREF_INFO = 9;
079: public final static byte CONSTANT_METHODREF_INFO = 10;
080: public final static byte CONSTANT_INTERFACEMETHODREF_INFO = 11;
081: public final static byte CONSTANT_NAMEANDTYPE_INFO = 12;
082:
083: /**
084: * This class does not need to be instantiated.
085: */
086: protected ClassFileUtilities() {
087: }
088:
089: /**
090: * Returns the dependencies of the given class.
091: * @param path The root class path.
092: * @param classpath The set of directories (Strings) to scan.
093: * @return a list of paths representing the used classes.
094: */
095: public static Set getClassDependencies(String path, Set classpath)
096: throws IOException {
097: InputStream is = new FileInputStream(path);
098:
099: Set result = new HashSet();
100: Set done = new HashSet();
101:
102: computeClassDependencies(is, classpath, done, result);
103:
104: return result;
105: }
106:
107: private static void computeClassDependencies(InputStream is,
108: Set classpath, Set done, Set result) throws IOException {
109: Iterator it = getClassDependencies(is).iterator();
110: while (it.hasNext()) {
111: String s = (String) it.next();
112: if (!done.contains(s)) {
113: done.add(s);
114:
115: Iterator cpit = classpath.iterator();
116: while (cpit.hasNext()) {
117: String root = (String) cpit.next();
118: StringBuffer sb = new StringBuffer(root);
119: sb.append('/').append(s).append(".class");
120: String path = sb.toString();
121:
122: File f = new File(path);
123: if (f.isFile()) {
124: result.add(path);
125:
126: computeClassDependencies(
127: new FileInputStream(f), classpath,
128: done, result);
129: }
130: }
131: }
132: }
133: }
134:
135: /**
136: * Returns the dependencies of the given class.
137: * @return a list of strings representing the used classes.
138: */
139: public static Set getClassDependencies(InputStream is)
140: throws IOException {
141: DataInputStream dis = new DataInputStream(is);
142:
143: if (dis.readInt() != 0xcafebabe) {
144: throw new IOException("Invalid classfile");
145: }
146:
147: dis.readInt();
148:
149: int len = dis.readShort();
150: String[] strs = new String[len];
151: Set classes = new HashSet();
152: Set desc = new HashSet();
153:
154: for (int i = 1; i < len; i++) {
155: switch (dis.readByte() & 0xff) {
156: case CONSTANT_LONG_INFO:
157: case CONSTANT_DOUBLE_INFO:
158: dis.readLong();
159: i++;
160: break;
161:
162: case CONSTANT_FIELDREF_INFO:
163: case CONSTANT_METHODREF_INFO:
164: case CONSTANT_INTERFACEMETHODREF_INFO:
165: case CONSTANT_INTEGER_INFO:
166: case CONSTANT_FLOAT_INFO:
167: dis.readInt();
168: break;
169:
170: case CONSTANT_CLASS_INFO:
171: classes.add(new Integer(dis.readShort() & 0xffff));
172: break;
173:
174: case CONSTANT_STRING_INFO:
175: dis.readShort();
176: break;
177:
178: case CONSTANT_NAMEANDTYPE_INFO:
179: dis.readShort();
180: desc.add(new Integer(dis.readShort() & 0xffff));
181: break;
182:
183: case CONSTANT_UTF8_INFO:
184: strs[i] = dis.readUTF();
185: break;
186:
187: default:
188: throw new RuntimeException();
189: }
190: }
191:
192: Set result = new HashSet();
193:
194: Iterator it = classes.iterator();
195: while (it.hasNext()) {
196: result.add(strs[((Integer) it.next()).intValue()]);
197: }
198:
199: it = desc.iterator();
200: while (it.hasNext()) {
201: result.addAll(getDescriptorClasses(strs[((Integer) it
202: .next()).intValue()]));
203: }
204:
205: return result;
206: }
207:
208: /**
209: * Returns the classes contained in a field or method desciptor.
210: */
211: protected static Set getDescriptorClasses(String desc) {
212: Set result = new HashSet();
213: int i = 0;
214: char c = desc.charAt(i);
215: switch (c) {
216: case '(':
217: loop: for (;;) {
218: c = desc.charAt(++i);
219: switch (c) {
220: case '[':
221: do {
222: c = desc.charAt(++i);
223: } while (c == '[');
224: if (c != 'L') {
225: break;
226: }
227:
228: case 'L':
229: c = desc.charAt(++i);
230: StringBuffer sb = new StringBuffer();
231: while (c != ';') {
232: sb.append(c);
233: c = desc.charAt(++i);
234: }
235: result.add(sb.toString());
236: break;
237:
238: default:
239: break;
240:
241: case ')':
242: break loop;
243: }
244: }
245: c = desc.charAt(++i);
246: switch (c) {
247: case '[':
248: do {
249: c = desc.charAt(++i);
250: } while (c == '[');
251: if (c != 'L') {
252: break;
253: }
254:
255: case 'L':
256: c = desc.charAt(++i);
257: StringBuffer sb = new StringBuffer();
258: while (c != ';') {
259: sb.append(c);
260: c = desc.charAt(++i);
261: }
262: result.add(sb.toString());
263: break;
264:
265: default:
266: case 'V':
267: }
268: break;
269:
270: case '[':
271: do {
272: c = desc.charAt(++i);
273: } while (c == '[');
274: if (c != 'L') {
275: break;
276: }
277:
278: case 'L':
279: c = desc.charAt(++i);
280: StringBuffer sb = new StringBuffer();
281: while (c != ';') {
282: sb.append(c);
283: c = desc.charAt(++i);
284: }
285: result.add(sb.toString());
286: break;
287:
288: default:
289: }
290:
291: return result;
292: }
293: }
|