View Javadoc

1   /*
2    * Copyright (c) 2003, Henri Yandell
3    * All rights reserved.
4    * 
5    * Redistribution and use in source and binary forms, with or 
6    * without modification, are permitted provided that the 
7    * following conditions are met:
8    * 
9    * + Redistributions of source code must retain the above copyright notice, 
10   *   this list of conditions and the following disclaimer.
11   * 
12   * + Redistributions in binary form must reproduce the above copyright notice, 
13   *   this list of conditions and the following disclaimer in the documentation 
14   *   and/or other materials provided with the distribution.
15   * 
16   * + Neither the name of Genjava-Core nor the names of its contributors 
17   *   may be used to endorse or promote products derived from this software 
18   *   without specific prior written permission.
19   * 
20   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 
21   * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
22   * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
23   * ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
24   * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
25   * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
26   * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
27   * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
28   * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
29   * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
30   * POSSIBILITY OF SUCH DAMAGE.
31   */
32  // hacked out of org.cyberiantiger's lovely class object model
33  package com.generationjava.lang;
34  
35  import org.apache.commons.lang.StringUtils;
36  
37  import java.util.*;
38  import java.io.*;
39  
40  /***
41   * A cut down copy of org.cyberiantiger's class object model.
42   * It provides a List of all the classes that a class file immediately
43   * depends on.
44   */
45  public class ClassConstantsReflector {
46  
47  	static public void main(String[] args) throws Throwable {
48  		ClassConstantsReflector c = new ClassConstantsReflector();
49          for(int i=0; i<args.length; i++) {
50              System.err.println("Class=" +c.getFullyQualifiedDottedName(args[i]));
51              System.err.println("Name=" + c.getName() + "; super=" + c.getSuperName());
52              System.err.println(c.getClassConstants(args[i]));
53              System.err.println("\n");
54          }
55      }
56  
57      static final public int MAGIC = 0xCAFEBABE;
58  
59  	static final public byte CLASS = 7;
60  	static final public byte FIELD_REF = 9;
61  	static final public byte METHOD_REF = 10;
62  	static final public byte INTERFACE_METHOD_REF = 11;
63  	static final public byte STRING = 8;
64  	static final public byte INTEGER = 3;
65  	static final public byte FLOAT = 4;
66  	static final public byte LONG = 5;
67  	static final public byte DOUBLE = 6;
68  	static final public byte NAME_AND_TYPE = 12;
69  	static final public byte UTF8 = 1;
70  
71      private String name = null;
72  
73      public String getName() {
74          return this.name;
75      }
76  
77      public String getSuperName() {
78          return ""+this.superClass;
79      }
80  
81      public String getFullyQualifiedDottedName(String filename) throws IOException {
82          String name = getFullyQualifiedName(filename);
83          return StringUtils.replace(name, "/", ".");
84      }
85      public String getFullyQualifiedName(String filename) throws IOException {
86          String name = null;
87  
88          try {
89              DataInputStream in = new DataInputStream(new FileInputStream(filename));
90              if(in.readInt() != MAGIC) {
91                  // not a .class file
92                  throw new IOException("Not a class file");
93              }
94  
95              in.readUnsignedShort();  // minor version
96              in.readUnsignedShort();  // major version
97              int length = in.readUnsignedShort();  // length. of file?
98              in.readByte(); // CLASS=7
99              in.readUnsignedShort();  // some class value
100             in.readByte(); // UTF8=1
101             name = in.readUTF();
102             in.close();
103         } catch(IOException ioe) {
104             ioe.printStackTrace();
105         }
106 
107         return name;
108     }
109     public List getClassConstants(String classname) {
110         this.name = classname.substring(0, classname.length()-6);
111         this.name = this.name.replace('/','.');
112         try {
113             DataInputStream in = new DataInputStream(new FileInputStream(classname));
114             readFrom(in);
115             resolve();
116             return getDependencies();
117         } catch(IOException ioe) {
118             ioe.printStackTrace();
119         }
120         return new ArrayList();
121 	}
122 
123     List getDependencies() {
124         ArrayList list = new ArrayList();
125         Iterator iterator = constantPool.iterator();
126         while(iterator.hasNext()) {
127             Object obj = iterator.next();
128             if(obj instanceof C7) {
129                 list.add(obj.toString());
130             }
131         }
132         return list;
133     }
134 
135 	// Class data
136 	int minorVersion;
137 	int majorVersion;
138 	ArrayList constantPool;
139 	int accessFlags;
140 	int this_class;
141 	Object thisClass;
142 	int super_class;
143 	Object superClass;
144 
145     // Read the byte codes and parse/decode into constantPool
146 	public void readFrom(DataInputStream in) throws IOException {
147 		if(in.readInt() != MAGIC) throw new IOException("Bad Magic Number");
148 		minorVersion = in.readUnsignedShort();
149 		majorVersion = in.readUnsignedShort();
150 		// check version number
151 		if(!(majorVersion == 45 || (majorVersion == 46 && minorVersion == 0))){
152 			throw new IOException("Unsupported version number");
153 		}
154 		int length = in.readUnsignedShort();
155 		constantPool = new ArrayList(length);
156 		// Hack
157 		constantPool.add(null);
158 
159 		for(int i=1; i<length; i++) {
160             int readByte = in.readByte();
161 			switch(readByte) {
162 			  case CLASS:
163 			    C7 c7 = new C7();
164 			    c7.readFrom(in);
165 			    constantPool.add(c7);
166 			    break;
167 			  case UTF8:
168                 C1 c1 = new C1();
169 			    c1.readFrom(in);
170 			    constantPool.add(c1);
171 			    break;
172 
173                 // we ignore all the other possible constants.
174                 // though we do read the necessary bytes
175 			  case FIELD_REF:
176                 in.readUnsignedShort();
177                 in.readUnsignedShort();
178 			    constantPool.add(null);
179 			    break;
180 			  case METHOD_REF:
181                 in.readUnsignedShort();
182                 in.readUnsignedShort();
183 			    constantPool.add(null);
184 			    break;
185 			  case INTERFACE_METHOD_REF:
186                 in.readUnsignedShort();
187                 in.readUnsignedShort();
188 			    constantPool.add(null);
189 			    break;
190 			  case STRING:
191                 in.readUnsignedShort();
192 			    constantPool.add(null);
193 			    break;
194 			  case INTEGER:
195                 in.readInt();
196 			    constantPool.add(null);
197 			    break;
198 			  case FLOAT:
199                 in.readFloat();
200 			    constantPool.add(null);
201 			    break;
202 			  case LONG:
203                 i++;
204                 in.readLong();
205 			    constantPool.add(null);
206 			    constantPool.add(null);
207 			    break;
208 			  case DOUBLE:
209                 i++;
210                 in.readDouble();
211 			    constantPool.add(null);
212 			    constantPool.add(null);
213 			    break;
214 			  case NAME_AND_TYPE:
215                 in.readUnsignedShort();
216                 in.readUnsignedShort();
217 			    constantPool.add(null);
218 			    break;
219 			  default:
220 				throw new IOException("Invalid Constant Found " + readByte +
221                                       " at " + i + " of " + length);
222 			}
223 		}
224         // various bits of class info
225         accessFlags = in.readUnsignedShort();
226         this_class = in.readUnsignedShort();
227         super_class = in.readUnsignedShort(); 
228 
229         // STOP HERE. Class has more data, we don't care.
230         in.close();
231 	}
232 
233 	public void resolve() {
234 		Iterator i = constantPool.iterator();
235 		// skip first value coz it's null
236 		i.next();
237 
238         Object obj;
239 		while(i.hasNext()) {
240 			obj = i.next();
241             if(obj instanceof C7) {
242                 ((C7)obj).resolve();
243             }
244 		}
245 
246 		if(this_class == 0 || this_class >= constantPool.size() ||
247 		   super_class == 0 || super_class >= constantPool.size() ) {
248 			throw new RuntimeException("Invalid Constant Pool Reference");
249 		}
250 
251 		Object ob = constantPool.get(this_class);
252 		if(!(ob instanceof C7) ) {
253 			throw new RuntimeException("Wrong type of object at reference in constant pool");
254 		}
255 		thisClass = (C7) ob;
256 		ob = constantPool.get(super_class);
257 		if(!(ob instanceof C7) ) {
258 			throw new RuntimeException("Wrong type of object at reference in constant pool");
259 		}
260 		superClass = (C7) ob;
261 	}
262 
263 	class C1 {
264 		String value;
265 		C1() {}
266 		C1(String value) {
267 			this.value = value;
268 		}
269 		public byte getType() { return UTF8; }
270 		public String getValue() { return value; }
271 		public void readFrom(DataInputStream in) throws IOException {
272 			value = in.readUTF();
273 		}
274 		public boolean equals(Object obj) {
275 			return (obj instanceof C1) && ((C1)obj).value.equals(value);
276 		}
277 		public int hashCode() { return value.hashCode(); }
278 		public String toString() { return value; }
279 	}
280 
281 	class C7 {
282 		int index;
283 		C1 name;
284 		public byte getType() { return CLASS; }
285 		public C1 getClassName() { return name; }
286 		public void readFrom(DataInputStream in) throws IOException {
287 			index = in.readUnsignedShort();
288 		}
289 		public void resolve() {
290 			if(index == 0) {
291 				throw new RuntimeException("Invalid Constant Pool Reference: "+index);
292 			}
293             if(index >= constantPool.size()) {
294 				throw new RuntimeException("Invalid Constant Pool Reference: "+index+"/"+constantPool.size());
295 			}
296 			Object ob = constantPool.get(index);
297 			if( !(ob instanceof C1) ) {
298 				throw new RuntimeException("Wrong type of object at reference in constant pool");
299 			}
300 			name = (C1) ob;
301 		}
302 		public boolean equals(Object obj) {
303 			return (obj instanceof C7) && ((C7)obj).name.equals(name);
304 		}
305 		public int hashCode() { return name.hashCode(); }
306 		public String toString() { return name.toString().replace('/','.');}
307 	}
308 
309 }