blob: 05d06b9b9eca2ff23e24d93a3ed35e1047d8870e [file] [log] [blame]
/* 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
// ----------------------------------------------------------------------------