/*
* Copyright 2010 JBoss Inc
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.eclipse.builder;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.antlr.runtime.RecognitionException;
import org.drools.commons.jci.problems.CompilationProblem;
import org.drools.compiler.DescrBuildError;
import org.drools.compiler.DroolsError;
import org.drools.compiler.DroolsParserException;
import org.drools.compiler.FactTemplateError;
import org.drools.compiler.FieldTemplateError;
import org.drools.compiler.FunctionError;
import org.drools.compiler.GlobalError;
import org.drools.compiler.ImportError;
import org.drools.compiler.ParserError;
import org.drools.compiler.RuleBuildError;
import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.drools.eclipse.DRLInfo;
import org.drools.eclipse.DroolsEclipsePlugin;
import org.drools.eclipse.ProcessInfo;
import org.drools.eclipse.preferences.IDroolsConstants;
import org.drools.eclipse.util.DroolsRuntimeManager;
import org.drools.eclipse.wizard.project.NewDroolsProjectWizard;
import org.drools.ide.common.client.modeldriven.brl.RuleModel;
import org.drools.ide.common.client.modeldriven.dt.TypeSafeGuidedDecisionTable;
import org.drools.ide.common.server.util.BRDRLPersistence;
import org.drools.ide.common.server.util.BRXMLPersistence;
import org.drools.ide.common.server.util.GuidedDTDRLPersistence;
import org.drools.ide.common.server.util.GuidedDTXMLPersistence;
import org.drools.lang.ExpanderException;
import org.drools.template.parser.DecisionTableParseException;
import org.eclipse.core.resources.IFile;
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.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.IncrementalProjectBuilder;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
/**
* Automatically syntax checks .drl files and adds possible
* errors or warnings to the problem list. Nominally is triggerd on save.
*/
public class DroolsBuilder extends IncrementalProjectBuilder {
public static final String BUILDER_ID = "org.drools.eclipse.droolsbuilder";
protected IProject[] build(int kind, Map args, IProgressMonitor monitor)
throws CoreException {
IProject currentProject = getProject();
if (currentProject == null || !currentProject.isAccessible()) {
return new IProject[0];
}
try {
if (monitor != null && monitor.isCanceled())
throw new OperationCanceledException();
if (kind == IncrementalProjectBuilder.FULL_BUILD) {
fullBuild(monitor);
} else {
IResourceDelta delta = getDelta(getProject());
if (delta == null) {
fullBuild(monitor);
} else {
incrementalBuild(delta, monitor);
}
}
} catch (CoreException e) {
IMarker marker = currentProject.createMarker(IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER);
marker.setAttribute(IMarker.MESSAGE, "Error when trying to build Drools project: " + e.getLocalizedMessage());
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
}
return getRequiredProjects(currentProject);
}
protected void fullBuild(IProgressMonitor monitor)
throws CoreException {
removeProblemsFor(getProject());
IJavaProject project = JavaCore.create(getProject());
IClasspathEntry[] classpathEntries = project.getRawClasspath();
for (int i = 0; i < classpathEntries.length; i++) {
if (NewDroolsProjectWizard.DROOLS_CLASSPATH_CONTAINER_PATH.equals(classpathEntries[i].getPath().toString())) {
String[] jars = DroolsRuntimeManager.getDroolsRuntimeJars(getProject());
if (jars == null || jars.length == 0) {
String runtime = DroolsRuntimeManager.getDroolsRuntime(getProject());
IMarker marker = getProject().createMarker(IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER);
if (runtime == null) {
marker.setAttribute(IMarker.MESSAGE, "Could not find default Drools runtime");
} else {
marker.setAttribute(IMarker.MESSAGE, "Could not find Drools runtime " + runtime);
}
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
return;
}
}
}
getProject().accept(new DroolsBuildVisitor());
}
protected void incrementalBuild(IResourceDelta delta,
IProgressMonitor monitor) throws CoreException {
boolean buildAll = DroolsEclipsePlugin.getDefault().getPreferenceStore().getBoolean(IDroolsConstants.BUILD_ALL);
if (buildAll) {
// to make sure that all rules are checked when a java file is changed
fullBuild(monitor);
} else {
delta.accept(new DroolsBuildDeltaVisitor());
}
}
private class DroolsBuildVisitor implements IResourceVisitor {
public boolean visit(IResource res) {
return parseResource(res, true);
}
}
private class DroolsBuildDeltaVisitor implements IResourceDeltaVisitor {
public boolean visit(IResourceDelta delta) throws CoreException {
return parseResource(delta.getResource(), false);
}
}
protected boolean parseResource(IResource res, boolean clean) {
try {
// exclude .guvnorinfo files
if (".guvnorinfo".equals(res.getName())) {
return false;
}
IJavaProject project = JavaCore.create(res.getProject());
// exclude files that are located in the output directory,
// unless the ouput directory is the same as the project location
if (!project.getOutputLocation().equals(project.getPath())
&& project.getOutputLocation().isPrefixOf(res.getFullPath())) {
return false;
}
} catch (JavaModelException e) {
// do nothing
}
if (!res.exists()) {
removeProblemsFor(res);
DroolsEclipsePlugin.getDefault().invalidateResource(res);
return false;
}
if (res instanceof IFile
&& ("drl".equals(res.getFileExtension())
|| "dslr".equals(res.getFileExtension())
|| ".package".equals(res.getName()))) {
removeProblemsFor(res);
try {
if (clean) {
DroolsEclipsePlugin.getDefault().invalidateResource(res);
}
DroolsBuildMarker[] markers = parseDRLFile((IFile) res, new String(Util.getResourceContentsAsCharArray((IFile) res)));
for (int i = 0; i < markers.length; i++) {
createMarker(res, markers[i].getText(), markers[i].getLine());
}
} catch (Throwable t) {
DroolsEclipsePlugin.log(t);
createMarker(res, t.getMessage(), -1);
}
return false;
} else if (res instanceof IFile && "xls".equals(res.getFileExtension())) {
removeProblemsFor(res);
try {
if (clean) {
DroolsEclipsePlugin.getDefault().invalidateResource(res);
}
DroolsBuildMarker[] markers = parseXLSFile((IFile) res);
for (int i = 0; i < markers.length; i++) {
createMarker(res, markers[i].getText(), markers[i].getLine());
}
} catch (Throwable t) {
createMarker(res, t.getMessage(), -1);
}
return false;
} else if (res instanceof IFile && "csv".equals(res.getFileExtension())) {
removeProblemsFor(res);
try {
if (clean) {
DroolsEclipsePlugin.getDefault().invalidateResource(res);
}
DroolsBuildMarker[] markers = parseCSVFile((IFile) res);
for (int i = 0; i < markers.length; i++) {
createMarker(res, markers[i].getText(), markers[i].getLine());
}
} catch (Throwable t) {
createMarker(res, t.getMessage(), -1);
}
return false;
} else if (res instanceof IFile && "brl".equals(res.getFileExtension())) {
removeProblemsFor(res);
try {
if (clean) {
DroolsEclipsePlugin.getDefault().invalidateResource(res);
}
DroolsBuildMarker[] markers = parseBRLFile((IFile) res);
for (int i = 0; i < markers.length; i++) {
createMarker(res, markers[i].getText(), markers[i].getLine());
}
} catch (Throwable t) {
createMarker(res, t.getMessage(), -1);
}
return false;
} else if (res instanceof IFile && "rf".equals(res.getFileExtension())) {
removeProblemsFor(res);
try {
if (clean) {
DroolsEclipsePlugin.getDefault().invalidateResource(res);
}
DroolsBuildMarker[] markers = parseRuleFlowFile((IFile) res);
for (int i = 0; i < markers.length; i++) {
createMarker(res, markers[i].getText(), markers[i].getLine());
}
} catch (Throwable t) {
createMarker(res, t.getMessage(), -1);
}
return false;
} else if (res instanceof IFile && ("bpmn".equals(res.getFileExtension())
|| "bpmn2".equals(res.getFileExtension()))) {
removeProblemsFor(res);
try {
if (clean) {
DroolsEclipsePlugin.getDefault().invalidateResource(res);
}
DroolsBuildMarker[] markers = parseRuleFlowFile((IFile) res);
for (int i = 0; i < markers.length; i++) {
createMarker(res, markers[i].getText(), markers[i].getLine());
}
} catch (Throwable t) {
createMarker(res, t.getMessage(), -1);
}
return false;
}
return true;
}
private DroolsBuildMarker[] parseDRLFile(IFile file, String content) {
List markers = new ArrayList();
try {
DRLInfo drlInfo =
DroolsEclipsePlugin.getDefault().parseResource(file, true);
//parser errors
markParseErrors(markers, drlInfo.getParserErrors());
markOtherErrors(markers, drlInfo.getBuilderErrors());
} catch (DroolsParserException e) {
// we have an error thrown from DrlParser
Throwable cause = e.getCause();
if (cause instanceof RecognitionException ) {
RecognitionException recogErr = (RecognitionException) cause;
markers.add(new DroolsBuildMarker(recogErr.getMessage(), recogErr.line)); //flick back the line number
}
} catch (Exception t) {
String message = t.getMessage();
if (message == null || message.trim().equals("")) {
message = "Error: " + t.getClass().getName();
}
markers.add(new DroolsBuildMarker(message));
}
return (DroolsBuildMarker[]) markers.toArray(new DroolsBuildMarker[markers.size()]);
}
private DroolsBuildMarker[] parseXLSFile(IFile file) {
List markers = new ArrayList();
try {
SpreadsheetCompiler converter = new SpreadsheetCompiler();
String drl = converter.compile(file.getContents(), InputType.XLS);
DRLInfo drlInfo =
DroolsEclipsePlugin.getDefault().parseXLSResource(drl, file);
// parser errors
markParseErrors(markers, drlInfo.getParserErrors());
markOtherErrors(markers, drlInfo.getBuilderErrors());
} catch (DroolsParserException e) {
// we have an error thrown from DrlParser
Throwable cause = e.getCause();
if (cause instanceof RecognitionException ) {
RecognitionException recogErr = (RecognitionException) cause;
markers.add(new DroolsBuildMarker(recogErr.getMessage(), recogErr.line)); //flick back the line number
}
} catch (DecisionTableParseException e) {
if (!"No RuleTable's were found in spreadsheet.".equals(e.getMessage())) {
throw e;
}
} catch (Exception t) {
String message = t.getMessage();
if (message == null || message.trim().equals( "" )) {
message = "Error: " + t.getClass().getName();
}
markers.add(new DroolsBuildMarker(message));
}
return (DroolsBuildMarker[]) markers.toArray(new DroolsBuildMarker[markers.size()]);
}
private DroolsBuildMarker[] parseCSVFile(IFile file) {
List markers = new ArrayList();
try {
SpreadsheetCompiler converter = new SpreadsheetCompiler();
String drl = converter.compile(file.getContents(), InputType.CSV);
DRLInfo drlInfo =
DroolsEclipsePlugin.getDefault().parseXLSResource(drl, file);
// parser errors
markParseErrors(markers, drlInfo.getParserErrors());
markOtherErrors(markers, drlInfo.getBuilderErrors());
} catch (DroolsParserException e) {
// we have an error thrown from DrlParser
Throwable cause = e.getCause();
if (cause instanceof RecognitionException ) {
RecognitionException recogErr = (RecognitionException) cause;
markers.add(new DroolsBuildMarker(recogErr.getMessage(), recogErr.line)); //flick back the line number
}
} catch (Exception t) {
String message = t.getMessage();
if (message == null || message.trim().equals( "" )) {
message = "Error: " + t.getClass().getName();
}
markers.add(new DroolsBuildMarker(message));
}
return (DroolsBuildMarker[]) markers.toArray(new DroolsBuildMarker[markers.size()]);
}
private DroolsBuildMarker[] parseBRLFile(IFile file) {
List markers = new ArrayList();
try {
String brl = convertToString(file.getContents());
RuleModel model = BRXMLPersistence.getInstance().unmarshal(brl);
String drl = BRDRLPersistence.getInstance().marshal(model);
// TODO pass this through DSL converter in case brl is based on dsl
DRLInfo drlInfo =
DroolsEclipsePlugin.getDefault().parseBRLResource(drl, file);
// parser errors
markParseErrors(markers, drlInfo.getParserErrors());
markOtherErrors(markers, drlInfo.getBuilderErrors());
} catch (DroolsParserException e) {
// we have an error thrown from DrlParser
Throwable cause = e.getCause();
if (cause instanceof RecognitionException ) {
RecognitionException recogErr = (RecognitionException) cause;
markers.add(new DroolsBuildMarker(recogErr.getMessage(), recogErr.line)); //flick back the line number
}
} catch (Exception t) {
String message = t.getMessage();
if (message == null || message.trim().equals( "" )) {
message = "Error: " + t.getClass().getName();
}
markers.add(new DroolsBuildMarker(message));
}
return (DroolsBuildMarker[]) markers.toArray(new DroolsBuildMarker[markers.size()]);
}
private DroolsBuildMarker[] parseGDSTFile(IFile file) {
List markers = new ArrayList();
try {
String gdst = convertToString(file.getContents());
TypeSafeGuidedDecisionTable dt = GuidedDTXMLPersistence.getInstance().unmarshal(gdst);
String drl = GuidedDTDRLPersistence.getInstance().marshal(dt);
// TODO pass this through DSL converter in case brl is based on dsl
DRLInfo drlInfo =
DroolsEclipsePlugin.getDefault().parseGDSTResource(drl, file);
// parser errors
markParseErrors(markers, drlInfo.getParserErrors());
markOtherErrors(markers, drlInfo.getBuilderErrors());
} catch (DroolsParserException e) {
// we have an error thrown from DrlParser
Throwable cause = e.getCause();
if (cause instanceof RecognitionException ) {
RecognitionException recogErr = (RecognitionException) cause;
markers.add(new DroolsBuildMarker(recogErr.getMessage(), recogErr.line)); //flick back the line number
}
} catch (Exception t) {
String message = t.getMessage();
if (message == null || message.trim().equals( "" )) {
message = "Error: " + t.getClass().getName();
}
markers.add(new DroolsBuildMarker(message));
}
return (DroolsBuildMarker[]) markers.toArray(new DroolsBuildMarker[markers.size()]);
}
protected DroolsBuildMarker[] parseRuleFlowFile(IFile file) {
if (!file.exists()) {
return new DroolsBuildMarker[0];
}
List markers = new ArrayList();
try {
String input = convertToString(file.getContents());
ProcessInfo processInfo =
DroolsEclipsePlugin.getDefault().parseProcess(input, file);
if (processInfo != null) {
markParseErrors(markers, processInfo.getErrors());
}
} catch (Exception t) {
t.printStackTrace();
String message = t.getMessage();
if (message == null || message.trim().equals( "" )) {
message = "Error: " + t.getClass().getName();
}
markers.add(new DroolsBuildMarker(message));
}
return (DroolsBuildMarker[]) markers.toArray(new DroolsBuildMarker[markers.size()]);
}
protected static String convertToString(final InputStream inputStream) throws IOException {
Reader reader = new InputStreamReader(inputStream);
final StringBuffer text = new StringBuffer();
final char[] buf = new char[1024];
int len = 0;
while ((len = reader.read(buf)) >= 0) {
text.append(buf, 0, len);
}
return text.toString();
}
/**
* This will create markers for parse errors.
* Parse errors mean that antlr has picked up some major typos in the input source.
*/
protected void markParseErrors(List markers, List parserErrors) {
for ( Iterator iter = parserErrors.iterator(); iter.hasNext(); ) {
Object error = iter.next();
if (error instanceof ParserError) {
ParserError err = (ParserError) error;
markers.add(new DroolsBuildMarker(err.getMessage(), err.getRow()));
} else if (error instanceof ExpanderException) {
ExpanderException exc = (ExpanderException) error;
// TODO line mapping is incorrect
markers.add(new DroolsBuildMarker(exc.getMessage(), -1));
} else {
markers.add(new DroolsBuildMarker(error.toString()));
}
}
}
/**
* This will create markers for build errors that happen AFTER parsing.
*/
private void markOtherErrors(List markers,
DroolsError[] buildErrors) {
// TODO are there warnings too?
for (int i = 0; i < buildErrors.length; i++ ) {
DroolsError error = buildErrors[i];
if (error instanceof GlobalError) {
GlobalError globalError = (GlobalError) error;
markers.add(new DroolsBuildMarker("Global error: " + globalError.getGlobal(), -1));
} else if (error instanceof RuleBuildError) {
RuleBuildError ruleError = (RuleBuildError) error;
// TODO try to retrieve line number (or even character start-end)
// disabled for now because line number are those of the rule class,
// not the rule file itself
if (ruleError.getObject() instanceof CompilationProblem[]) {
CompilationProblem[] problems = (CompilationProblem[]) ruleError.getObject();
for (int j = 0; j < problems.length; j++) {
markers.add(new DroolsBuildMarker(problems[j].getMessage(), ruleError.getLine()));
}
} else {
markers.add(new DroolsBuildMarker(ruleError.getRule().getName() + ":" + ruleError.getMessage(), ruleError.getLine()));
}
} else if (error instanceof ParserError) {
ParserError parserError = (ParserError) error;
// TODO try to retrieve character start-end
markers.add(new DroolsBuildMarker(parserError.getMessage(), parserError.getRow()));
} else if (error instanceof FunctionError) {
FunctionError functionError = (FunctionError) error;
// TODO add line to function error
// TODO try to retrieve character start-end
if (functionError.getObject() instanceof CompilationProblem[]) {
CompilationProblem[] problems = (CompilationProblem[]) functionError.getObject();
for (int j = 0; j < problems.length; j++) {
markers.add(new DroolsBuildMarker(problems[j].getMessage(), functionError.getErrorLines()[j]));
}
} else {
markers.add(new DroolsBuildMarker(functionError.getFunctionDescr().getName() + ":" + functionError.getMessage(), -1));
}
} else if (error instanceof FieldTemplateError) {
markers.add(new DroolsBuildMarker(error.getMessage(), ((FieldTemplateError) error).getLine()));
} else if (error instanceof FactTemplateError) {
markers.add(new DroolsBuildMarker(error.getMessage(), ((FactTemplateError) error).getLine()));
} else if (error instanceof ImportError) {
markers.add(new DroolsBuildMarker("ImportError: " + error.getMessage()));
} else if (error instanceof DescrBuildError) {
markers.add(new DroolsBuildMarker("BuildError: " + error.getMessage(), ((DescrBuildError) error).getLine()));
} else {
markers.add(new DroolsBuildMarker("Unknown DroolsError " + error.getClass() + ": " + error));
}
}
}
protected void createMarker(final IResource res, final String message, final int lineNumber) {
try {
IWorkspaceRunnable r= new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
IMarker marker = res
.createMarker(IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER);
marker.setAttribute(IMarker.MESSAGE, message);
marker.setAttribute(IMarker.SEVERITY,
IMarker.SEVERITY_ERROR);
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
}
};
res.getWorkspace().run(r, null, IWorkspace.AVOID_UPDATE, null);
} catch (CoreException e) {
DroolsEclipsePlugin.log(e);
}
}
protected void removeProblemsFor(IResource resource) {
try {
if (resource != null && resource.exists()) {
resource.deleteMarkers(
IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER, false,
IResource.DEPTH_INFINITE);
}
} catch (CoreException e) {
DroolsEclipsePlugin.log(e);
}
}
private IProject[] getRequiredProjects(IProject project) {
IJavaProject javaProject = JavaCore.create(project);
List projects = new ArrayList();
try {
IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
for (int i = 0, l = entries.length; i < l; i++) {
IClasspathEntry entry = entries[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
IProject p = project.getWorkspace().getRoot().getProject(entry.getPath().lastSegment()); // missing projects are considered too
if (p != null && !projects.contains(p)) {
projects.add(p);
}
}
}
} catch(JavaModelException e) {
return new IProject[0];
}
return (IProject[]) projects.toArray(new IProject[projects.size()]);
}
}