Package org.drools.compiler

Source Code of org.drools.compiler.PackageBuilder$ErrorHandler

package org.drools.compiler;

/*
* Copyright 2005 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.
*/

import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.drools.base.ClassFieldExtractorCache;
import org.drools.base.ClassTypeResolver;
import org.drools.base.TypeResolver;
import org.drools.commons.jci.problems.CompilationProblem;
import org.drools.facttemplates.FactTemplate;
import org.drools.facttemplates.FactTemplateImpl;
import org.drools.facttemplates.FieldTemplate;
import org.drools.facttemplates.FieldTemplateImpl;
import org.drools.lang.descr.BaseDescr;
import org.drools.lang.descr.FactTemplateDescr;
import org.drools.lang.descr.FieldTemplateDescr;
import org.drools.lang.descr.FunctionDescr;
import org.drools.lang.descr.FunctionImportDescr;
import org.drools.lang.descr.GlobalDescr;
import org.drools.lang.descr.ImportDescr;
import org.drools.lang.descr.PackageDescr;
import org.drools.lang.descr.QueryDescr;
import org.drools.lang.descr.RuleDescr;
import org.drools.rule.Package;
import org.drools.rule.Rule;
import org.drools.rule.builder.RuleBuildContext;
import org.drools.rule.builder.RuleBuilder;
import org.drools.ruleflow.common.core.Process;
import org.drools.xml.XmlPackageReader;
import org.xml.sax.SAXException;

/**
* This is the main compiler class for parsing and compiling rules and
* assembling or merging them into a binary Package instance. This can be done
* by merging into existing binary packages, or totally from source.
*
* If you are using the Java dialect the JavaDialectConfiguration will attempt to
* validate that the specified compiler is in the classpath, using ClassLoader.loasClass(String).
* If you intented to just Janino sa the compiler you must either overload the compiler
* property before instantiating this class or the PackageBuilder, or make sure Eclipse is in the
* classpath, as Eclipse is the default.
*/
public class PackageBuilder {

    private Package                     pkg;

    private List                        results;

    private PackageBuilderConfiguration configuration;

    private TypeResolver                typeResolver;

    private ClassFieldExtractorCache    classFieldExtractorCache;

    private RuleBuilder                 builder;

    private Dialect                     dialect;

    private ProcessBuilder              processBuilder;

    /**
     * Use this when package is starting from scratch.
     */
    public PackageBuilder() {
        this( null,
              null );
    }

    /**
     * This will allow you to merge rules into this pre existing package.
     */
    public PackageBuilder(final Package pkg) {
        this( pkg,
              null );
    }

    /**
     * Pass a specific configuration for the PackageBuilder
     *
     * @param configuration
     */
    public PackageBuilder(final PackageBuilderConfiguration configuration) {
        this( null,
              configuration );
    }

    /**
     * This allows you to pass in a pre existing package, and a configuration
     * (for instance to set the classloader).
     *
     * @param pkg
     *            A pre existing package (can be null if none exists)
     * @param configuration
     *            Optional configuration for this builder.
     */
    public PackageBuilder(final Package pkg,
                          PackageBuilderConfiguration configuration) {
        if ( configuration == null ) {
            configuration = new PackageBuilderConfiguration();
        }

        this.configuration = configuration;
        this.results = new ArrayList();
        this.pkg = pkg;
        this.classFieldExtractorCache = ClassFieldExtractorCache.getInstance();
       
        if ( this.pkg != null ) {
            this.typeResolver = new ClassTypeResolver( this.pkg.getImports(),
                                                       this.configuration.getClassLoader() );
            // make an automatic import for the current package
            this.typeResolver.addImport( this.pkg.getName() + ".*" );           
        } else {
            this.typeResolver = new ClassTypeResolver( new HashSet(),
                                                       this.configuration.getClassLoader() );
        }
        this.configuration.getDialectRegistry().initAll( this );
        if ( this.pkg != null ) {
            initDialectPackage( pkg );           
        }
       
        this.dialect = this.configuration.getDefaultDialect();

    }

