1 25 package classycle; 26 27 import java.io.DataInputStream ; 28 import java.io.File ; 29 import java.io.FileInputStream ; 30 import java.io.IOException ; 31 import java.io.InputStream ; 32 import java.util.ArrayList ; 33 import java.util.Arrays ; 34 import java.util.Enumeration ; 35 import java.util.HashMap ; 36 import java.util.Iterator ; 37 import java.util.List ; 38 import java.util.Map ; 39 import java.util.Set ; 40 import java.util.zip.ZipEntry ; 41 import java.util.zip.ZipFile ; 42 43 import classycle.classfile.ClassConstant; 44 import classycle.classfile.Constant; 45 import classycle.classfile.StringConstant; 46 import classycle.classfile.UTF8Constant; 47 import classycle.graph.AtomicVertex; 48 import classycle.util.StringPattern; 49 import classycle.util.TrueStringPattern; 50 51 58 public class Parser 59 { 60 private static final int ACC_INTERFACE = 0x200, ACC_ABSTRACT = 0x400; 61 private static final String [] ZIP_FILE_TYPES 62 = new String [] {".zip", ".jar", ".war", ".ear"}; 63 64 private static class UnresolvedNode implements Comparable 65 { 66 ClassAttributes attributes; 67 ArrayList nodes = new ArrayList (); 68 69 public UnresolvedNode() {} 70 71 public int compareTo(Object obj) 72 { 73 return attributes.getName().compareTo( 74 ((UnresolvedNode) obj).attributes.getName()); 75 } 76 77 public boolean isMatchedBy(StringPattern pattern) 78 { 79 return pattern.matches(attributes.getName()); 80 } 81 } 82 83 84 private Parser() { 85 } 86 87 92 public static AtomicVertex[] readClassFiles(String [] classFiles) 93 throws IOException 94 { 95 return readClassFiles(classFiles, new TrueStringPattern(), null, false); 96 } 97 98 127 public static AtomicVertex[] readClassFiles(String [] classFiles, 128 StringPattern pattern, 129 StringPattern reflectionPattern, 130 boolean mergeInnerClasses) 131 throws IOException 132 { 133 ArrayList unresolvedNodes = new ArrayList (); 134 for (int i = 0; i < classFiles.length; i++) 135 { 136 File file = new File (classFiles[i]); 137 if (file.isDirectory() || file.getName().endsWith(".class")) 138 { 139 analyseClassFile(file, unresolvedNodes, reflectionPattern); 140 } else if (isZipFile(file)) 141 { 142 analyseClassFiles(new ZipFile (file.getAbsoluteFile()), 143 unresolvedNodes, 144 reflectionPattern); 145 } else 146 { 147 throw new IOException (classFiles[i] + " is an invalid file."); 148 } 149 } 150 ArrayList filteredNodes = new ArrayList (); 151 for (int i = 0, n = unresolvedNodes.size(); i < n; i++) 152 { 153 UnresolvedNode node = (UnresolvedNode) unresolvedNodes.get(i); 154 if (node.isMatchedBy(pattern)) 155 { 156 filteredNodes.add(node); 157 } 158 } 159 UnresolvedNode[] nodes = new UnresolvedNode[filteredNodes.size()]; 160 nodes = (UnresolvedNode[]) filteredNodes.toArray(nodes); 161 Arrays.sort(nodes); 162 return createGraph(nodes, mergeInnerClasses); 163 } 164 165 private static boolean isZipFile(File file) 166 { 167 boolean result = false; 168 String name = file.getName(); 169 for (int i = 0; i < ZIP_FILE_TYPES.length; i++) 170 { 171 if (name.endsWith(ZIP_FILE_TYPES[i])) 172 { 173 result = true; 174 break; 175 } 176 } 177 return result; 178 } 179 180 private static void analyseClassFile(File file, ArrayList unresolvedNodes, 181 StringPattern reflectionPattern) 182 throws IOException 183 { 184 if (file.isDirectory()) 185 { 186 String [] files = file.list(); 187 for (int i = 0; i < files.length; i++) 188 { 189 File child = new File (file, files[i]); 190 if (child.isDirectory() || files[i].endsWith(".class")) 191 { 192 analyseClassFile(child, unresolvedNodes, reflectionPattern); 193 } 194 } 195 } else 196 { 197 unresolvedNodes.add(extractNode(file, reflectionPattern)); 198 } 199 } 200 201 private static UnresolvedNode extractNode(File file, 202 StringPattern reflectionPattern) 203 throws IOException 204 { 205 InputStream stream = null; 206 UnresolvedNode result = null; 207 try 208 { 209 stream = new FileInputStream (file); 210 result = Parser.createNode(stream, (int) file.length(), 211 reflectionPattern); 212 } finally 213 { 214 try 215 { 216 stream.close(); 217 } catch (IOException e) {} 218 } 219 return result; 220 } 221 222 private static void analyseClassFiles(ZipFile zipFile, 223 ArrayList unresolvedNodes, 224 StringPattern reflectionPattern) 225 throws IOException 226 { 227 Enumeration entries = zipFile.entries(); 228 while (entries.hasMoreElements()) 229 { 230 ZipEntry entry = (ZipEntry ) entries.nextElement(); 231 if (!entry.isDirectory() && entry.getName().endsWith(".class")) 232 { 233 InputStream stream = zipFile.getInputStream(entry); 234 unresolvedNodes.add(Parser.createNode(stream, (int) entry.getSize(), 235 reflectionPattern)); 236 } 237 } 238 } 239 240 251 private static UnresolvedNode createNode(InputStream stream, int size, 252 StringPattern reflectionPattern) 253 throws IOException 254 { 255 DataInputStream dataStream = new DataInputStream (stream); 257 Constant[] pool = Constant.extractConstantPool(dataStream); 258 int accessFlags = dataStream.readUnsignedShort(); 259 String name = 260 ((ClassConstant) pool[dataStream.readUnsignedShort()]).getName(); 261 ClassAttributes attributes = null; 262 if ((accessFlags & ACC_INTERFACE) != 0) 263 { 264 attributes = ClassAttributes.createInterface(name, size); 265 } else 266 { 267 if ((accessFlags & ACC_ABSTRACT) != 0) 268 { 269 attributes = ClassAttributes.createAbstractClass(name, size); 270 } else 271 { 272 attributes = ClassAttributes.createClass(name, size); 273 } 274 } 275 276 UnresolvedNode node = new UnresolvedNode(); 278 node.attributes = attributes; 279 for (int i = 0; i < pool.length; i++) 280 { 281 Constant constant = pool[i]; 282 if (constant instanceof ClassConstant) 283 { 284 ClassConstant cc = (ClassConstant) constant; 285 if (!cc.getName().startsWith(("[")) && !cc.getName().equals(name)) 286 { 287 node.nodes.add(cc.getName()); 288 } 289 } else if (constant instanceof UTF8Constant) 290 { 291 parseUTF8Constant((UTF8Constant) constant, node.nodes, name); 292 } else if (reflectionPattern != null 293 && constant instanceof StringConstant) 294 { 295 String str = ((StringConstant) constant).getString(); 296 if (ClassNameExtractor.isValid(str) && reflectionPattern.matches(str)) 297 { 298 node.nodes.add(str); 299 } 300 } 301 } 302 return node; 303 } 304 305 309 static void parseUTF8Constant(UTF8Constant constant, List nodes, 310 String className) 311 { 312 Set classNames = new ClassNameExtractor(constant).extract(); 313 for (Iterator iter = classNames.iterator(); iter.hasNext();) 314 { 315 String element = (String ) iter.next(); 316 if (className.equals(element) == false) 317 { 318 nodes.add(element); 319 } 320 } 321 } 322 323 332 private static AtomicVertex[] createGraph(UnresolvedNode[] unresolvedNodes, 333 boolean mergeInnerClasses) 334 { 335 Map vertices = createVertices(unresolvedNodes, mergeInnerClasses); 336 AtomicVertex[] result 337 = (AtomicVertex[]) vertices.values().toArray(new AtomicVertex[0]); 338 339 for (int i = 0; i < unresolvedNodes.length; i++) 341 { 342 UnresolvedNode node = unresolvedNodes[i]; 343 String name = normalize(node.attributes.getName(), mergeInnerClasses); 344 AtomicVertex vertex = (AtomicVertex) vertices.get(name); 345 for (int j = 0, m = node.nodes.size(); j < m; j++) 346 { 347 name = normalize((String ) node.nodes.get(j), mergeInnerClasses); 348 AtomicVertex head = (AtomicVertex) vertices.get(name); 349 if (head == null) 350 { 351 head = new AtomicVertex(ClassAttributes.createUnknownClass(name, 0)); 353 vertices.put(name, head); 354 } 355 if (vertex != head) 356 { 357 vertex.addOutgoingArcTo(head); 358 } 359 } 360 } 361 362 return result; 363 } 364 365 private static Map createVertices(UnresolvedNode[] unresolvedNodes, 366 boolean mergeInnerClasses) 367 { 368 Map vertices = new HashMap (); 369 for (int i = 0; i < unresolvedNodes.length; i++) 370 { 371 ClassAttributes attributes = unresolvedNodes[i].attributes; 372 String type = attributes.getType(); 373 String originalName = attributes.getName(); 374 int size = attributes.getSize(); 375 String name = normalize(originalName, mergeInnerClasses); 376 AtomicVertex vertex = (AtomicVertex) vertices.get(name); 377 if (vertex != null) 378 { 379 ClassAttributes vertexAttributes 380 = (ClassAttributes) vertex.getAttributes(); 381 size += vertexAttributes.getSize(); 382 if (name.equals(originalName) == false) 383 { 384 type = vertexAttributes.getType(); 385 } 386 } 387 attributes = new ClassAttributes(name, type, size); 388 vertex = new AtomicVertex(attributes); 389 vertices.put(name, vertex); 390 } 391 return vertices; 392 } 393 394 private static String normalize(String name, boolean mergeInnerClasses) 395 { 396 if (mergeInnerClasses) 397 { 398 int index = name.indexOf('$'); 399 if (index >= 0) 400 { 401 name = name.substring(0, index); 402 } 403 } 404 return name; 405 } 406 407 } | Popular Tags |