/*
* 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<String, Interceptor> 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<String, Class< ? >> primitivesMap = new HashMap<String, Class< ? >>();
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<String> 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<String, Class< ? >> 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<String> 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<Class, PropertyHandler> getPropertyHandlerClass() {
return propertyHandlerClass;
}
}
private static class InterceptorMap implements Map<String, Interceptor> {
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<? extends String, ? extends Interceptor> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
public Set<String> keySet() {
return new HashSet<String>() {{
add("Modify");
}};
}
public Collection<Interceptor> values() {
return new ArrayList<Interceptor>() {{
add(new ModifyInterceptor());
}};
}
public Set<Entry<String, Interceptor>> entrySet() {
return new HashSet<Entry<String, Interceptor>>() {{
add(new Entry<String, Interceptor>() {
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;
}
}