Package org.drools.compiler

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

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.beans.IntrospectionException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.drools.RuleBase;
import org.drools.base.ClassFieldAccessor;
import org.drools.base.ClassFieldAccessorCache;
import org.drools.base.ClassFieldAccessorStore;
import org.drools.builder.DecisionTableConfiguration;
import org.drools.builder.DecisionTableInputType;
import org.drools.builder.KnowledgeType;
import org.drools.builder.ResourceConfiguration;
import org.drools.common.InternalRuleBase;
import org.drools.commons.jci.problems.CompilationProblem;
import org.drools.definition.process.Process;
import org.drools.factmodel.ClassBuilder;
import org.drools.factmodel.ClassDefinition;
import org.drools.factmodel.FieldDefinition;
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.AttributeDescr;
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.lang.descr.TypeDeclarationDescr;
import org.drools.lang.descr.TypeFieldDescr;
import org.drools.lang.dsl.DSLMappingFile;
import org.drools.lang.dsl.DSLTokenizedMappingFile;
import org.drools.lang.dsl.DefaultExpander;
import org.drools.reteoo.ReteooRuleBase;
import org.drools.rule.CompositeClassLoader;
import org.drools.rule.JavaDialectRuntimeData;
import org.drools.rule.Package;
import org.drools.rule.Rule;
import org.drools.rule.TypeDeclaration;
import org.drools.rule.builder.RuleBuildContext;
import org.drools.rule.builder.RuleBuilder;
import org.drools.spi.InternalReadAccessor;
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 DialectRegistry              dialectRegistry;

    private Map<String, PackageRegistry>  pkgRegistryMap;

    private List<DroolsError>             results;

    private PackageBuilderConfiguration   configuration;

    public static final RuleBuilder       ruleBuilder = new RuleBuilder();

    /**
     * Optional RuleBase for incremental live building
     */
    private ReteooRuleBase                ruleBase;

    /**
     * Current default package
     */
    private String                        defaultNamespace;

    /**
     * default dialect
     */
    private final String                  defaultDialect;

    private CompositeClassLoader          rootClassLoader;

    private Map<String, Class>            globals;

    private String                        url;

    private List<DSLTokenizedMappingFile> dslFiles;

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

    /**
     * This will allow you to merge rules into this pre existing package.
     */

    public PackageBuilder(final Package pkg) {
        this( pkg,
              null );
    }

    public PackageBuilder(final RuleBase ruleBase) {
        this( ruleBase,
              null );
    }

    /**
     * Pass a specific configuration for the PackageBuilder
     *
     * PackageBuilderConfiguration is not thread safe and it also contains
     * state. Once it is created and used in one or more PackageBuilders it
     * should be considered immutable. Do not modify its properties while it is
     * being used by a PackageBuilder.
     *
     * @param configuration
     */
    public PackageBuilder(final PackageBuilderConfiguration configuration) {
        this( (RuleBase) 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 = ClassFieldAccessorCache.getInstance();
    //
    //        if ( this.pkg != null ) {
    //            ClassLoader cl = this.pkg.getDialectRuntimeRegistry().getClassLoader();
    //            this.typeResolver = new ClassTypeResolver( new HashSet<String>( this.pkg.getImports().keySet() ),
    //                                                       cl );
    //            // make an automatic import for the current package
    //            this.typeResolver.addImport( this.pkg.getName() + ".*" );
    //        } else {
    //            // this.typeResolver = new ClassTypeResolver( new HashSet<String>(),
    //            // this.configuration.getClassLoader() );
    //        }
    //
    //        this.dialectRegistry = this.configuration.buildDialectRegistry();
    //
    //        this.dialect = this.dialectRegistry.getDialect( this.configuration.getDefaultDialect() );
    //
    //        if ( this.pkg != null ) {
    //            this.dialectRegistry.initAll( this );
    //        }
    //
    //    }

    public PackageBuilder(Package pkg,
                          PackageBuilderConfiguration configuration) {
        if ( configuration == null ) {
            configuration = new PackageBuilderConfiguration();
        }
        this.configuration = configuration;

        this.rootClassLoader = new CompositeClassLoader( this.configuration.getClassLoader() );

        this.defaultNamespace = pkg.getName();
        this.defaultDialect = this.configuration.getDefaultDialect();

        this.pkgRegistryMap = new HashMap<String, PackageRegistry>();
        this.results = new ArrayList();

        PackageRegistry pkgRegistry = new PackageRegistry( this,
                                                           pkg );
        pkgRegistry.setDialect( this.defaultDialect );
        this.pkgRegistryMap.put( pkg.getName(),
                                 pkgRegistry );

        globals = new HashMap<String, Class>();
    }

    public PackageBuilder(RuleBase ruleBase,
                          PackageBuilderConfiguration configuration) {
        if ( configuration == null ) {
            configuration = new PackageBuilderConfiguration();
        }
        this.configuration = configuration;

        if ( ruleBase != null ) {
            this.rootClassLoader = ((InternalRuleBase) ruleBase).getRootClassLoader();
        } else {
            this.rootClassLoader = new CompositeClassLoader( this.configuration.getClassLoader() );
        }

        // FIXME, we need to get drools to support "default" namespace.
        //this.defaultNamespace = pkg.getName();       
        this.defaultDialect = this.configuration.getDefaultDialect();

        this.pkgRegistryMap = new HashMap<String, PackageRegistry>();
        this.results = new ArrayList();

        this.ruleBase = (ReteooRuleBase) ruleBase;

        globals = new HashMap<String, Class>();
    }

    /**
     * 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() );
        if ( !parser.hasErrors() ) {
            addPackage( pkg );
        }
    }

    public void addPackageFromDrl(final URL url) throws DroolsParserException,
                                                IOException {
        this.url = url.toExternalForm();
        addPackageFromDrl( new InputStreamReader( url.openStream() ) );
        this.url = null;
    }

    /**
     * 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( this.configuration.getSemanticModules() );

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

        addPackage( xmlReader.getPackageDescr() );
    }

    public void addPackageFromXml(final URL url) throws DroolsParserException,
                                                IOException {
        this.url = url.toExternalForm();
        addPackageFromXml( new InputStreamReader( url.openStream() ) );
        this.url = null;
    }

    /**
     * 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() );
        if ( !parser.hasErrors() ) {
            addPackage( pkg );
        }
    }

    public void addPackageFromDslr(final Reader source) {
        final DrlParser parser = new DrlParser();
        DefaultExpander expander = getDslExpander();

        try {
            String str = expander.expand( source );
            if ( expander.hasErrors() ) {
                this.results.addAll( expander.getErrors() );
            }

            final PackageDescr pkg = parser.parse( str );
            this.results.addAll( parser.getErrors() );
            if ( !parser.hasErrors() ) {
                addPackage( pkg );
            }
        } catch ( Exception e ) {
            throw new RuntimeException( e );
        }
    }

    public void addPackageFromDslr(final URL url) {
        this.url = url.toExternalForm();
        try {
            addPackageFromDslr( new InputStreamReader( url.openStream() ) );
        } catch ( IOException e ) {
            throw new RuntimeException( e );
        }
        this.url = null;
    }

    public void addDsl(Reader dsl) {
        try {
            DSLTokenizedMappingFile file = new DSLTokenizedMappingFile();
            file.parseAndLoad( dsl );
            if ( this.dslFiles == null ) {
                this.dslFiles = new ArrayList<DSLTokenizedMappingFile>();
            }
            this.dslFiles.add( file );
        } catch ( Exception e ) {
            throw new RuntimeException( e );
        }
    }

    public void addDsl(URL url) {
        this.url = url.toExternalForm();
        try {
            addDsl( new InputStreamReader( url.openStream() ) );
        } catch ( IOException e ) {
            throw new RuntimeException( e );
        }
        this.url = null;
    }

    /**
     * Add a ruleflow (.rfm) asset to this package.
     */
    public void addRuleFlow(Reader processSource) {
        addProcessFromXml( processSource );
    }

    public void addProcessFromXml(URL processSource) {
        this.url = processSource.toExternalForm();
        try {
            addProcessFromXml( new InputStreamReader( processSource.openStream() ) );
        } catch ( IOException e ) {
            throw new RuntimeException( e );
        }
        this.url = null;
    }

    public void addProcessFromXml(Reader processSource) {
        ProcessBuilder processBuilder = new ProcessBuilder( this );
        try {
            processBuilder.addProcessFromFile( processSource,
                                               url );
            this.results.addAll( processBuilder.getErrors() );
        } catch ( Exception e ) {
            if ( e instanceof RuntimeException ) {
                throw (RuntimeException) e;
            }
            this.results.add( new RuleFlowLoadError( "Unable to load the rule flow.",
                                                     e ) );
        }

        this.results = getResults( this.results );
    }

    public void addResource(URL url,
                            KnowledgeType type) {
        addResource( url,
                     type,
                     null );
    }

    public void addResource(URL url,
                            KnowledgeType type,
                            ResourceConfiguration configuration) {
        try {
            if ( type == KnowledgeType.DTABLE ) {
                DecisionTableConfiguration dtableConfiguration = (DecisionTableConfiguration) configuration;

                String string = DecisionTableFactory.loadFromInputStream( url.openStream(),
                                                                          dtableConfiguration );
                addPackageFromDrl( new StringReader( string ) );
            } else {
                addResource( new InputStreamReader( url.openStream() ),
                             type,
                             configuration );
            }
        } catch ( DroolsParserException e ) {
            throw new RuntimeException( e );
        } catch ( IOException e ) {
            throw new RuntimeException( e );
        }
    }

    public void addResource(Reader reader,
                            KnowledgeType type) {
        addResource( reader,
                     type,
                     null );
    }

    public void addResource(Reader reader,
                            KnowledgeType type,
                            ResourceConfiguration configuration) {
        try {
            switch ( type ) {
                case DRL : {
                    addPackageFromDrl( reader );
                    break;

                }
                case DSLR : {
                    addPackageFromDslr( reader );
                    break;
                }
                case DSL : {
                    addDsl( reader );
                    break;
                }
                case XDRL : {
                    addPackageFromXml( reader );
                    break;
                }
                case DRF : {
                    addProcessFromXml( reader );
                    break;
                }
                case DTABLE : {
                    DecisionTableConfiguration dtableConfiguration = (DecisionTableConfiguration) configuration;
                    if ( dtableConfiguration.getInputType() == DecisionTableInputType.XLS ) {
                        throw new IllegalArgumentException( "Use addResource(URL url, KnowledgeType type, ResourceConfiguration configuration) when adding XLS decision tables." );
                    }

                    String string = DecisionTableFactory.loadFromReader( reader,
                                                                         dtableConfiguration );
                    addPackageFromDrl( new StringReader( string ) );
                    break;
                }
            }
        } catch ( Exception e ) {
            throw new RuntimeException( 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 = this.defaultDialect;
        // see if this packageDescr overrides the current default dialect
        for ( Iterator it = packageDescr.getAttributes().iterator(); it.hasNext(); ) {
            AttributeDescr value = (AttributeDescr) it.next();
            if ( "dialect".equals( value.getName() ) ) {
                dialectName = value.getValue();
                break;
            }
        }

        if ( !isEmpty( packageDescr.getNamespace() ) ) {
            // use the default namespace
            if ( checkNamespace( packageDescr.getNamespace() ) ) {
                this.defaultNamespace = packageDescr.getNamespace();
            } else {
                //force the default.
                packageDescr.setNamespace( this.defaultNamespace );
            }
        } else {
            // packagedescr defines a new default namespace
            packageDescr.setNamespace( this.defaultNamespace );
        }

        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( packageDescr.getNamespace() );
        if ( pkgRegistry == null ) {
            // initialise the package and namespace if it hasn't been used before
            pkgRegistry = newPackage( packageDescr );
        } else {
            // merge into existing package
            mergePackage( packageDescr );
        }

        // set the default dialect for this package
        pkgRegistry.setDialect( dialectName );

        // 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() );
            }

            if ( !packageDescr.getFunctions().isEmpty() ) {

                for ( final Iterator it = packageDescr.getFunctions().iterator(); it.hasNext(); ) {
                    FunctionDescr functionDescr = (FunctionDescr) it.next();
                    if ( isEmpty( functionDescr.getNamespace() ) ) {
                        // make sure namespace is set on components
                        functionDescr.setNamespace( this.defaultNamespace );
                    }
                    if ( isEmpty( functionDescr.getDialect() ) ) {
                        // make sure namespace is set on components
                        functionDescr.setDialect( pkgRegistry.getDialect() );
                    }
                    preCompileAddFunction( functionDescr );
                }

                // iterate and compile
                for ( final Iterator it = packageDescr.getFunctions().iterator(); it.hasNext(); ) {
                    // inherit the dialect from the package
                    FunctionDescr functionDescr = (FunctionDescr) it.next();
                    addFunction( functionDescr );
                }

                // We need to compile all the functions now, so scripting
                // languages like mvel can find them
                compileAll();

                for ( final Iterator it = packageDescr.getFunctions().iterator(); it.hasNext(); ) {
                    FunctionDescr functionDescr = (FunctionDescr) it.next();
                    postCompileAddFunction( functionDescr );
                }
            }

            // iterate and compile
            for ( final Iterator it = packageDescr.getRules().iterator(); it.hasNext(); ) {
                RuleDescr ruleDescr = (RuleDescr) it.next();
                if ( isEmpty( ruleDescr.getNamespace() ) ) {
                    // make sure namespace is set on components
                    ruleDescr.setNamespace( this.defaultNamespace );
                }
                if ( isEmpty( ruleDescr.getDialect() ) ) {
                    ruleDescr.setDialect( pkgRegistry.getDialect() );
                }
                addRule( ruleDescr );
            }
        }

        compileAll();
        reloadAll();
        updateResults();

        // iterate and compile
        if ( this.ruleBase != null ) {
            for ( final Iterator it = packageDescr.getRules().iterator(); it.hasNext(); ) {
                RuleDescr ruleDescr = (RuleDescr) it.next();
                pkgRegistry = this.pkgRegistryMap.get( ruleDescr.getNamespace() );
                this.ruleBase.addRule( pkgRegistry.getPackage(),
                                       pkgRegistry.getPackage().getRule( ruleDescr.getName() ) );
            }
        }
    }

    /**
     * This checks to see if it should all be in the one namespace.
     */
    private boolean checkNamespace(String newName) {
        if ( this.configuration == null ) return true;
        if ( this.defaultNamespace == null ) return true;
        if ( this.defaultNamespace.equals( newName ) ) return true;
        return this.configuration.isAllowMultipleNamespaces();
    }

    public boolean isEmpty(String string) {
        return (string == null || string.trim().length() == 0);
    }

    public void updateResults() {
        // some of the rules and functions may have been redefined    
        this.results = getResults( this.results );
    }

    public void compileAll() {
        for ( PackageRegistry pkgRegistry : this.pkgRegistryMap.values() ) {
            pkgRegistry.compileAll();
        }
    }

    public void reloadAll() {
        for ( PackageRegistry pkgRegistry : this.pkgRegistryMap.values() ) {
            pkgRegistry.getDialectRuntimeRegistry().onBeforeExecute();
        }
    }

    private List getResults(List results) {
        for ( PackageRegistry pkgRegistry : this.pkgRegistryMap.values() ) {
            results = pkgRegistry.getDialectCompiletimeRegistry().addResults( results );
        }
        return 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();
        String namespace = packageDescr.getNamespace();
        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 PackageRegistry newPackage(final PackageDescr packageDescr) {
        Package pkg;
        if ( this.ruleBase != null && this.ruleBase.getPackage( packageDescr.getName() ) != null ) {
            // there is a rulebase and it already defines this package so use it.
            pkg = this.ruleBase.getPackage( packageDescr.getName() );
        } else {
            // define a new package
            pkg = new Package( packageDescr.getName() );
            pkg.setClassFieldAccessorCache( new ClassFieldAccessorCache( this.rootClassLoader ) );

            // if there is a rulebase then add the package.
            if ( this.ruleBase != null ) {
                this.ruleBase.addPackage( pkg );
                pkg = this.ruleBase.getPackage( packageDescr.getName() );
            } else {
                // the RuleBase will also initialise the
                pkg.getDialectRuntimeRegistry().onAdd( this.rootClassLoader );
            }
        }

        PackageRegistry pkgRegistry = new PackageRegistry( this,
                                                           pkg );

        // add default import for this namespace
        pkgRegistry.addImport( packageDescr.getNamespace() + ".*" );

        this.pkgRegistryMap.put( packageDescr.getName(),
                                 pkgRegistry );

        mergePackage( packageDescr );

        return pkgRegistry;
    }

    private void mergePackage(final PackageDescr packageDescr) {
        PackageRegistry pkgRegistry;
        if ( isEmpty( packageDescr.getName() ) ) {
            pkgRegistry = this.pkgRegistryMap.get( this.defaultNamespace );
        } else {
            pkgRegistry = this.pkgRegistryMap.get( packageDescr.getNamespace() );
        }

        final List imports = packageDescr.getImports();
        for ( final Iterator it = imports.iterator(); it.hasNext(); ) {
            ImportDescr importEntry = (ImportDescr) it.next();
            pkgRegistry.addImport( importEntry.getTarget() );
        }

        processTypeDeclarations( packageDescr );

        for ( final Iterator it = packageDescr.getFunctionImports().iterator(); it.hasNext(); ) {
            String importEntry = ((FunctionImportDescr) it.next()).getTarget();
            pkgRegistry.addStaticImport( importEntry );
            pkgRegistry.getPackage().addStaticImport( importEntry );
        }

        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 = pkgRegistry.getTypeResolver().resolveType( className );
                pkgRegistry.getPackage().addGlobal( identifier,
                                                    clazz );
                this.globals.put( identifier,
                                  clazz );
            } catch ( final ClassNotFoundException e ) {
                this.results.add( new GlobalError( identifier,
                                                   global.getLine() ) );
                e.printStackTrace();
            }
        }

    }

    /**
     * @param packageDescr
     */
    private void processTypeDeclarations(final PackageDescr packageDescr) {
        PackageRegistry defaultRegistry;
        if ( isEmpty( packageDescr.getName() ) ) {
            defaultRegistry = this.pkgRegistryMap.get( this.defaultNamespace );
        } else {
            defaultRegistry = this.pkgRegistryMap.get( packageDescr.getNamespace() );
        }

        PackageRegistry pkgRegistry = null;
        for ( TypeDeclarationDescr typeDescr : packageDescr.getTypeDeclarations() ) {
            // make sure namespace is set on components
            if ( isEmpty( typeDescr.getNamespace() ) ) {
                // use the default namespace
                typeDescr.setNamespace( defaultRegistry.getPackage().getName() );
                pkgRegistry = defaultRegistry;
            } else {
                // use the namespace specified by the type declaration
                pkgRegistry = this.pkgRegistryMap.get( typeDescr.getNamespace() );
            }

            TypeDeclaration type = new TypeDeclaration( typeDescr.getTypeName() );

            // is it a regular fact or an event?
            String role = typeDescr.getMetaAttribute( TypeDeclaration.Role.ID );
            if ( role != null ) {
                type.setRole( TypeDeclaration.Role.parseRole( role ) );
            }

            // is it a POJO or a template?
            String templateName = typeDescr.getMetaAttribute( TypeDeclaration.ATTR_TEMPLATE );
            if ( templateName != null ) {
                type.setFormat( TypeDeclaration.Format.TEMPLATE );
                FactTemplate template = pkgRegistry.getPackage().getFactTemplate( templateName );
                if ( template != null ) {
                    type.setTypeTemplate( template );
                } else {
                    this.results.add( new TypeDeclarationError( "Template not found '" + template + "' for type '" + type.getTypeName() + "'",
                                                                typeDescr.getLine() ) );
                    continue;
                }
            } else {
                String className = typeDescr.getMetaAttribute( TypeDeclaration.ATTR_CLASS );
                if ( className == null ) {
                    className = type.getTypeName();
                }
                type.setFormat( TypeDeclaration.Format.POJO );
                Class clazz;
                try {
                    if ( typeDescr.getFields().size() > 0 ) {
                        // generate the bean if its needed
                        generateDeclaredBean( typeDescr,
                                              type,
                                              pkgRegistry );
                    }
                    clazz = pkgRegistry.getTypeResolver().resolveType( className );
                    type.setTypeClass( clazz );
                    if ( type.getTypeClassDef() != null ) {
                        try {
                            buildFieldAccessors( type,
                                                 pkgRegistry );
                        } catch ( Exception e ) {
                            this.results.add( new TypeDeclarationError( "Error creating field accessors for '" + className + "' for type '" + type.getTypeName() + "'",
                                                                        typeDescr.getLine() ) );
                            continue;
                        }
                    }
                } catch ( final ClassNotFoundException e ) {

                    this.results.add( new TypeDeclarationError( "Class not found '" + className + "' for type '" + type.getTypeName() + "'",
                                                                typeDescr.getLine() ) );
                    continue;
                }
            }

            String timestamp = typeDescr.getMetaAttribute( TypeDeclaration.ATTR_TIMESTAMP );
            if ( timestamp != null ) {
                type.setTimestampAttribute( timestamp );
                ClassDefinition cd = type.getTypeClassDef();
                ClassFieldAccessorStore store = pkgRegistry.getPackage().getClassFieldAccessorStore();
                InternalReadAccessor extractor = store.getReader( type.getTypeClass().getName(),
                                                                  timestamp,
                                                                  type.new TimestampAccessorSetter() );
            }
            String duration = typeDescr.getMetaAttribute( TypeDeclaration.ATTR_DURATION );
            if ( duration != null ) {
                type.setDurationAttribute( duration );
                ClassDefinition cd = type.getTypeClassDef();
                ClassFieldAccessorStore store = pkgRegistry.getPackage().getClassFieldAccessorStore();
                InternalReadAccessor extractor = store.getReader( type.getTypeClass().getName(),
                                                                  duration,
                                                                  type.new DurationAccessorSetter() );
            }

            pkgRegistry.getPackage().addTypeDeclaration( type );
        }
    }

    /**
     *
     * @param pkgRegistry
     * @throws SecurityException
     * @throws IllegalArgumentException
     * @throws InstantiationException
     * @throws IllegalAccessException
     * @throws IOException
     * @throws IntrospectionException
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws NoSuchFieldException
     */
    private final void buildFieldAccessors(final TypeDeclaration type,
                                           final PackageRegistry pkgRegistry) throws SecurityException,
                                                                             IllegalArgumentException,
                                                                             InstantiationException,
                                                                             IllegalAccessException,
                                                                             IOException,
                                                                             IntrospectionException,
                                                                             ClassNotFoundException,
                                                                             NoSuchMethodException,
                                                                             InvocationTargetException,
                                                                             NoSuchFieldException {
        ClassDefinition cd = type.getTypeClassDef();
        ClassFieldAccessorStore store = pkgRegistry.getPackage().getClassFieldAccessorStore();
        for ( FieldDefinition attrDef : cd.getFieldsDefinitions() ) {
            ClassFieldAccessor accessor = store.getAccessor( cd.getDefinedClass().getName(),
                                                             attrDef.getName() );
            attrDef.setReadWriteAccessor( accessor );
        }
    }

    /**
     * Generates a bean, and adds it to the composite class loader that
     * everything is using.
     *
     */
    private void generateDeclaredBean(TypeDeclarationDescr typeDescr,
                                      TypeDeclaration type,
                                      PackageRegistry pkgRegistry) {
        // need to fix classloader?
        ClassBuilder cb = new ClassBuilder();
        String fullName = typeDescr.getNamespace() + "." + typeDescr.getTypeName();
        ClassDefinition def = new ClassDefinition( fullName );
        Map<String, TypeFieldDescr> flds = typeDescr.getFields();
        try {
            for ( TypeFieldDescr field : flds.values() ) {
                String fullFieldType = pkgRegistry.getTypeResolver().resolveType( field.getPattern().getObjectType() ).getName();
                FieldDefinition fieldDef = new FieldDefinition( field.getFieldName(),
                                                                fullFieldType );
                // field is marked as PK
                boolean isKey = field.getMetaAttributes().containsKey( "key" );
                fieldDef.setKey( isKey );
                def.addField( fieldDef );
            }

            byte[] d = cb.buildClass( def );

            JavaDialectRuntimeData dialect = (JavaDialectRuntimeData) pkgRegistry.getDialectRuntimeRegistry().getDialectData( "java" );

            dialect.write( JavaDialectRuntimeData.convertClassToResourcePath( fullName ),
                           d );

            type.setTypeClassDef( def );
        } catch ( Exception e ) {
            e.printStackTrace();
            this.results.add( new TypeDeclarationError( "Unable to create a class for declared type " + fullName + ": " + e.getMessage() + ";",
                                                        typeDescr.getLine() ) );
        }
    }

    private void addFunction(final FunctionDescr functionDescr) {
        functionDescr.setUrl( this.url );
        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( functionDescr.getNamespace() );
        Dialect dialect = pkgRegistry.getDialectCompiletimeRegistry().getDialect( functionDescr.getDialect() );
        dialect.addFunction( functionDescr,
                             pkgRegistry.getTypeResolver() );
    }

    private void preCompileAddFunction(final FunctionDescr functionDescr) {
        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( functionDescr.getNamespace() );
        Dialect dialect = pkgRegistry.getDialectCompiletimeRegistry().getDialect( functionDescr.getDialect() );
        dialect.preCompileAddFunction( functionDescr,
                                       pkgRegistry.getTypeResolver() );
    }

    private void postCompileAddFunction(final FunctionDescr functionDescr) {
        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( functionDescr.getNamespace() );
        Dialect dialect = pkgRegistry.getDialectCompiletimeRegistry().getDialect( functionDescr.getDialect() );
        dialect.postCompileAddFunction( functionDescr,
                                        pkgRegistry.getTypeResolver() );
    }

    private void addFactTemplate(final FactTemplateDescr factTemplateDescr) {
        final List fields = new ArrayList();
        int index = 0;
        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( this.defaultNamespace );
        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++,
                                                       pkgRegistry.getTypeResolver().resolveType( fieldTemplateDescr.getClassType() ) );
            } catch ( final ClassNotFoundException e ) {
                this.results.add( new FieldTemplateError( pkgRegistry.getPackage(),
                                                          fieldTemplateDescr,
                                                          null,
                                                          "Unable to resolve Class '" + fieldTemplateDescr.getClassType() + "'" ) );
            }
            fields.add( fieldTemplate );
        }

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

    private void addRule(final RuleDescr ruleDescr) {
        // this.dialect.init( ruleDescr );
        ruleDescr.setUrl( url );

        if ( ruleDescr instanceof QueryDescr ) {
            // ruleDescr.getLhs().insertDescr( 0, baseDescr );
        }

        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( ruleDescr.getNamespace() );

        DialectCompiletimeRegistry ctr = pkgRegistry.getDialectCompiletimeRegistry();
        RuleBuildContext context = new RuleBuildContext( this,
                                                         ruleDescr,
                                                         ctr,
                                                         pkgRegistry.getPackage(),
                                                         ctr.getDialect( pkgRegistry.getDialect() ) );
        this.ruleBuilder.build( context );

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

        context.getRule().setUrl( url );

        context.getDialect().addRule( context );

        if ( this.ruleBase != null ) {
            if ( pkgRegistry.getPackage().getRule( ruleDescr.getName() ) != null ) {
                this.ruleBase.removeRule( pkgRegistry.getPackage(),
                                          pkgRegistry.getPackage().getRule( ruleDescr.getName() ) );
            }
        }

        pkgRegistry.getPackage().addRule( context.getRule() );
    }

    /**
     * @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() {
        PackageRegistry pkgRegistry = this.pkgRegistryMap.get( this.defaultNamespace );
        Package pkg = null;
        if ( pkgRegistry != null ) {
            pkg = pkgRegistry.getPackage();
        }
        if ( hasErrors() && pkg != null ) {
            pkg.setError( getErrors().toString() );
        }
        return pkg;
    }

    public Package[] getPackages() {
        Package[] pkgs = new Package[this.pkgRegistryMap.size()];
        int i = 0;
        String errors = null;
        if ( !getErrors().isEmpty() ) {
            errors = getErrors().toString();
        }
        for ( PackageRegistry pkgRegistry : this.pkgRegistryMap.values() ) {
            Package pkg = pkgRegistry.getPackage();
            pkg.getDialectRuntimeRegistry().onBeforeExecute();
            if ( errors != null ) {
                pkg.setError( errors );
            }
            pkgs[i++] = pkg;
        }

        return pkgs;
    }

    /**
     * Return the PackageBuilderConfiguration for this PackageBuilder session
     *
     * @return The PackageBuilderConfiguration
     */
    public PackageBuilderConfiguration getPackageBuilderConfiguration() {
        return this.configuration;
    }

    public PackageRegistry getPackageRegistry(String name) {
        return this.pkgRegistryMap.get( name );
    }

    public Map<String, PackageRegistry> getPackageRegistry() {
        return this.pkgRegistryMap;
    }

    /**
     * Returns an expander for DSLs (only if there is a DSL configured for this package).
     */
    public DefaultExpander getDslExpander() {
        DefaultExpander expander = new DefaultExpander();
        if ( this.dslFiles == null || this.dslFiles.isEmpty() ) {
            return expander;
        }
        for ( DSLMappingFile file : this.dslFiles ) {
            expander.addDSLMapping( file.getMapping() );
        }
        return expander;
    }

    public Map<String, Class> getGlobals() {
        return this.globals;
    }

    /**
     * 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 String getDefaultNamespace() {
        return this.defaultNamespace;
    }

    public String getDefaultDialect() {
        return this.defaultDialect;
    }

    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 RuleBuildError( this.rule,
                                       this.descr,
                                       collectCompilerProblems(),
                                       this.message );
        }

    }

    public static class ProcessErrorHandler extends ErrorHandler {

        private BaseDescr descr;

        private Process   process;

        public ProcessErrorHandler(final BaseDescr ruleDescr,
                                   final Process process,
                                   final String message) {
            this.descr = ruleDescr;
            this.process = process;
            this.message = message;
        }

        public DroolsError getError() {
            return new ProcessBuildError( this.process,
                                          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 ProcessInvokerErrorHandler extends ProcessErrorHandler {

        public ProcessInvokerErrorHandler(final BaseDescr processDescr,
                                          final Process process,
                                          final String message) {
            super( processDescr,
                   process,
                   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 );
        }

    }

    public static class SrcErrorHandler extends ErrorHandler {

        public SrcErrorHandler(final String message) {
            this.message = message;
        }

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

    }

    public static class SrcError extends DroolsError {
        private Object object;
        private String message;
        private int[]  errorLines = new int[0];

        public SrcError(Object object,
                        String message) {
            this.object = object;
            this.message = message;
        }

        public Object getObject() {
            return this.object;
        }

        public int[] getErrorLines() {
            return this.errorLines;
        }

        public String getMessage() {
            return this.message;
        }

        public String toString() {
            final StringBuffer buf = new StringBuffer();
            buf.append( this.message );
            buf.append( " : " );
            buf.append( "\n" );
            if ( this.object instanceof CompilationProblem[] ) {
                final CompilationProblem[] problem = (CompilationProblem[]) this.object;
                for ( int i = 0; i < problem.length; i++ ) {
                    buf.append( "\t" );
                    buf.append( problem[i] );
                    buf.append( "\n" );
                }
            } else if ( this.object != null ) {
                buf.append( this.object );
            }
            return buf.toString();
        }
    }

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

    public CompositeClassLoader getRootClassLoader() {
        return this.rootClassLoader;
    }
}
TOP

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

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.