Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.drools.core.reteoo.ObjectTypeNode Maven / Gradle / Ivy
/*
* 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.reteoo;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.drools.core.InitialFact;
import org.drools.core.RuleBaseConfiguration;
import org.drools.core.WorkingMemoryEntryPoint;
import org.drools.core.base.ClassObjectType;
import org.drools.core.base.ValueType;
import org.drools.core.common.ClassAwareObjectStore;
import org.drools.core.common.DefaultFactHandle;
import org.drools.core.common.DroolsObjectInputStream;
import org.drools.core.common.EventFactHandle;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.Memory;
import org.drools.core.common.MemoryFactory;
import org.drools.core.common.RuleBasePartitionId;
import org.drools.core.common.UpdateContext;
import org.drools.core.impl.StatefulKnowledgeSessionImpl.WorkingMemoryReteExpireAction;
import org.drools.core.marshalling.impl.MarshallerReaderContext;
import org.drools.core.marshalling.impl.MarshallerWriteContext;
import org.drools.core.marshalling.impl.PersisterEnums;
import org.drools.core.marshalling.impl.ProtobufMessages;
import org.drools.core.marshalling.impl.ProtobufMessages.Timers.ExpireTimer;
import org.drools.core.marshalling.impl.ProtobufMessages.Timers.Timer;
import org.drools.core.marshalling.impl.TimersInputMarshaller;
import org.drools.core.marshalling.impl.TimersOutputMarshaller;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.reteoo.compiled.CompiledNetwork;
import org.drools.core.rule.EntryPointId;
import org.drools.core.spi.ObjectType;
import org.drools.core.spi.PropagationContext;
import org.drools.core.time.Job;
import org.drools.core.time.JobContext;
import org.drools.core.time.JobHandle;
import org.drools.core.time.TimerService;
import org.drools.core.time.impl.DefaultJobHandle;
import org.drools.core.time.impl.PointInTimeTrigger;
import org.drools.core.util.bitmask.BitMask;
import org.drools.core.util.bitmask.EmptyBitMask;
import static org.drools.core.rule.TypeDeclaration.NEVER_EXPIRES;
/**
* ObjectTypeNodes are responsible for filtering and propagating the matching
* fact assertions propagated from the Rete
node using ObjectType
interface.
*
* The assert and retract methods do not attempt to filter as this is the role of the Rete
* node which builds up a cache of matching ObjectTypdeNodes
s for each asserted object, using
* the matches(Object object)
method. Incorrect propagation in these methods is not checked and
* will result in ClassCastExpcections
later on in the network.
*
* Filters Objects
coming from the Rete
using a
* ObjectType
semantic module.
*
* @see Rete
*/
public class ObjectTypeNode extends ObjectSource
implements
ObjectSink,
Externalizable,
MemoryFactory {
// ------------------------------------------------------------
// Instance members
// ------------------------------------------------------------
private static final long serialVersionUID = 510l;
/**
* The ObjectType
semantic module.
*/
protected ObjectType objectType;
private boolean objectMemoryEnabled;
private static final transient ExpireJob job = new ExpireJob();
private long expirationOffset = -1;
private boolean queryNode;
protected CompiledNetwork compiledNetwork;
/* always dirty after serialisation */
private transient volatile boolean dirty;
/* reset counter when dirty */
protected transient IdGenerator idGenerator;
public int getOtnIdCounter() {
return idGenerator.otnIdCounter;
}
public ObjectTypeNode() {
}
/**
* Construct given a semantic ObjectType
and the provided
* unique id. All ObjectTypdeNode
have node memory.
*
* @param id The unique id for the node.
* @param objectType The semantic object-type differentiator.
*/
public ObjectTypeNode(final int id,
final EntryPointNode source,
final ObjectType objectType,
final BuildContext context) {
super(id,
RuleBasePartitionId.MAIN_PARTITION,
context.getKnowledgeBase().getConfiguration().isMultithreadEvaluation(),
source,
context.getKnowledgeBase().getConfiguration().getAlphaNodeHashingThreshold());
this.objectType = objectType;
idGenerator = new IdGenerator(id);
setObjectMemoryEnabled(context.isObjectTypeNodeMemoryEnabled());
if (ClassObjectType.DroolsQuery_ObjectType.isAssignableFrom(objectType)) {
queryNode = true;
}
this.dirty = true;
hashcode = calculateHashCode();
if (objectType != ClassObjectType.InitialFact_ObjectType && context.getKnowledgeBase().getConfiguration().isMultithreadEvaluation()) {
this.sink = new CompositePartitionAwareObjectSinkAdapter();
}
initMemoryId( context );
}
private static class IdGenerator {
private final int otnId;
private int otnIdCounter;
private IdGenerator(int otnId) {
this.otnId = otnId;
}
private Id nextId() {
return new Id(otnId, otnIdCounter++);
}
private void reset() {
otnIdCounter = 0;
}
}
public static final Id DEFAULT_ID = new Id(-1, 0);
public static class Id {
private final int otnId;
private final int id;
public Id(int otnId, int id) {
this.otnId = otnId;
this.id = id;
}
@Override
public String toString() {
return "ObjectTypeNode.Id[" + otnId + "#" + id + "]";
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof Id)) return false;
Id otherId = (Id) o;
return id == otherId.id && otnId == otherId.otnId;
}
@Override
public int hashCode() {
return 31 * otnId + 37 * id;
}
public boolean before(Id otherId) {
return otherId != null && (otnId < otherId.otnId || (otnId == otherId.otnId && id < otherId.id));
}
public int getId() {
return id;
}
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
super.readExternal(in);
objectType = (ObjectType) in.readObject();
// this is here as not all objectTypeNodes used ClassObjectTypes in packages (i.e. rules with those nodes did not exist yet)
// and thus have no wiring targets
if (objectType instanceof ClassObjectType) {
objectType = ((DroolsObjectInputStream) in).getKnowledgeBase().getClassFieldAccessorCache().getClassObjectType((ClassObjectType) objectType, true);
}
objectMemoryEnabled = in.readBoolean();
expirationOffset = in.readLong();
queryNode = in.readBoolean();
dirty = true;
idGenerator = new IdGenerator(id);
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeObject(objectType);
out.writeBoolean(objectMemoryEnabled);
out.writeLong(expirationOffset);
out.writeBoolean(queryNode);
}
public short getType() {
return NodeTypeEnums.ObjectTypeNode;
}
/**
* Retrieve the semantic ObjectType
differentiator.
*
* @return The semantic ObjectType
differentiator.
*/
public ObjectType getObjectType() {
return this.objectType;
}
/**
* Returns the partition ID for which this node belongs to
*/
public RuleBasePartitionId getPartitionId() {
return RuleBasePartitionId.MAIN_PARTITION;
}
@Override
public BitMask calculateDeclaredMask(List settableProperties) {
return EmptyBitMask.get();
}
public boolean isAssignableFrom(final ObjectType objectType) {
return this.objectType.isAssignableFrom(objectType);
}
public void setCompiledNetwork(CompiledNetwork compiledNetwork) {
this.compiledNetwork = compiledNetwork;
this.compiledNetwork.setObjectTypeNode(this);
}
public void assertInitialFact(final InternalFactHandle factHandle,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
if (objectMemoryEnabled) {
InitialFactObjectTypeNodeMemory memory = (InitialFactObjectTypeNodeMemory) workingMemory.getNodeMemory(this);
memory.add(factHandle);
}
checkDirty();
propagateAssert(factHandle, context, workingMemory);
}
protected void checkDirty() {
if (dirty) {
synchronized (this) {
if (dirty) {
resetIdGenerator();
updateTupleSinkId( this, this );
dirty = false;
}
}
}
}
/**
* Propagate the FactHandleimpl
through the Rete
network. All
* FactHandleImpl
should be remembered in the node memory, so that later runtime rule attachmnents
* can have the matched facts propagated to them.
*
* @param factHandle The fact handle.
* @param context The propagation context.
* @param workingMemory The working memory session.
*/
public void assertObject(final InternalFactHandle factHandle,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
}
public void propagateAssert(InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory) {
checkDirty();
if (compiledNetwork != null) {
compiledNetwork.assertObject(factHandle,
context,
workingMemory);
} else {
this.sink.propagateAssertObject(factHandle,
context,
workingMemory);
}
}
/**
* Retract the FactHandleimpl
from the Rete
network. Also remove the
* FactHandleImpl
from the node memory.
*
* @param factHandle The fact handle.
* @param context The propagation context.
* @param workingMemory The working memory session.
*/
public void retractObject(final InternalFactHandle factHandle,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
checkDirty();
doRetractObject( factHandle, context, workingMemory);
}
public void retractObject(final InternalFactHandle factHandle,
final PropagationContext context,
final InternalWorkingMemory workingMemory,
int partition) {
checkDirty();
retractRightTuples( factHandle, context, workingMemory, partition );
retractLeftTuples( factHandle, context, workingMemory, partition );
}
public static void doRetractObject(final InternalFactHandle factHandle,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
retractRightTuples( factHandle, context, workingMemory );
retractLeftTuples( factHandle, context, workingMemory );
}
public static void expireLeftTuple(LeftTuple leftTuple) {
if (!leftTuple.isExpired()) {
leftTuple.setExpired( true );
for ( LeftTuple child = leftTuple.getFirstChild(); child != null; child = child.getHandleNext() ) {
expireLeftTuple(child);
}
for ( LeftTuple peer = leftTuple.getPeer(); peer != null; peer = peer.getPeer() ) {
expireLeftTuple(peer);
}
}
}
public static void expireRightTuple(RightTuple rightTuple) {
for ( LeftTuple child = rightTuple.getFirstChild(); child != null; child = child.getHandleNext() ) {
expireLeftTuple(child);
}
}
public static void retractLeftTuples( InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory ) {
factHandle.forEachLeftTuple( lt -> {
LeftTupleSink sink = lt.getTupleSink();
((LeftInputAdapterNode) sink.getLeftTupleSource()).retractLeftTuple(lt,
context,
workingMemory);
} );
factHandle.clearLeftTuples();
}
public static void retractLeftTuples( InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory, int partition ) {
DefaultFactHandle.CompositeLinkedTuples linkedTuples = ( (DefaultFactHandle.CompositeLinkedTuples) factHandle.getLinkedTuples() );
linkedTuples.forEachLeftTuple( partition, lt -> {
LeftTupleSink sink = lt.getTupleSink();
((LeftInputAdapterNode) sink.getLeftTupleSource()).retractLeftTuple(lt,
context,
workingMemory);
} );
linkedTuples.clearLeftTuples(partition);
}
public static void retractRightTuples( InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory ) {
factHandle.forEachRightTuple( rt -> rt.retractTuple( context, workingMemory) );
factHandle.clearRightTuples();
}
public static void retractRightTuples( InternalFactHandle factHandle, PropagationContext context, InternalWorkingMemory workingMemory, int partition ) {
DefaultFactHandle.CompositeLinkedTuples linkedTuples = ( (DefaultFactHandle.CompositeLinkedTuples) factHandle.getLinkedTuples() );
linkedTuples.forEachRightTuple( partition, rt -> rt.retractTuple( context, workingMemory) );
linkedTuples.clearRightTuples(partition);
}
protected void resetIdGenerator() {
idGenerator.reset();
}
public void modifyObject(InternalFactHandle factHandle,
ModifyPreviousTuples modifyPreviousTuples,
PropagationContext context,
InternalWorkingMemory workingMemory) {
checkDirty();
if (compiledNetwork != null) {
compiledNetwork.modifyObject(factHandle,
modifyPreviousTuples,
context.adaptModificationMaskForObjectType(objectType, workingMemory),
workingMemory);
} else {
this.sink.propagateModifyObject(factHandle,
modifyPreviousTuples,
context.adaptModificationMaskForObjectType(objectType, workingMemory),
workingMemory);
}
}
public void updateSink(final ObjectSink sink,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
checkDirty();
// Regular updateSink
final ObjectTypeNodeMemory memory = workingMemory.getNodeMemory(this);
Iterator it = memory.iterator();
while (it.hasNext()) {
sink.assertObject(it.next(),
context,
workingMemory);
}
}
/**
* Rete needs to know that this ObjectTypeNode has been added
*/
public void attach(BuildContext context) {
this.source.addObjectSink(this);
InternalWorkingMemory[] workingMemories = context.getWorkingMemories();
InternalWorkingMemory workingMemory = workingMemories.length > 0 ? workingMemories[0] : null;
if ( workingMemory != null ) {
WorkingMemoryEntryPoint wmEntryPoint = workingMemory.getWorkingMemoryEntryPoint( ((EntryPointNode) source).getEntryPoint().getEntryPointId() );
ObjectTypeConf objectTypeConf = wmEntryPoint.getObjectTypeConfigurationRegistry().getObjectTypeConfByClass( ((ClassObjectType) objectType).getClassType() );
if (objectTypeConf != null) {
objectTypeConf.resetCache();
}
}
}
public void networkUpdated(UpdateContext updateContext) {
this.dirty = true;
}
protected static void updateTupleSinkId(ObjectTypeNode otn,
ObjectSource source) {
for (ObjectSink sink : source.sink.getSinks()) {
if (sink instanceof BetaNode) {
((BetaNode) sink).setRightInputOtnId(otn.nextOtnId());
} else if (sink instanceof LeftInputAdapterNode) {
for (LeftTupleSink liaChildSink : ((LeftInputAdapterNode) sink).getSinkPropagator().getSinks()) {
liaChildSink.setLeftInputOtnId(otn.nextOtnId());
}
} else if (sink instanceof WindowNode) {
((WindowNode) sink).setRightInputOtnId(otn.nextOtnId());
updateTupleSinkId(otn, (WindowNode) sink);
} else if (sink instanceof AlphaNode) {
updateTupleSinkId(otn, (AlphaNode) sink);
}
}
}
public Id nextOtnId() {
return idGenerator.nextId();
}
/**
* OTN needs to override remove to avoid releasing the node ID, since OTN are
* never removed from the rulebase in the current implementation
*/
public boolean remove(RuleRemovalContext context,
ReteooBuilder builder,
InternalWorkingMemory[] workingMemories) {
return doRemove(context,
builder,
workingMemories);
}
/**
* OTN needs to override remove to avoid releasing the node ID, since OTN are
* never removed from the rulebase in the current implementation
*/
protected boolean doRemove(final RuleRemovalContext context,
final ReteooBuilder builder,
final InternalWorkingMemory[] workingMemories) {
return false;
}
/**
* Creates memory for the node using PrimitiveLongMap as its optimised for storage and reteivals of Longs.
* However PrimitiveLongMap is not ideal for spase data. So it should be monitored incase its more optimal
* to switch back to a standard HashMap.
*/
public ObjectTypeNodeMemory createMemory(final RuleBaseConfiguration config, InternalWorkingMemory wm) {
Class classType = ((ClassObjectType) getObjectType()).getClassType();
if (InitialFact.class.isAssignableFrom(classType)) {
return new InitialFactObjectTypeNodeMemory(classType);
}
return new ObjectTypeNodeMemory(classType, wm);
}
public boolean isObjectMemoryEnabled() {
return this.objectMemoryEnabled;
}
public void setObjectMemoryEnabled(boolean objectMemoryEnabled) {
this.objectMemoryEnabled = objectMemoryEnabled;
}
public String toString() {
return "[ObjectTypeNode(" + this.id + ")::" + ((EntryPointNode) this.source).getEntryPoint() + " objectType=" + this.objectType + " expiration=" + this.getExpirationOffset() + "ms ]";
}
private int calculateHashCode() {
return (this.objectType != null ? this.objectType.hashCode() : 0) * 37 + (this.source != null ? this.source.hashCode() : 0) * 31;
}
public boolean equals(final Object object) {
return this == object ||
(internalEquals(object) && this.source.thisNodeEquals(((ObjectTypeNode) object).source));
}
@Override
protected boolean internalEquals( Object object ) {
if ( object == null || !(object instanceof ObjectTypeNode) || this.hashCode() != object.hashCode() ) {
return false;
}
return this.objectType.equals( ((ObjectTypeNode)object).objectType );
}
/**
* @return the entryPoint
*/
public EntryPointId getEntryPoint() {
return ((EntryPointNode) this.source).getEntryPoint();
}
public long getExpirationOffset() {
return expirationOffset;
}
public void setExpirationOffset(long expirationOffset) {
this.expirationOffset = expirationOffset;
if (!this.objectType.getValueType().equals(ValueType.QUERY_TYPE)) {
if (expirationOffset > 0) {
// override memory enabled settings
this.setObjectMemoryEnabled(true);
} else if (expirationOffset == 0) {
// disable memory
this.setObjectMemoryEnabled(false);
}
}
}
public void mergeExpirationOffset(ObjectTypeNode other) {
setExpirationOffset( expirationOffset == NEVER_EXPIRES || other.expirationOffset == NEVER_EXPIRES ?
NEVER_EXPIRES :
Math.max(expirationOffset, other.expirationOffset) );
}
public static class ExpireJob
implements
Job {
public void execute(JobContext ctx) {
ExpireJobContext context = (ExpireJobContext) ctx;
context.workingMemory.queueWorkingMemoryAction(context.expireAction);
context.getExpireAction().getFactHandle().removeJob( context.getJobHandle());
}
}
public static class ExpireJobContext
implements
JobContext,
Externalizable {
public final WorkingMemoryReteExpireAction expireAction;
public final InternalWorkingMemory workingMemory;
public JobHandle handle;
public ExpireJobContext(WorkingMemoryReteExpireAction expireAction,
InternalWorkingMemory workingMemory) {
super();
this.expireAction = expireAction;
this.workingMemory = workingMemory;
}
public JobHandle getJobHandle() {
return this.handle;
}
public void setJobHandle(JobHandle jobHandle) {
this.handle = jobHandle;
}
public WorkingMemoryReteExpireAction getExpireAction() {
return expireAction;
}
public InternalWorkingMemory getWorkingMemory() {
return workingMemory;
}
public JobHandle getHandle() {
return handle;
}
public void setHandle(JobHandle handle) {
this.handle = handle;
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
//this.behavior = (O)
}
public void writeExternal(ObjectOutput out) throws IOException {
// TODO Auto-generated method stub
}
}
public static class ExpireJobContextTimerOutputMarshaller
implements
TimersOutputMarshaller {
public void write(JobContext jobCtx,
MarshallerWriteContext outputCtx) throws IOException {
outputCtx.writeShort( PersisterEnums.EXPIRE_TIMER );
// ExpireJob, no state
ExpireJobContext ejobCtx = (ExpireJobContext) jobCtx;
WorkingMemoryReteExpireAction expireAction = ejobCtx.getExpireAction();
outputCtx.writeInt( expireAction.getFactHandle().getId() );
DefaultJobHandle jobHandle = (DefaultJobHandle) ejobCtx.getJobHandle();
PointInTimeTrigger trigger = (PointInTimeTrigger) jobHandle.getTimerJobInstance().getTrigger();
outputCtx.writeLong( trigger.hasNextFireTime().getTime() );
}
public ProtobufMessages.Timers.Timer serialize(JobContext jobCtx,
MarshallerWriteContext outputCtx) {
// ExpireJob, no state
ExpireJobContext ejobCtx = ( ExpireJobContext ) jobCtx;
WorkingMemoryReteExpireAction expireAction = ejobCtx.getExpireAction();
DefaultJobHandle jobHandle = ( DefaultJobHandle ) ejobCtx.getJobHandle();
PointInTimeTrigger trigger = ( PointInTimeTrigger ) jobHandle.getTimerJobInstance().getTrigger();
return ProtobufMessages.Timers.Timer.newBuilder()
.setType( ProtobufMessages.Timers.TimerType.EXPIRE )
.setExpire( ProtobufMessages.Timers.ExpireTimer.newBuilder()
.setHandleId( expireAction.getFactHandle().getId() )
.setNextFireTimestamp( trigger.hasNextFireTime().getTime() )
.build() )
.build();
}
}
public static class ExpireJobContextTimerInputMarshaller
implements
TimersInputMarshaller {
public void read(MarshallerReaderContext inCtx) throws IOException,
ClassNotFoundException {
InternalFactHandle factHandle = inCtx.handles.get( inCtx.readInt() );
long nextTimeStamp = inCtx.readLong();
TimerService clock = inCtx.wm.getTimerService();
JobContext jobctx = new ExpireJobContext( new WorkingMemoryReteExpireAction( (EventFactHandle) factHandle ),
inCtx.wm );
JobHandle handle = clock.scheduleJob( job,
jobctx,
new PointInTimeTrigger( nextTimeStamp,
null,
null ) );
jobctx.setJobHandle( handle );
}
public void deserialize(MarshallerReaderContext inCtx,
Timer _timer) throws ClassNotFoundException {
ExpireTimer _expire = _timer.getExpire();
InternalFactHandle factHandle = inCtx.handles.get( _expire.getHandleId() );
TimerService clock = inCtx.wm.getTimerService();
JobContext jobctx = new ExpireJobContext( new WorkingMemoryReteExpireAction((EventFactHandle)factHandle),
inCtx.wm );
JobHandle jobHandle = clock.scheduleJob( job,
jobctx,
new PointInTimeTrigger( _expire.getNextFireTimestamp(), null, null ) );
jobctx.setJobHandle( jobHandle );
((EventFactHandle) factHandle).addJob(jobHandle);
}
}
public void byPassModifyToBetaNode(InternalFactHandle factHandle,
ModifyPreviousTuples modifyPreviousTuples,
PropagationContext context,
InternalWorkingMemory workingMemory) {
throw new UnsupportedOperationException("This should never get called, as the PropertyReactive first happens at the AlphaNode");
}
public static class ObjectTypeNodeMemory implements Memory {
private ClassAwareObjectStore.SingleClassStore store;
private Class classType;
ObjectTypeNodeMemory(Class classType) {
this.classType = classType;
}
ObjectTypeNodeMemory(Class classType, InternalWorkingMemory wm) {
this(classType);
store = ((ClassAwareObjectStore) wm.getObjectStore()).getOrCreateClassStore(classType);
}
public short getNodeType() {
return NodeTypeEnums.ObjectTypeNode;
}
public Iterator iterator() {
return store.factHandlesIterator(true);
}
public SegmentMemory getSegmentMemory() {
return null;
}
public void setSegmentMemory(SegmentMemory segmentMemory) {
throw new UnsupportedOperationException();
}
public Memory getPrevious() {
throw new UnsupportedOperationException();
}
public void setPrevious(Memory previous) {
throw new UnsupportedOperationException();
}
public void nullPrevNext() {
throw new UnsupportedOperationException();
}
public void setNext(Memory next) {
throw new UnsupportedOperationException();
}
public Memory getNext() {
throw new UnsupportedOperationException();
}
public void reset() { }
public String toString() {
return "ObjectTypeMemory for " + classType;
}
}
public static class InitialFactObjectTypeNodeMemory extends ObjectTypeNodeMemory {
private List list = Collections.emptyList();
InitialFactObjectTypeNodeMemory(Class classType) {
super(classType);
}
public void add(InternalFactHandle factHandle) {
list = Collections.singletonList( factHandle );
}
@Override
public Iterator iterator() {
return list.iterator();
}
@Override
public void reset() {
list = Collections.emptyList();
}
}
}