PreprocessorBuilder.java
| Index Score | ||
|---|---|---|
![]() |
![]() |
eclipseme.core.internal.preprocessor |
![]() |
![]() |
EclipseME |
View: Reasons, Metrics, Source Code
These are the metrics that contribute to the Enerjy Score for this file, ranked by impact. So the metrics listed at the top influence the score to a greater extent that the metrics listed at the bottom.
/**
* Copyright (c) 2003-2005 Craig Setera
* All Rights Reserved.
* Licensed under the Eclipse Public License - v 1.0
* For more information see http://www.eclipse.org/legal/epl-v10.html
*/
package eclipseme.core.internal.preprocessor;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IResourceDeltaVisitor;
import org.eclipse.core.resources.IResourceVisitor;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.resources.ResourceAttributes;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.jface.text.IDocument;
import antenna.preprocessor.v2.IPreprocessorListener;
import antenna.preprocessor.v2.Preprocessor;
import antenna.preprocessor.v2.Preprocessor.ILineFilter;
import antenna.preprocessor.v2.Preprocessor.ILogger;
import eclipseme.core.BuildLoggingConfiguration;
import eclipseme.core.console.BuildConsoleProxy;
import eclipseme.core.console.IBuildConsoleProxy;
import eclipseme.core.hook.sourceMapper.SourceMapperAccess;
import eclipseme.core.internal.EclipseMECorePlugin;
import eclipseme.core.internal.utils.Utils;
import eclipseme.core.model.IMidletSuiteProject;
import eclipseme.core.model.MidletSuiteFactory;
import eclipseme.core.model.SymbolDefinitionSet;
import eclipseme.core.persistence.PersistenceException;
/**
* An incremental builder implementation that provides preprocess
* support as defined for the Antenna Ant tools.<br/>
* <p>
The preprocessor supports the following directives inside a Java
source file. All directives must immediately follow a "//" comment that
starts at the beginning of a line (whitespace is allowed left of them, but
no Java code). That way, they don't interfere with normal Java compilation.
Directives must not span multiple lines of code.
<p />
<table class="color">
<tr>
<th width="30%">
Directive
</th>
<th>
Decription
</th>
</tr>
<tr>
<td>
#define <identifier>
</td>
<td>
Defines an identifier, thus making its value "true" when it is
referenced in further expressions.
</td>
</tr>
<tr>
<td>
#undefine <identifier>
</td>
<td>
Undefines an identifier, thus making its value "false" when it is
referenced in further expressions.
</td>
</tr>
<tr>
<td>
#ifdef <identifier>
</td>
<td rowspan="8">
The following lines are compiled only if the given identifier
is defined (or undefined, in the case of an "#ifndef"
directive). "#else" does exactly what your think it does. Each
directive must be ultimately closed by an "#endif" directive.
The "#elifdef" and "#elifndef" directives help to specify longer conditional
cascades without having to nest each level.
<p />
The "#if" and "#elif" directives even allow to use complex expressions.
These expressions are very much like Java boolean expressions. They
may consist of identifiers, parentheses and the usual "&&", "||",
"^", and "!" operators.
<p />
Please note that "#ifdef" and "#ifndef" don't
support complex expressions. They only expect a single argument - the
symbol to be checked.
</td>
</tr>
<tr>
<td>
#ifndef <identifier>
</td>
</tr>
<tr>
<td>
#else
</td>
</tr>
<tr>
<td>
#endif
</td>
</tr>
<tr>
<td>
#elifdef <identifier>
</td>
</tr>
<tr>
<td>
#elifndef <identifier>
</td>
</tr>
<tr>
<td>
#if <expression>
</td>
</tr>
<tr>
<td>
#elif <expression>
</td>
</tr>
<tr>
<td>
#include <filename>
</td>
<td rowspan="2">
Includes the given source file at the current position. Must
be terminated by "#endinclude" for technical reasons. The
filename may also be given as an Ant-style property (${name}).
The property needs to be defined in your build.xml file then. Note
that relative file names are interpreted as relative to the
build.xml file.
</td>
</tr>
<tr>
<td>
#endinclude
</td>
</tr>
</table>
* <p />
* Copyright (c) 2003-2005 Craig Setera<br>
* All Rights Reserved.<br>
* Licensed under the Eclipse Public License - v 1.0<p/>
* <br>
* $Revision: 1.6 $
* <br>
* $Date: 2006/12/18 02:13:46 $
* <br>
* @author Craig Setera
*/
public class PreprocessorBuilder extends IncrementalProjectBuilder {
/** A preprocessor problem marker */
public static final String PROBLEM_MARKER = "eclipseme.core.preprocessor.problemmarker";
// The subdirectory into which the processed output will go
public static final String PROCESSED_DIRECTORY = ".processed";
private static final String PROCESSED_PATH = "/" + PROCESSED_DIRECTORY;
/**
* Return an appropriate output file for the specified resource.
* This file is not guaranteed to exist.
*
* @param resource
* @return
*/
public static IFile getOutputFile(IResource resource) {
IPath projectRelativePath = new Path(PROCESSED_PATH).append(resource.getProjectRelativePath());
return resource.getProject().getFile(projectRelativePath);
}
/**
* Return the project that is to hold the preprocess output results for the
* specified project.
*
* @param project
* @return
*/
public static IProject getOutputProject(IProject project) {
String ppProjectName = "." + project.getName() + "_PP";
return ResourcesPlugin.getWorkspace().getRoot().getProject(ppProjectName);
}
/**
* Return a boolean indicating whether or not the specified
* resource has already been preprocessed and should be ignored
* for further processing.
*
* @param resource
* @return
*/
public static boolean isPreprocessed(IResource resource) {
IPath projectPath = resource.getProjectRelativePath();
return projectPath.segment(0).equals(PROCESSED_DIRECTORY);
}
/**
* Resource visitor for visiting java files for preprocessing.
*/
protected class ResourceVisitor implements IResourceVisitor {
protected SymbolDefinitionSet symbols;
protected IProgressMonitor monitor;
protected ResourceVisitor(SymbolDefinitionSet symbols, IProgressMonitor monitor) {
this.symbols = symbols;
this.monitor = monitor;
}
public boolean visit(IResource resource)
throws CoreException
{
if (shouldBeProcessed(resource, monitor)) {
preprocess(resource, symbols, monitor);
}
return true;
}
}
/**
* Resource delta visitor for visiting changed java files for
* preprocessing.
*/
private class ResourceDeltaVisitor
extends ResourceVisitor
implements IResourceDeltaVisitor
{
protected ResourceDeltaVisitor(SymbolDefinitionSet symbols, IProgressMonitor monitor) {
super(symbols, monitor);
}
public boolean visit(IResourceDelta delta)
throws CoreException
{
boolean value = false;
switch (delta.getKind()) {
case IResourceDelta.ADDED:
case IResourceDelta.CHANGED:
value = visit(delta.getResource());
break;
case IResourceDelta.REMOVED: {
IFile outputFile = getOutputFile(delta.getResource());
if (outputFile.exists()) {
outputFile.delete(true, monitor);
}
}
break;
default:
// Do nothing
}
return value;
}
}
private BuildLoggingConfiguration buildLoggingConfig;
// Tracker for a warning check
private boolean checkedForHook;
/**
* Construct a new builder instance.
*/
public PreprocessorBuilder() {
super();
buildLoggingConfig = BuildLoggingConfiguration.instance;
// During the first actual build, we will check to see
// if the hook has been installed. If not, the build
// will continue, but the user will be warned via the error
// log that it won't actually work.
checkedForHook = false;
}
/**
* @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
*/
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException
{
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("> PreprocessorBuilder.build");
}
if (!checkedForHook) {
if (!SourceMapperAccess.isHookCodeInstalled()) {
EclipseMECorePlugin.log(
IStatus.WARNING,
"Preprocessor invoked, but hook is not installed. Consult the installation instructions for EclipseME.");
}
checkedForHook = true;
}
doPreprocessing(kind, args, monitor);
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("< PreprocessorBuilder.build");
}
return null;
}
/**
* @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor)
*/
protected void clean(IProgressMonitor monitor)
throws CoreException
{
IProject srcProject = getProject();
IFolder folder = srcProject.getFolder(PROCESSED_PATH);
Utils.clearContainer(folder, monitor);
}
/**
* Clear previous preprocessor markers from the specified resource.
*
* @param resource
* @throws CoreException
*/
private void clearPreprocessorMarkers(IResource resource)
throws CoreException
{
IMarker[] markers = resource.findMarkers(PROBLEM_MARKER, true, IResource.DEPTH_ONE);
for (int i = 0; i < markers.length; i++) {
markers[i].delete();
}
}
/**
* Create the parent folders of this file.
*
* @param outputFile
* @param monitor
* @throws CoreException
*/
private void createParentFolders(IResource resource, IProgressMonitor monitor)
throws CoreException
{
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("> PreprocessorBuilder.createParentFolders; resource = " + resource);
}
IContainer parent = resource.getParent();
if (parent.getType() == IResource.FOLDER) {
createParentFolders(parent, monitor);
IFolder folder = (IFolder) parent;
if (!folder.exists()) {
folder.create(true, true, monitor);
folder.setDerived(true);
}
}
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("< PreprocessorBuilder.createParentFolders");
}
}
/**
* Do the preprocessing build.
*
* @param kind
* @param args
* @param monitor
* @throws CoreException
*/
private void doPreprocessing(int kind, Map args, IProgressMonitor monitor)
throws CoreException
{
// Collect the java files that were updated
IProject project = getProject();
SymbolDefinitionSet symbols = getProjectSymbolDefinitions(project);
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln(
"> PreprocessorBuilder.doPreprocessing; project = " +
project +
"; symbols = " +
((symbols == null) ? "none" : symbols.getName()));
}
// Wrap up the symbol definition set in another wrapper set. Changes
// made to the set while processing the source will not propagate
// to the base definition set
// symbols = new SymbolDefinitionSet(symbols);
// Traverse the changes in the resource delta, processing the
// resources along the way.
IResourceDelta delta = getDelta(project);
if (delta == null) {
ResourceVisitor visitor = new ResourceVisitor(symbols, monitor);
project.accept(visitor);
} else {
ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(symbols, monitor);
delta.accept(visitor);
}
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("< PreprocessorBuilder.doPreprocessing");
}
}
/**
* Get the content encoding for the specified file. If none is set,
* attempt to pick up the appropriate encoding for the underlying
* platform.
*
* @param file
* @return
* @throws CoreException
*/
private String getContentEncoding(IFile file)
throws CoreException
{
String encoding = file.getContentDescription().getCharset();
if (encoding == null) {
encoding = System.getProperty("file.encoding", "UTF-8");
}
return encoding;
}
/**
* Return a document instance based on the contents of the
* specified file.
*
* @param file
* @return
* @throws CoreException
* @throws IOException
*/
private IDocument getDocumentForFile(IFile file)
throws CoreException
{
String charset = file.getCharset(true);
InputStream contentStream = file.getContents();
BufferedReader reader = null;
try {
reader = new BufferedReader(new InputStreamReader(contentStream, charset));
} catch (UnsupportedEncodingException e) {
// Should not happen
}
String line = null;
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
try {
while ((line = reader.readLine()) != null) {
writer.println(line);
}
} catch (IOException e) {
EclipseMECorePlugin.throwCoreException(IStatus.ERROR, -999, e);
} finally {
try { reader.close(); } catch (IOException e) { /* Munch */ }
}
return new Document(stringWriter.toString());
}
/**
* Return the symbols defined for the specified project.
*
* @param project
* @return
*/
private SymbolDefinitionSet getProjectSymbolDefinitions(IProject project) {
SymbolDefinitionSet symbols = null;
IMidletSuiteProject midletSuite = MidletSuiteFactory.getMidletSuiteProject(JavaCore.create(project));
try {
symbols = midletSuite.getEnabledSymbolDefinitionSet();
} catch (CoreException e) {
EclipseMECorePlugin.log(IStatus.ERROR, e);
} catch (PersistenceException e) {
EclipseMECorePlugin.log(IStatus.ERROR, e);
}
if (symbols == null) {
symbols = new SymbolDefinitionSet("__default" + System.currentTimeMillis());
}
return symbols;
}
/**
* Return a boolean indicating whether or not the specified resource
* is on the build path.
*
* @param resource
* @param monitor
* @return
*/
private boolean isOnBuildPath(IResource resource, IProgressMonitor monitor) {
boolean onBuildPath = false;
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("> PreprocessorBuilder.isOnBuildPath; resource = " + resource);
}
IJavaElement javaElement = JavaCore.create((IFile) resource);
if (javaElement != null) {
IPackageFragmentRoot root =
(IPackageFragmentRoot) javaElement.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
onBuildPath = root.exists();
}
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("< PreprocessorBuilder.isOnBuildPath; returning = " + onBuildPath);
}
return onBuildPath;
}
/**
* Preprocess the specified java resource.
*
* @param resource
* @param symbols
* @param monitor
* @throws CoreException
*/
protected void preprocess(final IResource resource, SymbolDefinitionSet symbols, IProgressMonitor monitor)
throws CoreException
{
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("> PreprocessorBuilder.preprocess; resource = " + resource);
BuildConsoleProxy.instance.traceln("- PreprocessorBuilder.preprocess symbol set = " + symbols.getName());
}
// The document being processed... Used for offset conversions
final IDocument document = getDocumentForFile((IFile) resource);
// Clear the previous preprocessor errors
clearPreprocessorMarkers(resource);
ILogger logger = new ILogger() {
public void log(String message) {
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln(message);
}
}
};
ILineFilter filter = new ILineFilter() {
public String filter(String line) {
return line;
}
};
IPreprocessorListener listener = new IPreprocessorListener() {
public void error(Exception e, int lineNumber, int offset, int length) {
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.getConsoleWriter(IBuildConsoleProxy.ID_ERROR_STREAM).println("Preprocessor error: " + e);
}
createResourceMarker(resource, document, e.getMessage(), lineNumber, offset, length, true);
}
public void warning(String message, int lineNumber, int offset, int length) {
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.getConsoleWriter(IBuildConsoleProxy.ID_OUTPUT_STREAM).println("Preprocessor warning: " + message);
}
createResourceMarker(resource, document, message, lineNumber, offset, length, false);
}
};
IFile file = (IFile) resource;
File localFile = file.getLocation().toFile();
String charset = getContentEncoding(file);
InputStream is = file.getContents();
ByteArrayOutputStream bos = new ByteArrayOutputStream();
Preprocessor preprocessor = new Preprocessor(logger, filter);
preprocessor.setFile(localFile);
preprocessor.setListener(listener);
try {
preprocessor.addDefines(symbols.toString());
preprocessor.preprocess(is, bos, charset);
writeProcessedResults(resource, bos.toByteArray(), monitor);
} catch (Exception e1) {
EclipseMECorePlugin.throwCoreException(IStatus.ERROR, -999, e1);
} finally {
try { is.close(); } catch (IOException e) {}
}
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("< PreprocessorBuilder.preprocess");
}
}
/**
* Create a new marker in the specified resource.
*
* @param resource
* @param document
* @param message
* @param lineNumber
* @param offset
* @param length
* @param error
* @throws CoreException
* @throws BadLocationException
*/
protected void createResourceMarker(
IResource resource,
IDocument document,
String message,
int lineNumber,
int offset,
int length,
boolean error)
{
int severity = error ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING;
try {
IMarker marker = resource.createMarker(PROBLEM_MARKER);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY, severity);
int start = document.getLineOffset(lineNumber) + offset;
int end = start + length;
marker.setAttribute(IMarker.CHAR_START, start);
marker.setAttribute(IMarker.CHAR_END, end);
} catch (Exception e) {
EclipseMECorePlugin.log(IStatus.ERROR, e);
}
}
/**
* Set the specified file to be read-only or not based on the
* specified flag.
*
* @param outputFile
* @param readOnly
* @throws CoreException
*/
private void setReadOnly(IFile outputFile, boolean readOnly)
throws CoreException
{
ResourceAttributes attributes = new ResourceAttributes();
attributes.setReadOnly(readOnly);
outputFile.setResourceAttributes(attributes);
}
/**
* Return a boolean indicating whether the specified resource
* should be processed.
*
* @param resource
* @param monitor
* @return
* @throws CoreException
*/
private boolean shouldBeProcessed(IResource resource, IProgressMonitor monitor)
throws CoreException
{
return
(resource.getType() == IResource.FILE) &&
(resource.getName().endsWith(".java")) &&
!isPreprocessed(resource) &&
isOnBuildPath(resource, monitor);
}
/**
* Write out the results of the processing.
*
* @param resource
* @param symbols
* @param sourceDocument
* @param monitor
* @throws CoreException
*/
private void writeProcessedResults(
final IResource resource,
byte[] contents,
IProgressMonitor monitor)
throws CoreException
{
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("> PreprocessorBuilder.writeProcessedResults");
}
ByteArrayInputStream is = new ByteArrayInputStream(contents);
final IFile outputFile = getOutputFile(resource);
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("- PreprocessorBuilder.writeProcessedResults; outputFile = " + outputFile);
}
if (outputFile.exists()) {
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("- PreprocessorBuilder.writeProcessedResults; outputFile exists, setting new contents");
}
setReadOnly(outputFile, false);
outputFile.setContents(is, true, false, monitor);
} else {
IContainer container = outputFile.getParent();
if (!container.exists()) {
createParentFolders(outputFile, monitor);
}
outputFile.create(is, false, monitor);
}
outputFile.setCharset("UTF-8", monitor);
outputFile.setDerived(true);
setReadOnly(outputFile, true);
// It seems this should not be necessary, but let's see if it
// helps on some problems we are seeing...
// outputFile.getProject().build(AUTO_BUILD, monitor);
if (buildLoggingConfig.isPreprocessorTraceEnabled()) {
BuildConsoleProxy.instance.traceln("< PreprocessorBuilder.writeProcessedResults");
}
}
}
The table below shows all metrics for PreprocessorBuilder.java.




