/* Copyright (C) 2004 Vladimir Roubtsov. All rights reserved. * * This program and the accompanying materials are made available under * the terms of the Common Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/cpl-v10.html * * $Id: ClassDep.java,v 1.1.2.2 2004/07/09 01:42:04 vlad_r Exp $ */ package com.vladium.tools; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Properties; import java.util.Set; import java.util.StringTokenizer; import com.vladium.jcd.cls.ClassDef; import com.vladium.jcd.cls.IConstantCollection; import com.vladium.jcd.cls.constant.CONSTANT_Class_info; import com.vladium.jcd.cls.constant.CONSTANT_info; import com.vladium.jcd.parser.ClassDefParser; import com.vladium.util.ByteArrayOStream; import com.vladium.util.Descriptors; // ---------------------------------------------------------------------------- /** * TODO: doc * * @author Vlad Roubtsov, (C) 2004 */ public class ClassDep { // public: ................................................................ public static void main (final String [] args) throws Exception { if (args.length < 2) throw new IllegalArgumentException ("usage: classpath output_file rootset_classname_1 [rootset_classname_2 ...]"); final String _classPath = args [0]; final URL [] classPath; { final StringTokenizer tokenizer = new StringTokenizer (_classPath, File.pathSeparator); classPath = new URL [tokenizer.countTokens ()]; for (int i = 0; tokenizer.hasMoreTokens (); ++ i) { classPath [i] = new File (tokenizer.nextToken ()).toURL (); } } final File outFile = new File (args [1]); final int offset = 2; final String [] rootSet = args.length > offset ? new String [args.length - offset] : new String [0]; { for (int a = 0; a < rootSet.length; ++ a) { rootSet [a] = args [a + offset]; } } final ClassDep _this = new ClassDep (rootSet, classPath); final String [] deps = _this.getDependencies (true); final StringBuffer s = new StringBuffer (); for (int d = deps.length - 1; d >= 0; -- d) // reverse topological order { s.append (deps [d]); if (d > 0) s.append (','); } final File parent = outFile.getParentFile (); if (parent != null) parent.mkdirs (); final FileOutputStream out = new FileOutputStream (outFile); final Properties result = new Properties (); result.setProperty ("closure", s.toString ()); result.store (out, "this file is auto-generated, do not edit"); out.close (); } public ClassDep (final String [] rootSet, final URL [] classPath) { if (rootSet == null) throw new IllegalArgumentException ("null input: rootSet"); if (classPath == null) throw new IllegalArgumentException ("null input: classPath"); m_rootSet = rootSet; m_classPath = classPath; } public String [] getDependencies (final boolean includeRootSet) throws IOException { final Set /* class Java name:String */ _result = new HashSet (); final ClassLoader loader = new URLClassLoader (m_classPath, null); if (includeRootSet) { for (int i = 0; i < m_rootSet.length; ++ i) { _result.add (m_rootSet [i]); } } final LinkedList /* class VM name:String */ queue = new LinkedList (); for (int i = 0; i < m_rootSet.length; ++ i) { queue.add (Descriptors.javaNameToVMName (m_rootSet [i])); } final ByteArrayOStream baos = new ByteArrayOStream (8 * 1024); final byte [] readbuf = new byte [8 * 1024]; while (! queue.isEmpty ()) { final String classVMName = (String) queue.removeFirst (); // keep at most one file descriptor open: InputStream in = null; try { // NOTE: getResources() not used in = loader.getResourceAsStream (classVMName + ".class"); if (in == null) { throw new IllegalArgumentException ("class [" + Descriptors.vmNameToJavaName (classVMName) + "] not found in the input classpath"); } else { baos.reset (); for (int read; (read = in.read (readbuf)) >= 0; baos.write (readbuf, 0, read)); } } finally { if (in != null) try { in.close (); } catch (IOException ignore) { ignore.printStackTrace (); } } in = null; final ClassDef cls = ClassDefParser.parseClass (baos.getByteArray (), baos.size ()); final List /* class VM name:String */ clsDeps = getCONSTANT_Class_info (cls); for (Iterator i = clsDeps.iterator (); i.hasNext (); ) { final String classDepVMName = (String) i.next (); if (classDepVMName.startsWith ("com/vladium/")) // TODO: generic filtering { if (_result.add (Descriptors.vmNameToJavaName (classDepVMName))) { queue.addLast (classDepVMName); } } } } final String [] result = new String [_result.size ()]; _result.toArray (result); return result; } /** * @return array of class VM names [may contain duplicates] */ public static List getCONSTANT_Class_info (final ClassDef cls) { if (cls == null) throw new IllegalArgumentException ("null input: cls"); final IConstantCollection constants = cls.getConstants (); final IConstantCollection.IConstantIterator i = constants.iterator (); final List result = new ArrayList (); for (CONSTANT_info entry; (entry = i.nextConstant ()) != null; ) { if (entry instanceof CONSTANT_Class_info) { result.add (((CONSTANT_Class_info) entry).getName (cls)); } } return result; } // protected: ............................................................. // package: ............................................................... // private: ............................................................... private final String [] m_rootSet; private final URL [] m_classPath; } // end of class // ----------------------------------------------------------------------------