/**
* Copyright 2010 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.
*/
package org.drools.base.mvel;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.drools.WorkingMemory;
import org.drools.common.InternalFactHandle;
import org.drools.reteoo.LeftTuple;
import org.drools.rule.Declaration;
import org.drools.runtime.KnowledgeRuntime;
import org.drools.spi.KnowledgeHelper;
import org.drools.spi.Tuple;
import org.mvel2.CompileException;
import org.mvel2.UnresolveablePropertyException;
import org.mvel2.integration.VariableResolver;
import org.mvel2.integration.impl.BaseVariableResolverFactory;
import org.mvel2.integration.impl.LocalVariableResolverFactory;
import org.mvel2.integration.impl.StaticMethodImportResolverFactory;
public class DroolsMVELFactory extends BaseVariableResolverFactory
implements
DroolsGlobalVariableMVELFactory,
DroolsLocalVariableMVELFactory,
LocalVariableResolverFactory,
Externalizable,
Cloneable {
private static final long serialVersionUID = 510l;
/**
* Holds the instance of the variables.
*/
private InternalFactHandle[] tupleObjects;
private KnowledgeHelper knowledgeHelper;
private Object object;
private Map localDeclarations;
private Map<String, Declaration> previousDeclarations;
// this is a cache for previously declared objects in case they
// get retracted during the execution of an MVEL consequence
private Map<String, Object> previousDeclarationsObjectCache;
private Map globals;
private WorkingMemory workingMemory;
private KnowledgeRuntime kruntime;
private Map localVariables;
private String[] inputIdentifiers;
public DroolsMVELFactory() {
previousDeclarationsObjectCache = new HashMap<String, Object>();
}
public DroolsMVELFactory(final Map previousDeclarations,
final Map localDeclarations,
final Map globals) {
this(previousDeclarations,
localDeclarations,
globals,
null);
}
public DroolsMVELFactory(final Map previousDeclarations,
final Map localDeclarations,
final Map globals,
final String[] inputIdentifiers) {
this.previousDeclarations = (Map<String, Declaration>) previousDeclarations;
this.localDeclarations = localDeclarations;
this.globals = globals;
this.inputIdentifiers = inputIdentifiers;
if (inputIdentifiers != null && MVELDebugHandler.isDebugMode()) {
for (int i = 0; i < inputIdentifiers.length; i++) {
isResolveable(inputIdentifiers[i]);
}
}
previousDeclarationsObjectCache = new HashMap<String, Object>();
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
tupleObjects = (InternalFactHandle[]) in.readObject();
knowledgeHelper = (KnowledgeHelper) in.readObject();
object = in.readObject();
localDeclarations = (Map) in.readObject();
previousDeclarations = (Map<String, Declaration>) in.readObject();
globals = (Map) in.readObject();
workingMemory = (WorkingMemory) in.readObject();
kruntime = (KnowledgeRuntime) in.readObject();
localVariables = (Map) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(tupleObjects);
out.writeObject(knowledgeHelper);
out.writeObject(object);
out.writeObject(localDeclarations);
out.writeObject(previousDeclarations);
out.writeObject(globals);
out.writeObject(workingMemory);
out.writeObject(kruntime);
out.writeObject(localVariables);
}
public static void addStaticImport(StaticMethodImportResolverFactory factory,
String staticImportEntry,
ClassLoader classLoader) {
int index = staticImportEntry.lastIndexOf('.');
String className = staticImportEntry.substring(0,
index);
String methodName = staticImportEntry.substring(index + 1);
try {
Class cls = classLoader.loadClass(className);
Method[] methods = cls.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName)) {
factory.createVariable(methodName,
methods[i]);
break;
}
}
}
catch (ClassNotFoundException e) {
throw new RuntimeException("Unable to dynamically load method '" + staticImportEntry + "'");
}
}
public Map getVariableResolvers() {
return this.variableResolvers;
}
public Object getObject() {
return this.object;
}
public WorkingMemory getWorkingMemory() {
return this.workingMemory;
}
public void setContext(final Tuple tuple,
final KnowledgeHelper knowledgeHelper,
final Object object,
final WorkingMemory workingMemory,
final Map<String, Object> variables ) {
if (tuple != null) {
this.tupleObjects = ((LeftTuple) tuple).toFactHandles();
}
this.knowledgeHelper = knowledgeHelper;
this.object = object;
this.workingMemory = workingMemory;
if (variables == null) {
if (this.localVariables == null) {
this.localVariables = new HashMap();
}
else {
this.localVariables.clear();
}
}
else {
this.localVariables = variables;
}
if( this.previousDeclarations != null ) {
// take a snapshot of required objects for variable resolution
for( Map.Entry<String, Declaration> entry : this.previousDeclarations.entrySet() ) {
Declaration decl = knowledgeHelper != null ? knowledgeHelper.getDeclaration( entry.getValue().getIdentifier() ) : entry.getValue();
this.previousDeclarationsObjectCache.put( entry.getKey(), getTupleObjectFor( decl ) );
}
}
}
public void setContext(final Tuple tuple,
final Object object,
final KnowledgeRuntime kruntime,
final Map<String, Object> variables ) {
if (tuple != null) {
this.tupleObjects = ((LeftTuple) tuple).toFactHandles();
}
this.object = object;
this.kruntime = kruntime;
if (variables == null) {
if (this.localVariables == null) {
this.localVariables = new HashMap();
} else {
this.localVariables.clear();
}
} else {
this.localVariables = variables;
}
}
private Object getTupleObjectFor(Declaration declaration) {
int i = declaration.getPattern().getOffset();
return ( i < this.tupleObjects.length ) ? this.tupleObjects[i].getObject() : null;
}
public KnowledgeHelper getKnowledgeHelper() {
return this.knowledgeHelper;
}
public Object getValue(final Declaration declaration) {
if( this.previousDeclarationsObjectCache.containsKey( declaration.getIdentifier() ) ) {
return this.previousDeclarationsObjectCache.get( declaration.getIdentifier() );
} else {
return getTupleObjectFor( declaration );
}
}
public InternalFactHandle getFactHandle(final Declaration declaration){
int i = declaration.getPattern().getOffset();
return this.tupleObjects[i];
}
public Object getValue(final String identifier) {
if (this.workingMemory != null) {
return this.workingMemory.getGlobal(identifier);
}
if (this.kruntime != null) {
return this.kruntime.getGlobal(identifier);
}
return null;
}
public Object getLocalValue(final String identifier) {
return this.localVariables.get(identifier);
}
public void setLocalValue(final String identifier,
final Object value) {
if (this.localVariables == null) {
this.localVariables = new HashMap();
}
this.localVariables.put(identifier,
value);
}
public VariableResolver createVariable(String name, Object value) {
VariableResolver vr;
try {
vr = getVariableResolver(name);
}
catch (UnresolveablePropertyException e) {
vr = null;
}
if (vr != null && vr.getType() != null) {
throw new CompileException("variable already defined within scope: " + vr.getType() + " " + name);
}
else {
return addResolver(name, new LocalVariableResolver(this, name), value);
}
}
public VariableResolver createVariable(String name,
Object value,
Class type) {
VariableResolver vr;
try {
vr = getVariableResolver(name);
}
catch (UnresolveablePropertyException e) {
vr = null;
}
if (vr != null && vr.getType() != null) {
throw new CompileException("variable already defined within scope: " + vr.getType() + " " + name);
}
else {
return addResolver(name, new LocalVariableResolver(this, name, type), value);
}
}
@Override
public VariableResolver createIndexedVariable(int index,
String name,
Object value) {
return super.createIndexedVariable(index,
name,
value);
}
@Override
public VariableResolver createIndexedVariable(int index,
String name,
Object value,
Class<?> type) {
return super.createIndexedVariable(index,
name,
value,
type);
}
public boolean isResolveable(String name) {
if (DroolsMVELKnowledgeHelper.DROOLS.equals(name)) {
addResolver(DroolsMVELKnowledgeHelper.DROOLS,
new DroolsMVELKnowledgeHelper(this));
return true;
}
else if (this.variableResolvers != null && this.variableResolvers.containsKey(name)) {
return true;
}
else if (DroolsMVELKnowledgeHelper.CONTEXT.equals(name)) {
addResolver(DroolsMVELKnowledgeHelper.CONTEXT,
new DroolsMVELKnowledgeHelper(this));
return true;
}
else if (this.previousDeclarations != null && this.previousDeclarations.containsKey(name)) {
addResolver(name,
new DroolsMVELPreviousDeclarationVariable((Declaration) this.previousDeclarations.get(name),
this));
return true;
}
else if (this.localDeclarations != null && this.localDeclarations.containsKey(name)) {
addResolver(name,
new DroolsMVELLocalDeclarationVariable((Declaration) this.localDeclarations.get(name),
this));
return true;
}
else if (this.globals.containsKey(name)) {
addResolver(name,
new DroolsMVELGlobalVariable(name,
(Class) this.globals.get(name),
this));
return true;
}
else if (nextFactory != null) {
return nextFactory.isResolveable(name);
}
return false;
}
public VariableResolver addResolver(String name,
VariableResolver vr,
Object value) {
if (this.variableResolvers == null) {
this.variableResolvers = new HashMap();
}
this.variableResolvers.put(name, vr);
vr.setValue(value);
return vr;
}
public VariableResolver addResolver(String name,
VariableResolver vr) {
if (this.variableResolvers == null) {
this.variableResolvers = new HashMap();
}
this.variableResolvers.put(name, vr);
return vr;
}
public boolean isTarget(String name) {
if (this.variableResolvers != null) {
return this.variableResolvers.containsKey(name);
}
else {
return false;
}
}
public Object clone() {
return new DroolsMVELFactory(this.previousDeclarations,
this.localDeclarations,
this.globals,
this.inputIdentifiers );
}
/**
* @return the localDeclarations
*/
public Map getLocalDeclarations() {
return localDeclarations;
}
/**
* @return the previousDeclarations
*/
public Map getPreviousDeclarations() {
return previousDeclarations;
}
/**
* @return the globals
*/
protected Map getGlobals() {
return globals;
}
/**
* @return the localVariables
*/
protected Map getLocalVariables() {
return localVariables;
}
}