All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.drools.core.base.mvel.MVELCompilationUnit Maven / Gradle / Ivy

There is a newer version: 9.44.0.Final
Show newest version
/*
 * Copyright 2010 Red Hat, Inc. and/or its affiliates.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.drools.core.base.mvel;

import org.drools.core.base.EvaluatorWrapper;
import org.drools.core.base.ModifyInterceptor;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.reteoo.LeftTuple;
import org.drools.core.rule.Declaration;
import org.drools.core.rule.MVELDialectRuntimeData;
import org.drools.core.spi.GlobalResolver;
import org.drools.core.spi.KnowledgeHelper;
import org.drools.core.spi.Tuple;
import org.kie.api.definition.rule.Rule;
import org.mvel2.DataConversion;
import org.mvel2.MVEL;
import org.mvel2.ParserConfiguration;
import org.mvel2.ParserContext;
import org.mvel2.compiler.ExecutableStatement;
import org.mvel2.integration.Interceptor;
import org.mvel2.integration.PropertyHandler;
import org.mvel2.integration.PropertyHandlerFactory;
import org.mvel2.integration.VariableResolver;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.OptimizerFactory;
import org.mvel2.util.SimpleVariableSpaceModel;

import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class MVELCompilationUnit
    implements
    Externalizable,
    Cloneable {

    private static final long                    serialVersionUID = 510l;

    private String                               name;

    private String                               expression;

    private String[]                             globalIdentifiers;
    private EvaluatorWrapper[]                   operators;

    private Declaration[]                        previousDeclarations;
    private Declaration[]                        localDeclarations;
    private String[]                             otherIdentifiers;

    private String[]                             inputIdentifiers;
    private String[]                             inputTypes;

    private int                                  languageLevel;
    private boolean                              strictMode;
    
    private boolean                              readLocalsFromTuple;
    
    private SimpleVariableSpaceModel             varModel;

    private int                                  allVarsLength;

    public static final Map INTERCEPTORS = new InterceptorMap();

    public enum Scope {
        CONSTRAINT, CONSEQUENCE, EXPRESSION;

        public boolean hasRule() {
            return this != CONSTRAINT;
        }
    }

    static {
        //for handling dates as string literals
        DataConversion.addConversionHandler( Date.class,
                                             new MVELDateCoercion() );
        DataConversion.addConversionHandler( Calendar.class,
                                             new MVELCalendarCoercion() );

        // always use mvel reflective optimizer
        OptimizerFactory.setDefaultOptimizer( OptimizerFactory.SAFE_REFLECTIVE );
    }

    private static final Map> primitivesMap    = new HashMap>();
    static {
        primitivesMap.put( "int",
                           int.class );
        primitivesMap.put( "boolean",
                           boolean.class );
        primitivesMap.put( "float",
                           float.class );
        primitivesMap.put( "long",
                           long.class );
        primitivesMap.put( "short",
                           short.class );
        primitivesMap.put( "byte",
                           byte.class );
        primitivesMap.put( "double",
                           double.class );
        primitivesMap.put( "char",
                           char.class );
    }

    public MVELCompilationUnit() {
    }

    public MVELCompilationUnit(String name,
                               String expression,
                               String[] globalIdentifiers,
                               EvaluatorWrapper[] operators,
                               Declaration[] previousDeclarations,
                               Declaration[] localDeclarations,
                               String[] otherIdentifiers,
                               String[] inputIdentifiers,
                               String[] inputTypes,
                               int languageLevel,
                               boolean strictMode,
                               boolean readLocalsFromTuple ) {
        this.name = name;
        this.expression = expression;

        this.globalIdentifiers = globalIdentifiers;
        this.operators = operators;

        this.previousDeclarations = previousDeclarations;
        this.localDeclarations = localDeclarations;
        this.otherIdentifiers = otherIdentifiers;

        this.inputIdentifiers = inputIdentifiers;
        this.inputTypes = inputTypes;

        this.languageLevel = languageLevel;
        this.strictMode = strictMode;
        
        this.readLocalsFromTuple = readLocalsFromTuple;
    }

    public String getExpression() {
        return expression;
    }

    @Override
    public boolean equals( Object obj ) {
        if (this == obj) {
            return true;
        }
        if (obj == null || !(obj instanceof MVELCompilationUnit)) {
            return false;
        }

        MVELCompilationUnit other = (MVELCompilationUnit) obj;

        return expression.equals( other.expression ) &&
               Arrays.equals(previousDeclarations, other.previousDeclarations) &&
               Arrays.equals(localDeclarations, other.localDeclarations);
    }

    @Override
    public int hashCode() {
        return 23 * expression.hashCode() +
               29 * Arrays.hashCode( previousDeclarations ) +
               31 * Arrays.hashCode( localDeclarations );
    }

    public void writeExternal( ObjectOutput out ) throws IOException {
        out.writeUTF( name );

        out.writeUTF( expression );

        out.writeObject( globalIdentifiers );
        out.writeObject( operators );

        out.writeObject( previousDeclarations );
        out.writeObject( localDeclarations );
        out.writeObject( otherIdentifiers );

        out.writeObject( inputIdentifiers );
        out.writeObject( inputTypes );

        out.writeInt( languageLevel );
        out.writeBoolean( strictMode );
        
        out.writeBoolean( readLocalsFromTuple );
    }

    public void readExternal( ObjectInput in ) throws IOException,
                                              ClassNotFoundException {
        name = in.readUTF();
        expression = in.readUTF();

        globalIdentifiers = (String[]) in.readObject();
        operators = (EvaluatorWrapper[]) in.readObject();

        previousDeclarations = (Declaration[]) in.readObject();
        localDeclarations = (Declaration[]) in.readObject();
        otherIdentifiers = (String[]) in.readObject();

        inputIdentifiers = (String[]) in.readObject();
        inputTypes = (String[]) in.readObject();

        languageLevel = in.readInt();
        strictMode = in.readBoolean();
        
        readLocalsFromTuple = in.readBoolean();
    }    

    public Serializable getCompiledExpression(MVELDialectRuntimeData runtimeData) {
        return getCompiledExpression(runtimeData, null);
    }

    public Serializable getCompiledExpression(MVELDialectRuntimeData runtimeData, Object evaluationContext) {
        ParserConfiguration conf = runtimeData.getParserConfiguration();
        final ParserContext parserContext = new ParserContext( conf, evaluationContext );
        if ( MVELDebugHandler.isDebugMode() ) {
            parserContext.setDebugSymbols( true );
        }

        parserContext.setStrictTypeEnforcement( strictMode );
        parserContext.setStrongTyping( strictMode );
        parserContext.setIndexAllocation( true );

        if ( INTERCEPTORS != null ) {
            parserContext.setInterceptors(INTERCEPTORS);
        }

        parserContext.addIndexedInput( inputIdentifiers );
                
        String identifier = null;
        String type = null;
        try {
            for ( int i = 0, length = inputIdentifiers.length; i < length; i++ ) {
                identifier = inputIdentifiers[i];
                type = inputTypes[i];
                Class< ? > cls = loadClass( runtimeData.getPackageClassLoader(),
                                            inputTypes[i] );
                parserContext.addInput( inputIdentifiers[i],
                                        cls );
            }
        } catch ( ClassNotFoundException e ) {
            throw new RuntimeException( "Unable to resolve class '" + type + "' for identifier '" + identifier );
        }

        parserContext.setSourceFile( name );

        String[] varNames = parserContext.getIndexedVarNames();
        
        ExecutableStatement stmt = (ExecutableStatement) compile( expression, parserContext );
        
        Set localNames = parserContext.getVariables().keySet();

        parserContext.addIndexedLocals(localNames);

        String[] locals = localNames.toArray(new String[localNames.size()]);
        String[] allVars = new String[varNames.length + locals.length];

        System.arraycopy(varNames, 0, allVars, 0, varNames.length);
        System.arraycopy(locals, 0, allVars, varNames.length, locals.length);        
        
        this.varModel = new SimpleVariableSpaceModel(allVars);
        this.allVarsLength = allVars.length;
        
        return stmt;
    }
    
    public VariableResolverFactory createFactory() {
        Object[] vals = new Object[inputIdentifiers.length];

        VariableResolverFactory factory = varModel.createFactory( vals );
        factory.setNextFactory( new DroolsVarFactory() );
        return factory;
    }
    
    public VariableResolverFactory getFactory(final Object knowledgeHelper,
                                              final Declaration[] prevDecl,
                                              final Rule rule,
                                              final Tuple tuples,
                                              final Object[] otherVars,
                                              final InternalWorkingMemory workingMemory,
                                              final GlobalResolver globals) {
        VariableResolverFactory factory = createFactory();
        updateFactory(knowledgeHelper, prevDecl, rule, null, knowledgeHelper, tuples, otherVars, workingMemory, globals, factory );
        return factory;
    }

    public VariableResolverFactory getFactory(final Object knowledgeHelper,
                                              final Declaration[] prevDecl,
                                              final Rule rule,
                                              final InternalFactHandle rightHandle,
                                              final Tuple tuple,
                                              final Object[] otherVars,
                                              final InternalWorkingMemory workingMemory,
                                              final GlobalResolver globals) {
        VariableResolverFactory factory = createFactory();
        updateFactory(knowledgeHelper, prevDecl, rule, rightHandle, rightHandle != null ? rightHandle.getObject() : null, tuple, otherVars, workingMemory, globals, factory);
        return factory;
    }
    
    public void updateFactory( InternalFactHandle rightHandle,
                               Tuple tuple,
                               Object[] localVars,
                               InternalWorkingMemory workingMemory,
                               GlobalResolver globalResolver,
                               VariableResolverFactory factory ) {
        updateFactory( null, null, null, rightHandle, rightHandle != null ? rightHandle.getObject() : null, tuple, localVars, workingMemory, globalResolver, factory );
    }    
    
    private void updateFactory( Object knowledgeHelper,
                                Declaration[] prevDecl,
                                Rule rule,
                                InternalFactHandle rightHandle,
                                Object rightObject,
                                Tuple tuple,
                                Object[] otherVars,
                                InternalWorkingMemory workingMemory,
                                GlobalResolver globals,
                                VariableResolverFactory factory ) {
        int varLength = inputIdentifiers.length;

        int i = 0;
        if ( "this".equals( inputIdentifiers[0] ) ) {
            factory.getIndexedVariableResolver( i++ ).setValue( rightObject );
        }
        factory.getIndexedVariableResolver( i++ ).setValue( knowledgeHelper );
        factory.getIndexedVariableResolver( i++ ).setValue( knowledgeHelper );
        if (inputIdentifiers.length > i && "rule".equals( inputIdentifiers[i] )) {
            factory.getIndexedVariableResolver( i++ ).setValue( rule );
        }

        if ( globalIdentifiers != null ) {
            for (String globalIdentifier : globalIdentifiers) {
                factory.getIndexedVariableResolver(i++).setValue(globals.resolveGlobal(globalIdentifier));
            }
        }

        InternalFactHandle[] handles = tuple instanceof LeftTuple ? ( (LeftTuple) tuple ).toFactHandles() : null;
        if ( operators.length > 0 ) {
            for (EvaluatorWrapper operator : operators) {
                // TODO: need to have one operator per working memory
                factory.getIndexedVariableResolver(i++).setValue(operator);
                operator.loadHandles(workingMemory, handles, rightHandle);
            }
        }

        Object[] objs = null;

        if ( tuple != null ) {
            if (handles == null) {
                objs = tuple.toObjects();
            }
            if ( this.previousDeclarations != null && this.previousDeclarations.length > 0 ) {
                // Consequences with 'or's will have different declaration offsets, so use the one's from the RTN's subrule.
                if ( prevDecl == null ) {
                    // allows the caller to override the member var
                    // used for rules, salience and timers so they work with 'or' CEs
                    prevDecl =  this.previousDeclarations;
                }

                for (Declaration decl : prevDecl) {
                    int offset = decl.getPattern().getOffset();
                    Object o = decl.getValue(workingMemory, objs != null ? objs[offset] : handles[offset].getObject());
                    factory.getIndexedVariableResolver(i++).setValue(o);
                }
            }
        }

        if ( this.localDeclarations != null && this.localDeclarations.length > 0 ) {
            for ( Declaration decl : this.localDeclarations ) {
                Object value;
                if( readLocalsFromTuple && tuple != null ) {
                    int offset = decl.getPattern().getOffset();
                    value = decl.getValue( workingMemory,
                                           objs != null ? objs[offset] : handles[offset].getObject() );
                } else {
                    value = decl.getValue( workingMemory,
                                          rightObject ); 
                }
                factory.getIndexedVariableResolver( i++ ).setValue( value );
            }
        }

        int otherVarsPos = 0;
        if ( otherVars != null ) {
            otherVarsPos = i;
            for ( Object o : otherVars ) {
                factory.getIndexedVariableResolver( i++ ).setValue( o );
            }
        }
        int otherVarsLength = i - otherVarsPos;
        
        for ( i = varLength; i < this.allVarsLength; i++ ) {
            // null all local vars
            factory.getIndexedVariableResolver( i ).setValue( null );
        }
        
        DroolsVarFactory df = ( DroolsVarFactory )  factory.getNextFactory();

        df.setOtherVarsPos( otherVarsPos );
        df.setOtherVarsLength( otherVarsLength );
        
        if ( knowledgeHelper instanceof KnowledgeHelper ) {
            KnowledgeHelper kh = ( KnowledgeHelper ) knowledgeHelper;
            df.setKnowledgeHelper( kh );
        }        
    }

    public static InternalFactHandle getFactHandle( Declaration declaration,
                                                    InternalFactHandle[] handles ) {
        return handles != null && handles.length > declaration.getPattern().getOffset() ? handles[declaration.getPattern().getOffset()] : null;
    }

    private static Serializable compile( final String text,
                                         final ParserContext parserContext ) {
        MVEL.COMPILER_OPT_ALLOW_NAKED_METH_CALL = true;
        MVEL.COMPILER_OPT_ALLOW_OVERRIDE_ALL_PROPHANDLING = true;
        MVEL.COMPILER_OPT_ALLOW_RESOLVE_INNERCLASSES_WITH_DOTNOTATION = true;
        MVEL.COMPILER_OPT_SUPPORT_JAVA_STYLE_CLASS_LITERALS = true;   
        
        if ( MVELDebugHandler.isDebugMode() ) {
            parserContext.setDebugSymbols( true );
        }

        return MVEL.compileExpression( text.trim(),
                                       parserContext );
    }

    public static Class loadClass( ClassLoader classLoader,
                                   String className ) throws ClassNotFoundException {
        Class cls = primitivesMap.get( className );
        if ( cls == null ) {
            cls = classLoader.loadClass( className );
        }
        return cls;
    }

    public void replaceDeclaration( Declaration declaration,
                                    Declaration resolved ) {
        if ( previousDeclarations != null ) {
            for ( int i = 0; i < previousDeclarations.length; i++ ) {
                if ( previousDeclarations[i].equals( declaration ) ) {
                    previousDeclarations[i] = resolved;
                }
            }
        }
        if ( localDeclarations != null ) {
            for ( int i = 0; i < localDeclarations.length; i++ ) {
                if ( localDeclarations[i].equals( declaration ) ) {
                    localDeclarations[i] = resolved;
                }
            }
        }
    }

    @Override
    public MVELCompilationUnit clone() {
        Declaration[] clonedPreviousDeclarations = null;
        if (previousDeclarations != null) {
            clonedPreviousDeclarations = new Declaration[previousDeclarations.length];
            System.arraycopy(previousDeclarations, 0, clonedPreviousDeclarations, 0, previousDeclarations.length);
        }
        Declaration[] clonedLocalDeclarations = null;
        if (localDeclarations != null) {
            clonedLocalDeclarations = new Declaration[localDeclarations.length];
            System.arraycopy(localDeclarations, 0, clonedLocalDeclarations, 0, localDeclarations.length);
        }

        MVELCompilationUnit unit = new MVELCompilationUnit( name,
                                                            expression,
                                                            globalIdentifiers,
                                                            operators,
                                                            clonedPreviousDeclarations,
                                                            clonedLocalDeclarations,
                                                            otherIdentifiers,
                                                            inputIdentifiers,
                                                            inputTypes,
                                                            languageLevel,
                                                            strictMode,
                                                            readLocalsFromTuple );
        unit.varModel = this.varModel;
        return unit;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }

    public String getName() {
        return name;
    }
    
    public String[] getGlobalIdentifiers() {
        return globalIdentifiers;
    }

    public Declaration[] getPreviousDeclarations() {
        return previousDeclarations;
    }

    public void setPreviousDeclarations( Declaration[] previousDeclarations ) {
        this.previousDeclarations = previousDeclarations;
    }

    public Declaration[] getLocalDeclarations() {
        return localDeclarations;
    }

    public String[] getOtherIdentifiers() {
        return otherIdentifiers;
    }

    public String[] getInputIdentifiers() {
        return inputIdentifiers;
    }

    public String[] getInputTypes() {
        return inputTypes;
    }
    
    public int getLanguageLevel() {
        return languageLevel;
    }

    public boolean isStrictMode() {
        return strictMode;
    }

    public static Map getInterceptors() {
        return INTERCEPTORS;
    }

    public static Map> getPrimitivesmap() {
        return primitivesMap;
    }

    public static class DroolsVarFactory implements VariableResolverFactory {
    
        private KnowledgeHelper knowledgeHelper;
    
        private int             otherVarsPos;
        private int             otherVarsLength;
        
        public KnowledgeHelper getKnowledgeHelper() {
            return this.knowledgeHelper ;
        }
    
        public void setKnowledgeHelper(KnowledgeHelper kh) {
            this.knowledgeHelper = kh;
        }
    
        public int getOtherVarsPos() {
            return otherVarsPos;
        }
    
        public void setOtherVarsPos( int otherVarsPos ) {
            this.otherVarsPos = otherVarsPos;
        }
    
        public int getOtherVarsLength() {
            return otherVarsLength;
        }
    
        public void setOtherVarsLength( int otherVarsLength ) {
            this.otherVarsLength = otherVarsLength;
        }

        public VariableResolver createIndexedVariable( int index,
                                                       String name,
                                                       Object value ) {
            throw new UnsupportedOperationException(); 
        }
    
        public VariableResolver getIndexedVariableResolver( int index ) {
            throw new UnsupportedOperationException(); 
        }
    
        public VariableResolver createVariable( String name,
                                                Object value ) {
            throw new UnsupportedOperationException();            
        }
    
        public VariableResolver createVariable( String name,
                                                Object value,
                                                Class< ? > type ) {
            throw new UnsupportedOperationException();            
        }
    
        public VariableResolver getVariableResolver( String name ) {
            return null;
        }
    
        public boolean isResolveable( String name ) {
            return false;
        }
    
        public boolean isTarget( String name ) {
            return false;
        }
    
        public Set getKnownVariables() {
            return Collections.emptySet();
        }
    
        public void clear() { }
    
        public boolean isIndexedFactory() {
            return false;
        }

        public VariableResolver createIndexedVariable(int index,
                                                      String name,
                                                      Object value,
                                                      Class< ? > typee) {
            // TODO Auto-generated method stub
            return null;
        }

        public VariableResolver setIndexedVariableResolver(int index,
                                                           VariableResolver variableResolver) {
            // TODO Auto-generated method stub
            return null;
        }

        public VariableResolverFactory getNextFactory() {
            // TODO Auto-generated method stub
            return null;
        }

        public VariableResolverFactory setNextFactory(VariableResolverFactory resolverFactory) {
            // TODO Auto-generated method stub
            return null;
        }

        public int variableIndexOf(String name) {
            // TODO Auto-generated method stub
            return 0;
        }

        public boolean tiltFlag() {
            // TODO Auto-generated method stub
            return false;
        }

        public void setTiltFlag(boolean tilt) {
            // TODO Auto-generated method stub
        }
    }
    
    public static class PropertyHandlerFactoryFixer extends PropertyHandlerFactory {
        public static  Map getPropertyHandlerClass() {
            return propertyHandlerClass;
        }
    }

    private static class InterceptorMap implements Map {
        public int size() {
            return 1;
        }

        public boolean isEmpty() {
            return false;
        }

        public boolean containsKey(Object key) {
            return "Modify".equals(key);
        }

        public boolean containsValue(Object value) {
            return false;
        }

        public Interceptor get(Object key) {
            return new ModifyInterceptor();
        }

        public Interceptor put(String key, Interceptor value) {
            throw new UnsupportedOperationException();
        }

        public Interceptor remove(Object key) {
            throw new UnsupportedOperationException();
        }

        public void putAll(Map m) {
            throw new UnsupportedOperationException();
        }

        public void clear() {
            throw new UnsupportedOperationException();
        }

        public Set keySet() {
            return new HashSet() {{
                add("Modify");
            }};
        }

        public Collection values() {
            return new ArrayList() {{
                add(new ModifyInterceptor());
            }};
        }

        public Set> entrySet() {
            return new HashSet>() {{
                add(new Entry() {
                    public String getKey() {
                        return "Modify";
                    }
                    public Interceptor getValue() {
                        return new ModifyInterceptor();
                    }
                    public Interceptor setValue(Interceptor value) {
                        throw new UnsupportedOperationException();
                    }
                });
            }};
        }
    }

    @Override
    public String toString() {
        return expression;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy