Clover coverage report - Drools - 2.0-rc2
Coverage timestamp: Wed May 11 2005 07:12:26 BST
file stats: LOC: 603   Methods: 18
NCLOC: 285   Classes: 1
 
 Source file Conditionals Statements Methods TOTAL
Builder.java 85.4% 92.4% 100% 91.3%
coverage coverage
 1    package org.drools.reteoo;
 2   
 3    /*
 4    * $Id: Builder.java,v 1.72 2005/02/02 00:23:21 mproctor Exp $
 5    *
 6    * Copyright 2001-2003 (C) The Werken Company. All Rights Reserved.
 7    *
 8    * Redistribution and use of this software and associated documentation
 9    * ("Software"), with or without modification, are permitted provided that the
 10    * following conditions are met:
 11    *
 12    * 1. Redistributions of source code must retain copyright statements and
 13    * notices. Redistributions must also contain a copy of this document.
 14    *
 15    * 2. Redistributions in binary form must reproduce the above copyright notice,
 16    * this list of conditions and the following disclaimer in the documentation
 17    * and/or other materials provided with the distribution.
 18    *
 19    * 3. The name "drools" must not be used to endorse or promote products derived
 20    * from this Software without prior written permission of The Werken Company.
 21    * For written permission, please contact bob@werken.com.
 22    *
 23    * 4. Products derived from this Software may not be called "drools" nor may
 24    * "drools" appear in their names without prior written permission of The Werken
 25    * Company. "drools" is a trademark of The Werken Company.
 26    *
 27    * 5. Due credit should be given to The Werken Company. (http://werken.com/)
 28    *
 29    * THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS ``AS IS''
 30    * AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 31    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 32    * ARE DISCLAIMED. IN NO EVENT SHALL THE WERKEN COMPANY OR ITS CONTRIBUTORS BE
 33    * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 34    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 35    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 36    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 37    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 38    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 39    * POSSIBILITY OF SUCH DAMAGE.
 40    *
 41    */
 42   
 43    import java.util.ArrayList;
 44    import java.util.HashMap;
 45    import java.util.Iterator;
 46    import java.util.LinkedList;
 47    import java.util.List;
 48    import java.util.Map;
 49    import java.util.Set;
 50   
 51    import org.drools.RuleBase;
 52    import org.drools.RuleIntegrationException;
 53    import org.drools.RuleSetIntegrationException;
 54    import org.drools.conflict.DefaultConflictResolver;
 55    import org.drools.rule.Declaration;
 56    import org.drools.rule.Rule;
 57    import org.drools.rule.RuleSet;
 58    import org.drools.spi.Condition;
 59    import org.drools.spi.ConflictResolver;
 60    import org.drools.spi.RuleBaseContext;
 61   
 62    /**
 63    * Builds the Rete-OO network for a <code>RuleSet</code>.
 64    *
 65    * @see org.drools.rule.RuleSet
 66    *
 67    * @author <a href="mailto:bob@werken.com">bob mcwhirter </a>
 68    *
 69    * TODO Make joinForCondition actually be intelligent enough to build optimal
 70    * joins. Currently using forgy's original description of 2-input nodes, which I
 71    * feel (but don't know for sure, is sub-optimal.
 72    */
 73    public class Builder
 74    {
 75    // ------------------------------------------------------------
 76    // Instance members
 77    // ------------------------------------------------------------
 78   
 79    /** Rete network to build against. */
 80    private Rete rete;
 81   
 82    /** Rule-sets added. */
 83    private List ruleSets;
 84   
 85    /** Nodes that have been attached. */
 86    private Map attachedNodes;
 87   
 88    private Map applicationData;
 89   
 90    private FactHandleFactory factHandleFactory;
 91   
 92    private ConflictResolver conflictResolver;
 93   
 94    private RuleBaseContext ruleBaseContext;
 95   
 96    // ------------------------------------------------------------
 97    // Constructors
 98    // ------------------------------------------------------------
 99   
 100    /**
 101    * Construct a <code>Builder</code> against an existing <code>Rete</code>
 102    * network.
 103    */
 104  7 public Builder()
 105    {
 106  7 reset( );
 107  7 this.ruleBaseContext = new RuleBaseContext( );
 108    }
 109   
 110    /**
 111    * Construct a <code>Builder</code> against an existing <code>Rete</code>
 112    * network.
 113    */
 114  66 public Builder(RuleBaseContext ruleBaseContext)
 115    {
 116  66 reset( );
 117  66 this.ruleBaseContext = ruleBaseContext;
 118    }
 119   
 120    // ------------------------------------------------------------
 121    // Instance methods
 122    // ------------------------------------------------------------
 123   
 124    /**
 125    * Build the <code>RuleBase</code>.
 126    *
 127    * @return The rule-base.
 128    */
 129  67 public RuleBase buildRuleBase()
 130    {
 131  67 RuleBase ruleBase = new RuleBaseImpl( this.rete,
 132    this.conflictResolver,
 133    this.factHandleFactory,
 134    this.ruleSets,
 135    this.applicationData,
 136    this.ruleBaseContext );
 137   
 138  67 reset( );
 139   
 140  67 return ruleBase;
 141    }
 142   
 143    /**
 144    * Set the <code>FactHandleFactory</code>.
 145    *
 146    * @param factHandleFactory
 147    * The fact handle factory.
 148    */
 149  61 public void setFactHandleFactory(FactHandleFactory factHandleFactory)
 150    {
 151  61 this.factHandleFactory = factHandleFactory;
 152    }
 153   
 154    /**
 155    * Set the <code>ConflictResolver</code>.
 156    *
 157    * @param conflictResolver
 158    * The conflict resolver.
 159    */
 160  3 public void setConflictResolver(ConflictResolver conflictResolver)
 161    {
 162  3 this.conflictResolver = conflictResolver;
 163    }
 164   
 165    /**
 166    * Add a <code>RuleSet</code> to the network.
 167    *
 168    * @param ruleSet
 169    * The rule-set to add.
 170    *
 171    * @throws RuleIntegrationException
 172    * if an error prevents complete construction of the network for
 173    * the <code>Rule</code>.
 174    */
 175  67 public void addRuleSet(RuleSet ruleSet) throws RuleIntegrationException,
 176    RuleSetIntegrationException
 177    {
 178  67 this.ruleSets.add( ruleSet );
 179   
 180  67 Map newApplicationData = ruleSet.getApplicationData( );
 181  67 Iterator it = newApplicationData.keySet( ).iterator( );
 182  67 String identifier;
 183  67 Class type;
 184  67 while ( it.hasNext( ) )
 185    {
 186  1 identifier = (String) it.next( );
 187  1 type = (Class) newApplicationData.get( identifier );
 188  1 if ( this.applicationData.containsKey( identifier ) && !this.applicationData.get( identifier ).equals( type ) )
 189    {
 190  0 throw new RuleSetIntegrationException( ruleSet );
 191    }
 192   
 193    }
 194  67 this.applicationData.putAll( newApplicationData );
 195   
 196  67 Rule[] rules = ruleSet.getRules( );
 197   
 198  67 for ( int i = 0; i < rules.length; ++i )
 199    {
 200  88 addRule( rules[i] );
 201    }
 202    }
 203   
 204    /**
 205    * Add a <code>Rule</code> to the network.
 206    *
 207    * @param rule
 208    * The rule to add.
 209    *
 210    * @throws RuleIntegrationException
 211    * if an error prevents complete construction of the network for
 212    * the <code>Rule</code>.
 213    */
 214  88 protected void addRule(Rule rule) throws RuleIntegrationException
 215    {
 216  88 List conds = new LinkedList( rule.getConditions( ) );
 217  88 List leafNodes = createParameterNodes( rule );
 218   
 219  88 boolean performedJoin;
 220  88 boolean joinedForCondition;
 221   
 222  88 while ( true )
 223    {
 224  185 joinedForCondition = false;
 225   
 226  185 if ( !conds.isEmpty( ) )
 227    {
 228  173 attachConditions( rule,
 229    conds,
 230    leafNodes );
 231    }
 232   
 233  185 performedJoin = createJoinNodes( leafNodes );
 234   
 235  185 if ( !performedJoin && !conds.isEmpty( ) )
 236    {
 237  85 joinedForCondition = joinForCondition( conds,
 238    leafNodes );
 239    }
 240   
 241  185 if ( joinedForCondition )
 242    {
 243  85 continue;
 244    }
 245   
 246  100 if ( leafNodes.size( ) > 1 )
 247    {
 248  12 if ( !performedJoin )
 249    {
 250  12 joinArbitrary( leafNodes );
 251    }
 252    }
 253    else
 254    {
 255  88 break;
 256    }
 257    }
 258   
 259  88 if ( leafNodes.size( ) != 1 )
 260    {
 261  0 throw new RuleIntegrationException( rule );
 262    }
 263   
 264  88 TupleSource lastNode = (TupleSource) leafNodes.iterator( ).next( );
 265   
 266  88 TerminalNode terminal = new TerminalNode( lastNode,
 267    rule );
 268    }
 269   
 270    /**
 271    * Create the <code>ParameterNode</code> s for the <code>Rule</code>,
 272    * and link into the network.
 273    *
 274    * @param rule
 275    * The rule.
 276    *
 277    * @return A <code>Set</code> of <code>ParameterNodes</code> created and
 278    * linked into the network.
 279    */
 280  93 List createParameterNodes(Rule rule)
 281    {
 282  93 List leafNodes = new LinkedList( );
 283  93 Iterator declIter = rule.getParameterDeclarations( ).iterator( );
 284   
 285  93 Declaration eachDecl;
 286  93 while ( declIter.hasNext( ) )
 287    {
 288  191 eachDecl = (Declaration) declIter.next( );
 289   
 290  191 attachNode( new ParameterNode( this.rete.getOrCreateObjectTypeNode( eachDecl.getObjectType( ) ),
 291    eachDecl ),
 292    leafNodes );
 293    }
 294   
 295  93 return leafNodes;
 296    }
 297   
 298    /**
 299    * Attaches a node into the network. If a node already exists that could
 300    * substitute, it is used instead.
 301    *
 302    * @param candidate
 303    * The node to attach.
 304    * @param leafNodes
 305    * The list to which the newly added node will be added.
 306    */
 307  422 private void attachNode(TupleSource candidate,
 308    List leafNodes)
 309    {
 310  422 TupleSource node = (TupleSource) this.attachedNodes.get( candidate );
 311   
 312  422 if ( node == null )
 313    {
 314  369 candidate.attach( );
 315   
 316  369 this.attachedNodes.put( candidate,
 317    candidate );
 318   
 319  369 node = candidate;
 320    }
 321   
 322  422 leafNodes.add( node );
 323    }
 324   
 325    /**
 326    * Create and attach <code>Condition</code> s to the network.
 327    *
 328    * <p>
 329    * It may not be possible to satisfy all filder conditions on the first
 330    * pass. This method removes satisfied conditions from the
 331    * <code>Condition</code> parameter, and leaves unsatisfied ones in the
 332    * <code>Set</code>.
 333    * </p>
 334    *
 335    * @param rule
 336    * The rule.
 337    * @param conds
 338    * Set of <code>Conditions</code> to attempt attaching.
 339    * @param leafNodes
 340    * The leaf node.
 341    */
 342  173 private void attachConditions(Rule rule,
 343    List conds,
 344    List leafNodes)
 345    {
 346  173 Iterator condIter = conds.iterator( );
 347  173 Condition eachCond;
 348  173 TupleSource tupleSource;
 349   
 350  173 while ( condIter.hasNext( ) )
 351    {
 352  240 eachCond = (Condition) condIter.next( );
 353   
 354  240 tupleSource = findMatchingTupleSourceForCondition( eachCond,
 355    leafNodes );
 356   
 357  240 if ( tupleSource == null )
 358    {
 359  106 continue;
 360    }
 361   
 362  134 condIter.remove( );
 363   
 364  134 attachNode( new ConditionNode( rule,
 365    tupleSource,
 366    eachCond ),
 367    leafNodes );
 368    }
 369    }
 370   
 371    /**
 372    * Join two arbitrary leaves in order to satisfy a filter that currently
 373    * cannot be applied.
 374    *
 375    * @param conds
 376    * The filter conditions remaining.
 377    * @param leafNodes
 378    * Available leaf nodes.
 379    *
 380    * @return <code>true</code> if a join was possible, otherwise,
 381    * <code>false</code>.
 382    */
 383  85 private boolean joinForCondition(List conds,
 384    List leafNodes)
 385    {
 386  85 return joinArbitrary( leafNodes );
 387    }
 388   
 389    /**
 390    * Join two arbitrary leaves in order to satisfy a filter that currently
 391    * cannot be applied.
 392    *
 393    * @param leafNodes
 394    * Available leaf nodes.
 395    *
 396    * @return <code>true</code> if successfully joined some nodes, otherwise
 397    * <code>false</code>.
 398    */
 399  97 private boolean joinArbitrary(List leafNodes)
 400    {
 401  97 Iterator leafIter = leafNodes.iterator( );
 402   
 403  97 TupleSource left = (TupleSource) leafIter.next( );
 404   
 405  97 if ( !leafIter.hasNext( ) )
 406    {
 407  0 return false;
 408    }
 409   
 410  97 leafIter.remove( );
 411   
 412  97 TupleSource right = (TupleSource) leafIter.next( );
 413   
 414  97 leafIter.remove( );
 415   
 416  97 attachNode( new JoinNode( left,
 417    right ),
 418    leafNodes );
 419   
 420  97 return true;
 421    }
 422   
 423    /**
 424    * Create and attach <code>JoinNode</code> s to the network.
 425    *
 426    * <p>
 427    * It may not be possible to join all <code>leafNodes</code>.
 428    * </p>
 429    *
 430    * <p>
 431    * Any <code>leafNodes</code> member that particiates in a <i>join </i> is
 432    * removed from the <code>leafNodes</code> collection, and replaced by the
 433    * joining <code>JoinNode</code>.
 434    * </p>
 435    *
 436    * @param leafNodes
 437    * The current attachable leaf nodes of the network.
 438    *
 439    * @return <code>true</code> if at least one <code>JoinNode</code> was
 440    * created, else <code>false</code>.
 441    */
 442  185 private boolean createJoinNodes(List leafNodes)
 443    {
 444  185 boolean performedJoin = false;
 445   
 446  185 Object[] nodesArray = leafNodes.toArray( );
 447   
 448  185 TupleSource left;
 449  185 TupleSource right;
 450   
 451  185 for ( int i = 0; i < nodesArray.length; ++i )
 452    {
 453  299 left = (TupleSource) nodesArray[i];
 454   
 455  299 if ( leafNodes.contains( left ) )
 456    {
 457  299 for ( int j = i + 1; j < nodesArray.length; ++j )
 458    {
 459  131 right = (TupleSource) nodesArray[j];
 460   
 461  131 if ( leafNodes.contains( right ) && canBeJoined( left,
 462    right ) )
 463    {
 464  0 leafNodes.remove( left );
 465  0 leafNodes.remove( right );
 466   
 467  0 attachNode( new JoinNode( left,
 468    right ),
 469    leafNodes );
 470   
 471  0 performedJoin = true;
 472   
 473  0 break;
 474    }
 475    }
 476    }
 477    }
 478   
 479  185 return performedJoin;
 480    }
 481   
 482    /**
 483    * Determine if two <code>TupleSource</code> s can be joined.
 484    *
 485    * @param left
 486    * The left tuple source
 487    * @param right
 488    * The right tuple source
 489    *
 490    * @return <code>true</code> if they can be joined (they share at least
 491    * one common member declaration), else <code>false</code>.
 492    */
 493  131 private boolean canBeJoined(TupleSource left,
 494    TupleSource right)
 495    {
 496  131 Set leftDecls = left.getTupleDeclarations( );
 497  131 Iterator rightDeclIter = right.getTupleDeclarations( ).iterator( );
 498   
 499  131 while ( rightDeclIter.hasNext( ) )
 500    {
 501  148 if ( leftDecls.contains( rightDeclIter.next( ) ) )
 502    {
 503  0 return true;
 504    }
 505    }
 506   
 507  131 return false;
 508    }
 509   
 510    /**
 511    * Locate a <code>TupleSource</code> suitable for attaching the
 512    * <code>Condition</code> and remove it.
 513    *
 514    * @param condition
 515    * The <code>Condition</code> to attach.
 516    * @param sources
 517    * Candidate <code>TupleSources</code>.
 518    *
 519    * @return Matching <code>TupleSource</code> if a suitable one can be
 520    * found, else <code>null</code>.
 521    */
 522  243 TupleSource findMatchingTupleSourceForCondition(Condition condition,
 523    List sources)
 524    {
 525  243 Iterator sourceIter = sources.iterator( );
 526  243 TupleSource eachSource;
 527   
 528  243 while ( sourceIter.hasNext( ) )
 529    {
 530  391 eachSource = (TupleSource) sourceIter.next( );
 531   
 532  391 if ( matches( condition,
 533    eachSource.getTupleDeclarations( ) ) )
 534    {
 535  136 sourceIter.remove( );
 536  136 return eachSource;
 537    }
 538    }
 539   
 540  107 return null;
 541    }
 542   
 543    /**
 544    * Determine if a set of <code>Declarations</code> match those required by
 545    * a <code>Condition</code>.
 546    *
 547    * @param condition
 548    * The <code>Condition</code>.
 549    * @param declarations
 550    * The set of <code>Declarations</code> to compare against.
 551    *
 552    * @return <code>true</code> if the set of <code>Declarations</code> is
 553    * a super-set of the <code>Declarations</code> required by the
 554    * <code>Condition</code>.
 555    */
 556  394 boolean matches(Condition condition,
 557    Set declarations)
 558    {
 559  394 return containsAll( declarations,
 560    condition.getRequiredTupleMembers( ) );
 561    }
 562   
 563    /**
 564    * Determine if a set of <code>Declarations</code> is a super set of
 565    * required <code>Declarations</code>
 566    *
 567    * @param declarations
 568    * The set of <code>Declarations</code> to compare against.
 569    *
 570    * @param requiredDecls
 571    * The required <code>Declarations</code>.
 572    * @return <code>true</code> if the set of <code>Declarations</code> is
 573    * a super-set of the <code>Declarations</code> required by the
 574    * <code>Condition</code>.
 575    */
 576  394 private boolean containsAll(Set declarations,
 577    Declaration[] requiredDecls)
 578    {
 579  394 for ( int i = requiredDecls.length - 1; i >= 0; i-- )
 580    {
 581  584 if ( !declarations.contains( requiredDecls[i] ) )
 582    {
 583  257 return false;
 584    }
 585   
 586    }
 587   
 588  137 return true;
 589    }
 590   
 591    /**
 592    * Reset the internal state.
 593    */
 594  140 private void reset()
 595    {
 596  140 this.rete = new Rete( );
 597  140 this.ruleSets = new ArrayList( );
 598  140 this.attachedNodes = new HashMap( );
 599  140 this.applicationData = new HashMap( );
 600  140 this.factHandleFactory = new DefaultFactHandleFactory( );
 601  140 this.conflictResolver = DefaultConflictResolver.getInstance( );
 602    }
 603    }