    /**
     * Load a rule package from DRL source.
     *
     * @param reader
     * @throws DroolsParserException
     * @throws IOException
     */
    public void addPackageFromDrl(final Reader reader) throws DroolsParserException,
                                                      IOException {
        final DrlParser parser = new DrlParser();
        final PackageDescr pkg = parser.parse( reader );
        this.results.addAll( parser.getErrors() );
        addPackage( pkg );
    }

    /**
     * Load a rule package from XML source.
     *
     * @param reader
     * @throws DroolsParserException
     * @throws IOException
     */
    public void addPackageFromXml(final Reader reader) throws DroolsParserException,
                                                      IOException {
        final XmlPackageReader xmlReader = new XmlPackageReader();

        try {
            xmlReader.read( reader );
        } catch ( final SAXException e ) {
            throw new DroolsParserException( e.toString(),
                                             e.getCause() );
        }

        addPackage( xmlReader.getPackageDescr() );
    }

    /**
     * Load a rule package from DRL source using the supplied DSL configuration.
     *
     * @param source
     *            The source of the rules.
     * @param dsl
     *            The source of the domain specific language configuration.
     * @throws DroolsParserException
     * @throws IOException
     */
    public void addPackageFromDrl(final Reader source,
                                  final Reader dsl) throws DroolsParserException,
                                                   IOException {
        final DrlParser parser = new DrlParser();
        final PackageDescr pkg = parser.parse( source,
                                               dsl );
        this.results.addAll( parser.getErrors() );
        addPackage( pkg );
    }

    /**
     * Add a ruleflow (.rt) asset to this package.
     */
    public void addRuleFlow(Reader processSource) {
        if ( this.processBuilder == null ) {
            this.processBuilder = new ProcessBuilder( this );
        }
        if ( this.pkg == null) {
          this.pkg = new Package();
        }
        try {
            this.processBuilder.addProcessFromFile( processSource );
        } catch ( Exception e ) {
            if ( e instanceof RuntimeException ) {
                throw (RuntimeException) e;
            }
            this.results.add( new RuleFlowLoadError("Unable to load the rule flow.", e) );
        }
    }

    /**
     * This adds a package from a Descr/AST This will also trigger a compile, if
     * there are any generated classes to compile of course.
     */
    public void addPackage(final PackageDescr packageDescr) {       
        validatePackageName( packageDescr );
        validateUniqueRuleNames( packageDescr );
       

        String dialectName = null;      
//MN: not needed as overrides are done in the compiler before here
//as we can have mixed dialect types - still not quite right here.      
//        for ( Iterator it = packageDescr.getAttributes().iterator(); it.hasNext(); ) {
//            AttributeDescr value = ( AttributeDescr ) it.next();
//            if ( "dialect".equals( value.getName() )) {  
//                dialectName = value.getValue();
//                break;
//            }
//        }
       
        // The Package does not have a default dialect, so set it
        if ( dialectName == null && this.dialect == null ) {
                this.dialect = this.configuration.getDefaultDialect();
        }
           
        if ( dialectName != null ) {
            this.dialect = this.configuration.getDialectRegistry().getDialectConfiguration( dialectName ).getDialect();
        } else if ( this.dialect == null ) {
            this.dialect = this.configuration.getDefaultDialect();
        }
                  
        if ( this.pkg != null ) {
            // mergePackage( packageDescr ) ;
            mergePackage( this.pkg,
                          packageDescr );
        } else {
            this.pkg = newPackage( packageDescr );
        }

        this.builder = new RuleBuilder();

        // only try to compile if there are no parse errors
        if ( !hasErrors() ) {
            for ( final Iterator it = packageDescr.getFactTemplates().iterator(); it.hasNext(); ) {
                addFactTemplate( (FactTemplateDescr) it.next() );
            }
           
            // add static imports for all functions
            for ( final Iterator it = packageDescr.getFunctions().iterator(); it.hasNext(); ) {
                FunctionDescr functionDescr = (FunctionDescr) it.next();
                final String functionClassName = this.pkg.getName() + "." + ucFirst( functionDescr.getName() );
                functionDescr.setClassName( functionClassName );
                this.pkg.addStaticImport( functionClassName + "." + functionDescr.getName() );
            }

            // iterate and compile
            for ( final Iterator it = packageDescr.getFunctions().iterator(); it.hasNext(); ) {
                addFunction( (FunctionDescr) it.next() );
            }                           

            // iterate and compile
            for ( final Iterator it = packageDescr.getRules().iterator(); it.hasNext(); ) {
                addRule( (RuleDescr) it.next() );
            }
        }

        this.configuration.getDialectRegistry().compileAll();
       
        // some of the rules and functions may have been redefined
        if ( this.pkg.getPackageCompilationData().isDirty() ) {
            this.pkg.getPackageCompilationData().reload();
        }
        this.results = this.configuration.getDialectRegistry().addResults( this.results );
    }

