1 /* Copyright (C) 2004 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: ClassDep.java,v 1.1.2.2 2004/07/09 01:42:04 vlad_r Exp $ 8 */ 9 package com.vladium.tools; 10 11 import java.io.File; 12 import java.io.FileOutputStream; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.net.URL; 16 import java.net.URLClassLoader; 17 import java.util.ArrayList; 18 import java.util.HashSet; 19 import java.util.Iterator; 20 import java.util.LinkedList; 21 import java.util.List; 22 import java.util.Properties; 23 import java.util.Set; 24 import java.util.StringTokenizer; 25 26 import com.vladium.jcd.cls.ClassDef; 27 import com.vladium.jcd.cls.IConstantCollection; 28 import com.vladium.jcd.cls.constant.CONSTANT_Class_info; 29 import com.vladium.jcd.cls.constant.CONSTANT_info; 30 import com.vladium.jcd.parser.ClassDefParser; 31 import com.vladium.util.ByteArrayOStream; 32 import com.vladium.util.Descriptors; 33 34 // ---------------------------------------------------------------------------- 35 /** 36 * TODO: doc 37 * 38 * @author Vlad Roubtsov, (C) 2004 39 */ 40 public class ClassDep 41 { 42 // public: ................................................................ 43 main(final String [] args)44 public static void main (final String [] args) 45 throws Exception 46 { 47 if (args.length < 2) 48 throw new IllegalArgumentException ("usage: classpath output_file rootset_classname_1 [rootset_classname_2 ...]"); 49 50 final String _classPath = args [0]; 51 final URL [] classPath; 52 { 53 final StringTokenizer tokenizer = new StringTokenizer (_classPath, File.pathSeparator); 54 classPath = new URL [tokenizer.countTokens ()]; 55 56 for (int i = 0; tokenizer.hasMoreTokens (); ++ i) 57 { 58 classPath [i] = new File (tokenizer.nextToken ()).toURL (); 59 } 60 } 61 62 final File outFile = new File (args [1]); 63 64 final int offset = 2; 65 final String [] rootSet = args.length > offset 66 ? new String [args.length - offset] 67 : new String [0]; 68 { 69 for (int a = 0; a < rootSet.length; ++ a) 70 { 71 rootSet [a] = args [a + offset]; 72 } 73 } 74 75 final ClassDep _this = new ClassDep (rootSet, classPath); 76 final String [] deps = _this.getDependencies (true); 77 78 final StringBuffer s = new StringBuffer (); 79 for (int d = deps.length - 1; d >= 0; -- d) // reverse topological order 80 { 81 s.append (deps [d]); 82 if (d > 0) s.append (','); 83 } 84 85 final File parent = outFile.getParentFile (); 86 if (parent != null) parent.mkdirs (); 87 88 final FileOutputStream out = new FileOutputStream (outFile); 89 90 final Properties result = new Properties (); 91 result.setProperty ("closure", s.toString ()); 92 93 result.store (out, "this file is auto-generated, do not edit"); 94 95 out.close (); 96 } 97 98 ClassDep(final String [] rootSet, final URL [] classPath)99 public ClassDep (final String [] rootSet, final URL [] classPath) 100 { 101 if (rootSet == null) 102 throw new IllegalArgumentException ("null input: rootSet"); 103 104 if (classPath == null) 105 throw new IllegalArgumentException ("null input: classPath"); 106 107 m_rootSet = rootSet; 108 m_classPath = classPath; 109 } 110 getDependencies(final boolean includeRootSet)111 public String [] getDependencies (final boolean includeRootSet) 112 throws IOException 113 { 114 final Set /* class Java name:String */ _result = new HashSet (); 115 final ClassLoader loader = new URLClassLoader (m_classPath, null); 116 117 if (includeRootSet) 118 { 119 for (int i = 0; i < m_rootSet.length; ++ i) 120 { 121 _result.add (m_rootSet [i]); 122 } 123 } 124 125 final LinkedList /* class VM name:String */ queue = new LinkedList (); 126 for (int i = 0; i < m_rootSet.length; ++ i) 127 { 128 queue.add (Descriptors.javaNameToVMName (m_rootSet [i])); 129 } 130 131 final ByteArrayOStream baos = new ByteArrayOStream (8 * 1024); 132 final byte [] readbuf = new byte [8 * 1024]; 133 134 while (! queue.isEmpty ()) 135 { 136 final String classVMName = (String) queue.removeFirst (); 137 138 // keep at most one file descriptor open: 139 140 InputStream in = null; 141 try 142 { 143 // NOTE: getResources() not used 144 in = loader.getResourceAsStream (classVMName + ".class"); 145 146 if (in == null) 147 { 148 throw new IllegalArgumentException ("class [" + Descriptors.vmNameToJavaName (classVMName) + "] not found in the input classpath"); 149 } 150 else 151 { 152 baos.reset (); 153 for (int read; (read = in.read (readbuf)) >= 0; baos.write (readbuf, 0, read)); 154 } 155 } 156 finally 157 { 158 if (in != null) try { in.close (); } catch (IOException ignore) { ignore.printStackTrace (); } 159 } 160 in = null; 161 162 final ClassDef cls = ClassDefParser.parseClass (baos.getByteArray (), baos.size ()); 163 final List /* class VM name:String */ clsDeps = getCONSTANT_Class_info (cls); 164 165 for (Iterator i = clsDeps.iterator (); i.hasNext (); ) 166 { 167 final String classDepVMName = (String) i.next (); 168 169 if (classDepVMName.startsWith ("com/vladium/")) // TODO: generic filtering 170 { 171 if (_result.add (Descriptors.vmNameToJavaName (classDepVMName))) 172 { 173 queue.addLast (classDepVMName); 174 } 175 } 176 } 177 } 178 179 final String [] result = new String [_result.size ()]; 180 _result.toArray (result); 181 182 return result; 183 } 184 185 /** 186 * @return array of class VM names [may contain duplicates] 187 */ getCONSTANT_Class_info(final ClassDef cls)188 public static List getCONSTANT_Class_info (final ClassDef cls) 189 { 190 if (cls == null) 191 throw new IllegalArgumentException ("null input: cls"); 192 193 final IConstantCollection constants = cls.getConstants (); 194 final IConstantCollection.IConstantIterator i = constants.iterator (); 195 196 final List result = new ArrayList (); 197 198 for (CONSTANT_info entry; (entry = i.nextConstant ()) != null; ) 199 { 200 if (entry instanceof CONSTANT_Class_info) 201 { 202 result.add (((CONSTANT_Class_info) entry).getName (cls)); 203 } 204 } 205 206 return result; 207 } 208 209 // protected: ............................................................. 210 211 // package: ............................................................... 212 213 // private: ............................................................... 214 215 216 private final String [] m_rootSet; 217 private final URL [] m_classPath; 218 219 } // end of class 220 // ----------------------------------------------------------------------------