Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
BinaryMessageTree |
|
| 2.888888888888889;2.889 | ||||
BinaryMessageTree$1 |
|
| 2.888888888888889;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 | 0 | 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 | 0 | private static final TreeAdaptor adaptor = new CommonTreeAdaptor() { |
49 | public Object create(Token payload) { | |
50 | 0 | return new BooleanNode(payload); |
51 | } | |
52 | }; | |
53 | ||
54 | /** | |
55 | * Constructor. | |
56 | */ | |
57 | 0 | public BinaryMessageTree() { |
58 | 0 | language = Locale.getDefault().getLanguage(); |
59 | 0 | nodes = new ArrayList<BooleanNode>(); |
60 | 0 | } |
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 | 0 | public BinaryMessageTree(String language, Map<String, ? extends BooleanMessage> messageMap) { |
71 | 0 | this.language = language; |
72 | 0 | nodeMessageMap = messageMap; |
73 | 0 | nodes = new ArrayList<BooleanNode>(); |
74 | 0 | } |
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 | 0 | if (booleanFunction == null || booleanFunction.trim().isEmpty()) { |
87 | 0 | return null; |
88 | } | |
89 | ||
90 | 0 | BooleanFunctionLexer lex = new BooleanFunctionLexer(new ANTLRStringStream(booleanFunction) ); |
91 | 0 | CommonTokenStream tokens = new CommonTokenStream(lex); |
92 | ||
93 | 0 | BooleanFunctionParser parser = new BooleanFunctionParser(tokens); |
94 | 0 | parser.setTreeAdaptor(adaptor); |
95 | ||
96 | // parse the expression | |
97 | 0 | BooleanFunctionParser.booleanExpression_return booleanExpression = null; |
98 | try { | |
99 | 0 | booleanExpression = parser.booleanExpression(); |
100 | 0 | } catch (RecognitionException e) { |
101 | 0 | String parserErrorMsg = parser.getErrorMessage(e, parser.getTokenNames()); |
102 | 0 | String errorMsg = "Boolean Function Parser Error. " + |
103 | "Invalid Boolean Expression: '" + booleanFunction + "'; " + parserErrorMsg; | |
104 | 0 | throw new BooleanFunctionException(errorMsg, e); |
105 | 0 | } |
106 | // get the root of the AST from the parsed expression | |
107 | 0 | 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 | 0 | for (int i = 0; i < bnode.getChildCount(); i++) { |
120 | 0 | traverseTreePostOrder((BooleanNode) bnode.getChild(i), bnode); |
121 | } | |
122 | 0 | setNode(bnode); |
123 | 0 | if (parent != null) { |
124 | 0 | bnode.setParent(parent); |
125 | } | |
126 | ||
127 | 0 | if(logger.isDebugEnabled()) { |
128 | 0 | logger.debug(bnode.getText()); |
129 | } | |
130 | 0 | nodes.add(bnode); |
131 | 0 | } |
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 | 0 | if (bnode != null) { |
143 | 0 | for (int i = 0; i < bnode.getChildCount(); i++ ) { |
144 | 0 | traverseTreePostOrderDontSetNode((BooleanNode)bnode.getChild(i), bnode); |
145 | } | |
146 | ||
147 | 0 | if (parent != null) { |
148 | 0 | bnode.setParent(parent); |
149 | } | |
150 | ||
151 | 0 | if(logger.isDebugEnabled()) { |
152 | 0 | logger.debug(bnode.getText()); |
153 | } | |
154 | 0 | nodes.add(bnode); |
155 | } | |
156 | 0 | } |
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 | 0 | bnode.setLanguage(language); |
167 | ||
168 | 0 | if (bnode.getChildCount() == 0) { |
169 | // If node is a leaf then set value and message | |
170 | 0 | BooleanMessage message = nodeMessageMap.get(bnode.getLabel()); |
171 | 0 | bnode.setValue(message.isSuccesful()); |
172 | 0 | bnode.setNodeMessage(message.getMessage()); |
173 | 0 | } |
174 | else { | |
175 | // If node is intermediate, compute value and set it | |
176 | 0 | BooleanNode child0 = (BooleanNode)bnode.getChild(0); |
177 | 0 | BooleanNode child1 = (BooleanNode)bnode.getChild(1); |
178 | ||
179 | 0 | if ( bnode.getLabel().equalsIgnoreCase("+") ) { |
180 | // OR node | |
181 | 0 | Boolean newValue = child0.getValue() || child1.getValue(); |
182 | 0 | bnode.setValue(newValue); |
183 | 0 | bnode.setNodeMessage("null"); |
184 | 0 | } |
185 | 0 | else if ( bnode.getLabel().equalsIgnoreCase("*") ) { |
186 | // AND node | |
187 | 0 | Boolean newValue = child0.getValue() && child1.getValue(); |
188 | 0 | bnode.setValue(newValue); |
189 | 0 | bnode.setNodeMessage("null"); |
190 | } | |
191 | } | |
192 | 0 | } |
193 | ||
194 | /** | |
195 | * Gets all boolean nodes. | |
196 | * | |
197 | * @return All boolean nodes | |
198 | */ | |
199 | public List<BooleanNode> getAllNodes() { | |
200 | 0 | return nodes; |
201 | } | |
202 | ||
203 | /** | |
204 | * Gets the root boolean node. | |
205 | * | |
206 | * @return Root boolean node | |
207 | */ | |
208 | public BooleanNode getRoot() { | |
209 | 0 | return root; |
210 | } | |
211 | } |