    private void validatePackageName(final PackageDescr packageDescr) {
        if ( ( this.pkg == null || this.pkg.getName() == null || this.pkg.getName().equals( "" ) )
                && ( packageDescr.getName() == null || "".equals( packageDescr.getName() ) ) ) {
            throw new MissingPackageNameException( "Missing package name for rule package." );
        }
        if ( this.pkg != null && packageDescr.getName() != null &&
             ! "".equals( packageDescr.getName() ) &&
             ! this.pkg.getName().equals( packageDescr.getName() ) ) {
            throw new PackageMergeException( "Can't merge packages with different names. This package: "+this.pkg.getName()+" - New package: "+packageDescr.getName() ) ;
        }
        return;
    }

    private void validateUniqueRuleNames(final PackageDescr packageDescr) {
        final Set names = new HashSet();
        for ( final Iterator iter = packageDescr.getRules().iterator(); iter.hasNext(); ) {
            final RuleDescr rule = (RuleDescr) iter.next();
            final String name = rule.getName();
            if ( names.contains( name ) ) {
                this.results.add( new ParserError( "Duplicate rule name: " + name,
                                                   rule.getLine(),
                                                   rule.getColumn() ) );
            }
            names.add( name );
        }
    }

    private Package newPackage(final PackageDescr packageDescr) {
        final Package pkg = new Package( packageDescr.getName(),
                                         this.configuration.getClassLoader() );      

       initDialectPackage( pkg );

        mergePackage( pkg,
                      packageDescr );

        return pkg;
    }
   
    private void initDialectPackage(Package pkg) {
        for ( Iterator it = this.configuration.getDialectRegistry().iterator(); it.hasNext(); ) {
            Dialect dialect = (( DialectConfiguration ) it.next()).getDialect();
            dialect.init( pkg );
        }
       
    }

    private void mergePackage(final Package pkg,
                              final PackageDescr packageDescr) {

        // make sure we have initialised this typeResolver with "default" imports
        if ( this.typeResolver.getImports().isEmpty() ) {
            this.typeResolver.addImport( pkg.getName() + ".*" );
        }

        final List imports = packageDescr.getImports();
        for ( final Iterator it = imports.iterator(); it.hasNext(); ) {
            String importEntry = ((ImportDescr) it.next()).getTarget();
            pkg.addImport( importEntry );
            this.typeResolver.addImport( importEntry );           
            this.configuration.getDialectRegistry().addImport( importEntry );
        }

        for ( final Iterator it = packageDescr.getFunctionImports().iterator(); it.hasNext(); ) {
            String importEntry = ((FunctionImportDescr) it.next()).getTarget();
            this.configuration.getDialectRegistry().addStaticImport( importEntry );
            pkg.addStaticImport( importEntry );
        }

        ((ClassTypeResolver) this.typeResolver).setClassLoader( pkg.getPackageCompilationData().getClassLoader() );

        final List globals = packageDescr.getGlobals();
        for ( final Iterator it = globals.iterator(); it.hasNext(); ) {
            final GlobalDescr global = (GlobalDescr) it.next();
            final String identifier = global.getIdentifier();
            final String className = global.getType();

            Class clazz;
            try {
                clazz = typeResolver.resolveType( className );
                pkg.addGlobal( identifier,
                               clazz );
            } catch ( final ClassNotFoundException e ) {
                this.results.add(new GlobalError( identifier, global.getLine() ));
            }
        }
    }

