Coverage Report - org.kuali.student.common.messagebuilder.booleanmessage.ast.BinaryMessageTree
 
Classes in this File Line Coverage Branch Coverage Complexity
BinaryMessageTree
95%
60/63
81%
26/32
2.889
BinaryMessageTree$1
100%
2/2
N/A
2.889
 
 1  
 /**
 2  
  * Copyright 2010 The Kuali Foundation Licensed under the
 3  
  * Educational Community License, Version 2.0 (the "License"); you may
 4  
  * not use this file except in compliance with the License. You may
 5  
  * obtain a copy of the License at
 6  
  *
 7  
  * http://www.osedu.org/licenses/ECL-2.0
 8  
  *
 9  
  * Unless required by applicable law or agreed to in writing,
 10  
  * software distributed under the License is distributed on an "AS IS"
 11  
  * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 12  
  * or implied. See the License for the specific language governing
 13  
  * permissions and limitations under the License.
 14  
  */
 15  
 
 16  
 package org.kuali.student.common.messagebuilder.booleanmessage.ast;
 17  
 
 18  
 import java.util.ArrayList;
 19  
 import java.util.List;
 20  
 import java.util.Locale;
 21  
 import java.util.Map;
 22  
 
 23  
 import org.antlr.runtime.ANTLRStringStream;
 24  
 import org.antlr.runtime.CommonTokenStream;
 25  
 import org.antlr.runtime.RecognitionException;
 26  
 import org.antlr.runtime.Token;
 27  
 import org.antlr.runtime.tree.CommonTreeAdaptor;
 28  
 import org.antlr.runtime.tree.TreeAdaptor;
 29  
 
 30  
 import org.kuali.student.common.messagebuilder.booleanmessage.BooleanMessage;
 31  
 import org.kuali.student.common.messagebuilder.booleanmessage.ast.exceptions.BooleanFunctionException;
 32  
 import org.kuali.student.common.messagebuilder.booleanmessage.ast.parsers.BooleanFunctionLexer;
 33  
 import org.kuali.student.common.messagebuilder.booleanmessage.ast.parsers.BooleanFunctionParser;
 34  
 
 35  
 import org.slf4j.Logger;
 36  
 import org.slf4j.LoggerFactory;
 37  
 
 38  
 public class BinaryMessageTree {
 39  
     /** SLF4J logging framework */
 40  1
     final static Logger logger = LoggerFactory.getLogger(BinaryMessageTree.class);
 41  
 
 42  
     private BooleanNode root;
 43  
         private ArrayList<BooleanNode> nodes;
 44  
         private String language;
 45  
         
 46  
         private Map<String, ? extends BooleanMessage> nodeMessageMap;
 47  
         
 48  1
         private static final TreeAdaptor adaptor = new CommonTreeAdaptor() {
 49  
                 public Object create(Token payload) {
 50  720
                         return new BooleanNode(payload);
 51  
                 }
 52  
         };
 53  
         
 54  
         /**
 55  
          * Constructor.
 56  
          */
 57  24
         public BinaryMessageTree() {
 58  24
                 language = Locale.getDefault().getLanguage();
 59  24
                 nodes = new ArrayList<BooleanNode>();
 60  24
         }
 61  
 
 62  
         /**
 63  
          * Constructor. 
 64  
          * Creates a new binary message tree for the in the specified language
 65  
          * for the boolean message map.
 66  
          *  
 67  
          * @param messageLanguage Language for the message 
 68  
          * @param messageMap map of boolean messages
 69  
          */
 70  22
         public BinaryMessageTree(String language, Map<String, ? extends BooleanMessage> messageMap) {
 71  22
                 this.language = language;
 72  22
                 nodeMessageMap = messageMap;
 73  22
                 nodes = new ArrayList<BooleanNode>();
 74  22
         }
 75  
         
 76  
         /**
 77  
          * <p>Builds the binary message tree from the <code>booleanFunction</code>.</p>
 78  
          * <p>Order of boolean operation: ANDs before ORs and 
 79  
          * operations inside parentheses before anything else.</p>
 80  
          * <p>Example: A AND B OR C AND D evaluates to (A AND B) OR (C AND D).</p>
 81  
          * 
 82  
          * @param booleanFunction: Boolean expression 
 83  
          * @return Root boolean node
 84  
          */
 85  
         public BooleanNode buildTree(String booleanFunction){
 86  46
                 if (booleanFunction == null || booleanFunction.trim().isEmpty()) {
 87  0
                         return null;
 88  
                 }
 89  
                 
 90  46
                 BooleanFunctionLexer lex = new BooleanFunctionLexer(new ANTLRStringStream(booleanFunction) );
 91  46
                 CommonTokenStream tokens = new CommonTokenStream(lex);
 92  
                 
 93  46
                 BooleanFunctionParser parser = new BooleanFunctionParser(tokens);
 94  46
                 parser.setTreeAdaptor(adaptor);
 95  
                 
 96  
                 // parse the expression
 97  46
                 BooleanFunctionParser.booleanExpression_return booleanExpression = null;
 98  
         try {
 99  46
                 booleanExpression = parser.booleanExpression();  
 100  1
         } catch (RecognitionException e)  {
 101  1
                 String parserErrorMsg = parser.getErrorMessage(e, parser.getTokenNames());
 102  1
                 String errorMsg = "Boolean Function Parser Error. " +
 103  
                                 "Invalid Boolean Expression: '" + booleanFunction + "'; " + parserErrorMsg;  
 104  1
                 throw new BooleanFunctionException(errorMsg, e);
 105  45
         }
 106  
         // get the root of the AST from the parsed expression
 107  45
         return root = (BooleanNode) booleanExpression.getTree();
 108  
         }
 109  
         
 110  
         /** 
 111  
          * This method walks the tree depth first, while setting node values with 
 112  
          * setNode(). Setting a nodes parent and adding the node to the ArrayList 
 113  
          * for later. This method has to be called after buildTree. 
 114  
          * 
 115  
          * @param bnode Boolean node
 116  
          * @param parent Parent boolean node
 117  
          */
 118  
         public void traverseTreePostOrder(BooleanNode bnode, BooleanNode parent) {
 119  212
                 for (int i = 0; i < bnode.getChildCount(); i++) {
 120  96
                         traverseTreePostOrder((BooleanNode) bnode.getChild(i), bnode);
 121  
                 }
 122  116
                 setNode(bnode);
 123  116
                 if (parent != null) {
 124  96
                         bnode.setParent(parent);
 125  
                 }
 126  
 
 127  116
                 if(logger.isDebugEnabled()) {
 128  0
                     logger.debug(bnode.getText());
 129  
                 }
 130  116
                 nodes.add(bnode);
 131  116
         }
 132  
 
 133  
         /**
 134  
          * This method walks the tree depth first. Setting a nodes parent and 
 135  
          * adding the node to the ArrayList for later. 
 136  
          * This method has to be called after buildTree. 
 137  
          * 
 138  
          * @param bnode Boolean node
 139  
          * @param parent Parent boolean node
 140  
          */
 141  
         public void traverseTreePostOrderDontSetNode(BooleanNode bnode, BooleanNode parent) {
 142  126
                 if (bnode != null) {
 143  230
                         for (int i = 0; i < bnode.getChildCount(); i++ ) {
 144  104
                                 traverseTreePostOrderDontSetNode((BooleanNode)bnode.getChild(i), bnode);
 145  
                         }
 146  
 
 147  126
                         if (parent != null) {
 148  104
                                 bnode.setParent(parent);
 149  
                         }
 150  
                     
 151  126
                         if(logger.isDebugEnabled()) {
 152  0
                                 logger.debug(bnode.getText());
 153  
                         }
 154  126
                         nodes.add(bnode);
 155  
                 }
 156  126
         }
 157  
 
 158  
         /** 
 159  
          * Here we set the value(true or false) and the failure message for each node. 
 160  
          * Setting the failure message here does not mean the function failed, the node just holds the message. 
 161  
          * Failure is determined by the rules engine which will extract the failure message.
 162  
          * 
 163  
          * @param bnode Boolean node
 164  
          */
 165  
         private void setNode(BooleanNode bnode) {
 166  116
                 bnode.setLanguage(language);
 167  
                 
 168  116
                 if (bnode.getChildCount() == 0) {
 169  
                         // If node is a leaf then set value and message
 170  68
                         BooleanMessage message = nodeMessageMap.get(bnode.getLabel());
 171  68
                         bnode.setValue(message.isSuccesful());
 172  68
                         bnode.setNodeMessage(message.getMessage());
 173  68
                 }
 174  
                 else {
 175  
                         // If node is intermediate, compute value and set it
 176  48
                         BooleanNode child0 = (BooleanNode)bnode.getChild(0);
 177  48
                         BooleanNode child1 = (BooleanNode)bnode.getChild(1);
 178  
                         
 179  48
                         if ( bnode.getLabel().equalsIgnoreCase("+") ) {
 180  
                                 // OR node
 181  20
                                 Boolean newValue = child0.getValue() || child1.getValue();
 182  20
                                 bnode.setValue(newValue);
 183  20
                                 bnode.setNodeMessage("null");
 184  20
                         }
 185  28
                         else if ( bnode.getLabel().equalsIgnoreCase("*") ) {
 186  
                                 // AND node
 187  28
                                 Boolean newValue = child0.getValue() && child1.getValue();
 188  28
                                 bnode.setValue(newValue);
 189  28
                                 bnode.setNodeMessage("null");
 190  
                         }
 191  
                 }
 192  116
         }
 193  
         
 194  
         /**
 195  
          * Gets all boolean nodes.
 196  
          * 
 197  
          * @return All boolean nodes
 198  
          */
 199  
         public List<BooleanNode> getAllNodes() {
 200  42
                 return nodes;
 201  
         }
 202  
 
 203  
         /**
 204  
          * Gets the root boolean node.
 205  
          * 
 206  
          * @return Root boolean node
 207  
          */
 208  
         public BooleanNode getRoot() {
 209  39
                 return root;
 210  
         }
 211  
 }