/**
* 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.rule;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Arrays;
import org.drools.RuntimeDroolsException;
import org.drools.common.InternalFactHandle;
import org.drools.common.InternalWorkingMemory;
import org.drools.core.util.ArrayUtils;
import org.drools.reteoo.LeftTuple;
import org.drools.spi.AlphaNodeFieldConstraint;
import org.drools.spi.BetaNodeFieldConstraint;
import org.drools.spi.Constraint;
/**
* A superclass for all composite constraints, like "OR" and "AND"
*
* @author etirelli
*/
public abstract class AbstractCompositeConstraint extends MutableTypeConstraint {
protected AlphaNodeFieldConstraint[] alphaConstraints = new AlphaNodeFieldConstraint[0];
protected BetaNodeFieldConstraint[] betaConstraints = new BetaNodeFieldConstraint[0];
protected Declaration[] requiredDeclarations = new Declaration[0];
public AbstractCompositeConstraint() {
super();
this.setType( Constraint.ConstraintType.ALPHA );
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
alphaConstraints = (AlphaNodeFieldConstraint[])in.readObject();
betaConstraints = (BetaNodeFieldConstraint[])in.readObject();
requiredDeclarations = (Declaration[])in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeObject(alphaConstraints);
out.writeObject(betaConstraints);
out.writeObject(requiredDeclarations);
}
public AlphaNodeFieldConstraint[] getAlphaConstraints() {
return alphaConstraints;
}
public BetaNodeFieldConstraint[] getBetaConstraints() {
return betaConstraints;
}
public boolean isTemporal() {
for( AlphaNodeFieldConstraint c : this.alphaConstraints ) {
if( c.isTemporal() ) {
return true;
}
}
for( BetaNodeFieldConstraint c : this.betaConstraints ) {
if( c.isTemporal() ) {
return true;
}
}
return false;
}
/**
* Adds an alpha constraint to the multi field OR constraint
*
* @param constraint
*/
public void addAlphaConstraint(AlphaNodeFieldConstraint constraint) {
if ( constraint != null ) {
AlphaNodeFieldConstraint[] tmp = this.alphaConstraints;
this.alphaConstraints = new AlphaNodeFieldConstraint[tmp.length + 1];
System.arraycopy( tmp,
0,
this.alphaConstraints,
0,
tmp.length );
this.alphaConstraints[this.alphaConstraints.length - 1] = constraint;
this.updateRequiredDeclarations( constraint );
}
}
/**
* Adds a beta constraint to this multi field OR constraint
* @param constraint
*/
public void addBetaConstraint(BetaNodeFieldConstraint constraint) {
if ( constraint != null ) {
BetaNodeFieldConstraint[] tmp = this.betaConstraints;
this.betaConstraints = new BetaNodeFieldConstraint[tmp.length + 1];
System.arraycopy( tmp,
0,
this.betaConstraints,
0,
tmp.length );
this.betaConstraints[this.betaConstraints.length - 1] = constraint;
this.updateRequiredDeclarations( constraint );
this.setType( Constraint.ConstraintType.BETA );
}
}
/**
* Adds a constraint too all lists it belongs to by checking for its type
* @param constraint
*/
public void addConstraint(Constraint constraint) {
if ( ConstraintType.ALPHA.equals(constraint.getType())) {
this.addAlphaConstraint( (AlphaNodeFieldConstraint) constraint );
} else if ( ConstraintType.BETA.equals(constraint.getType())) {
this.addBetaConstraint( (BetaNodeFieldConstraint) constraint );
} else {
throw new RuntimeDroolsException( "Constraint type MUST be known in advance.");
}
}
/**
* Updades the cached required declaration array
*
* @param constraint
*/
protected void updateRequiredDeclarations(Constraint constraint) {
Declaration[] decs = constraint.getRequiredDeclarations();
if ( decs != null && decs.length > 0 ) {
for ( int i = 0; i < decs.length; i++ ) {
Declaration dec = decs[i];
// check for duplications
for ( int j = 0; j < this.requiredDeclarations.length; j++ ) {
if ( dec.equals( this.requiredDeclarations[j] ) ) {
dec = null;
break;
}
}
if ( dec != null ) {
Declaration[] tmp = this.requiredDeclarations;
this.requiredDeclarations = new Declaration[tmp.length + 1];
System.arraycopy( tmp,
0,
this.requiredDeclarations,
0,
tmp.length );
this.requiredDeclarations[this.requiredDeclarations.length - 1] = dec;
}
}
}
}
/**
* {@inheritDoc}
*/
public Declaration[] getRequiredDeclarations() {
return this.requiredDeclarations;
}
/**
* {@inheritDoc}
*/
public void replaceDeclaration(Declaration oldDecl,
Declaration newDecl) {
for ( int i = 0; i < this.alphaConstraints.length; i++ ) {
this.alphaConstraints[i].replaceDeclaration( oldDecl,
newDecl );
}
for ( int i = 0; i < this.betaConstraints.length; i++ ) {
this.betaConstraints[i].replaceDeclaration( oldDecl,
newDecl );
}
for ( int i = 0; i < this.requiredDeclarations.length; i++ ) {
if ( this.requiredDeclarations[i] == oldDecl ) {
this.requiredDeclarations[i] = newDecl;
}
}
}
/**
* {@inheritDoc}
*/
public ContextEntry createContextEntry() {
return new MultiFieldConstraintContextEntry( this.alphaConstraints,
this.betaConstraints );
}
public int hashCode() {
final int PRIME = 31;
int result = 1;
result = PRIME * result + ArrayUtils.hashCode( this.alphaConstraints );
result = PRIME * result + ArrayUtils.hashCode( this.betaConstraints );
result = PRIME * result + ArrayUtils.hashCode( this.requiredDeclarations );
return result;
}
public boolean equals(final Object object) {
if ( this == object ) {
return true;
}
if ( object == null || object.getClass() != AbstractCompositeConstraint.class ) {
return false;
}
final AbstractCompositeConstraint other = (AbstractCompositeConstraint) object;
return Arrays.equals( this.alphaConstraints,
other.alphaConstraints ) && Arrays.equals( this.betaConstraints,
other.betaConstraints ) && Arrays.equals( this.requiredDeclarations,
other.requiredDeclarations );
}
public abstract Object clone();
/**
* A context entry for composite restrictions
*
* @author etirelli
*/
protected static class MultiFieldConstraintContextEntry
implements
ContextEntry {
private static final long serialVersionUID = 510l;
public ContextEntry[] alphas;
public ContextEntry[] betas;
public ContextEntry next;
public InternalWorkingMemory workingMemory;
public InternalFactHandle handle;
public MultiFieldConstraintContextEntry() {
}
public MultiFieldConstraintContextEntry(final AlphaNodeFieldConstraint[] alphas,
final BetaNodeFieldConstraint[] betas) {
this.alphas = new ContextEntry[alphas.length];
for ( int i = 0; i < alphas.length; i++ ) {
this.alphas[i] = alphas[i].createContextEntry();
}
this.betas = new ContextEntry[betas.length];
for ( int i = 0; i < betas.length; i++ ) {
this.betas[i] = betas[i].createContextEntry();
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
alphas = (ContextEntry[])in.readObject();
betas = (ContextEntry[])in.readObject();
next = (ContextEntry)in.readObject();
workingMemory = (InternalWorkingMemory)in.readObject();
handle = (InternalFactHandle)in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(alphas);
out.writeObject(betas);
out.writeObject(next);
out.writeObject(workingMemory);
out.writeObject(handle);
}
public ContextEntry getNext() {
return this.next;
}
public void setNext(ContextEntry entry) {
this.next = entry;
}
public void updateFromFactHandle(InternalWorkingMemory workingMemory,
InternalFactHandle handle) {
this.workingMemory = workingMemory;
this.handle = handle;
for ( int i = 0; i < alphas.length; i++ ) {
if ( alphas[i] != null ) {
alphas[i].updateFromFactHandle( workingMemory,
handle );
}
}
for ( int i = 0; i < betas.length; i++ ) {
betas[i].updateFromFactHandle( workingMemory,
handle );
}
}
public void updateFromTuple(InternalWorkingMemory workingMemory,
LeftTuple tuple) {
this.workingMemory = workingMemory;
for ( int i = 0; i < alphas.length; i++ ) {
if ( alphas[i] != null ) {
alphas[i].updateFromTuple( workingMemory,
tuple );
}
}
for ( int i = 0; i < betas.length; i++ ) {
betas[i].updateFromTuple( workingMemory,
tuple );
}
}
public void resetTuple() {
this.workingMemory = null;
for ( int i = 0, length = this.alphas.length; i < length; i++ ) {
if ( alphas[i] != null ) {
this.alphas[i].resetTuple();
}
}
for ( int i = 0, length = this.betas.length; i < length; i++ ) {
this.betas[i].resetTuple();
}
}
public void resetFactHandle() {
this.workingMemory = null;
this.handle = null;
for ( int i = 0, length = this.alphas.length; i < length; i++ ) {
if ( alphas[i] != null ) {
this.alphas[i].resetFactHandle();
}
}
for ( int i = 0, length = this.betas.length; i < length; i++ ) {
this.betas[i].resetFactHandle();
}
}
}
}