    private void addFunction(final FunctionDescr functionDescr) {
        this.dialect.addFunction( functionDescr,
                                  getTypeResolver() );
    }

    private void addFactTemplate(final FactTemplateDescr factTemplateDescr) {
        final List fields = new ArrayList();
        int index = 0;
        for ( final Iterator it = factTemplateDescr.getFields().iterator(); it.hasNext(); ) {
            final FieldTemplateDescr fieldTemplateDescr = (FieldTemplateDescr) it.next();
            FieldTemplate fieldTemplate = null;
            try {
                fieldTemplate = new FieldTemplateImpl( fieldTemplateDescr.getName(),
                                                       index++,
                                                       getTypeResolver().resolveType( fieldTemplateDescr.getClassType() ) );
            } catch ( final ClassNotFoundException e ) {
                this.results.add( new FieldTemplateError( this.pkg,
                                                          fieldTemplateDescr,
                                                          null,
                                                          "Unable to resolve Class '" + fieldTemplateDescr.getClassType() + "'" ) );
            }
            fields.add( fieldTemplate );
        }

        final FactTemplate factTemplate = new FactTemplateImpl( this.pkg,
                                                                factTemplateDescr.getName(),
                                                                (FieldTemplate[]) fields.toArray( new FieldTemplate[fields.size()] ) );
    }

    private void addRule(final RuleDescr ruleDescr) {
        //this.dialect.init( ruleDescr );
       
        if ( ruleDescr instanceof QueryDescr ) {
            //ruleDescr.getLhs().insertDescr( 0, baseDescr );
        }

        RuleBuildContext context = new RuleBuildContext( this.configuration,
                                                         pkg,
                                                         ruleDescr,
                                                         this.configuration.getDialectRegistry(),
                                                         this.dialect );
        this.builder.build( context );

        this.results.addAll( context.getErrors() );

        context.getDialect().addRule( context );

        this.pkg.addRule( context.getRule() );
    }

    /**
     * @return a Type resolver, lazily. If one does not exist yet, it will be
     *         initialised.
     */
    public TypeResolver getTypeResolver() {
        return this.typeResolver;
    }

    /**
     * @return The compiled package. The package may contain errors, which you
     *         can report on by calling getErrors or printErrors. If you try to
     *         add an invalid package (or rule) to a RuleBase, you will get a
     *         runtime exception.
     *
     * Compiled packages are serializable.
     */
    public Package getPackage() {
        if ( this.pkg != null && this.pkg.getPackageCompilationData() != null && this.pkg.getPackageCompilationData().isDirty() ) {
            this.pkg.getPackageCompilationData().reload();
        }
       
        addRuleFlowsToPackage( this.processBuilder, pkg );
        if ( hasErrors() ) {
            this.pkg.setError( getErrors().toString() );
        }
        return this.pkg;
    }
   
    /**
     * Return the PackageBuilderConfiguration for this PackageBuilder session
     * @return
     *      The PackageBuilderConfiguration
     */
    public PackageBuilderConfiguration getPackageBuilderConfiguration() {
        return this.configuration;
    }

    private void addRuleFlowsToPackage(ProcessBuilder processBuilder,
                                       Package pkg) {
        if ( processBuilder != null ) {
            Process[] processes = processBuilder.getProcesses();
            for ( int i = 0; i < processes.length; i++ ) {
                pkg.addRuleFlow( processes[i] );
            }
            this.results.addAll(processBuilder.getErrors());
        }
    }

    /**
     * Return the ClassFieldExtractorCache, this should only be used internally, and is subject to change
     * @return
     *      the ClsasFieldExtractorCache
     */
    public ClassFieldExtractorCache getClassFieldExtractorCache() {
        return this.classFieldExtractorCache;
    }

