/** * 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.reteoo.compiled; import org.drools.base.ClassFieldReader; import org.drools.core.util.Iterator; import org.drools.core.util.ObjectHashMap; import org.drools.reteoo.*; import org.drools.rule.LiteralConstraint; import org.drools.spi.AlphaNodeFieldConstraint; /** * This class is used for reading an {@link ObjectTypeNode} using callbacks. * <p/> * The user defines a number of callback methods in a {@link NetworkHandler} that will be called when events occur * during parsing. The events include : * <li>ObjectTypeNode</li> * <li>Non-hashed and hashed AlphaNodes</li> * <li>BetaNodes</li> * <li>LeftInputAdapterNodes</li> * <p/> * Events are fired when each of these network features are encountered, and again when the end of them is encountered. * OTN parsing is unidirectional; previously parsed data cannot be re-read without starting the parsing operation again. * * @author <a href="mailto:stampy88@yahoo.com">dave sinclair</a> */ public class ObjectTypeNodeParser { /** * OTN we are parsing/traversing */ private final ObjectTypeNode objectTypeNode; /** * Creates a new parser for the specified ObjectTypeNode * * @param objectTypeNode otn to parse */ public ObjectTypeNodeParser(ObjectTypeNode objectTypeNode) { this.objectTypeNode = objectTypeNode; } /** * Parse the {@link #objectTypeNode}. * <p/> * <p>The application can use this method to instruct the OTN parser to begin parsing an {@link ObjectTypeNode}.</p> * <p/> * Once a parse is complete, an application may reuse the same Parser object, possibly with a different * {@link NetworkHandler}.</p> * * @param handler handler that will receieve the events generated by this parser * @see NetworkHandler */ public void accept(NetworkHandler handler) { ObjectSinkPropagator propagator = objectTypeNode.getSinkPropagator(); handler.startObjectTypeNode(objectTypeNode); traversePropagator(propagator, handler); handler.endObjectTypeNode(objectTypeNode); } private void traversePropagator(ObjectSinkPropagator propagator, NetworkHandler handler) { if (propagator instanceof SingleObjectSinkAdapter) { // we know there is only a single child sink for this propagator ObjectSink sink = propagator.getSinks()[0]; traverseSink(sink, handler); } else if (propagator instanceof CompositeObjectSinkAdapter) { CompositeObjectSinkAdapter composite = (CompositeObjectSinkAdapter) propagator; traverseSinkLisk(composite.getHashableSinks(), handler); traverseSinkLisk(composite.getOthers(), handler); traverseHashedAlphaNodes(composite.getHashedSinkMap(), handler); } } private void traversePropagator(LeftTupleSinkPropagator propagator, NetworkHandler handler) { if (propagator instanceof SingleLeftTupleSinkAdapter) { // we know there is only a single child sink for this propagator LeftTupleSink sink = propagator.getSinks()[0]; traverseSink(sink, handler); } else if (propagator instanceof CompositeLeftTupleSinkAdapter) { CompositeLeftTupleSinkAdapter composite = (CompositeLeftTupleSinkAdapter) propagator; LeftTupleSink[] sinks = composite.getSinks(); traverseSinkLisk(sinks, handler); } } private void traverseSinkLisk(ObjectSinkNodeList sinks, NetworkHandler handler) { if (sinks != null) { for (ObjectSinkNode sink = sinks.getFirst(); sink != null; sink = sink.getNextObjectSinkNode()) { traverseSink(sink, handler); } } } private void traverseSinkLisk(LeftTupleSink[] sinks, NetworkHandler handler) { if (sinks != null) { for (int sinkIndex = 0; sinkIndex < sinks.length; ++sinkIndex) { traverseSink(sinks[sinkIndex], handler); } } } private void traverseHashedAlphaNodes(ObjectHashMap hashedAlphaNodes, NetworkHandler handler) { if (hashedAlphaNodes != null && hashedAlphaNodes.size() > 0) { AlphaNode firstAlpha = getFirstAlphaNode(hashedAlphaNodes); ClassFieldReader hashedFieldReader = getClassFieldReaderForHashedAlpha(firstAlpha); // start the hashed alphas handler.startHashedAlphaNodes(hashedFieldReader); Iterator iter = hashedAlphaNodes.iterator(); for (ObjectHashMap.ObjectEntry entry = (ObjectHashMap.ObjectEntry) iter.next(); entry != null; entry = (ObjectHashMap.ObjectEntry) iter.next()) { CompositeObjectSinkAdapter.HashKey hashKey = (CompositeObjectSinkAdapter.HashKey) entry.getKey(); AlphaNode alphaNode = (AlphaNode) entry.getValue(); handler.startHashedAlphaNode(alphaNode, hashKey.getObjectValue()); // traverse the propagator for each alpha traversePropagator(alphaNode.getSinkPropagator(), handler); handler.endHashedAlphaNode(alphaNode, hashKey.getObjectValue()); } // end of the hashed alphas handler.endHashedAlphaNodes(hashedFieldReader); } } private void traverseSink(ObjectSink sink, NetworkHandler handler) { if (sink instanceof AlphaNode) { AlphaNode alphaNode = (AlphaNode) sink; handler.startNonHashedAlphaNode(alphaNode); traversePropagator(alphaNode.getSinkPropagator(), handler); handler.endNonHashedAlphaNode(alphaNode); } else if (sink instanceof BetaNode) { BetaNode betaNode = (BetaNode) sink; handler.startBetaNode(betaNode); // todo traverse beta handler.endBetaNode(betaNode); } else if (sink instanceof LeftInputAdapterNode) { LeftInputAdapterNode leftInputAdapterNode = (LeftInputAdapterNode) sink; handler.startLeftInputAdapterNode(leftInputAdapterNode); // todo traverse lia handler.endLeftInputAdapterNode(leftInputAdapterNode); } } private void traverseSink(LeftTupleSink sink, NetworkHandler handler) { // todo traverse sink's propagator } /** * Returns the first {@link org.drools.reteoo.AlphaNode} from the specified {@link ObjectHashMap}. * * @param hashedAlphaNodes map of hashed AlphaNodes * @return first alpha from the specified map * @throws IllegalArgumentException thrown if the map doesn't contain any alpha nodes */ private AlphaNode getFirstAlphaNode(final ObjectHashMap hashedAlphaNodes) throws IllegalArgumentException { AlphaNode firstAlphaNode; final Iterator iter = hashedAlphaNodes.iterator(); final ObjectHashMap.ObjectEntry entry = (ObjectHashMap.ObjectEntry) iter.next(); if (entry != null) { firstAlphaNode = (AlphaNode) entry.getValue(); } else { throw new IllegalArgumentException("ObjectHashMap does not contain any hashed AlphaNodes!"); } return firstAlphaNode; } /** * Returns the {@link ClassFieldReader} for the hashed AlphaNode. The AlphaNode's constraint has to be a * LiteralConstraint. This is the only type of hashed alpha currently supported. * * @param alphaNode hashed alpha to get reader for * @return ClassFieldReader * @throws IllegalArgumentException thrown if the AlphaNode's {@link org.drools.spi.AlphaNodeFieldConstraint} is not a * {@link org.drools.rule.LiteralConstraint}. */ private ClassFieldReader getClassFieldReaderForHashedAlpha(final AlphaNode alphaNode) throws IllegalArgumentException { final AlphaNodeFieldConstraint fieldConstraint = alphaNode.getConstraint(); if (!(fieldConstraint instanceof LiteralConstraint)) { throw new IllegalArgumentException("Only support LiteralConstraint hashed AlphaNodes, not " + fieldConstraint.getClass()); } // we need to get the first alpha in the map to get the attribute name that be use for the prefix of the // generated variable name final LiteralConstraint literalConstraint = (LiteralConstraint) alphaNode.getConstraint(); return (ClassFieldReader) literalConstraint.getFieldExtractor(); } }