/*
* 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.WorkingMemory;
import org.drools.core.base.mvel.MVELCompilationUnit.DroolsVarFactory;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.definitions.InternalKnowledgePackage;
import org.drools.core.definitions.rule.impl.RuleImpl;
import org.drools.core.rule.Declaration;
import org.drools.core.rule.MVELDialectRuntimeData;
import org.drools.core.spi.MvelAccumulator;
import org.drools.core.spi.Tuple;
import org.drools.core.util.MVELSafeHelper;
import org.mvel2.integration.VariableResolverFactory;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* An MVEL accumulator implementation
*/
public class MVELAccumulator
implements MVELCompileable, MvelAccumulator, Externalizable {
private static final long serialVersionUID = 510l;
MVELCompilationUnit initUnit;
MVELCompilationUnit actionUnit;
MVELCompilationUnit reverseUnit;
MVELCompilationUnit resultUnit;
private Serializable init;
private Serializable action;
private Serializable reverse;
private Serializable result;
public MVELAccumulator() {
}
public MVELAccumulator(final MVELCompilationUnit initUnit,
final MVELCompilationUnit actionUnit,
final MVELCompilationUnit reverseUnit,
final MVELCompilationUnit resultUnit) {
super();
this.initUnit = initUnit;
this.actionUnit = actionUnit;
this.reverseUnit = reverseUnit;
this.resultUnit = resultUnit;
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
initUnit = (MVELCompilationUnit) in.readObject();
actionUnit = (MVELCompilationUnit) in.readObject();
reverseUnit = (MVELCompilationUnit) in.readObject();
resultUnit = (MVELCompilationUnit) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( initUnit );
out.writeObject( actionUnit );
out.writeObject( reverseUnit );
out.writeObject( resultUnit );
}
public void compile(MVELDialectRuntimeData runtimeData) {
compile(runtimeData, null);
}
public void compile(MVELDialectRuntimeData runtimeData, RuleImpl rule) {
init = initUnit.getCompiledExpression( runtimeData );
action = actionUnit.getCompiledExpression( runtimeData );
result = resultUnit.getCompiledExpression( runtimeData );
if ( reverseUnit != null ) {
reverse = reverseUnit.getCompiledExpression( runtimeData, rule != null ? rule.toRuleNameAndPathString() : null );
}
}
/* (non-Javadoc)
* @see org.kie.spi.Accumulator#createContext()
*/
public Serializable createContext() {
Map<Integer, Object[]> shadow = null;
if ( this.reverse != null ) {
shadow = new HashMap<Integer, Object[]>();
}
return new MVELAccumulatorContext( shadow );
}
/* (non-Javadoc)
* @see org.kie.spi.Accumulator#init(java.lang.Object, org.kie.spi.Tuple, org.kie.rule.Declaration[], org.kie.WorkingMemory)
*/
public void init(Object workingMemoryContext,
Object context,
Tuple tuple,
Declaration[] declarations,
WorkingMemory workingMemory) throws Exception {
Object[] localVars = new Object[initUnit.getOtherIdentifiers().length];
MVELAccumulatorFactoryContext factoryContext = (MVELAccumulatorFactoryContext)workingMemoryContext;
VariableResolverFactory factory = factoryContext.getInitFactory();
initUnit.updateFactory( null, tuple, localVars, (InternalWorkingMemory) workingMemory, workingMemory.getGlobalResolver(), factory );
InternalKnowledgePackage pkg = workingMemory.getKnowledgeBase().getPackage( "MAIN" );
if ( pkg != null ) {
MVELDialectRuntimeData data = (MVELDialectRuntimeData) pkg.getDialectRuntimeRegistry().getDialectData( "mvel" );
factory.setNextFactory( data.getFunctionFactory() );
}
MVELSafeHelper.getEvaluator().executeExpression( this.init,
null,
factory );
DroolsVarFactory df = ( DroolsVarFactory ) factory.getNextFactory();
if ( localVars.length > 0 ) {
for ( int i = 0; i < df.getOtherVarsLength(); i++ ) {
localVars[i] = factory.getIndexedVariableResolver( df.getOtherVarsPos() + i ).getValue();
}
}
((MVELAccumulatorContext) context).setVariables( localVars );
}
/* (non-Javadoc)
* @see org.kie.spi.Accumulator#accumulate(java.lang.Object, org.kie.spi.Tuple, org.kie.common.InternalFactHandle, org.kie.rule.Declaration[], org.kie.rule.Declaration[], org.kie.WorkingMemory)
*/
public void accumulate(Object workingMemoryContext,
Object context,
Tuple tuple,
InternalFactHandle handle,
Declaration[] declarations,
Declaration[] innerDeclarations,
WorkingMemory workingMemory) throws Exception {
Object[] localVars = ((MVELAccumulatorContext) context).getVariables();
MVELAccumulatorFactoryContext factoryContext = (MVELAccumulatorFactoryContext)workingMemoryContext;
VariableResolverFactory factory = factoryContext.getActionFactory();
actionUnit.updateFactory( handle, tuple, localVars, (InternalWorkingMemory) workingMemory, workingMemory.getGlobalResolver(), factory );
DroolsVarFactory df = ( DroolsVarFactory ) factory.getNextFactory();
if ( reverse != null ) {
Object[] shadow = new Object [df.getOtherVarsPos()];
for ( int i = 0; i < df.getOtherVarsPos(); i++ ) {
shadow[i] = factory.getIndexedVariableResolver( i ).getValue();
}
// SNAPSHOT variable values
((MVELAccumulatorContext) context).getShadow().put( handle.getId(), shadow);
}
MVELSafeHelper.getEvaluator().executeExpression( this.action,
null,
factory );
if ( localVars.length > 0 ) {
for ( int i = 0; i < df.getOtherVarsLength(); i++ ) {
localVars[i] = factory.getIndexedVariableResolver( df.getOtherVarsPos() + i ).getValue();
}
}
((MVELAccumulatorContext) context).setVariables( localVars );
}
public void reverse(Object workingMemoryContext,
Object context,
Tuple leftTuple,
InternalFactHandle handle,
Declaration[] declarations,
Declaration[] innerDeclarations,
WorkingMemory workingMemory) throws Exception {
Object[] localVars = ((MVELAccumulatorContext) context).getVariables();
MVELAccumulatorFactoryContext factoryContext = (MVELAccumulatorFactoryContext)workingMemoryContext;
VariableResolverFactory factory = factoryContext.getActionFactory();
DroolsVarFactory df = ( DroolsVarFactory ) factory.getNextFactory();
Object[] vars = ((MVELAccumulatorContext) context).getShadow().remove( handle.getId() );
for ( int i = 0; i < df.getOtherVarsPos(); i++ ) {
factory.getIndexedVariableResolver( i ).setValue(vars[i]);
}
if ( localVars.length > 0 ) {
for ( int i = 0; i < df.getOtherVarsLength(); i++ ) {
factory.getIndexedVariableResolver( df.getOtherVarsPos() + i ).setValue( localVars[i] );
}
}
// reverseUnit.updateFactory( null, null, handle.getObject(), (LeftTuple) leftTuple, localVars, (InternalWorkingMemory) workingMemory, workingMemory.getGlobalResolver(), factory );
MVELSafeHelper.getEvaluator().executeExpression( this.reverse,
null,
factory );
if ( localVars.length > 0 ) {
for ( int i = 0; i < df.getOtherVarsLength(); i++ ) {
localVars[i] = factory.getIndexedVariableResolver( df.getOtherVarsPos() + i ).getValue();
}
}
((MVELAccumulatorContext) context).setVariables( localVars );
}
/* (non-Javadoc)
* @see org.kie.spi.Accumulator#getResult(java.lang.Object, org.kie.spi.Tuple, org.kie.rule.Declaration[], org.kie.WorkingMemory)
*/
public Object getResult(Object workingMemoryContext,
Object context,
Tuple tuple,
Declaration[] declarations,
WorkingMemory workingMemory) throws Exception {
Object[] localVars = ((MVELAccumulatorContext) context).getVariables();
MVELAccumulatorFactoryContext factoryContext = (MVELAccumulatorFactoryContext)workingMemoryContext;
VariableResolverFactory factory = factoryContext.getResultFactory();
resultUnit.updateFactory( null, tuple, localVars, (InternalWorkingMemory) workingMemory, workingMemory.getGlobalResolver(), factory );
return MVELSafeHelper.getEvaluator().executeExpression( this.result, null, factory );
}
public boolean supportsReverse() {
return this.reverse != null;
}
public Object createWorkingMemoryContext() {
return new MVELAccumulatorFactoryContext(initUnit.createFactory(),
actionUnit.createFactory(),
resultUnit.createFactory() );
}
@Override
public Declaration[] getRequiredDeclarations() {
Set<Declaration> declarationSet = new HashSet<Declaration>();
if ( initUnit != null ) {
declarationSet.addAll( Arrays.asList( initUnit.getPreviousDeclarations() ) );
}
if ( actionUnit != null ) {
declarationSet.addAll( Arrays.asList( actionUnit.getPreviousDeclarations() ) );
}
if ( resultUnit != null ) {
declarationSet.addAll( Arrays.asList( resultUnit.getPreviousDeclarations() ) );
}
if ( reverseUnit != null ) {
declarationSet.addAll( Arrays.asList( reverseUnit.getPreviousDeclarations() ) );
}
if ( ! declarationSet.isEmpty() ) {
return declarationSet.toArray( new Declaration[ declarationSet.size() ] );
} else {
return new Declaration[ 0 ];
}
}
private static class MVELAccumulatorFactoryContext {
VariableResolverFactory initFactory;
VariableResolverFactory actionFactory;
VariableResolverFactory resultFactory;
public MVELAccumulatorFactoryContext(VariableResolverFactory initFactory,
VariableResolverFactory actionFactory,
VariableResolverFactory resultFactory) {
this.initFactory = initFactory;
this.actionFactory = actionFactory;
this.resultFactory = resultFactory;
}
public VariableResolverFactory getInitFactory() {
return initFactory;
}
public VariableResolverFactory getActionFactory() {
return actionFactory;
}
public VariableResolverFactory getResultFactory() {
return resultFactory;
}
}
private static class MVELAccumulatorContext
implements
Serializable {
private static final long serialVersionUID = 510l;
private Object[] variables;
private Map<Integer, Object[]> shadow;
public MVELAccumulatorContext(Map<Integer, Object[]> shadow) {
this.shadow = shadow;
}
public Object[] getVariables() {
return variables;
}
public void setVariables(Object[] variables) {
this.variables = variables;
}
public Map<Integer, Object[]> getShadow() {
return shadow;
}
public void setShadow(Map<Integer, Object[]> shadow) {
this.shadow = shadow;
}
}
}