001: package org.drools.compiler;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.io.IOException;
020: import java.io.Reader;
021: import java.util.ArrayList;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Set;
026:
027: import org.drools.base.ClassFieldExtractorCache;
028: import org.drools.base.ClassTypeResolver;
029: import org.drools.base.TypeResolver;
030: import org.drools.commons.jci.problems.CompilationProblem;
031: import org.drools.facttemplates.FactTemplate;
032: import org.drools.facttemplates.FactTemplateImpl;
033: import org.drools.facttemplates.FieldTemplate;
034: import org.drools.facttemplates.FieldTemplateImpl;
035: import org.drools.lang.descr.BaseDescr;
036: import org.drools.lang.descr.FactTemplateDescr;
037: import org.drools.lang.descr.FieldTemplateDescr;
038: import org.drools.lang.descr.FunctionDescr;
039: import org.drools.lang.descr.FunctionImportDescr;
040: import org.drools.lang.descr.GlobalDescr;
041: import org.drools.lang.descr.ImportDescr;
042: import org.drools.lang.descr.PackageDescr;
043: import org.drools.lang.descr.QueryDescr;
044: import org.drools.lang.descr.RuleDescr;
045: import org.drools.rule.Package;
046: import org.drools.rule.Rule;
047: import org.drools.rule.builder.RuleBuildContext;
048: import org.drools.rule.builder.RuleBuilder;
049: import org.drools.ruleflow.common.core.Process;
050: import org.drools.xml.XmlPackageReader;
051: import org.xml.sax.SAXException;
052:
053: /**
054: * This is the main compiler class for parsing and compiling rules and
055: * assembling or merging them into a binary Package instance. This can be done
056: * by merging into existing binary packages, or totally from source.
057: *
058: * If you are using the Java dialect the JavaDialectConfiguration will attempt to
059: * validate that the specified compiler is in the classpath, using ClassLoader.loasClass(String).
060: * If you intented to just Janino sa the compiler you must either overload the compiler
061: * property before instantiating this class or the PackageBuilder, or make sure Eclipse is in the
062: * classpath, as Eclipse is the default.
063: */
064: public class PackageBuilder {
065:
066: private Package pkg;
067:
068: private List results;
069:
070: private PackageBuilderConfiguration configuration;
071:
072: private TypeResolver typeResolver;
073:
074: private ClassFieldExtractorCache classFieldExtractorCache;
075:
076: private RuleBuilder builder;
077:
078: private Dialect dialect;
079:
080: private ProcessBuilder processBuilder;
081:
082: /**
083: * Use this when package is starting from scratch.
084: */
085: public PackageBuilder() {
086: this (null, null);
087: }
088:
089: /**
090: * This will allow you to merge rules into this pre existing package.
091: */
092: public PackageBuilder(final Package pkg) {
093: this (pkg, null);
094: }
095:
096: /**
097: * Pass a specific configuration for the PackageBuilder
098: *
099: * @param configuration
100: */
101: public PackageBuilder(
102: final PackageBuilderConfiguration configuration) {
103: this (null, configuration);
104: }
105:
106: /**
107: * This allows you to pass in a pre existing package, and a configuration
108: * (for instance to set the classloader).
109: *
110: * @param pkg
111: * A pre existing package (can be null if none exists)
112: * @param configuration
113: * Optional configuration for this builder.
114: */
115: public PackageBuilder(final Package pkg,
116: PackageBuilderConfiguration configuration) {
117: if (configuration == null) {
118: configuration = new PackageBuilderConfiguration();
119: }
120:
121: this .configuration = configuration;
122: this .results = new ArrayList();
123: this .pkg = pkg;
124: this .classFieldExtractorCache = ClassFieldExtractorCache
125: .getInstance();
126:
127: if (this .pkg != null) {
128: this .typeResolver = new ClassTypeResolver(this .pkg
129: .getImports(), this .configuration.getClassLoader());
130: // make an automatic import for the current package
131: this .typeResolver.addImport(this .pkg.getName() + ".*");
132: } else {
133: this .typeResolver = new ClassTypeResolver(new HashSet(),
134: this .configuration.getClassLoader());
135: }
136: this .configuration.getDialectRegistry().initAll(this );
137: if (this .pkg != null) {
138: initDialectPackage(pkg);
139: }
140:
141: this .dialect = this .configuration.getDefaultDialect();
142:
143: }
144:
145: /**
146: * Load a rule package from DRL source.
147: *
148: * @param reader
149: * @throws DroolsParserException
150: * @throws IOException
151: */
152: public void addPackageFromDrl(final Reader reader)
153: throws DroolsParserException, IOException {
154: final DrlParser parser = new DrlParser();
155: final PackageDescr pkg = parser.parse(reader);
156: this .results.addAll(parser.getErrors());
157: addPackage(pkg);
158: }
159:
160: /**
161: * Load a rule package from XML source.
162: *
163: * @param reader
164: * @throws DroolsParserException
165: * @throws IOException
166: */
167: public void addPackageFromXml(final Reader reader)
168: throws DroolsParserException, IOException {
169: final XmlPackageReader xmlReader = new XmlPackageReader();
170:
171: try {
172: xmlReader.read(reader);
173: } catch (final SAXException e) {
174: throw new DroolsParserException(e.toString(), e.getCause());
175: }
176:
177: addPackage(xmlReader.getPackageDescr());
178: }
179:
180: /**
181: * Load a rule package from DRL source using the supplied DSL configuration.
182: *
183: * @param source
184: * The source of the rules.
185: * @param dsl
186: * The source of the domain specific language configuration.
187: * @throws DroolsParserException
188: * @throws IOException
189: */
190: public void addPackageFromDrl(final Reader source, final Reader dsl)
191: throws DroolsParserException, IOException {
192: final DrlParser parser = new DrlParser();
193: final PackageDescr pkg = parser.parse(source, dsl);
194: this .results.addAll(parser.getErrors());
195: addPackage(pkg);
196: }
197:
198: /**
199: * Add a ruleflow (.rt) asset to this package.
200: */
201: public void addRuleFlow(Reader processSource) {
202: if (this .processBuilder == null) {
203: this .processBuilder = new ProcessBuilder(this );
204: }
205: if (this .pkg == null) {
206: this .pkg = new Package();
207: }
208: try {
209: this .processBuilder.addProcessFromFile(processSource);
210: } catch (Exception e) {
211: if (e instanceof RuntimeException) {
212: throw (RuntimeException) e;
213: }
214: this .results.add(new RuleFlowLoadError(
215: "Unable to load the rule flow.", e));
216: }
217: }
218:
219: /**
220: * This adds a package from a Descr/AST This will also trigger a compile, if
221: * there are any generated classes to compile of course.
222: */
223: public void addPackage(final PackageDescr packageDescr) {
224: validatePackageName(packageDescr);
225: validateUniqueRuleNames(packageDescr);
226:
227: String dialectName = null;
228: //MN: not needed as overrides are done in the compiler before here
229: //as we can have mixed dialect types - still not quite right here.
230: // for ( Iterator it = packageDescr.getAttributes().iterator(); it.hasNext(); ) {
231: // AttributeDescr value = ( AttributeDescr ) it.next();
232: // if ( "dialect".equals( value.getName() )) {
233: // dialectName = value.getValue();
234: // break;
235: // }
236: // }
237:
238: // The Package does not have a default dialect, so set it
239: if (dialectName == null && this .dialect == null) {
240: this .dialect = this .configuration.getDefaultDialect();
241: }
242:
243: if (dialectName != null) {
244: this .dialect = this .configuration.getDialectRegistry()
245: .getDialectConfiguration(dialectName).getDialect();
246: } else if (this .dialect == null) {
247: this .dialect = this .configuration.getDefaultDialect();
248: }
249:
250: if (this .pkg != null) {
251: // mergePackage( packageDescr ) ;
252: mergePackage(this .pkg, packageDescr);
253: } else {
254: this .pkg = newPackage(packageDescr);
255: }
256:
257: this .builder = new RuleBuilder();
258:
259: // only try to compile if there are no parse errors
260: if (!hasErrors()) {
261: for (final Iterator it = packageDescr.getFactTemplates()
262: .iterator(); it.hasNext();) {
263: addFactTemplate((FactTemplateDescr) it.next());
264: }
265:
266: // add static imports for all functions
267: for (final Iterator it = packageDescr.getFunctions()
268: .iterator(); it.hasNext();) {
269: FunctionDescr functionDescr = (FunctionDescr) it.next();
270: final String functionClassName = this .pkg.getName()
271: + "." + ucFirst(functionDescr.getName());
272: functionDescr.setClassName(functionClassName);
273: this .pkg.addStaticImport(functionClassName + "."
274: + functionDescr.getName());
275: }
276:
277: // iterate and compile
278: for (final Iterator it = packageDescr.getFunctions()
279: .iterator(); it.hasNext();) {
280: addFunction((FunctionDescr) it.next());
281: }
282:
283: // iterate and compile
284: for (final Iterator it = packageDescr.getRules().iterator(); it
285: .hasNext();) {
286: addRule((RuleDescr) it.next());
287: }
288: }
289:
290: this .configuration.getDialectRegistry().compileAll();
291:
292: // some of the rules and functions may have been redefined
293: if (this .pkg.getPackageCompilationData().isDirty()) {
294: this .pkg.getPackageCompilationData().reload();
295: }
296: this .results = this .configuration.getDialectRegistry()
297: .addResults(this .results);
298: }
299:
300: private void validatePackageName(final PackageDescr packageDescr) {
301: if ((this .pkg == null || this .pkg.getName() == null || this .pkg
302: .getName().equals(""))
303: && (packageDescr.getName() == null || ""
304: .equals(packageDescr.getName()))) {
305: throw new MissingPackageNameException(
306: "Missing package name for rule package.");
307: }
308: if (this .pkg != null && packageDescr.getName() != null
309: && !"".equals(packageDescr.getName())
310: && !this .pkg.getName().equals(packageDescr.getName())) {
311: throw new PackageMergeException(
312: "Can't merge packages with different names. This package: "
313: + this .pkg.getName() + " - New package: "
314: + packageDescr.getName());
315: }
316: return;
317: }
318:
319: private void validateUniqueRuleNames(final PackageDescr packageDescr) {
320: final Set names = new HashSet();
321: for (final Iterator iter = packageDescr.getRules().iterator(); iter
322: .hasNext();) {
323: final RuleDescr rule = (RuleDescr) iter.next();
324: final String name = rule.getName();
325: if (names.contains(name)) {
326: this .results.add(new ParserError(
327: "Duplicate rule name: " + name, rule.getLine(),
328: rule.getColumn()));
329: }
330: names.add(name);
331: }
332: }
333:
334: private Package newPackage(final PackageDescr packageDescr) {
335: final Package pkg = new Package(packageDescr.getName(),
336: this .configuration.getClassLoader());
337:
338: initDialectPackage(pkg);
339:
340: mergePackage(pkg, packageDescr);
341:
342: return pkg;
343: }
344:
345: private void initDialectPackage(Package pkg) {
346: for (Iterator it = this .configuration.getDialectRegistry()
347: .iterator(); it.hasNext();) {
348: Dialect dialect = ((DialectConfiguration) it.next())
349: .getDialect();
350: dialect.init(pkg);
351: }
352:
353: }
354:
355: private void mergePackage(final Package pkg,
356: final PackageDescr packageDescr) {
357:
358: // make sure we have initialised this typeResolver with "default" imports
359: if (this .typeResolver.getImports().isEmpty()) {
360: this .typeResolver.addImport(pkg.getName() + ".*");
361: }
362:
363: final List imports = packageDescr.getImports();
364: for (final Iterator it = imports.iterator(); it.hasNext();) {
365: String importEntry = ((ImportDescr) it.next()).getTarget();
366: pkg.addImport(importEntry);
367: this .typeResolver.addImport(importEntry);
368: this .configuration.getDialectRegistry().addImport(
369: importEntry);
370: }
371:
372: for (final Iterator it = packageDescr.getFunctionImports()
373: .iterator(); it.hasNext();) {
374: String importEntry = ((FunctionImportDescr) it.next())
375: .getTarget();
376: this .configuration.getDialectRegistry().addStaticImport(
377: importEntry);
378: pkg.addStaticImport(importEntry);
379: }
380:
381: ((ClassTypeResolver) this .typeResolver).setClassLoader(pkg
382: .getPackageCompilationData().getClassLoader());
383:
384: final List globals = packageDescr.getGlobals();
385: for (final Iterator it = globals.iterator(); it.hasNext();) {
386: final GlobalDescr global = (GlobalDescr) it.next();
387: final String identifier = global.getIdentifier();
388: final String className = global.getType();
389:
390: Class clazz;
391: try {
392: clazz = typeResolver.resolveType(className);
393: pkg.addGlobal(identifier, clazz);
394: } catch (final ClassNotFoundException e) {
395: this .results.add(new GlobalError(identifier, global
396: .getLine()));
397: }
398: }
399: }
400:
401: private void addFunction(final FunctionDescr functionDescr) {
402: this .dialect.addFunction(functionDescr, getTypeResolver());
403: }
404:
405: private void addFactTemplate(
406: final FactTemplateDescr factTemplateDescr) {
407: final List fields = new ArrayList();
408: int index = 0;
409: for (final Iterator it = factTemplateDescr.getFields()
410: .iterator(); it.hasNext();) {
411: final FieldTemplateDescr fieldTemplateDescr = (FieldTemplateDescr) it
412: .next();
413: FieldTemplate fieldTemplate = null;
414: try {
415: fieldTemplate = new FieldTemplateImpl(
416: fieldTemplateDescr.getName(), index++,
417: getTypeResolver().resolveType(
418: fieldTemplateDescr.getClassType()));
419: } catch (final ClassNotFoundException e) {
420: this .results.add(new FieldTemplateError(this .pkg,
421: fieldTemplateDescr, null,
422: "Unable to resolve Class '"
423: + fieldTemplateDescr.getClassType()
424: + "'"));
425: }
426: fields.add(fieldTemplate);
427: }
428:
429: final FactTemplate factTemplate = new FactTemplateImpl(
430: this .pkg, factTemplateDescr.getName(),
431: (FieldTemplate[]) fields
432: .toArray(new FieldTemplate[fields.size()]));
433: }
434:
435: private void addRule(final RuleDescr ruleDescr) {
436: //this.dialect.init( ruleDescr );
437:
438: if (ruleDescr instanceof QueryDescr) {
439: //ruleDescr.getLhs().insertDescr( 0, baseDescr );
440: }
441:
442: RuleBuildContext context = new RuleBuildContext(
443: this .configuration, pkg, ruleDescr, this .configuration
444: .getDialectRegistry(), this .dialect);
445: this .builder.build(context);
446:
447: this .results.addAll(context.getErrors());
448:
449: context.getDialect().addRule(context);
450:
451: this .pkg.addRule(context.getRule());
452: }
453:
454: /**
455: * @return a Type resolver, lazily. If one does not exist yet, it will be
456: * initialised.
457: */
458: public TypeResolver getTypeResolver() {
459: return this .typeResolver;
460: }
461:
462: /**
463: * @return The compiled package. The package may contain errors, which you
464: * can report on by calling getErrors or printErrors. If you try to
465: * add an invalid package (or rule) to a RuleBase, you will get a
466: * runtime exception.
467: *
468: * Compiled packages are serializable.
469: */
470: public Package getPackage() {
471: if (this .pkg != null
472: && this .pkg.getPackageCompilationData() != null
473: && this .pkg.getPackageCompilationData().isDirty()) {
474: this .pkg.getPackageCompilationData().reload();
475: }
476:
477: addRuleFlowsToPackage(this .processBuilder, pkg);
478: if (hasErrors()) {
479: this .pkg.setError(getErrors().toString());
480: }
481: return this .pkg;
482: }
483:
484: /**
485: * Return the PackageBuilderConfiguration for this PackageBuilder session
486: * @return
487: * The PackageBuilderConfiguration
488: */
489: public PackageBuilderConfiguration getPackageBuilderConfiguration() {
490: return this .configuration;
491: }
492:
493: private void addRuleFlowsToPackage(ProcessBuilder processBuilder,
494: Package pkg) {
495: if (processBuilder != null) {
496: Process[] processes = processBuilder.getProcesses();
497: for (int i = 0; i < processes.length; i++) {
498: pkg.addRuleFlow(processes[i]);
499: }
500: this .results.addAll(processBuilder.getErrors());
501: }
502: }
503:
504: /**
505: * Return the ClassFieldExtractorCache, this should only be used internally, and is subject to change
506: * @return
507: * the ClsasFieldExtractorCache
508: */
509: public ClassFieldExtractorCache getClassFieldExtractorCache() {
510: return this .classFieldExtractorCache;
511: }
512:
513: /**
514: * This will return true if there were errors in the package building and
515: * compiling phase
516: */
517: public boolean hasErrors() {
518: return this .results.size() > 0;
519: }
520:
521: /**
522: * @return A list of Error objects that resulted from building and compiling
523: * the package.
524: */
525: public PackageBuilderErrors getErrors() {
526: return new PackageBuilderErrors((DroolsError[]) this .results
527: .toArray(new DroolsError[this .results.size()]));
528: }
529:
530: /**
531: * Reset the error list. This is useful when incrementally building
532: * packages. Care should be used when building this, if you clear this when
533: * there were errors on items that a rule depends on (eg functions), then
534: * you will get spurious errors which will not be that helpful.
535: */
536: protected void resetErrors() {
537: this .results.clear();
538: }
539:
540: public static class MissingPackageNameException extends
541: IllegalArgumentException {
542: private static final long serialVersionUID = 400L;
543:
544: public MissingPackageNameException(final String message) {
545: super (message);
546: }
547:
548: }
549:
550: public static class PackageMergeException extends
551: IllegalArgumentException {
552: private static final long serialVersionUID = 400L;
553:
554: public PackageMergeException(final String message) {
555: super (message);
556: }
557:
558: }
559:
560: /**
561: * This is the super of the error handlers. Each error handler knows how to
562: * report a compile error of its type, should it happen. This is needed, as
563: * the compiling is done as one hit at the end, and we need to be able to
564: * work out what rule/ast element caused the error.
565: *
566: * An error handler it created for each class task that is queued to be
567: * compiled. This doesn't mean an error has occurred, it just means it *may*
568: * occur in the future and we need to be able to map it back to the AST
569: * element that originally spawned the code to be compiled.
570: */
571: public abstract static class ErrorHandler {
572: private final List errors = new ArrayList();
573:
574: protected String message;
575:
576: private boolean inError = false;
577:
578: /** This needes to be checked if there is infact an error */
579: public boolean isInError() {
580: return this .inError;
581: }
582:
583: public void addError(final CompilationProblem err) {
584: this .errors.add(err);
585: this .inError = true;
586: }
587:
588: /**
589: *
590: * @return A DroolsError object populated as appropriate, should the
591: * unthinkable happen and this need to be reported.
592: */
593: public abstract DroolsError getError();
594:
595: /**
596: * We must use an error of JCI problem objects. If there are no
597: * problems, null is returned. These errors are placed in the
598: * DroolsError instances. Its not 1 to 1 with reported errors.
599: */
600: protected CompilationProblem[] collectCompilerProblems() {
601: if (this .errors.size() == 0) {
602: return null;
603: } else {
604: final CompilationProblem[] list = new CompilationProblem[this .errors
605: .size()];
606: this .errors.toArray(list);
607: return list;
608: }
609: }
610: }
611:
612: public static class RuleErrorHandler extends ErrorHandler {
613:
614: private BaseDescr descr;
615:
616: private Rule rule;
617:
618: public RuleErrorHandler(final BaseDescr ruleDescr,
619: final Rule rule, final String message) {
620: this .descr = ruleDescr;
621: this .rule = rule;
622: this .message = message;
623: }
624:
625: public DroolsError getError() {
626: return new RuleError(this .rule, this .descr,
627: collectCompilerProblems(), this .message);
628: }
629:
630: }
631:
632: /**
633: * There isn't much point in reporting invoker errors, as they are no help.
634: */
635: public static class RuleInvokerErrorHandler extends
636: RuleErrorHandler {
637:
638: public RuleInvokerErrorHandler(final BaseDescr ruleDescr,
639: final Rule rule, final String message) {
640: super (ruleDescr, rule, message);
641: }
642: }
643:
644: public static class FunctionErrorHandler extends ErrorHandler {
645:
646: private FunctionDescr descr;
647:
648: public FunctionErrorHandler(final FunctionDescr functionDescr,
649: final String message) {
650: this .descr = functionDescr;
651: this .message = message;
652: }
653:
654: public DroolsError getError() {
655: return new FunctionError(this .descr,
656: collectCompilerProblems(), this .message);
657: }
658:
659: }
660:
661: private String ucFirst(final String name) {
662: return name.toUpperCase().charAt(0) + name.substring(1);
663: }
664: }
|