/*
* 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.CompiledInvoker;
import org.drools.core.spi.Evaluator;
import org.drools.core.spi.InternalReadAccessor;
import org.drools.core.spi.PredicateExpression;
import org.drools.core.spi.Restriction;
import org.drools.core.spi.Tuple;
import org.drools.core.spi.Wireable;
import org.kie.internal.security.KiePolicyHelper;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.security.PrivilegedExceptionAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* A predicate can be written as a top level constraint or be nested
* inside inside a field constraint (and as so, must implement the
* Restriction interface).
*/
public class PredicateConstraint extends MutableTypeConstraint
implements
Restriction,
Wireable,
Externalizable {
private static final long serialVersionUID = 510l;
private PredicateExpression expression;
private Declaration[] requiredDeclarations;
private Declaration[] previousDeclarations;
private Declaration[] localDeclarations;
private List<PredicateConstraint> cloned = Collections.<PredicateConstraint> emptyList();
private static final Declaration[] EMPTY_DECLARATIONS = new Declaration[0];
public PredicateConstraint() {
this( null );
}
public PredicateConstraint(final PredicateExpression evaluator) {
this( evaluator,
null,
null );
}
public PredicateConstraint(final Declaration[] previousDeclarations,
final Declaration[] localDeclarations) {
this( null,
previousDeclarations,
localDeclarations );
}
public PredicateConstraint(final PredicateExpression expression,
final Declaration[] previousDeclarations,
final Declaration[] localDeclarations ) {
this.expression = expression;
if ( previousDeclarations == null ) {
this.previousDeclarations = PredicateConstraint.EMPTY_DECLARATIONS;
} else {
this.previousDeclarations = previousDeclarations;
}
if ( localDeclarations == null ) {
this.localDeclarations = PredicateConstraint.EMPTY_DECLARATIONS;
} else {
this.localDeclarations = localDeclarations;
}
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 {
super.readExternal( in );
this.expression = (PredicateExpression) in.readObject();
this.requiredDeclarations = (Declaration[]) in.readObject();
this.previousDeclarations = (Declaration[]) in.readObject();
this.localDeclarations = (Declaration[]) in.readObject();
this.cloned = (List<PredicateConstraint>) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal( out );
if ( this.expression instanceof CompiledInvoker ) {
out.writeObject( null );
} else {
out.writeObject( this.expression );
}
out.writeObject( this.requiredDeclarations );
out.writeObject( this.previousDeclarations );
out.writeObject( this.localDeclarations );
out.writeObject( this.cloned );
}
public Declaration[] getRequiredDeclarations() {
return this.requiredDeclarations;
}
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;
}
}
}
public void wire(Object object) {
setPredicateExpression( KiePolicyHelper.isPolicyEnabled() ? new SafePredicateExpression( (PredicateExpression) object ):(PredicateExpression) object );
for ( PredicateConstraint clone : this.cloned ) {
clone.wire( object );
}
}
public void setPredicateExpression(final PredicateExpression expression) {
this.expression = expression;
}
public PredicateExpression getPredicateExpression() {
return this.expression;
}
public boolean isTemporal() {
return false;
}
public String toString() {
return "[PredicateConstraint previousDeclarations=" + Arrays.toString(this.previousDeclarations) +
" localDeclarations=" + Arrays.toString(this.localDeclarations) + "]";
}
public int hashCode() {
return this.expression != null ? this.expression.hashCode() : 0;
}
public boolean equals(final Object object) {
if ( object == this ) {
return true;
}
if ( object == null || object.getClass() != PredicateConstraint.class ) {
return false;
}
final PredicateConstraint other = (PredicateConstraint) object;
if ( this.previousDeclarations.length != other.previousDeclarations.length ) {
return false;
}
if ( this.localDeclarations.length != other.localDeclarations.length ) {
return false;
}
for ( int i = 0, length = this.previousDeclarations.length; i < length; i++ ) {
if ( this.previousDeclarations[i].getPattern().getOffset() != other.previousDeclarations[i].getPattern().getOffset() ) {
return false;
}
if ( !this.previousDeclarations[i].getExtractor().equals( other.previousDeclarations[i].getExtractor() ) ) {
return false;
}
}
for ( int i = 0, length = this.localDeclarations.length; i < length; i++ ) {
if ( this.localDeclarations[i].getPattern().getOffset() != other.localDeclarations[i].getPattern().getOffset() ) {
return false;
}
if ( !this.localDeclarations[i].getExtractor().equals( other.localDeclarations[i].getExtractor() ) ) {
return false;
}
}
return this.expression.equals( other.expression );
}
public ContextEntry createContextEntry() {
PredicateContextEntry ctx = new PredicateContextEntry();
ctx.dialectContext = this.expression.createContext();
return ctx;
}
public boolean isAllowed(final InternalFactHandle handle,
final InternalWorkingMemory workingMemory) {
try {
return this.expression.evaluate( handle,
null,
this.previousDeclarations,
this.localDeclarations,
workingMemory,
null ); //((PredicateContextEntry) ctx).dialectContext );
} catch ( final Exception e ) {
throw new RuntimeException( "Exception executing predicate " + this.expression,
e );
}
}
public boolean isAllowed(InternalReadAccessor extractor,
InternalFactHandle handle,
InternalWorkingMemory workingMemory,
ContextEntry context) {
throw new UnsupportedOperationException( "Method not supported. Please contact development team." );
}
public boolean isAllowedCachedLeft(final ContextEntry context,
final InternalFactHandle handle) {
try {
final PredicateContextEntry ctx = (PredicateContextEntry) context;
return this.expression.evaluate( handle,
ctx.tuple,
this.previousDeclarations,
this.localDeclarations,
ctx.workingMemory,
ctx.dialectContext );
} catch ( final Exception e ) {
throw new RuntimeException( "Exception executing predicate " + this.expression,
e );
}
}
public boolean isAllowedCachedRight(final Tuple tuple,
final ContextEntry context) {
try {
final PredicateContextEntry ctx = (PredicateContextEntry) context;
return this.expression.evaluate( ctx.rightHandle,
tuple,
this.previousDeclarations,
this.localDeclarations,
ctx.workingMemory,
ctx.dialectContext );
} catch ( final Exception e ) {
throw new RuntimeException( "Exception executing predicate " + this.expression,
e );
}
}
public PredicateConstraint 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();
}
PredicateConstraint clone = new PredicateConstraint( this.expression,
previous,
local );
if ( this.cloned == Collections.EMPTY_LIST ) {
this.cloned = new ArrayList<PredicateConstraint>( 1 );
}
this.cloned.add( clone );
return clone;
}
public static class PredicateContextEntry
implements
ContextEntry {
private static final long serialVersionUID = 510l;
public Tuple tuple;
public InternalFactHandle rightHandle;
public InternalWorkingMemory workingMemory;
public Object dialectContext;
private ContextEntry entry;
public PredicateContextEntry() {
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
tuple = (LeftTuple) in.readObject();
rightHandle = (InternalFactHandle) in.readObject();
workingMemory = (InternalWorkingMemory) in.readObject();
dialectContext = in.readObject();
entry = (ContextEntry) in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( tuple );
out.writeObject( rightHandle );
out.writeObject( workingMemory );
out.writeObject( dialectContext );
out.writeObject( entry );
}
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.rightHandle = handle;
}
public void updateFromTuple(final InternalWorkingMemory workingMemory,
final Tuple tuple) {
this.workingMemory = workingMemory;
this.tuple = tuple;
}
public void resetTuple() {
this.tuple = null;
}
public void resetFactHandle() {
this.rightHandle = null;
}
}
public Evaluator getEvaluator() {
return null;
}
public static class SafePredicateExpression implements PredicateExpression, Serializable {
private static final long serialVersionUID = -4570820770000524010L;
private PredicateExpression delegate;
public SafePredicateExpression(PredicateExpression delegate) {
this.delegate = delegate;
}
public Object createContext() {
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return delegate.createContext();
}
}, KiePolicyHelper.getAccessContext());
}
public boolean evaluate(final InternalFactHandle handle,
final Tuple tuple,
final Declaration[] previousDeclarations,
final Declaration[] localDeclarations,
final WorkingMemory workingMemory,
final Object context) throws Exception {
return AccessController.doPrivileged(new PrivilegedExceptionAction<Boolean>() {
@Override
public Boolean run() throws Exception {
return delegate.evaluate(handle, tuple, previousDeclarations, localDeclarations, workingMemory, context);
}
}, KiePolicyHelper.getAccessContext());
}
}
}