blob: d8e2f2fc22e07c9139b8984b11acea9944708c70 [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: InstrProcessor.java,v 1.1.1.1.2.3 2004/07/17 16:57:14 vlad_r Exp $
*/
package com.vladium.emma.instr;
import java.io.File;
import com.vladium.util.Files;
import com.vladium.util.IConstants;
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.Processor;
import com.vladium.emma.filter.IInclExclFilter;
// ----------------------------------------------------------------------------
/*
* This class was not meant to be public by design. It is made to to work around
* access bugs in reflective invocations.
*/
/**
* @author Vlad Roubtsov, (C) 2003
*/
public
abstract class InstrProcessor extends Processor
implements IPathEnumerator.IPathHandler
{
// public: ................................................................
public static final String PROPERTY_EXCLUDE_SYNTHETIC_METHODS = "instr.exclude_synthetic_methods";
public static final String PROPERTY_EXCLUDE_BRIDGE_METHODS = "instr.exclude_bridge_methods";
public static final String PROPERTY_DO_SUID_COMPENSATION = "instr.do_suid_compensation";
public static final String DEFAULT_EXCLUDE_SYNTHETIC_METHODS = "true";
public static final String DEFAULT_EXCLUDE_BRIDGE_METHODS = "true";
public static final String DEFAULT_DO_SUID_COMPENSATION = "true";
public static InstrProcessor create ()
{
return new InstrProcessorST ();
}
/**
*
* @param path [null is equivalent to an empty array]
* @param canonical
*/
public synchronized final void setInstrPath (final String [] path, final boolean canonical)
{
if ((path == null) || (path.length == 0))
m_instrPath = IConstants.EMPTY_FILE_ARRAY;
else
m_instrPath = Files.pathToFiles (path, canonical);
m_canonical = canonical;
}
public synchronized final void setDependsMode (final boolean enable)
{
m_dependsMode = enable;
}
/**
*
* @param specs [null is equivalent to no filtering (everything is included)]
*/
public synchronized final void setInclExclFilter (final String [] specs)
{
if (specs == null)
m_coverageFilter = null;
else
m_coverageFilter = IInclExclFilter.Factory.create (specs);
}
/**
*
* @param fileName [null unsets the previous override setting]
*/
public synchronized final void setMetaOutFile (final String fileName)
{
if (fileName == null)
m_mdataOutFile = null;
else
{
final File _file = new File (fileName);
if (_file.exists () && ! _file.isFile ())
throw new IllegalArgumentException ("not a file: [" + _file.getAbsolutePath () + "]");
m_mdataOutFile = _file;
}
}
/**
*
* @param merge [null unsets the previous override setting]
*/
public synchronized final void setMetaOutMerge (final Boolean merge)
{
m_mdataOutMerge = merge;
}
/**
*
* @param dir [null unsets the previous setting]
*/
public synchronized final void setInstrOutDir (final String dir)
{
if (dir == null)
m_outDir = null;
else
{
final File _outDir = new File (dir);
if (_outDir.exists () && ! _outDir.isDirectory ())
throw new IllegalArgumentException ("not a directory: [" + _outDir.getAbsolutePath () + "]");
m_outDir = _outDir;
}
}
/**
*
* @param mode [may not be null]
*/
public synchronized final void setOutMode (final OutMode mode)
{
if (mode == null)
throw new IllegalArgumentException ("null input: mode");
m_outMode = mode;
}
// protected: .............................................................
protected InstrProcessor ()
{
m_dependsMode = true;
}
protected void validateState ()
{
super.validateState ();
if ((m_instrPath == null) || (m_instrPath.length == 0))
throw new IllegalStateException ("instrumentation path not set");
// [m_coverageFilter can be null]
if (m_outMode == null)
throw new IllegalStateException ("output mode not set");
if (m_outMode != OutMode.OUT_MODE_OVERWRITE)
{
if (m_outDir == null)
throw new IllegalStateException ("output directory not set");
// for non-overwrite modes output directory must not overlap
// with the instr path:
// [the logic below does not quite catch all possibilities due to
// Class-Path: manifest attributes and dir nesting, but it should
// intercept most common mistakes]
if ($assert.ENABLED)
{
$assert.ASSERT (m_outDir != null, "m_outDir = null");
$assert.ASSERT (m_instrPath != null, "m_instrPath = null");
}
final File canonicalOutDir = Files.canonicalizeFile (m_outDir);
final File [] canonicalInstrPath;
if (m_canonical)
canonicalInstrPath = m_instrPath;
else
{
canonicalInstrPath = new File [m_instrPath.length];
for (int ip = 0; ip < canonicalInstrPath.length; ++ ip)
{
canonicalInstrPath [ip] = Files.canonicalizeFile (m_instrPath [ip]);
}
}
// FR_SF988785: detect if the user attempted to use a parent of m_outDir as one of
// the input directories (prevents spurious "already instrumented" errors)
final int instrPathLength = canonicalInstrPath.length;
for (File dir = canonicalOutDir; dir != null; dir = dir.getParentFile ()) // getParentFile() does no real I/O
{
for (int ip = 0; ip < instrPathLength; ++ ip)
{
if (dir.equals (canonicalInstrPath [ip]))
throw new IllegalStateException ("output directory [" + canonicalOutDir + "] cannot be one of the instrumentation path directories (or a child thereof)");
}
}
}
// [m_mdataOutFile can be null]
// [m_mdataOutMerge can be null]
}
protected void reset ()
{
m_classCopies = m_classInstrs = 0;
}
protected final void createDir (final File dir, final boolean mkall)
throws EMMARuntimeException
{
if (mkall)
{
if (! dir.mkdirs () && ! dir.exists ())
throw new EMMARuntimeException (IAppErrorCodes.OUT_MKDIR_FAILURE, new Object [] {dir.getAbsolutePath ()});
}
else
{
if (! dir.mkdir () && ! dir.exists ())
throw new EMMARuntimeException (IAppErrorCodes.OUT_MKDIR_FAILURE, new Object [] {dir.getAbsolutePath ()});
}
}
protected final File getFullOutDir (final File pathDir, final boolean isClass)
{
if (m_outMode == OutMode.OUT_MODE_OVERWRITE)
{
return pathDir;
}
else if (m_outMode == OutMode.OUT_MODE_COPY)
{
return m_outDir;
}
else if (m_outMode == OutMode.OUT_MODE_FULLCOPY)
{
return isClass ? Files.newFile (m_outDir, CLASSES) : Files.newFile (m_outDir, LIB);
}
else throw new IllegalStateException ("invalid out mode state: " + m_outMode);
}
protected final File getFullOutFile (final File pathDir, final File file, final boolean isClass)
{
return Files.newFile (getFullOutDir (pathDir, isClass), file.getPath ());
}
// caller-settable state [scoped to this runner instance]:
protected File [] m_instrPath; // required to be non-null/non-empty for run()
protected boolean m_dependsMode;
protected boolean m_canonical;
protected IInclExclFilter m_coverageFilter; // can be null for run()
protected OutMode m_outMode; // required to be set for run()
protected File m_outDir; // required to be non-null for run(), unless output mode is 'overwrite'
protected File m_mdataOutFile; // user override; can be null for run()
protected Boolean m_mdataOutMerge; // user override; can be null for run()
// internal run()-scoped state:
protected int m_classCopies, m_classInstrs;
protected static final String CLASSES = "classes";
protected static final String LIB = "lib";
protected static final boolean IN_CLASSES = true;
protected static final boolean IN_LIB = ! IN_CLASSES;
// package: ...............................................................
// TODO: access level [public to workaround Sun's bugs in access level in reflective invocations]
public static final class OutMode
{
public static final OutMode OUT_MODE_COPY = new OutMode ("copy");
public static final OutMode OUT_MODE_FULLCOPY = new OutMode ("fullcopy");
public static final OutMode OUT_MODE_OVERWRITE = new OutMode ("overwrite");
public String getName ()
{
return m_name;
}
public String toString ()
{
return m_name;
}
public static OutMode nameToMode (final String name)
{
if (OUT_MODE_COPY.m_name.equals (name))
return OUT_MODE_COPY;
else if (OUT_MODE_FULLCOPY.m_name.equals (name))
return OUT_MODE_FULLCOPY;
else if (OUT_MODE_OVERWRITE.m_name.equals (name))
return OUT_MODE_OVERWRITE;
return null;
}
private OutMode (final String name)
{
m_name = name;
}
private final String m_name;
} // end of nested class
// private: ...............................................................
} // end of class
// ----------------------------------------------------------------------------