001: /*
002:
003: Licensed to the Apache Software Foundation (ASF) under one or more
004: contributor license agreements. See the NOTICE file distributed with
005: this work for additional information regarding copyright ownership.
006: The ASF licenses this file to You under the Apache License, Version 2.0
007: (the "License"); you may not use this file except in compliance with
008: the License. You may obtain a copy of the License at
009:
010: http://www.apache.org/licenses/LICENSE-2.0
011:
012: Unless required by applicable law or agreed to in writing, software
013: distributed under the License is distributed on an "AS IS" BASIS,
014: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: See the License for the specific language governing permissions and
016: limitations under the License.
017:
018: */
019: package org.apache.batik.util;
020:
021: import java.io.DataInputStream;
022: import java.io.File;
023: import java.io.FileInputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.util.HashSet;
027: import java.util.Iterator;
028: import java.util.Set;
029:
030: /**
031: * This class contains utility methods to manipulate Java classes.
032: *
033: * @author <a href="mailto:stephane@hillion.org">Stephane Hillion</a>
034: * @version $Id: ClassFileUtilities.java 478169 2006-11-22 14:23:24Z dvholten $
035: */
036: public class ClassFileUtilities {
037:
038: // Constant pool info tags
039: public static final byte CONSTANT_UTF8_INFO = 1;
040: public static final byte CONSTANT_INTEGER_INFO = 3;
041: public static final byte CONSTANT_FLOAT_INFO = 4;
042: public static final byte CONSTANT_LONG_INFO = 5;
043: public static final byte CONSTANT_DOUBLE_INFO = 6;
044: public static final byte CONSTANT_CLASS_INFO = 7;
045: public static final byte CONSTANT_STRING_INFO = 8;
046: public static final byte CONSTANT_FIELDREF_INFO = 9;
047: public static final byte CONSTANT_METHODREF_INFO = 10;
048: public static final byte CONSTANT_INTERFACEMETHODREF_INFO = 11;
049: public static final byte CONSTANT_NAMEANDTYPE_INFO = 12;
050:
051: /**
052: * This class does not need to be instantiated.
053: */
054: protected ClassFileUtilities() {
055: }
056:
057: /**
058: * Returns the dependencies of the given class.
059: * @param path The root class path.
060: * @param classpath The set of directories (Strings) to scan.
061: * @return a list of paths representing the used classes.
062: */
063: public static Set getClassDependencies(String path, Set classpath)
064: throws IOException {
065: InputStream is = new FileInputStream(path);
066:
067: Set result = new HashSet();
068: Set done = new HashSet();
069:
070: computeClassDependencies(is, classpath, done, result);
071:
072: return result;
073: }
074:
075: private static void computeClassDependencies(InputStream is,
076: Set classpath, Set done, Set result) throws IOException {
077: Iterator it = getClassDependencies(is).iterator();
078: while (it.hasNext()) {
079: String s = (String) it.next();
080: if (!done.contains(s)) {
081: done.add(s);
082:
083: Iterator cpit = classpath.iterator();
084: while (cpit.hasNext()) {
085: String root = (String) cpit.next();
086: StringBuffer sb = new StringBuffer(root);
087: sb.append('/').append(s).append(".class");
088: String path = sb.toString();
089:
090: File f = new File(path);
091: if (f.isFile()) {
092: result.add(path);
093:
094: computeClassDependencies(
095: new FileInputStream(f), classpath,
096: done, result);
097: }
098: }
099: }
100: }
101: }
102:
103: /**
104: * Returns the dependencies of the given class.
105: * @return a list of strings representing the used classes.
106: */
107: public static Set getClassDependencies(InputStream is)
108: throws IOException {
109: DataInputStream dis = new DataInputStream(is);
110:
111: if (dis.readInt() != 0xcafebabe) {
112: throw new IOException("Invalid classfile");
113: }
114:
115: dis.readInt();
116:
117: int len = dis.readShort();
118: String[] strs = new String[len];
119: Set classes = new HashSet();
120: Set desc = new HashSet();
121:
122: for (int i = 1; i < len; i++) {
123: switch (dis.readByte() & 0xff) {
124: case CONSTANT_LONG_INFO:
125: case CONSTANT_DOUBLE_INFO:
126: dis.readLong();
127: i++;
128: break;
129:
130: case CONSTANT_FIELDREF_INFO:
131: case CONSTANT_METHODREF_INFO:
132: case CONSTANT_INTERFACEMETHODREF_INFO:
133: case CONSTANT_INTEGER_INFO:
134: case CONSTANT_FLOAT_INFO:
135: dis.readInt();
136: break;
137:
138: case CONSTANT_CLASS_INFO:
139: classes.add(new Integer(dis.readShort() & 0xffff));
140: break;
141:
142: case CONSTANT_STRING_INFO:
143: dis.readShort();
144: break;
145:
146: case CONSTANT_NAMEANDTYPE_INFO:
147: dis.readShort();
148: desc.add(new Integer(dis.readShort() & 0xffff));
149: break;
150:
151: case CONSTANT_UTF8_INFO:
152: strs[i] = dis.readUTF();
153: break;
154:
155: default:
156: throw new RuntimeException();
157: }
158: }
159:
160: Set result = new HashSet();
161:
162: Iterator it = classes.iterator();
163: while (it.hasNext()) {
164: result.add(strs[((Integer) it.next()).intValue()]);
165: }
166:
167: it = desc.iterator();
168: while (it.hasNext()) {
169: result.addAll(getDescriptorClasses(strs[((Integer) it
170: .next()).intValue()]));
171: }
172:
173: return result;
174: }
175:
176: /**
177: * Returns the classes contained in a field or method desciptor.
178: */
179: protected static Set getDescriptorClasses(String desc) {
180: Set result = new HashSet();
181: int i = 0;
182: char c = desc.charAt(i);
183: switch (c) {
184: case '(':
185: loop: for (;;) {
186: c = desc.charAt(++i);
187: switch (c) {
188: case '[':
189: do {
190: c = desc.charAt(++i);
191: } while (c == '[');
192: if (c != 'L') {
193: break;
194: }
195:
196: case 'L':
197: c = desc.charAt(++i);
198: StringBuffer sb = new StringBuffer();
199: while (c != ';') {
200: sb.append(c);
201: c = desc.charAt(++i);
202: }
203: result.add(sb.toString());
204: break;
205:
206: default:
207: break;
208:
209: case ')':
210: break loop;
211: }
212: }
213: c = desc.charAt(++i);
214: switch (c) {
215: case '[':
216: do {
217: c = desc.charAt(++i);
218: } while (c == '[');
219: if (c != 'L') {
220: break;
221: }
222:
223: case 'L':
224: c = desc.charAt(++i);
225: StringBuffer sb = new StringBuffer();
226: while (c != ';') {
227: sb.append(c);
228: c = desc.charAt(++i);
229: }
230: result.add(sb.toString());
231: break;
232:
233: default:
234: case 'V':
235: }
236: break;
237:
238: case '[':
239: do {
240: c = desc.charAt(++i);
241: } while (c == '[');
242: if (c != 'L') {
243: break;
244: }
245:
246: case 'L':
247: c = desc.charAt(++i);
248: StringBuffer sb = new StringBuffer();
249: while (c != ';') {
250: sb.append(c);
251: c = desc.charAt(++i);
252: }
253: result.add(sb.toString());
254: break;
255:
256: default:
257: }
258:
259: return result;
260: }
261: }
|