001: /*
002: * Copyright 2005 JBoss Inc
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.drools.reteoo;
018:
019: import java.util.Arrays;
020: import java.util.Collection;
021:
022: import org.drools.common.BetaConstraints;
023: import org.drools.common.EmptyBetaConstraints;
024: import org.drools.common.InternalFactHandle;
025: import org.drools.common.InternalWorkingMemory;
026: import org.drools.rule.Collect;
027: import org.drools.spi.AlphaNodeFieldConstraint;
028: import org.drools.spi.PropagationContext;
029: import org.drools.util.ArrayUtils;
030: import org.drools.util.Entry;
031: import org.drools.util.FactEntry;
032: import org.drools.util.Iterator;
033: import org.drools.util.ObjectHashMap.ObjectEntry;
034:
035: /**
036: * @author etirelli
037: *
038: */
039: public class CollectNode extends BetaNode implements TupleSink,
040: ObjectSink {
041:
042: private static final long serialVersionUID = 400L;
043:
044: private final Collect collect;
045: private final AlphaNodeFieldConstraint[] resultConstraints;
046: private final BetaConstraints resultsBinder;
047: private final boolean unwrapRightObject;
048:
049: /**
050: * Constructor.
051: *
052: * @param id
053: * The id for the node
054: * @param leftInput
055: * The left input <code>TupleSource</code>.
056: * @param rightInput
057: * The right input <code>ObjectSource</code>.
058: * @param collect
059: * The collect conditional element
060: */
061: CollectNode(final int id, final TupleSource leftInput,
062: final ObjectSource rightInput, final Collect collect) {
063: this (id, leftInput, rightInput,
064: new AlphaNodeFieldConstraint[0], EmptyBetaConstraints
065: .getInstance(), EmptyBetaConstraints
066: .getInstance(), collect, false);
067: }
068:
069: /**
070: * Constructor.
071: *
072: * @param id
073: * The id for the node
074: * @param leftInput
075: * The left input <code>TupleSource</code>.
076: * @param rightInput
077: * The right input <code>ObjectSource</code>.
078: * @param resultConstraints
079: * The alpha constraints to be applied to the resulting collection
080: * @param sourceBinder
081: * The beta binder to be applied to the source facts
082: * @param resultsBinder
083: * The beta binder to be applied to the resulting collection
084: * @param collect
085: * The collect conditional element
086: */
087: public CollectNode(final int id, final TupleSource leftInput,
088: final ObjectSource rightInput,
089: final AlphaNodeFieldConstraint[] resultConstraints,
090: final BetaConstraints sourceBinder,
091: final BetaConstraints resultsBinder, final Collect collect,
092: final boolean unwrapRight) {
093: super (id, leftInput, rightInput, sourceBinder);
094: this .resultsBinder = resultsBinder;
095: this .resultConstraints = resultConstraints;
096: this .collect = collect;
097: this .unwrapRightObject = unwrapRight;
098: }
099:
100: /**
101: * @inheritDoc
102: *
103: * When a new tuple is asserted into a CollectNode, do this:
104: *
105: * 1. Select all matching objects from right memory
106: * 2. Add them to the resulting collection object
107: * 3. Apply resultConstraints and resultsBinder to the resulting collection
108: * 4. In case all of them evaluates to true do the following:
109: * 4.1. Create a new InternalFactHandle for the resulting collection and add it to the tuple
110: * 4.2. Propagate the tuple
111: *
112: */
113: public void assertTuple(final ReteTuple leftTuple,
114: final PropagationContext context,
115: final InternalWorkingMemory workingMemory) {
116:
117: final BetaMemory memory = (BetaMemory) workingMemory
118: .getNodeMemory(this );
119:
120: final Collection result = this .collect
121: .instantiateResultObject();
122: final InternalFactHandle resultHandle = workingMemory
123: .getFactHandleFactory().newFactHandle(result);
124: CollectResult colresult = new CollectResult();
125: colresult.handle = resultHandle;
126: colresult.propagated = false;
127:
128: // do not add tuple and result to the memory in sequential mode
129: if (!workingMemory.isSequential()) {
130: memory.getTupleMemory().add(leftTuple);
131: memory.getCreatedHandles().put(leftTuple, colresult, false);
132: }
133:
134: final Iterator it = memory.getFactHandleMemory().iterator(
135: leftTuple);
136: this .constraints.updateFromTuple(workingMemory, leftTuple);
137:
138: for (FactEntry entry = (FactEntry) it.next(); entry != null; entry = (FactEntry) it
139: .next()) {
140: InternalFactHandle handle = entry.getFactHandle();
141: if (this .constraints
142: .isAllowedCachedLeft(handle.getObject())) {
143: if (this .unwrapRightObject) {
144: handle = ((ReteTuple) handle.getObject())
145: .getLastHandle();
146: }
147: result.add(handle.getObject());
148: }
149: }
150:
151: // First alpha node filters
152: boolean isAllowed = true;
153: for (int i = 0, length = this .resultConstraints.length; i < length; i++) {
154: if (!this .resultConstraints[i].isAllowed(result,
155: workingMemory)) {
156: isAllowed = false;
157: break;
158: }
159: }
160: if (isAllowed) {
161: this .resultsBinder
162: .updateFromTuple(workingMemory, leftTuple);
163: if (this .resultsBinder.isAllowedCachedLeft(result)) {
164: colresult.propagated = true;
165: this .sink.propagateAssertTuple(leftTuple, resultHandle,
166: context, workingMemory);
167: }
168: }
169: }
170:
171: /**
172: * @inheritDoc
173: */
174: public void retractTuple(final ReteTuple leftTuple,
175: final PropagationContext context,
176: final InternalWorkingMemory workingMemory) {
177:
178: final BetaMemory memory = (BetaMemory) workingMemory
179: .getNodeMemory(this );
180: memory.getTupleMemory().remove(leftTuple);
181: CollectResult result = (CollectResult) memory
182: .getCreatedHandles().remove(leftTuple);
183: final InternalFactHandle handle = result.handle;
184:
185: // if tuple was propagated
186: if (result.propagated) {
187:
188: this .sink.propagateRetractTuple(leftTuple, handle, context,
189: workingMemory);
190:
191: // Destroying the acumulate result object
192: workingMemory.getFactHandleFactory().destroyFactHandle(
193: handle);
194: }
195: }
196:
197: /**
198: * @inheritDoc
199: *
200: * When a new object is asserted into a CollectNode, do this:
201: *
202: * 1. Select all matching tuples from left memory
203: * 2. For each matching tuple, call a modify tuple
204: *
205: */
206: public void assertObject(final InternalFactHandle handle,
207: final PropagationContext context,
208: final InternalWorkingMemory workingMemory) {
209:
210: final BetaMemory memory = (BetaMemory) workingMemory
211: .getNodeMemory(this );
212: memory.getFactHandleMemory().add(handle);
213:
214: if (workingMemory.isSequential()) {
215: // do nothing here, as we know there are no left tuples at this stage in sequential mode.
216: return;
217: }
218:
219: this .constraints.updateFromFactHandle(workingMemory, handle);
220:
221: // need to clone the tuples to avoid concurrent modification exceptions
222: Entry[] tuples = memory.getTupleMemory().toArray();
223: for (int i = 0; i < tuples.length; i++) {
224: ReteTuple tuple = (ReteTuple) tuples[i];
225: if (this .constraints.isAllowedCachedRight(tuple)) {
226: this .modifyTuple(true, tuple, handle, context,
227: workingMemory);
228: }
229: }
230: }
231:
232: /**
233: * @inheritDoc
234: *
235: * If an object is retract, call modify tuple for each
236: * tuple match.
237: */
238: public void retractObject(final InternalFactHandle handle,
239: final PropagationContext context,
240: final InternalWorkingMemory workingMemory) {
241:
242: final BetaMemory memory = (BetaMemory) workingMemory
243: .getNodeMemory(this );
244: if (!memory.getFactHandleMemory().remove(handle)) {
245: return;
246: }
247:
248: this .constraints.updateFromFactHandle(workingMemory, handle);
249:
250: // need to clone the tuples to avoid concurrent modification exceptions
251: Entry[] tuples = memory.getTupleMemory().toArray();
252: for (int i = 0; i < tuples.length; i++) {
253: ReteTuple tuple = (ReteTuple) tuples[i];
254: if (this .constraints.isAllowedCachedRight(tuple)) {
255:
256: this .modifyTuple(false, tuple, handle, context,
257: workingMemory);
258: }
259: }
260: }
261:
262: /**
263: * Modifies the results match for a tuple, retracting it and repropagating
264: * if constraints allow it
265: *
266: * @param leftTuple
267: * @param handle
268: * @param context
269: * @param workingMemory
270: */
271: public void modifyTuple(final boolean isAssert,
272: final ReteTuple leftTuple, InternalFactHandle handle,
273: final PropagationContext context,
274: final InternalWorkingMemory workingMemory) {
275:
276: final BetaMemory memory = (BetaMemory) workingMemory
277: .getNodeMemory(this );
278:
279: CollectResult result = (CollectResult) memory
280: .getCreatedHandles().get(leftTuple);
281:
282: // if tuple was propagated
283: if (result.propagated) {
284: this .sink.propagateRetractTuple(leftTuple, result.handle,
285: context, workingMemory);
286: result.propagated = false;
287: }
288:
289: // if there is a subnetwork, we need to unwrapp the object from inside the tuple
290: if (this .unwrapRightObject) {
291: handle = ((ReteTuple) handle.getObject()).getLastHandle();
292: }
293:
294: if (context.getType() == PropagationContext.ASSERTION) {
295: ((Collection) result.handle.getObject()).add(handle
296: .getObject());
297: } else if (context.getType() == PropagationContext.RETRACTION) {
298: ((Collection) result.handle.getObject()).remove(handle
299: .getObject());
300: } else if (context.getType() == PropagationContext.MODIFICATION) {
301: if (isAssert) {
302: ((Collection) result.handle.getObject()).add(handle
303: .getObject());
304: } else {
305: ((Collection) result.handle.getObject()).remove(handle
306: .getObject());
307: }
308: }
309:
310: // First alpha node filters
311: boolean isAllowed = true;
312: for (int i = 0, length = this .resultConstraints.length; i < length; i++) {
313: if (!this .resultConstraints[i].isAllowed(result.handle
314: .getObject(), workingMemory)) {
315: isAllowed = false;
316: break;
317: }
318: }
319: if (isAllowed) {
320: this .resultsBinder
321: .updateFromTuple(workingMemory, leftTuple);
322: if (this .resultsBinder.isAllowedCachedLeft(result.handle
323: .getObject())) {
324: result.propagated = true;
325: this .sink.propagateAssertTuple(leftTuple,
326: result.handle, context, workingMemory);
327: }
328: }
329: }
330:
331: public void updateSink(final TupleSink sink,
332: final PropagationContext context,
333: final InternalWorkingMemory workingMemory) {
334: final BetaMemory memory = (BetaMemory) workingMemory
335: .getNodeMemory(this );
336:
337: final Iterator it = memory.getCreatedHandles().iterator();
338:
339: for (ObjectEntry entry = (ObjectEntry) it.next(); entry != null; entry = (ObjectEntry) it
340: .next()) {
341: CollectResult result = (CollectResult) entry.getValue();
342: sink.assertTuple(new ReteTuple((ReteTuple) entry.getKey(),
343: result.handle), context, workingMemory);
344: }
345: }
346:
347: /* (non-Javadoc)
348: * @see org.drools.reteoo.BaseNode#hashCode()
349: */
350: public int hashCode() {
351: return this .leftInput.hashCode() ^ this .rightInput.hashCode()
352: ^ this .collect.hashCode()
353: ^ this .resultsBinder.hashCode()
354: ^ ArrayUtils.hashCode(this .resultConstraints);
355: }
356:
357: /* (non-Javadoc)
358: * @see java.lang.Object#equals(java.lang.Object)
359: */
360: public boolean equals(final Object object) {
361: if (this == object) {
362: return true;
363: }
364:
365: if (object == null || !(object instanceof CollectNode)) {
366: return false;
367: }
368:
369: final CollectNode other = (CollectNode) object;
370:
371: if (this .getClass() != other.getClass()
372: || (!this .leftInput.equals(other.leftInput))
373: || (!this .rightInput.equals(other.rightInput))
374: || (!this .constraints.equals(other.constraints))) {
375: return false;
376: }
377:
378: return this .collect.equals(other.collect)
379: && resultsBinder.equals(other.resultsBinder)
380: && Arrays.equals(this .resultConstraints,
381: other.resultConstraints);
382: }
383:
384: public String toString() {
385: return "[ " + this .getClass().getName() + "(" + this .id + ") ]";
386: }
387:
388: private static class CollectResult {
389: // keeping attributes public just for performance
390: public InternalFactHandle handle;
391: public boolean propagated;
392: }
393: }
|