/* * Copyright 2013 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.reteoo; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.util.BitSet; import java.util.Collection; import org.drools.core.base.ClassObjectType; import org.drools.core.common.InternalFactHandle; import org.drools.core.common.InternalWorkingMemory; import org.drools.core.factmodel.traits.Thing; import org.drools.core.factmodel.traits.TraitProxy; import org.drools.core.factmodel.traits.TraitType; import org.drools.core.factmodel.traits.TraitTypeMap; import org.drools.core.reteoo.builder.BuildContext; import org.drools.core.spi.ObjectType; import org.drools.core.spi.PropagationContext; import org.drools.core.util.HierarchyEncoderImpl; import org.drools.core.util.bitmask.BitMask; public class TraitObjectTypeNode extends ObjectTypeNode { private BitSet typeMask; public TraitObjectTypeNode() { } public TraitObjectTypeNode( int id, EntryPointNode source, ObjectType objectType, BuildContext context ) { super( id, source, objectType, context ); typeMask = context.getKnowledgeBase().getConfiguration().getComponentFactory().getTraitRegistry().getHierarchy().getCode( ((ClassObjectType) objectType).getClassName() ); } public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException { super.readExternal( in ); typeMask = (BitSet) in.readObject(); } public void writeExternal( ObjectOutput out ) throws IOException { super.writeExternal( out ); out.writeObject( typeMask ); } @Override public void propagateAssert( InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory ) { if ( isAssertAllowed( factHandle ) ) { super.propagateAssert( factHandle, context, workingMemory ); } } private boolean isAssertAllowed( InternalFactHandle factHandle ) { if ( factHandle.isTraiting() ) { TraitProxy proxy = (TraitProxy) factHandle.getObject(); BitSet vetoMask = proxy.computeInsertionVetoMask(); boolean vetoed = ( vetoMask != null && ! typeMask.isEmpty() && HierarchyEncoderImpl.supersetOrEqualset( vetoMask, this.typeMask ) ); boolean allowed = ! vetoed || sameAndNotCoveredByDescendants( (TraitProxy) factHandle.getObject(), typeMask ); if ( allowed ) { //System.err.println(" INSERT PASS !! " + factHandle.getObject() + " " + ( (TraitProxy) factHandle.getObject() )._getTypeCode() + " >> " + vetoMask + " checks in " + typeMask ); proxy.assignOtn( this.typeMask ); } else { //System.err.println(" INSERT BLOCK !! " + factHandle.getObject() + " " + ( (TraitProxy) factHandle.getObject() )._getTypeCode() + " >> " + vetoMask + " checks in " + typeMask ); } return allowed; } return true; } /** * Edge case: due to the way traits are encoded, consider this hierarchy: * A B * C * D * On don/insertion of C, C may be vetoed by its parents, but might have been * already covered by one of its descendants (D) */ private boolean sameAndNotCoveredByDescendants( TraitProxy proxy, BitSet typeMask ) { boolean isSameType = typeMask.equals( proxy._getTypeCode() ); if ( isSameType ) { TraitTypeMap<String,Thing<?>,?> ttm = (TraitTypeMap<String,Thing<?>,?>) proxy.getObject()._getTraitMap(); Collection<Thing<?>> descs = ttm.lowerDescendants( typeMask ); // we have to exclude the "mock" bottom proxy if ( descs == null || descs.isEmpty() ) { return true; } else { for ( Thing sub : descs ) { TraitType tt = (TraitType) sub; if ( tt != proxy && tt._hasTypeCode( typeMask ) ) { return false; } } return true; } } else { return false; } } private boolean isModifyAllowed( InternalFactHandle factHandle ) { if ( factHandle.isTraiting() ) { TraitProxy proxy = ( (TraitProxy) factHandle.getObject() ); return proxy.listAssignedOtnTypeCodes().contains( this.typeMask ); } return true; } public void modifyObject( InternalFactHandle factHandle, ModifyPreviousTuples modifyPreviousTuples, PropagationContext context, InternalWorkingMemory workingMemory ) { if (!isModifyAllowed( factHandle )) { return; } checkDirty(); if ( compiledNetwork != null ) { compiledNetwork.modifyObject( factHandle, modifyPreviousTuples, context.adaptModificationMaskForObjectType( objectType, workingMemory ), workingMemory ); } else { if ( factHandle.isTraiting() ) { if ( isModifyAllowed( factHandle ) ) { this.sink.propagateModifyObject( factHandle, modifyPreviousTuples, context.adaptModificationMaskForObjectType( objectType, workingMemory ), workingMemory ); } else { //System.err.println( ((ClassObjectType) this.getObjectType()).getClassName() + " : MODIFY BLOCK !! " + ( (TraitProxy) factHandle.getObject() ).getTraitName() + " " + ( (TraitProxy) factHandle.getObject() )._getTypeCode() + " >> " + " checks in " + typeMask ); } } else { this.sink.propagateModifyObject( factHandle, modifyPreviousTuples, !context.getModificationMask().isSet(PropertySpecificUtil.TRAITABLE_BIT) ? context.adaptModificationMaskForObjectType( objectType, workingMemory ) : context, workingMemory ); } } } @Override public BitMask updateMask(BitMask mask) { BitMask returnMask; returnMask = declaredMask.clone().setAll( mask ); inferredMask = inferredMask.setAll( returnMask ); return returnMask; } public BitSet getLocalTypeCode() { return typeMask; } }