/** * 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.GuidedDecisionTable; 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. * * @author <a href="mailto:kris_verlaenen@hotmail.com">kris verlaenen </a> */ 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())) { 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()); GuidedDecisionTable 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()]); } }