blob: 2e06948f402760b59d79a86b9b7bbb30a6bab56b [file] [log] [blame]
/* Copyright (C) 2003 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: ClassPathProcessorST.java,v 1.1.1.1.2.1 2004/07/16 23:32:03 vlad_r Exp $
*/
package com.vladium.emma.rt;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import com.vladium.jcd.cls.ClassDef;
import com.vladium.jcd.parser.ClassDefParser;
import com.vladium.logging.Logger;
import com.vladium.util.ByteArrayOStream;
import com.vladium.util.Files;
import com.vladium.util.IPathEnumerator;
import com.vladium.util.asserts.$assert;
import com.vladium.emma.IAppErrorCodes;
import com.vladium.emma.EMMARuntimeException;
import com.vladium.emma.data.IMetaData;
import com.vladium.emma.filter.IInclExclFilter;
import com.vladium.emma.instr.InstrVisitor;
// ----------------------------------------------------------------------------
/**
* @author Vlad Roubtsov, (C) 2003
*/
public
final class ClassPathProcessorST implements IPathEnumerator.IPathHandler, IAppErrorCodes
{
// public: ................................................................
public void run ()
{
long start = System.currentTimeMillis ();
// construct instr path enumerator [throws on illegal input only]:
final IPathEnumerator enumerator = IPathEnumerator.Factory.create (m_path, m_canonical, this);
// allocate I/O buffers:
m_readbuf = new byte [BUF_SIZE]; // don't reuse this across run() calls to reset it to the original size
m_readpos = 0;
m_baos = new ByteArrayOStream (BUF_SIZE); // don't reuse this across run() calls to reset it to the original size
if (m_log.atINFO ())
{
m_log.info ("processing classpath ...");
}
// actual work is driven by the path enumerator:
try
{
enumerator.enumerate ();
}
catch (IOException ioe)
{
throw new EMMARuntimeException (INSTR_IO_FAILURE, ioe);
}
if (m_log.atINFO ())
{
final long end = System.currentTimeMillis ();
m_log.info ("[" + m_classCount + " class(es) processed in " + (end - start) + " ms]");
}
}
// IPathEnumerator.IPathHandler:
public void handleArchiveStart (final File parentDir, final File archive, final Manifest manifest)
{
m_archiveFile = Files.newFile (parentDir, archive.getPath ());
}
public void handleArchiveEntry (final JarInputStream in, final ZipEntry entry)
{
if (m_log.atTRACE2 ()) m_log.trace2 ("handleArchiveEntry", "[" + entry.getName () + "]");
final String name = entry.getName ();
final String lcName = name.toLowerCase ();
if (lcName.endsWith (".class"))
{
final String className = name.substring (0, name.length () - 6).replace ('/', '.');
if ((m_coverageFilter == null) || m_coverageFilter.included (className))
{
String srcURL = null;
InputStream clsin = null;
try
{
readZipEntry (in, entry);
srcURL = "jar:".concat (m_archiveFile.toURL ().toExternalForm ()).concat ("!/").concat (name);
}
catch (FileNotFoundException fnfe)
{
// ignore: this should never happen
if ($assert.ENABLED)
{
fnfe.printStackTrace (System.out);
}
}
catch (IOException ioe)
{
// TODO: error code
throw new EMMARuntimeException (ioe);
}
finally
{
if (clsin != null)
try
{
clsin.close ();
clsin = null;
}
catch (Exception e)
{
// TODO: error code
throw new EMMARuntimeException (e);
}
}
// [original class def read into m_readbuf]
try
{
ClassDef clsDef = ClassDefParser.parseClass (m_readbuf, m_readpos);
if (! clsDef.isInterface ()) ++ m_classCount;
m_visitor.process (clsDef, false, false, true, m_instrResult); // get metadata only
clsDef = null;
boolean cacheClassDef = true;
if (m_instrResult.m_descriptor != null)
{
// do not overwrite existing descriptors to support "first
// in the classpath wins" semantics:
if (! m_mdata.add (m_instrResult.m_descriptor, false))
cacheClassDef = false;
}
if (cacheClassDef && (m_cache != null))
{
final byte [] bytes = new byte [m_readpos];
System.arraycopy (m_readbuf, 0, bytes, 0, m_readpos);
m_cache.put (className, new ClassPathCacheEntry (bytes, srcURL));
}
}
catch (IOException ioe)
{
// TODO: error code
throw new EMMARuntimeException (ioe);
}
}
}
}
public void handleArchiveEnd (final File parentDir, final File archive)
{
m_archiveFile = null;
}
public void handleDirStart (final File pathDir, final File dir)
{
// do nothing
}
public void handleFile (final File pathDir, final File file)
{
if (m_log.atTRACE2 ()) m_log.trace2 ("handleFile", "[" + pathDir + "] [" + file + "]");
final String name = file.getPath ();
final String lcName = name.toLowerCase ();
if (lcName.endsWith (".class"))
{
final String className = name.substring (0, name.length () - 6).replace (File.separatorChar, '.');
if ((m_coverageFilter == null) || m_coverageFilter.included (className))
{
String srcURL = null;
InputStream clsin = null;
try
{
final File inFile = Files.newFile (pathDir, file.getPath ());
readFile (inFile);
srcURL = inFile.toURL ().toExternalForm ();
}
catch (FileNotFoundException fnfe)
{
// ignore: this should never happen
if ($assert.ENABLED)
{
fnfe.printStackTrace (System.out);
}
}
catch (IOException ioe)
{
// TODO: error code
throw new EMMARuntimeException (ioe);
}
finally
{
if (clsin != null)
try
{
clsin.close ();
clsin = null;
}
catch (Exception e)
{
// TODO: error code
throw new EMMARuntimeException (e);
}
}
// [original class def read into m_readbuf]
try
{
ClassDef clsDef = ClassDefParser.parseClass (m_readbuf, m_readpos);
if (! clsDef.isInterface ()) ++ m_classCount;
m_visitor.process (clsDef, false, false, true, m_instrResult); // get metadata only
clsDef = null;
boolean cacheClassDef = true;
if (m_instrResult.m_descriptor != null)
{
// do not overwrite existing descriptors to support "first
// in the classpath wins" semantics:
if (! m_mdata.add (m_instrResult.m_descriptor, false))
cacheClassDef = false;
}
if (cacheClassDef && (m_cache != null))
{
final byte [] bytes = new byte [m_readpos];
System.arraycopy (m_readbuf, 0, bytes, 0, m_readpos);
m_cache.put (className, new ClassPathCacheEntry (bytes, srcURL));
}
}
catch (IOException ioe)
{
// TODO: error code
throw new EMMARuntimeException (ioe);
}
}
}
}
public void handleDirEnd (final File pathDir, final File dir)
{
// do nothing
}
// protected: .............................................................
// package: ...............................................................
/*
* null 'cache' indicates to only populate the metadata and not bother with
* caching instrumented class defs
*/
ClassPathProcessorST (final File [] path, final boolean canonical,
final IMetaData mdata, final IInclExclFilter filter,
final Map cache)
{
if (path == null) throw new IllegalArgumentException ("null input: path");
if (mdata == null) throw new IllegalArgumentException ("null input: mdata");
m_path = path;
m_canonical = canonical;
m_mdata = mdata;
m_coverageFilter = filter;
m_cache = cache; // can be null
m_visitor = new InstrVisitor (mdata.getOptions ());
m_instrResult = new InstrVisitor.InstrResult ();
m_log = Logger.getLogger ();
}
// private: ...............................................................
/*
* Reads into m_readbuf (m_readpos is updated correspondingly)
*/
private void readFile (final File file)
throws IOException
{
final int length = (int) file.length ();
ensureReadCapacity (length);
InputStream in = null;
try
{
in = new FileInputStream (file);
int totalread = 0;
for (int read;
(totalread < length) && (read = in.read (m_readbuf, totalread, length - totalread)) >= 0;
totalread += read);
m_readpos = totalread;
}
finally
{
if (in != null) try { in.close (); } catch (Exception ignore) {}
}
}
/*
* Reads into m_readbuf (m_readpos is updated correspondingly)
*/
private void readZipEntry (final ZipInputStream in, final ZipEntry entry)
throws IOException
{
final int length = (int) entry.getSize (); // can be -1 if unknown
if (length >= 0)
{
ensureReadCapacity (length);
int totalread = 0;
for (int read;
(totalread < length) && (read = in.read (m_readbuf, totalread, length - totalread)) >= 0;
totalread += read);
m_readpos = totalread;
}
else
{
ensureReadCapacity (BUF_SIZE);
m_baos.reset ();
for (int read; (read = in.read (m_readbuf)) >= 0; m_baos.write (m_readbuf, 0, read));
m_readbuf = m_baos.copyByteArray ();
m_readpos = m_readbuf.length;
}
}
private void ensureReadCapacity (final int capacity)
{
if (m_readbuf.length < capacity)
{
final int readbuflen = m_readbuf.length;
m_readbuf = null;
m_readbuf = new byte [Math.max (readbuflen << 1, capacity)];
}
}
private final File [] m_path; // never null
private final boolean m_canonical;
private final IMetaData m_mdata; // never null
private final IInclExclFilter m_coverageFilter; // can be null
private final InstrVisitor m_visitor;
private final InstrVisitor.InstrResult m_instrResult;
private final Map /* classJavaName:String -> ClassPathCacheEntry */ m_cache; // can be null
private final Logger m_log; // this class is instantiated and used on a single thread
private int m_classCount;
private byte [] m_readbuf;
private int m_readpos;
private ByteArrayOStream m_baos; // TODO: code to guard this from becoming too large
private File m_archiveFile;
private static final int BUF_SIZE = 32 * 1024;
} // end of class
// ----------------------------------------------------------------------------