/*
* 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;
import org.drools.core.base.mvel.MVELCompilationUnit.DroolsVarFactory;
import org.drools.core.reteoo.PropertySpecificUtil;
import org.drools.core.rule.TypeDeclaration;
import org.drools.core.spi.KnowledgeHelper;
import org.drools.core.util.bitmask.AllSetBitMask;
import org.drools.core.util.bitmask.BitMask;
import org.mvel2.ast.ASTNode;
import org.mvel2.ast.WithNode;
import org.mvel2.compiler.AccessorNode;
import org.mvel2.compiler.CompiledAccExpression;
import org.mvel2.compiler.ExecutableAccessor;
import org.mvel2.integration.Interceptor;
import org.mvel2.integration.VariableResolverFactory;
import org.mvel2.optimizers.impl.refl.nodes.MethodAccessor;
import org.mvel2.optimizers.impl.refl.nodes.SetterAccessor;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import static org.drools.core.reteoo.PropertySpecificUtil.allSetButTraitBitMask;
import static org.drools.core.reteoo.PropertySpecificUtil.getEmptyPropertyReactiveMask;
import static org.drools.core.reteoo.PropertySpecificUtil.setPropertyOnMask;
import static org.drools.core.util.ClassUtils.setter2property;
public class ModifyInterceptor
implements
Interceptor,
Externalizable {
private static final long serialVersionUID = 510l;
private BitMask modificationMask = AllSetBitMask.get();
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
modificationMask = (BitMask) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(modificationMask);
}
public int doBefore(ASTNode node,
VariableResolverFactory factory) {
return 0;
}
public int doAfter(Object value,
ASTNode node,
VariableResolverFactory factory) {
while ( factory != null && !(factory instanceof DroolsVarFactory)) {
factory = factory.getNextFactory();
}
if ( factory == null ) {
throw new RuntimeException( "Unable to find DroolsMVELIndexedFactory" );
}
KnowledgeHelper knowledgeHelper = ((DroolsVarFactory)factory).getKnowledgeHelper();
if (modificationMask.isSet(PropertySpecificUtil.TRAITABLE_BIT)) {
calculateModificationMask(knowledgeHelper, (WithNode)node);
}
knowledgeHelper.update(value, modificationMask, value.getClass());
return 0;
}
private void calculateModificationMask(KnowledgeHelper knowledgeHelper, WithNode node) {
Class<?> nodeClass = node.getEgressType();
TypeDeclaration typeDeclaration = knowledgeHelper.getWorkingMemory().getKnowledgeBase().getTypeDeclaration(nodeClass);
if (typeDeclaration == null || !typeDeclaration.isPropertyReactive()) {
modificationMask = allSetButTraitBitMask();
return;
}
List<String> settableProperties = typeDeclaration.getAccessibleProperties();
modificationMask = getEmptyPropertyReactiveMask(settableProperties.size());
// TODO: access parmValuePairs without reflection
WithNode.ParmValuePair[] parmValuePairs = getFieldValue(WithNode.class, "withExpressions", node);
for (WithNode.ParmValuePair parmValuePair : parmValuePairs) {
Method method = extractMethod(parmValuePair);
if (method == null) {
modificationMask = allSetButTraitBitMask();
return;
}
String propertyName = setter2property(method.getName());
if (propertyName != null) {
int index = settableProperties.indexOf(propertyName);
if (index >= 0) {
modificationMask = setPropertyOnMask(modificationMask, index);
}
}
List<String> modifiedProps = typeDeclaration.getTypeClassDef().getModifiedPropsByMethod(method);
if (modifiedProps != null) {
for (String modifiedProp : modifiedProps) {
int index = settableProperties.indexOf(modifiedProp);
if (index >= 0) {
modificationMask = setPropertyOnMask(modificationMask, index);
}
}
}
}
}
private Method extractMethod(WithNode.ParmValuePair parmValuePair) {
Serializable setExpression = parmValuePair.getSetExpression();
if (setExpression != null) {
SetterAccessor setterAccessor = (SetterAccessor)((CompiledAccExpression) setExpression).getAccessor();
return setterAccessor.getMethod();
} else {
ExecutableAccessor accessor = (ExecutableAccessor)parmValuePair.getStatement();
AccessorNode accessorNode = (AccessorNode)accessor.getNode().getAccessor();
MethodAccessor methodAccessor = (MethodAccessor)accessorNode.getNextNode();
return methodAccessor.getMethod();
}
}
private <T, V> V getFieldValue(Class<T> clazz, String fieldName, T object) {
try {
Field f = clazz.getDeclaredField(fieldName);
f.setAccessible(true);
return (V)f.get(object);
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}