    /**
     * This will return true if there were errors in the package building and
     * compiling phase
     */
    public boolean hasErrors() {
        return this.results.size() > 0;
    }

    /**
     * @return A list of Error objects that resulted from building and compiling
     *         the package.
     */
    public PackageBuilderErrors getErrors() {
        return new PackageBuilderErrors( (DroolsError[]) this.results.toArray( new DroolsError[this.results.size()] ) );
    }

    /**
     * Reset the error list. This is useful when incrementally building
     * packages. Care should be used when building this, if you clear this when
     * there were errors on items that a rule depends on (eg functions), then
     * you will get spurious errors which will not be that helpful.
     */
    protected void resetErrors() {
        this.results.clear();
    }

    public static class MissingPackageNameException extends IllegalArgumentException {
        private static final long serialVersionUID = 400L;

        public MissingPackageNameException(final String message) {
            super( message );
        }

    }

    public static class PackageMergeException extends IllegalArgumentException {
        private static final long serialVersionUID = 400L;

        public PackageMergeException(final String message) {
            super( message );
        }

    }

    /**
     * This is the super of the error handlers. Each error handler knows how to
     * report a compile error of its type, should it happen. This is needed, as
     * the compiling is done as one hit at the end, and we need to be able to
     * work out what rule/ast element caused the error.
     *
     * An error handler it created for each class task that is queued to be
     * compiled. This doesn't mean an error has occurred, it just means it *may*
     * occur in the future and we need to be able to map it back to the AST
     * element that originally spawned the code to be compiled.
     */
    public abstract static class ErrorHandler {
        private final List errors  = new ArrayList();

        protected String   message;

        private boolean    inError = false;

        /** This needes to be checked if there is infact an error */
        public boolean isInError() {
            return this.inError;
        }

        public void addError(final CompilationProblem err) {
            this.errors.add( err );
            this.inError = true;
        }

        /**
         *
         * @return A DroolsError object populated as appropriate, should the
         *         unthinkable happen and this need to be reported.
         */
        public abstract DroolsError getError();

        /**
         * We must use an error of JCI problem objects. If there are no
         * problems, null is returned. These errors are placed in the
         * DroolsError instances. Its not 1 to 1 with reported errors.
         */
        protected CompilationProblem[] collectCompilerProblems() {
            if ( this.errors.size() == 0 ) {
                return null;
            } else {
                final CompilationProblem[] list = new CompilationProblem[this.errors.size()];
                this.errors.toArray( list );
                return list;
            }
        }
    }

    public static class RuleErrorHandler extends ErrorHandler {

        private BaseDescr descr;

        private Rule      rule;

        public RuleErrorHandler(final BaseDescr ruleDescr,
                                final Rule rule,
                                final String message) {
            this.descr = ruleDescr;
            this.rule = rule;
            this.message = message;
        }

        public DroolsError getError() {
            return new RuleError( this.rule,
                                  this.descr,
                                  collectCompilerProblems(),
                                  this.message );
        }

    }

    /**
     * There isn't much point in reporting invoker errors, as they are no help.
     */
    public static class RuleInvokerErrorHandler extends RuleErrorHandler {

        public RuleInvokerErrorHandler(final BaseDescr ruleDescr,
                                       final Rule rule,
                                       final String message) {
            super( ruleDescr,
                   rule,
                   message );
        }
    }

    public static class FunctionErrorHandler extends ErrorHandler {

        private FunctionDescr descr;

        public FunctionErrorHandler(final FunctionDescr functionDescr,
                                    final String message) {
            this.descr = functionDescr;
            this.message = message;
        }

        public DroolsError getError() {
            return new FunctionError( this.descr,
                                      collectCompilerProblems(),
                                      this.message );
        }

    }

    private String ucFirst(final String name) {
        return name.toUpperCase().charAt( 0 ) + name.substring( 1 );
    }
}
TOP

Related Classes of org.drools.compiler.PackageBuilder$ErrorHandler

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.