001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright © 2002 Jesse Stockall. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above copyright
011: * notice, this list of conditions and the following disclaimer.
012: *
013: * 2. Redistributions in binary form must reproduce the above copyright
014: * notice, this list of conditions and the following disclaimer in
015: * the documentation and/or other materials provided with the
016: * distribution.
017: *
018: * 3. The end-user documentation included with the redistribution, if
019: * any, must include the following acknowlegement:
020: * "This product includes software developed by the
021: * Apache Software Foundation (http://www.apache.org/)."
022: * Alternately, this acknowlegement may appear in the software itself,
023: * if and wherever such third-party acknowlegements normally appear.
024: *
025: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
026: * Foundation" must not be used to endorse or promote products derived
027: * from this software without prior written permission. For written
028: * permission, please contact apache@apache.org.
029: *
030: * 5. Products derived from this software may not be called "Apache"
031: * nor may "Apache" appear in their names without prior written
032: * permission of the Apache Group.
033: *
034: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
035: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
036: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
037: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
038: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
039: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
040: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
041: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
042: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
043: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
044: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
045: * SUCH DAMAGE.
046: * ====================================================================
047: *
048: * This software consists of voluntary contributions made by many
049: * individuals on behalf of the Apache Software Foundation. For more
050: * information on the Apache Software Foundation, please see
051: * <http://www.apache.org/>.
052: */
053: package org.apache.tools.ant.taskdefs.optional.genjar;
054:
055: import java.io.DataInputStream;
056: import java.io.File;
057: import java.io.FileInputStream;
058: import java.io.IOException;
059: import java.io.InputStream;
060: import java.util.ArrayList;
061: import java.util.Iterator;
062: import java.util.List;
063:
064: /**
065: * Examines a class file and returns a list of all referenced classes.
066: *
067: * @author Original Code: <a href="mailto:jake@riggshill.com">John W. Kohler
068: * </a>
069: * @author Jesse Stockall
070: * @version $Revision: 1.1.1.1 $ $Date: 2002/09/26 20:27:16 $
071: */
072: public class ClassUtil {
073:
074: //
075: // ConstantPool entry identifiers
076: //
077: private static final int CONSTANT_Utf8 = 1;
078: private static final int CONSTANT_Integer = 3;
079: private static final int CONSTANT_Float = 4;
080: private static final int CONSTANT_Long = 5;
081: private static final int CONSTANT_Double = 6;
082: private static final int CONSTANT_Class = 7;
083: private static final int CONSTANT_String = 8;
084: private static final int CONSTANT_Fieldref = 9;
085: private static final int CONSTANT_Methodref = 10;
086: private static final int CONSTANT_InterfaceMethodref = 11;
087: private static final int CONSTANT_NameAndType = 12;
088:
089: /** no ctor necessary */
090: private ClassUtil() {
091: }
092:
093: /**
094: * Reads the indicated class file and returns a list of all referenced
095: * classes (dependancy list). The list will not refer itself.
096: *
097: * @param classFile the name of the class file to read
098: * @return List a list of all referenced class names
099: * @throws IOException when IO errors occur
100: */
101: public static List getDependancies(String classFile)
102: throws IOException {
103: return getDependancies(new File(classFile));
104: }
105:
106: /**
107: * Reads the indicated class file and returns a list of all referenced
108: * classes (dependancy list). The list will not refer itself.
109: *
110: * @param classFile a File indicating the class file to read
111: * @return List a list of all referenced class names
112: * @throws IOException when IO errors occur
113: */
114: public static List getDependancies(File classFile)
115: throws IOException {
116: InputStream is = null;
117: try {
118: is = new FileInputStream(classFile);
119: return getDependancies(is);
120: } finally {
121: if (is != null) {
122: is.close();
123: }
124: }
125: }
126:
127: /**
128: * Reads the indicated class file and returns a list of all referenced
129: * classes (dependancy list). The list will not refer itself.
130: *
131: * @param is an inputstream opened to the first byte of a class
132: * file
133: * @return List a list of all referenced class names
134: * @throws IOException when IO errors occur
135: */
136: public static List getDependancies(InputStream is)
137: throws IOException {
138: return getDependancies(new DataInputStream(is));
139: }
140:
141: /**
142: * Reads the indicated class file and returns a list of all referenced
143: * classes (dependancy list). The list will not refer itself.
144: *
145: * @param is a DataInput opened to the first byte of a class file
146: * @return List a list of all referenced class names
147: * @throws IOException when IO errors occur
148: */
149: public static List getDependancies(DataInputStream is)
150: throws IOException {
151: //
152: // read the prefix stuff
153: //
154: if (is.readInt() != 0xcafebabe) {
155: throw new IllegalStateException("NOT A CLASS FILE");
156: }
157:
158: is.readUnsignedShort(); // minor
159: is.readUnsignedShort(); // major
160:
161: int pool_count = is.readUnsignedShort();
162:
163: //
164: // this array holds various objects from the constant pool
165: // (in essence it IS the constant pool although we're
166: // not populating with all types)
167: //
168: Object[] cp = new Object[pool_count];
169: //
170: // this list holds all class references from the pool
171: // poolCount / 4 should be larger than we ever need
172: // this holds indices into the UTFs for class class names
173: //
174: List classRefs = new ArrayList(pool_count / 4);
175: //
176: // now read the constant pool - storing only
177: // UTF and CLASS entries
178: //
179: for (int i = 1; i < pool_count; ++i) {
180: switch (is.readUnsignedByte()) {
181:
182: case CONSTANT_Utf8:
183: cp[i] = is.readUTF();
184: break;
185: case CONSTANT_Integer:
186: case CONSTANT_Float:
187: is.readInt();
188: break;
189: case CONSTANT_Long:
190: case CONSTANT_Double:
191: is.readInt();
192: is.readInt();
193: // section 4.4.5 of the Java VM spec states that all 8
194: // byte constants take up 2 entries in the constant
195: // table - the next entry (n+1) is not used|usable
196: // therefore we need to skip it - if we don't then
197: // things sometimes get munged
198: //
199: ++i;
200: break;
201: case CONSTANT_Class:
202: //
203: // class references are what we're really after so store
204: // 'em in a seperate list for simple/fast retrieval
205: // note: the ONLY reason I'm storing the idx object in the
206: // cp array is so that we can deref the this_class index later
207: //
208: Integer idx = new Integer(is.readUnsignedShort());
209: cp[i] = idx;
210: classRefs.add(idx);
211: break;
212: case CONSTANT_String:
213: is.readUnsignedShort();
214: break;
215: case CONSTANT_Fieldref:
216: case CONSTANT_Methodref:
217: case CONSTANT_InterfaceMethodref:
218: is.readUnsignedShort();
219: is.readUnsignedShort();
220: break;
221: case CONSTANT_NameAndType:
222: is.readUnsignedShort();
223: is.readUnsignedShort();
224: break;
225: default:
226: break;
227: }
228: }
229:
230: int access = is.readUnsignedShort(); // access flags
231: int this ClassIdx = ((Integer) (cp[is.readUnsignedShort()]))
232: .intValue();
233: //
234: // do we need to consume the entire class file? If we open the
235: // file obviuously not but if the user is passing us a stream
236: // couldn't they assume that the entire file was read? I can't
237: // come up with a scenario where it'd matter, but...
238: //
239: List classNames = new ArrayList(classRefs.size());
240: for (Iterator it = classRefs.iterator(); it.hasNext();) {
241: int idx = ((Integer) it.next()).intValue();
242: if (idx != this ClassIdx) {
243: // strip array references here
244: classNames.add(cp[idx]);
245: }
246: }
247: return classNames;
248: }
249:
250: // public static void main( String[] argv )
251: // throws Exception
252: // {
253: // List classes = getDependancies( argv[ 0 ] );
254: // long end = System.currentTimeMillis();
255: // for ( Iterator it = classes.iterator(); it.hasNext(); )
256: // System.out.println( it.next().toString() );
257: // }
258: }
|