001: package org.drools.eclipse.builder;
002:
003: import java.io.IOException;
004: import java.io.InputStream;
005: import java.io.InputStreamReader;
006: import java.io.Reader;
007: import java.util.ArrayList;
008: import java.util.Iterator;
009: import java.util.List;
010: import java.util.Map;
011:
012: import org.antlr.runtime.RecognitionException;
013: import org.drools.brms.client.modeldriven.brl.RuleModel;
014: import org.drools.brms.server.util.BRDRLPersistence;
015: import org.drools.brms.server.util.BRXMLPersistence;
016: import org.drools.commons.jci.problems.CompilationProblem;
017: import org.drools.compiler.DroolsError;
018: import org.drools.compiler.DroolsParserException;
019: import org.drools.compiler.FactTemplateError;
020: import org.drools.compiler.FieldTemplateError;
021: import org.drools.compiler.FunctionError;
022: import org.drools.compiler.GlobalError;
023: import org.drools.compiler.ImportError;
024: import org.drools.compiler.PackageBuilder;
025: import org.drools.compiler.ParserError;
026: import org.drools.compiler.ProcessBuilder;
027: import org.drools.compiler.RuleError;
028: import org.drools.decisiontable.InputType;
029: import org.drools.decisiontable.SpreadsheetCompiler;
030: import org.drools.eclipse.DRLInfo;
031: import org.drools.eclipse.DroolsEclipsePlugin;
032: import org.drools.eclipse.flow.ruleflow.core.RuleFlowProcessWrapper;
033: import org.drools.eclipse.preferences.IDroolsConstants;
034: import org.drools.lang.ExpanderException;
035: import org.eclipse.core.resources.IFile;
036: import org.eclipse.core.resources.IMarker;
037: import org.eclipse.core.resources.IProject;
038: import org.eclipse.core.resources.IResource;
039: import org.eclipse.core.resources.IResourceDelta;
040: import org.eclipse.core.resources.IResourceDeltaVisitor;
041: import org.eclipse.core.resources.IResourceVisitor;
042: import org.eclipse.core.resources.IWorkspace;
043: import org.eclipse.core.resources.IWorkspaceRunnable;
044: import org.eclipse.core.resources.IncrementalProjectBuilder;
045: import org.eclipse.core.runtime.CoreException;
046: import org.eclipse.core.runtime.IProgressMonitor;
047: import org.eclipse.core.runtime.OperationCanceledException;
048: import org.eclipse.jdt.core.IClasspathEntry;
049: import org.eclipse.jdt.core.IJavaProject;
050: import org.eclipse.jdt.core.JavaCore;
051: import org.eclipse.jdt.core.JavaModelException;
052:
053: import com.thoughtworks.xstream.XStream;
054:
055: /**
056: * Automatically syntax checks .drl files and adds possible
057: * errors or warnings to the problem list. Nominally is triggerd on save.
058: *
059: * @author <a href="mailto:kris_verlaenen@hotmail.com">kris verlaenen </a>
060: */
061: public class DroolsBuilder extends IncrementalProjectBuilder {
062:
063: public static final String BUILDER_ID = "org.drools.eclipse.droolsbuilder";
064:
065: protected IProject[] build(int kind, Map args,
066: IProgressMonitor monitor) throws CoreException {
067: IProject currentProject = getProject();
068: if (currentProject == null || !currentProject.isAccessible()) {
069: return new IProject[0];
070: }
071: try {
072: if (monitor != null && monitor.isCanceled())
073: throw new OperationCanceledException();
074:
075: if (kind == IncrementalProjectBuilder.FULL_BUILD) {
076: fullBuild(monitor);
077: } else {
078: IResourceDelta delta = getDelta(getProject());
079: if (delta == null) {
080: fullBuild(monitor);
081: } else {
082: incrementalBuild(delta, monitor);
083: }
084: }
085: } catch (CoreException e) {
086: IMarker marker = currentProject
087: .createMarker(IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER);
088: marker.setAttribute(IMarker.MESSAGE,
089: "Error when trying to build Drools project: "
090: + e.getLocalizedMessage());
091: marker.setAttribute(IMarker.SEVERITY,
092: IMarker.SEVERITY_ERROR);
093: }
094: return getRequiredProjects(currentProject);
095: }
096:
097: protected void fullBuild(IProgressMonitor monitor)
098: throws CoreException {
099: getProject().accept(new DroolsBuildVisitor());
100: }
101:
102: protected void incrementalBuild(IResourceDelta delta,
103: IProgressMonitor monitor) throws CoreException {
104: boolean buildAll = DroolsEclipsePlugin.getDefault()
105: .getPreferenceStore().getBoolean(
106: IDroolsConstants.BUILD_ALL);
107: if (buildAll) {
108: // to make sure that all rules are checked when a java file is changed
109: fullBuild(monitor);
110: } else {
111: delta.accept(new DroolsBuildDeltaVisitor());
112: }
113: }
114:
115: private class DroolsBuildVisitor implements IResourceVisitor {
116: public boolean visit(IResource res) {
117: return parseResource(res, true);
118: }
119: }
120:
121: private class DroolsBuildDeltaVisitor implements
122: IResourceDeltaVisitor {
123: public boolean visit(IResourceDelta delta) throws CoreException {
124: return parseResource(delta.getResource(), false);
125: }
126: }
127:
128: private boolean parseResource(IResource res, boolean clean) {
129: try {
130: IJavaProject project = JavaCore.create(res.getProject());
131: // exclude files that are located in the output directory,
132: // unless the ouput directory is the same as the project location
133: if (!project.getOutputLocation().equals(project.getPath())
134: && project.getOutputLocation().isPrefixOf(
135: res.getFullPath())) {
136: return false;
137: }
138: } catch (JavaModelException e) {
139: // do nothing
140: }
141:
142: if (res instanceof IFile
143: && ("drl".equals(res.getFileExtension())
144: || "dslr".equals(res.getFileExtension()) || ".package"
145: .equals(res.getName()))) {
146: removeProblemsFor(res);
147: try {
148: if (clean) {
149: DroolsEclipsePlugin.getDefault()
150: .invalidateResource(res);
151: }
152: DroolsBuildMarker[] markers = parseDRLFile(
153: (IFile) res,
154: new String(
155: Util
156: .getResourceContentsAsCharArray((IFile) res)));
157: for (int i = 0; i < markers.length; i++) {
158: createMarker(res, markers[i].getText(), markers[i]
159: .getLine());
160: }
161: } catch (Throwable t) {
162: createMarker(res, t.getMessage(), -1);
163: }
164: return false;
165: } else if (res instanceof IFile
166: && "xls".equals(res.getFileExtension())) {
167: removeProblemsFor(res);
168: try {
169: if (clean) {
170: DroolsEclipsePlugin.getDefault()
171: .invalidateResource(res);
172: }
173: DroolsBuildMarker[] markers = parseXLSFile((IFile) res);
174: for (int i = 0; i < markers.length; i++) {
175: createMarker(res, markers[i].getText(), markers[i]
176: .getLine());
177: }
178: } catch (Throwable t) {
179: createMarker(res, t.getMessage(), -1);
180: }
181: return false;
182: } else if (res instanceof IFile
183: && "brl".equals(res.getFileExtension())) {
184: removeProblemsFor(res);
185: try {
186: if (clean) {
187: DroolsEclipsePlugin.getDefault()
188: .invalidateResource(res);
189: }
190: DroolsBuildMarker[] markers = parseBRLFile((IFile) res);
191: for (int i = 0; i < markers.length; i++) {
192: createMarker(res, markers[i].getText(), markers[i]
193: .getLine());
194: }
195: } catch (Throwable t) {
196: createMarker(res, t.getMessage(), -1);
197: }
198: return false;
199: } else if (res instanceof IFile
200: && "rf".equals(res.getFileExtension())) {
201: removeProblemsFor(res);
202: try {
203: if (clean) {
204: DroolsEclipsePlugin.getDefault()
205: .invalidateResource(res);
206: }
207: DroolsBuildMarker[] markers = parseRuleFlowFile((IFile) res);
208: for (int i = 0; i < markers.length; i++) {
209: createMarker(res, markers[i].getText(), markers[i]
210: .getLine());
211: }
212: } catch (Throwable t) {
213: createMarker(res, t.getMessage(), -1);
214: }
215: return false;
216: }
217:
218: return true;
219: }
220:
221: private DroolsBuildMarker[] parseDRLFile(IFile file, String content) {
222: List markers = new ArrayList();
223: try {
224: DRLInfo drlInfo = DroolsEclipsePlugin.getDefault()
225: .parseResource(file, true);
226: //parser errors
227: markParseErrors(markers, drlInfo.getParserErrors());
228: markOtherErrors(markers, drlInfo.getBuilderErrors());
229: } catch (DroolsParserException e) {
230: // we have an error thrown from DrlParser
231: Throwable cause = e.getCause();
232: if (cause instanceof RecognitionException) {
233: RecognitionException recogErr = (RecognitionException) cause;
234: markers.add(new DroolsBuildMarker(
235: recogErr.getMessage(), recogErr.line)); //flick back the line number
236: }
237: } catch (Exception t) {
238: String message = t.getMessage();
239: if (message == null || message.trim().equals("")) {
240: message = "Error: " + t.getClass().getName();
241: }
242: markers.add(new DroolsBuildMarker(message));
243: }
244: return (DroolsBuildMarker[]) markers
245: .toArray(new DroolsBuildMarker[markers.size()]);
246: }
247:
248: private DroolsBuildMarker[] parseXLSFile(IFile file) {
249: List markers = new ArrayList();
250: try {
251: SpreadsheetCompiler converter = new SpreadsheetCompiler();
252: String drl = converter.compile(file.getContents(),
253: InputType.XLS);
254: DRLInfo drlInfo = DroolsEclipsePlugin.getDefault()
255: .parseXLSResource(drl, file);
256: // parser errors
257: markParseErrors(markers, drlInfo.getParserErrors());
258: markOtherErrors(markers, drlInfo.getBuilderErrors());
259: } catch (DroolsParserException e) {
260: // we have an error thrown from DrlParser
261: Throwable cause = e.getCause();
262: if (cause instanceof RecognitionException) {
263: RecognitionException recogErr = (RecognitionException) cause;
264: markers.add(new DroolsBuildMarker(
265: recogErr.getMessage(), recogErr.line)); //flick back the line number
266: }
267: } catch (Exception t) {
268: String message = t.getMessage();
269: if (message == null || message.trim().equals("")) {
270: message = "Error: " + t.getClass().getName();
271: }
272: markers.add(new DroolsBuildMarker(message));
273: }
274: return (DroolsBuildMarker[]) markers
275: .toArray(new DroolsBuildMarker[markers.size()]);
276: }
277:
278: private DroolsBuildMarker[] parseBRLFile(IFile file) {
279: List markers = new ArrayList();
280: try {
281: String brl = convertToString(file.getContents());
282: RuleModel model = BRXMLPersistence.getInstance().unmarshal(
283: brl);
284: String drl = BRDRLPersistence.getInstance().marshal(model);
285:
286: // TODO pass this through DSL converter in case brl is based on dsl
287:
288: DRLInfo drlInfo = DroolsEclipsePlugin.getDefault()
289: .parseBRLResource(drl, file);
290: // parser errors
291: markParseErrors(markers, drlInfo.getParserErrors());
292: markOtherErrors(markers, drlInfo.getBuilderErrors());
293: } catch (DroolsParserException e) {
294: // we have an error thrown from DrlParser
295: Throwable cause = e.getCause();
296: if (cause instanceof RecognitionException) {
297: RecognitionException recogErr = (RecognitionException) cause;
298: markers.add(new DroolsBuildMarker(
299: recogErr.getMessage(), recogErr.line)); //flick back the line number
300: }
301: } catch (Exception t) {
302: String message = t.getMessage();
303: if (message == null || message.trim().equals("")) {
304: message = "Error: " + t.getClass().getName();
305: }
306: markers.add(new DroolsBuildMarker(message));
307: }
308: return (DroolsBuildMarker[]) markers
309: .toArray(new DroolsBuildMarker[markers.size()]);
310: }
311:
312: private DroolsBuildMarker[] parseRuleFlowFile(IFile file) {
313: List markers = new ArrayList();
314: try {
315: String ruleflow = convertToString(file.getContents());
316: XStream stream = new XStream();
317: stream.setMode(XStream.ID_REFERENCES);
318:
319: ClassLoader oldLoader = Thread.currentThread()
320: .getContextClassLoader();
321: ClassLoader newLoader = this .getClass().getClassLoader();
322: try {
323: Thread.currentThread().setContextClassLoader(newLoader);
324: Object o = stream.fromXML(ruleflow);
325: if (o instanceof RuleFlowProcessWrapper) {
326: ProcessBuilder processBuilder = new ProcessBuilder(
327: new PackageBuilder());
328: processBuilder
329: .addProcess(((RuleFlowProcessWrapper) o)
330: .getRuleFlowProcess());
331: markParseErrors(markers, processBuilder.getErrors());
332: }
333: } finally {
334: Thread.currentThread().setContextClassLoader(oldLoader);
335: }
336: } catch (Exception t) {
337: String message = t.getMessage();
338: if (message == null || message.trim().equals("")) {
339: message = "Error: " + t.getClass().getName();
340: }
341: markers.add(new DroolsBuildMarker(message));
342: }
343: return (DroolsBuildMarker[]) markers
344: .toArray(new DroolsBuildMarker[markers.size()]);
345: }
346:
347: private static String convertToString(final InputStream inputStream)
348: throws IOException {
349: Reader reader = new InputStreamReader(inputStream);
350: final StringBuffer text = new StringBuffer();
351: final char[] buf = new char[1024];
352: int len = 0;
353: while ((len = reader.read(buf)) >= 0) {
354: text.append(buf, 0, len);
355: }
356: return text.toString();
357: }
358:
359: /**
360: * This will create markers for parse errors.
361: * Parse errors mean that antlr has picked up some major typos in the input source.
362: */
363: private void markParseErrors(List markers, List parserErrors) {
364: for (Iterator iter = parserErrors.iterator(); iter.hasNext();) {
365: Object error = iter.next();
366: if (error instanceof ParserError) {
367: ParserError err = (ParserError) error;
368: markers.add(new DroolsBuildMarker(err.getMessage(), err
369: .getRow()));
370: } else if (error instanceof ExpanderException) {
371: ExpanderException exc = (ExpanderException) error;
372: // TODO line mapping is incorrect
373: markers
374: .add(new DroolsBuildMarker(exc.getMessage(), -1));
375: } else {
376: markers.add(new DroolsBuildMarker(error.toString()));
377: }
378: }
379: }
380:
381: /**
382: * This will create markers for build errors that happen AFTER parsing.
383: */
384: private void markOtherErrors(List markers, DroolsError[] buildErrors) {
385: // TODO are there warnings too?
386: for (int i = 0; i < buildErrors.length; i++) {
387: DroolsError error = buildErrors[i];
388: if (error instanceof GlobalError) {
389: GlobalError globalError = (GlobalError) error;
390: markers.add(new DroolsBuildMarker(globalError
391: .getGlobal(), -1));
392: } else if (error instanceof RuleError) {
393: RuleError ruleError = (RuleError) error;
394: // TODO try to retrieve line number (or even character start-end)
395: // disabled for now because line number are those of the rule class,
396: // not the rule file itself
397: if (ruleError.getObject() instanceof CompilationProblem[]) {
398: CompilationProblem[] problems = (CompilationProblem[]) ruleError
399: .getObject();
400: for (int j = 0; j < problems.length; j++) {
401: markers.add(new DroolsBuildMarker(problems[j]
402: .getMessage(), ruleError.getLine()));
403: }
404: } else {
405: markers.add(new DroolsBuildMarker(ruleError
406: .getRule().getName()
407: + ":" + ruleError.getMessage(), ruleError
408: .getLine()));
409: }
410: } else if (error instanceof ParserError) {
411: ParserError parserError = (ParserError) error;
412: // TODO try to retrieve character start-end
413: markers.add(new DroolsBuildMarker(parserError
414: .getMessage(), parserError.getRow()));
415: } else if (error instanceof FunctionError) {
416: FunctionError functionError = (FunctionError) error;
417: // TODO add line to function error
418: // TODO try to retrieve character start-end
419: if (functionError.getObject() instanceof CompilationProblem[]) {
420: CompilationProblem[] problems = (CompilationProblem[]) functionError
421: .getObject();
422: for (int j = 0; j < problems.length; j++) {
423: markers.add(new DroolsBuildMarker(problems[j]
424: .getMessage(), functionError
425: .getErrorLines()[j]));
426: }
427: } else {
428: markers.add(new DroolsBuildMarker(functionError
429: .getFunctionDescr().getName()
430: + ":" + functionError.getMessage(), -1));
431: }
432: } else if (error instanceof FieldTemplateError) {
433: markers.add(new DroolsBuildMarker(error.getMessage(),
434: ((FieldTemplateError) error).getLine()));
435: } else if (error instanceof FactTemplateError) {
436: markers.add(new DroolsBuildMarker(error.getMessage(),
437: ((FactTemplateError) error).getLine()));
438: } else if (error instanceof ImportError) {
439: markers.add(new DroolsBuildMarker("ImportError: "
440: + error.getMessage()));
441: } else {
442: markers.add(new DroolsBuildMarker(
443: "Unknown DroolsError " + error.getClass()
444: + ": " + error));
445: }
446: }
447: }
448:
449: private void createMarker(final IResource res,
450: final String message, final int lineNumber) {
451: try {
452: IWorkspaceRunnable r = new IWorkspaceRunnable() {
453: public void run(IProgressMonitor monitor)
454: throws CoreException {
455: IMarker marker = res
456: .createMarker(IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER);
457: marker.setAttribute(IMarker.MESSAGE, message);
458: marker.setAttribute(IMarker.SEVERITY,
459: IMarker.SEVERITY_ERROR);
460: marker
461: .setAttribute(IMarker.LINE_NUMBER,
462: lineNumber);
463: }
464: };
465: res.getWorkspace().run(r, null, IWorkspace.AVOID_UPDATE,
466: null);
467: } catch (CoreException e) {
468: DroolsEclipsePlugin.log(e);
469: }
470: }
471:
472: private void removeProblemsFor(IResource resource) {
473: try {
474: if (resource != null && resource.exists()) {
475: resource.deleteMarkers(
476: IDroolsModelMarker.DROOLS_MODEL_PROBLEM_MARKER,
477: false, IResource.DEPTH_INFINITE);
478: }
479: } catch (CoreException e) {
480: DroolsEclipsePlugin.log(e);
481: }
482: }
483:
484: private IProject[] getRequiredProjects(IProject project) {
485: IJavaProject javaProject = JavaCore.create(project);
486: List projects = new ArrayList();
487: try {
488: IClasspathEntry[] entries = javaProject
489: .getResolvedClasspath(true);
490: for (int i = 0, l = entries.length; i < l; i++) {
491: IClasspathEntry entry = entries[i];
492: if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
493: IProject p = project.getWorkspace().getRoot()
494: .getProject(entry.getPath().lastSegment()); // missing projects are considered too
495: if (p != null && !projects.contains(p)) {
496: projects.add(p);
497: }
498: }
499: }
500: } catch (JavaModelException e) {
501: return new IProject[0];
502: }
503: return (IProject[]) projects.toArray(new IProject[projects
504: .size()]);
505: }
506:
507: }
|