/* * Copyright 2005 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.rule; import org.drools.core.WorkingMemory; import org.drools.core.common.InternalFactHandle; import org.drools.core.common.InternalWorkingMemory; import org.drools.core.reteoo.LeftTuple; import org.drools.core.spi.AcceptsReadAccessor; import org.drools.core.spi.CompiledInvoker; import org.drools.core.spi.Evaluator; import org.drools.core.spi.FieldValue; import org.drools.core.spi.InternalReadAccessor; import org.drools.core.spi.ReadAccessor; import org.drools.core.spi.Restriction; import org.drools.core.spi.ReturnValueExpression; import org.drools.core.spi.ReturnValueExpression.SafeReturnValueExpression; import org.drools.core.spi.Tuple; import org.drools.core.spi.Wireable; import org.kie.internal.security.KiePolicyHelper; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; public class ReturnValueRestriction implements Restriction, AcceptsReadAccessor, Wireable { private static final long serialVersionUID = 510l; private ReturnValueExpression expression; private String[] requiredGlobals; private Declaration[] requiredDeclarations; private Declaration[] previousDeclarations; private Declaration[] localDeclarations; private Evaluator evaluator; private InternalReadAccessor readAccessor; private static final Declaration[] noRequiredDeclarations = new Declaration[]{}; private static final String[] noRequiredGlobals = new String[]{}; private List<ReturnValueRestriction> cloned = Collections.<ReturnValueRestriction> emptyList(); public ReturnValueRestriction() { } public ReturnValueRestriction(final InternalReadAccessor fieldExtractor, final Declaration[] previousDeclarations, final Declaration[] localDeclarations, final String[] requiredGlobals, final Evaluator evaluator) { this( fieldExtractor, null, previousDeclarations, localDeclarations, requiredGlobals, evaluator ); } public ReturnValueRestriction(final InternalReadAccessor fieldExtractor, final ReturnValueExpression returnValueExpression, final Declaration[] previousDeclarations, final Declaration[] localDeclarations, final String[] requiredGlobals, final Evaluator evaluator) { this.expression = returnValueExpression; this.readAccessor = fieldExtractor; if ( previousDeclarations != null ) { this.previousDeclarations = previousDeclarations; } else { this.previousDeclarations = ReturnValueRestriction.noRequiredDeclarations; } if ( localDeclarations != null ) { this.localDeclarations = localDeclarations; } else { this.localDeclarations = ReturnValueRestriction.noRequiredDeclarations; } if ( requiredGlobals != null ) { this.requiredGlobals = requiredGlobals; } else { this.requiredGlobals = ReturnValueRestriction.noRequiredGlobals; } this.evaluator = evaluator; this.requiredDeclarations = new Declaration[this.previousDeclarations.length + this.localDeclarations.length]; System.arraycopy( this.previousDeclarations, 0, this.requiredDeclarations, 0, this.previousDeclarations.length ); System.arraycopy( this.localDeclarations, 0, this.requiredDeclarations, this.previousDeclarations.length, this.localDeclarations.length ); } @SuppressWarnings("unchecked") public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { expression = (ReturnValueExpression) in.readObject(); requiredGlobals = (String[]) in.readObject(); requiredDeclarations = (Declaration[]) in.readObject(); previousDeclarations = (Declaration[]) in.readObject(); localDeclarations = (Declaration[]) in.readObject(); evaluator = (Evaluator) in.readObject(); readAccessor = (InternalReadAccessor) in.readObject(); this.cloned = (List<ReturnValueRestriction>) in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { if ( this.expression instanceof CompiledInvoker ) { out.writeObject( null ); } else { out.writeObject( this.expression ); } out.writeObject( requiredGlobals ); out.writeObject( requiredDeclarations ); out.writeObject( previousDeclarations ); out.writeObject( localDeclarations ); out.writeObject( evaluator ); out.writeObject( readAccessor ); out.writeObject( this.cloned ); } public void setReadAccessor(InternalReadAccessor readAccessor) { this.readAccessor = readAccessor; } public Declaration[] getRequiredDeclarations() { return this.requiredDeclarations; } public Declaration[] getPreviousDeclarations() { return this.previousDeclarations; } public Declaration[] getLocalDeclarations() { return this.localDeclarations; } public String[] getRequiredGlobals() { return this.requiredGlobals; } public void replaceDeclaration(Declaration oldDecl, Declaration newDecl) { for ( int i = 0; i < this.requiredDeclarations.length; i++ ) { if ( this.requiredDeclarations[i].equals( oldDecl ) ) { this.requiredDeclarations[i] = newDecl; } } for ( int i = 0; i < this.previousDeclarations.length; i++ ) { if ( this.previousDeclarations[i].equals( oldDecl ) ) { this.previousDeclarations[i] = newDecl; } } for ( int i = 0; i < this.localDeclarations.length; i++ ) { if ( this.localDeclarations[i].equals( oldDecl ) ) { this.localDeclarations[i] = newDecl; } } this.expression.replaceDeclaration( oldDecl, newDecl ); } public void wire(Object object) { setReturnValueExpression( KiePolicyHelper.isPolicyEnabled() ? new SafeReturnValueExpression( (ReturnValueExpression) object ) : (ReturnValueExpression) object ); for ( ReturnValueRestriction clone : this.cloned ) { clone.wire( object ); } } public void setReturnValueExpression(final ReturnValueExpression expression) { this.expression = expression; } public ReturnValueExpression getExpression() { return this.expression; } public Evaluator getEvaluator() { return this.evaluator; } public boolean isTemporal() { return this.evaluator.isTemporal(); } public boolean isAllowed(final InternalReadAccessor readAccessor, final InternalFactHandle handle, final Tuple tuple, final WorkingMemory workingMemory, final ContextEntry context) { try { return this.evaluator.evaluate( (InternalWorkingMemory) workingMemory, this.readAccessor, handle, this.expression.evaluate( handle, tuple, this.previousDeclarations, this.localDeclarations, workingMemory, ((ReturnValueContextEntry) context).dialectContext ) ); } catch ( final Exception e ) { throw new RuntimeException( e ); } } public boolean isAllowed(final InternalReadAccessor extractor, final InternalFactHandle handle, final InternalWorkingMemory workingMemory, final ContextEntry context) { try { ReturnValueContextEntry ctx = (ReturnValueContextEntry) context; FieldValue value = this.expression.evaluate( handle, null, this.previousDeclarations, this.localDeclarations, workingMemory, ctx.dialectContext ); return this.evaluator.evaluate( workingMemory, this.readAccessor, handle, value ); } catch ( final Exception e ) { throw new RuntimeException( e ); } } public boolean isAllowedCachedLeft(final ContextEntry context, final InternalFactHandle handle) { try { ReturnValueContextEntry ctx = (ReturnValueContextEntry) context; FieldValue value = this.expression.evaluate( handle, ctx.tuple, this.previousDeclarations, this.localDeclarations, ctx.workingMemory, ctx.dialectContext ); return this.evaluator.evaluate( ctx.workingMemory, this.readAccessor, handle, value ); } catch ( final Exception e ) { throw new RuntimeException( e ); } } public boolean isAllowedCachedRight(final Tuple tuple, final ContextEntry context) { try { ReturnValueContextEntry ctx = (ReturnValueContextEntry) context; FieldValue value = this.expression.evaluate( ctx.handle, tuple, this.previousDeclarations, this.localDeclarations, ctx.workingMemory, ctx.dialectContext ); return this.evaluator.evaluate( ctx.workingMemory, this.readAccessor, ctx.handle, value ); } catch ( final Exception e ) { throw new RuntimeException( e ); } } public int hashCode() { final int PRIME = 31; int result = 1; result = PRIME * result + this.evaluator.hashCode(); result = PRIME * result + ((this.expression != null) ? this.expression.hashCode() : 0); result = PRIME * result + ReturnValueRestriction.hashCode( this.localDeclarations ); result = PRIME * result + ReturnValueRestriction.hashCode( this.previousDeclarations ); result = PRIME * result + ReturnValueRestriction.hashCode( this.requiredGlobals ); return result; } public boolean equals(final Object object) { if ( object == this ) { return true; } if ( object == null || object.getClass() != ReturnValueRestriction.class ) { return false; } final ReturnValueRestriction other = (ReturnValueRestriction) object; if ( this.localDeclarations.length != other.localDeclarations.length ) { return false; } if ( this.previousDeclarations.length != other.previousDeclarations.length ) { return false; } if ( this.requiredGlobals.length != other.requiredGlobals.length ) { return false; } if ( !Arrays.equals( this.localDeclarations, other.localDeclarations ) ) { return false; } if ( !Arrays.equals( this.previousDeclarations, other.previousDeclarations ) ) { return false; } return Arrays.equals(this.requiredGlobals, other.requiredGlobals) && this.evaluator.equals(other.evaluator) && this.expression.equals(other.expression); } private static int hashCode(final Object[] array) { final int PRIME = 31; if ( array == null ) { return 0; } int result = 1; for (Object anArray : array) { result = PRIME * result + (anArray == null ? 0 : anArray.hashCode()); } return result; } public ContextEntry createContextEntry() { ReturnValueContextEntry ctx = new ReturnValueContextEntry( this.readAccessor, this.previousDeclarations, this.localDeclarations ); ctx.dialectContext = this.expression.createContext(); return ctx; } public ReturnValueRestriction clone() { Declaration[] previous = new Declaration[this.previousDeclarations.length]; for ( int i = 0; i < previous.length; i++ ) { previous[i] = this.previousDeclarations[i].clone(); } Declaration[] local = new Declaration[this.localDeclarations.length]; for ( int i = 0; i < local.length; i++ ) { local[i] = this.localDeclarations[i].clone(); } ReturnValueRestriction clone = new ReturnValueRestriction( this.readAccessor, this.expression, previous, local, this.requiredGlobals, this.evaluator ); if ( this.cloned == Collections.EMPTY_LIST ) { this.cloned = new ArrayList<ReturnValueRestriction>( 1 ); } this.cloned.add( clone ); return clone; } public static class ReturnValueContextEntry implements ContextEntry { private static final long serialVersionUID = 510l; public ReadAccessor fieldExtractor; public InternalFactHandle handle; public Tuple tuple; public InternalWorkingMemory workingMemory; public Declaration[] previousDeclarations; public Declaration[] localDeclarations; private ContextEntry entry; public Object dialectContext; public ReturnValueContextEntry() { } public ReturnValueContextEntry(final ReadAccessor fieldExtractor, final Declaration[] previousDeclarations, final Declaration[] localDeclarations) { this.fieldExtractor = fieldExtractor; this.previousDeclarations = previousDeclarations; this.localDeclarations = localDeclarations; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { fieldExtractor = (ReadAccessor) in.readObject(); handle = (InternalFactHandle) in.readObject(); tuple = (LeftTuple) in.readObject(); workingMemory = (InternalWorkingMemory) in.readObject(); previousDeclarations = (Declaration[]) in.readObject(); localDeclarations = (Declaration[]) in.readObject(); entry = (ContextEntry) in.readObject(); dialectContext = in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject( fieldExtractor ); out.writeObject( handle ); out.writeObject( tuple ); out.writeObject( workingMemory ); out.writeObject( previousDeclarations ); out.writeObject( localDeclarations ); out.writeObject( entry ); out.writeObject( dialectContext ); } public ContextEntry getNext() { return this.entry; } public void setNext(final ContextEntry entry) { this.entry = entry; } public void updateFromFactHandle(final InternalWorkingMemory workingMemory, final InternalFactHandle handle) { this.workingMemory = workingMemory; this.handle = handle; } public void updateFromTuple(final InternalWorkingMemory workingMemory, final Tuple tuple) { this.workingMemory = workingMemory; this.tuple = tuple; } /* (non-Javadoc) * @see org.kie.rule.ReturnValueContextEntry#getFieldExtractor() */ public ReadAccessor getFieldExtractor() { return this.fieldExtractor; } /* (non-Javadoc) * @see org.kie.rule.ReturnValueContextEntry#getObject() */ public InternalFactHandle getHandle() { return this.handle; } /* (non-Javadoc) * @see org.kie.rule.ReturnValueContextEntry#getRequiredDeclarations() */ public Declaration[] getPreviousDeclarations() { return this.previousDeclarations; } public Declaration[] getLocalDeclarations() { return this.localDeclarations; } /* (non-Javadoc) * @see org.kie.rule.ReturnValueContextEntry#getWorkingMemory() */ public InternalWorkingMemory getWorkingMemory() { return this.workingMemory; } public void resetTuple() { this.tuple = null; } public void resetFactHandle() { this.handle = null; } } }