/** * 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(); } } } }