Home | History | Annotate | Download | only in tools
      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 
     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 
     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 
    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      */
    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 // ----------------------------------------------------------------------------