001: package org.drools.reteoo;
002:
003: /*
004: * Copyright 2005 JBoss Inc
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: */
018:
019: import java.io.Serializable;
020:
021: import org.drools.RuleBaseConfiguration;
022: import org.drools.common.BaseNode;
023: import org.drools.common.InternalFactHandle;
024: import org.drools.common.InternalWorkingMemory;
025: import org.drools.common.NodeMemory;
026: import org.drools.common.PropagationContextImpl;
027: import org.drools.rule.Declaration;
028: import org.drools.spi.Constraint;
029: import org.drools.spi.ObjectType;
030: import org.drools.spi.PropagationContext;
031: import org.drools.util.FactEntry;
032: import org.drools.util.FactHashTable;
033: import org.drools.util.Iterator;
034: import org.drools.util.AbstractHashTable.FactEntryImpl;
035:
036: /**
037: * <code>ObjectTypeNodes<code> are responsible for filtering and propagating the matching
038: * fact assertions propagated from the <code>Rete</code> node using <code>ObjectType</code> interface.
039: * <p>
040: * The assert and retract methods do not attempt to filter as this is the role of the <code>Rete</code>
041: * node which builds up a cache of matching <code>ObjectTypdeNodes</code>s for each asserted object, using
042: * the <code>matches(Object object)</code> method. Incorrect propagation in these methods is not checked and
043: * will result in <code>ClassCastExpcections</code> later on in the network.
044: * <p>
045: * Filters <code>Objects</code> coming from the <code>Rete</code> using a
046: * <code>ObjectType</code> semantic module.
047: *
048: *
049: * @see ObjectType
050: * @see Rete
051: *
052: * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a>
053: * @author <a href="mailto:bob@werken.com">Bob McWhirter</a>
054: */
055: public class ObjectTypeNode extends ObjectSource implements ObjectSink,
056: Serializable, NodeMemory
057:
058: {
059: // ------------------------------------------------------------
060: // Instance members
061: // ------------------------------------------------------------
062:
063: /**
064: *
065: */
066: private static final long serialVersionUID = 400L;
067:
068: /** The <code>ObjectType</code> semantic module. */
069: private final ObjectType objectType;
070:
071: /** The parent Rete node */
072: private final Rete rete;
073:
074: protected boolean skipOnModify = false;
075:
076: // ------------------------------------------------------------
077: // Constructors
078: // ------------------------------------------------------------
079:
080: /**
081: * Construct given a semantic <code>ObjectType</code> and the provided
082: * unique id. All <code>ObjectTypdeNode</code> have node memory.
083: *
084: * @param id
085: * The unique id for the node.
086: * @param objectType
087: * The semantic object-type differentiator.
088: */
089: public ObjectTypeNode(final int id, final ObjectType objectType,
090: final Rete rete, final int alphaNodeHashingThreshold) {
091: super (id, null, alphaNodeHashingThreshold);
092: this .rete = rete;
093: this .objectType = objectType;
094: setHasMemory(true);
095: }
096:
097: // ------------------------------------------------------------
098: // Instance methods
099: // ------------------------------------------------------------
100:
101: /**
102: * Retrieve the semantic <code>ObjectType</code> differentiator.
103: *
104: * @return
105: * The semantic <code>ObjectType</code> differentiator.
106: */
107: public ObjectType getObjectType() {
108: return this .objectType;
109: }
110:
111: /**
112: * Tests the provided object to see if this <code>ObjectTypeNode</code> can receive the object
113: * for assertion and retraction propagations.
114: *
115: * @param object
116: * @return
117: * boolean value indicating whether the <code>ObjectTypeNode</code> can receive the object.
118: */
119: public boolean matches(final Object object) {
120: return this .objectType.matches(object);
121: }
122:
123: public boolean isAssignableFrom(final Object object) {
124: return this .objectType.isAssignableFrom(object);
125: }
126:
127: /**
128: * Propagate the <code>FactHandleimpl</code> through the <code>Rete</code> network. All
129: * <code>FactHandleImpl</code> should be remembered in the node memory, so that later runtime rule attachmnents
130: * can have the matched facts propagated to them.
131: *
132: * @param handle
133: * The fact handle.
134: * @param object
135: * The object to assert.
136: * @param workingMemory
137: * The working memory session.
138: */
139: public void assertObject(final InternalFactHandle handle,
140: final PropagationContext context,
141: final InternalWorkingMemory workingMemory) {
142: if (context.getType() == PropagationContext.MODIFICATION
143: && this .skipOnModify
144: && context.getDormantActivations() == 0) {
145: // we do this after the shadowproxy update, just so that its up to date for the future
146: return;
147: }
148:
149: if (!workingMemory.isSequential()) {
150: final FactHashTable memory = (FactHashTable) workingMemory
151: .getNodeMemory(this );
152: memory.add(handle, false);
153: }
154:
155: this .sink.propagateAssertObject(handle, context, workingMemory);
156: }
157:
158: /**
159: * Retract the <code>FactHandleimpl</code> from the <code>Rete</code> network. Also remove the
160: * <code>FactHandleImpl</code> from the node memory.
161: *
162: * @param handle
163: * The fact handle.
164: * @param object
165: * The object to assert.
166: * @param workingMemory
167: * The working memory session.
168: */
169: public void retractObject(final InternalFactHandle handle,
170: final PropagationContext context,
171: final InternalWorkingMemory workingMemory) {
172:
173: if (context.getType() == PropagationContext.MODIFICATION
174: && this .skipOnModify
175: && context.getDormantActivations() == 0) {
176: return;
177: }
178:
179: final FactHashTable memory = (FactHashTable) workingMemory
180: .getNodeMemory(this );
181: memory.remove(handle);
182:
183: this .sink.propagateRetractObject(handle, context,
184: workingMemory, true);
185: }
186:
187: public void updateSink(final ObjectSink sink,
188: final PropagationContext context,
189: final InternalWorkingMemory workingMemory) {
190: final FactHashTable memory = (FactHashTable) workingMemory
191: .getNodeMemory(this );
192: final Iterator it = memory.iterator();
193: for (FactEntry entry = (FactEntry) it.next(); entry != null; entry = (FactEntry) it
194: .next()) {
195: sink.assertObject(entry.getFactHandle(), context,
196: workingMemory);
197: }
198: }
199:
200: /**
201: * Rete needs to know that this ObjectTypeNode has been added
202: */
203: public void attach() {
204: this .rete.addObjectSink(this );
205: }
206:
207: public void attach(final InternalWorkingMemory[] workingMemories) {
208: attach();
209:
210: // we need to call updateSink on Rete, because someone
211: // might have already added facts matching this ObjectTypeNode
212: // to working memories
213: for (int i = 0, length = workingMemories.length; i < length; i++) {
214: final InternalWorkingMemory workingMemory = workingMemories[i];
215: final PropagationContext propagationContext = new PropagationContextImpl(
216: workingMemory.getNextPropagationIdCounter(),
217: PropagationContext.RULE_ADDITION, null, null);
218: this .rete.updateSink(this , propagationContext,
219: workingMemory);
220: }
221: }
222:
223: public void remove(final BaseNode node,
224: final InternalWorkingMemory[] workingMemories) {
225: if (!node.isInUse()) {
226: removeObjectSink((ObjectSink) node);
227: }
228: removeShare();
229: if (!this .isInUse()) {
230: for (int i = 0, length = workingMemories.length; i < length; i++) {
231: workingMemories[i].clearNodeMemory(this );
232: }
233: this .rete.removeObjectSink(this );
234: }
235: }
236:
237: /**
238: * Rete needs to know that this ObjectTypeNode has had new nodes attached to
239: * it one one of its ancestors
240: */
241: public void addShare() {
242: super .addShare();
243: }
244:
245: /**
246: * Creates memory for the node using PrimitiveLongMap as its optimised for storage and reteivals of Longs.
247: * However PrimitiveLongMap is not ideal for spase data. So it should be monitored incase its more optimal
248: * to switch back to a standard HashMap.
249: */
250: public Object createMemory(final RuleBaseConfiguration config) {
251: return new FactHashTable();
252: }
253:
254: public String toString() {
255: return "[ObjectTypeNode(" + this .id + ") objectType="
256: + this .objectType + "]";
257: }
258:
259: /**
260: * Uses he hashCode() of the underlying ObjectType implementation.
261: */
262: public int hashCode() {
263: return this .objectType.hashCode();
264: }
265:
266: public boolean equals(final Object object) {
267: if (this == object) {
268: return true;
269: }
270:
271: if (object == null || !(object instanceof ObjectTypeNode)) {
272: return false;
273: }
274:
275: final ObjectTypeNode other = (ObjectTypeNode) object;
276:
277: return this .objectType.equals(other.objectType);
278: }
279:
280: /**
281: * @inheritDoc
282: */
283: protected void addObjectSink(final ObjectSink objectSink) {
284: super .addObjectSink(objectSink);
285: this .skipOnModify = canSkipOnModify(this .sink.getSinks());
286: }
287:
288: /**
289: * @inheritDoc
290: */
291: protected void removeObjectSink(final ObjectSink objectSink) {
292: super .removeObjectSink(objectSink);
293: this .skipOnModify = canSkipOnModify(this .sink.getSinks());
294: }
295:
296: /**
297: * Checks if a modify action on this object type may
298: * be skipped because no constraint is applied to it
299: *
300: * @param sinks
301: * @return
302: */
303: private boolean canSkipOnModify(final Sink[] sinks) {
304: // If we have no alpha or beta node with constraints on this ObjectType, we can just skip modifies
305: boolean hasConstraints = false;
306: for (int i = 0; i < sinks.length && !hasConstraints; i++) {
307: if (sinks[i] instanceof AlphaNode) {
308: hasConstraints = this
309: .usesDeclaration(((AlphaNode) sinks[i])
310: .getConstraint());
311: } else if (sinks[i] instanceof BetaNode
312: && ((BetaNode) sinks[i]).getConstraints().length > 0) {
313: hasConstraints = this
314: .usesDeclaration(((BetaNode) sinks[i])
315: .getConstraints());
316: }
317: if (!hasConstraints && sinks[i] instanceof ObjectSource) {
318: hasConstraints = this
319: .canSkipOnModify(((ObjectSource) sinks[i])
320: .getSinkPropagator().getSinks());
321: } else if (sinks[i] instanceof TupleSource) {
322: hasConstraints = this
323: .canSkipOnModify(((TupleSource) sinks[i])
324: .getSinkPropagator().getSinks());
325: }
326: }
327:
328: // Can only skip if we have no constraints
329: return !hasConstraints;
330: }
331:
332: private boolean usesDeclaration(final Constraint[] constraints) {
333: boolean usesDecl = false;
334: for (int i = 0; !usesDecl && i < constraints.length; i++) {
335: usesDecl = this .usesDeclaration(constraints[i]);
336: }
337: return usesDecl;
338: }
339:
340: private boolean usesDeclaration(final Constraint constraint) {
341: boolean usesDecl = false;
342: final Declaration[] declarations = constraint
343: .getRequiredDeclarations();
344: for (int j = 0; !usesDecl && j < declarations.length; j++) {
345: usesDecl = (declarations[j].getPattern().getObjectType() == this.objectType);
346: }
347: return usesDecl;
348: }
349: }
|