View Javadoc

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.ui.client.widgets.table;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.Stack;
21  
22  import org.kuali.student.common.ui.client.widgets.rules.Token;
23  
24  /**
25   * This is the parser for boolean expression.
26   * It checks the error, creates the Reverse Polish notation,
27   * merge the binary tree, sort the nodes. 
28   * 
29   * */
30  public class ExpressionParser {
31      /**
32       * Error messages are stored in a list
33       * */
34      private List<String> errorMessageList = new ArrayList<String>();
35      
36      public ExpressionParser() {
37      }
38      /**
39       * After parsing, it tells if the expression has error.
40       *  
41       * @return If the input expression has error
42       * */
43      public boolean hasError(){
44          return errorMessageList.size() > 0;
45      }
46      /**
47       * It returns all error messages
48       * 
49       * @return List of error messages
50       * */
51      public List<String> getErrorMessage(){
52          return errorMessageList;
53      }
54      /**
55       * Parse the boolean expression
56       * 
57       * @return the tree of input tokens
58       * */
59      public Node<Token> parse(String expression) {
60          errorMessageList = new ArrayList<String>();
61          List<String> tokenValueList = getTokenValue(expression);
62          List<Token> tokenList = getTokenList(tokenValueList);
63          errorCheck(tokenList);
64          if(hasError()){
65              return null;
66          }
67          List<Node<Token>> nodeList = toNodeList(tokenList);
68          List<Node<Token>> rpnList = getRPN(nodeList);
69  
70          Node<Token> root = binaryTreeFromRPN(rpnList);
71          
72          //Node<Token> ruleRoot = root;
73          Node<Token> ruleRoot = mergeBinaryTree(root);
74        
75          
76          //  ruleRoot = reStructure(root);
77          ruleRoot = orderLeafChildren(ruleRoot, tokenList );
78          ruleRoot = orderNonLeafChildren(ruleRoot, tokenList );
79          return ruleRoot;
80      }
81      /**
82       * Create the boolean expression from tree
83       * 
84       * @return boolean expression
85       * */
86      public static String getExpressionString(Node root){
87          while(root.getChildCount()> 1){
88              List<List<Node>> level = root.toLevel();
89              for(Node<Token> n: level.get(level.size()-1)){
90                  if(n.isLeaf() && n.getParent() != null){
91                      Node parent = n.getParent();
92                      StringBuilder sb = new StringBuilder();
93                      if(parent.getChildAt(0).getUserObject() instanceof ExpressionNode
94                              && parent.getChildAt(0).getUserObject() instanceof ExpressionNode
95                          && (( ExpressionNode)parent.getChildAt(0).getUserObject()).token.type == Token.Or
96                          && (( Token)parent.getUserObject()).type == Token.And){
97                          sb.append(" ( "+parent.getChildAt(0).getUserObject().toString()+" ) ");
98                      }else{
99                          sb.append(" "+parent.getChildAt(0).getUserObject().toString()+" ");    
100                     }
101                     
102                     
103                     for(int i=1;i<parent.getChildCount();i++){
104                         Node child = parent.getChildAt(i);
105 
106                         if(parent.getChildAt(i).getUserObject() instanceof ExpressionNode
107                                 && parent.getChildAt(i).getUserObject() instanceof ExpressionNode
108                             && (( ExpressionNode)parent.getChildAt(i).getUserObject()).token.type == Token.Or
109                             && (( Token)parent.getUserObject()).type == Token.And){
110                             sb.append(parent.getUserObject().toString()+ " ( "+
111                                     child.getUserObject().toString()+" ) ");
112 
113                         }else{
114                             sb.append(" "+parent.getUserObject().toString()+ "  "+
115                                     child.getUserObject().toString()+" ");
116                         }
117                     }
118                     ExpressionNode expressionNode = new ExpressionNode();
119                     expressionNode.token = (Token)parent.getUserObject();
120                     expressionNode.expression = sb.toString();
121                     parent.setUserObject(expressionNode);
122                     parent.removeAllChildren();
123                     break;
124                 }
125             }
126             
127         }
128         
129         
130         return root.getUserObject().toString();
131     }
132     /**
133      * Order the nonleaf children
134      * */
135     private Node<Token> orderNonLeafChildren(Node<Token> binaryTree,List<Token> tokenList ) {
136         List<Node> list = binaryTree.getNonLeafChildren();
137         if(list.size() > 1){
138             sequeceNonLeaves(list, tokenList);
139             binaryTree.children().removeAll(list);
140             for(Node n: list){
141                 binaryTree.addNode(n);
142             }
143         }
144         for(Node n: binaryTree.children()){
145             if(n.isLeaf() == false){
146                 orderNonLeafChildren(n,tokenList );
147             }
148         }
149         
150         List<Node> nonLeafChildList = binaryTree.getNonLeafChildren();
151         List<Node> leafChildList = binaryTree.getLeafChildren();
152         binaryTree.children().removeAll(nonLeafChildList);
153         binaryTree.children().removeAll(leafChildList);
154         for(Node n: nonLeafChildList){
155             binaryTree.addNode(n);
156         }
157         
158         for(Node n: leafChildList){
159             binaryTree.addNode(n);
160         }
161         return binaryTree;
162    }
163    /**
164     * Order the non leaves
165     * */
166    private void sequeceNonLeaves(List<Node> nonLeafChildList, List<Token> list){
167        if(nonLeafChildList.size() == 2){
168            if (indexInInputTokenList((Token)nonLeafChildList.get(0).getFirstLeafDescendant().getUserObject(), list)> 
169            indexInInputTokenList((Token)nonLeafChildList.get(1).getFirstLeafDescendant().getUserObject(), list)) {
170                Node buffer = nonLeafChildList.get(0);
171                nonLeafChildList.remove(0);
172                nonLeafChildList.add(buffer);
173            }
174        }
175        for (int out = nonLeafChildList.size() - 1; out > 1; out--){
176          for (int in = 0; in < out; in++){
177            // inner loop (forward)
178            if (indexInInputTokenList((Token)nonLeafChildList.get(in).getFirstLeafDescendant().getUserObject(), list)> 
179            indexInInputTokenList((Token)nonLeafChildList.get(in + 1).getFirstLeafDescendant().getUserObject(), list)) {
180              // swap them
181                Node node = nonLeafChildList.get(in);
182                nonLeafChildList.remove(in);
183                nonLeafChildList.add(in+1, node);
184            }
185          }
186        }
187    }
188     /**
189      * Order the leaf children
190      * */
191     private Node<Token> orderLeafChildren(Node<Token> binaryTree,List<Token> tokenList ) {
192          List<Node> list = binaryTree.getLeafChildren();
193          if(list.size() > 1){
194              sequeceLeaves(list, tokenList);
195              binaryTree.children().removeAll(list);
196              for(Node n: list){
197                  binaryTree.addNode(n);
198              }
199          }
200          for(Node n: binaryTree.children()){
201              if(n.isLeaf() == false){
202                  orderLeafChildren(n,tokenList );
203              }
204          }
205         return binaryTree;
206     }
207     /**Reorder the children*/
208     private void sequeceLeaves(List<Node> leafChildList, List<Token> list){
209         if(leafChildList.size() == 2){
210             if (indexInInputTokenList((Token)leafChildList.get(0).getUserObject(), list)> 
211             indexInInputTokenList((Token)leafChildList.get(1).getUserObject(), list)) {
212               // swap them
213                 Token buffer = (Token)leafChildList.get(0).getUserObject();
214                 leafChildList.get(0).setUserObject(leafChildList.get(1).getUserObject());
215                 leafChildList.get(1).setUserObject(buffer);
216             }
217             
218         }
219         
220         for (int out = leafChildList.size() - 1; out > 1; out--){
221           for (int in = 0; in < out; in++){
222             // inner loop (forward)
223             if (indexInInputTokenList((Token)leafChildList.get(in).getUserObject(), list)> 
224             indexInInputTokenList((Token)leafChildList.get(in + 1).getUserObject(), list)) {
225               // swap them
226                 Token buffer = (Token)leafChildList.get(in).getUserObject();
227                 leafChildList.get(in).setUserObject(leafChildList.get(in + 1).getUserObject());
228                 leafChildList.get(in + 1).setUserObject(buffer);
229             }
230           }
231         }
232     }
233     /**Get the index of a token in the token list*/
234     private int indexInInputTokenList(Token token, List<Token> list){
235         int i = -1;
236         for(Token n: list){
237             if(n.value != null && token.value != null && n.value.equals(token.value)){
238                 return i;
239             }
240             i++;
241         }
242         return i;
243     }
244 
245 /*    public static  Node<Token> reStructure(Node<Token> binaryTree) {
246         Node root = new Node();
247         //Node root = binaryTree;
248         root.setUserObject(binaryTree.getUserObject());
249         List<Node> childList = binaryTree.children();
250 
251         ListIterator<Node> itr = childList.listIterator();
252         for (; itr.hasNext();) {
253             Node node = getDeeperNode(childList);
254             if (node.isLeaf()) {
255                 root.addNode(node);
256             } else {
257                 root.addNode(reStructure(node));
258             }
259             childList.remove(node);
260         }
261 
262         return root.clone();
263     }
264 */
265     private static Node getDeeperNode(List<Node> nodeList) {
266         int childCount = 0;
267         for (Node node : nodeList) {
268             if (childCount < node.getAllChildren().size()) {
269                 childCount = node.getAllChildren().size();
270             }
271         }
272         for (Node node : nodeList) {
273             if (childCount == node.getAllChildren().size()) {
274                 return node;
275             }
276         }
277 
278         return null;
279     }
280     /** Merge the binary tree. */
281     public static Node<Token> mergeBinaryTree(Node<Token> binaryTree) {
282         while (parentEqualsGrandParent(binaryTree)) {
283             List<Node<Token>> list = binaryTree.getAllChildren();
284 
285             for (Node node : list) {
286                 if (node.getParent() != null && node.getParent().getParent() != null) {
287                     Node parentNode = node.getParent();
288                     Node grandParentNode = node.getParent().getParent();
289                     Token parentToken = (Token) parentNode.getUserObject();
290                     Token grandParentToken = (Token) grandParentNode.getUserObject();
291 
292                     if (parentToken.type == grandParentToken.type) {
293                         for (int i = 0; i < parentNode.getChildCount(); i++) {
294                             Node n = parentNode.getChildAt(i);
295                             grandParentNode.addNode(n);
296                         }
297                         grandParentNode.remove(parentNode);
298                     }
299                 }
300             }
301         }
302         return binaryTree;
303     }
304 
305     private static boolean parentEqualsGrandParent(Node<Token> binaryTree) {
306         List<Node<Token>> list = binaryTree.getAllChildren();
307 
308         for (Node node : list) {
309             if (node.getParent() != null && node.getParent().getParent() != null) {
310 
311                 Token parentToken = (Token) node.getParent().getUserObject();
312                 Token grandParentToken = (Token) node.getParent().getParent().getUserObject();
313                 if (parentToken.type == grandParentToken.type) {
314                     return true;
315                 }
316             }
317         }
318 
319         return false;
320     }
321     /** Build the binary tree from list of tokens*/
322     private Node<Token> binaryTreeFromRPN(List<Node<Token>> rpnList) {
323         Stack<Node<Token>> conditionStack = new Stack<Node<Token>>();
324         for (Node<Token> node : rpnList) {
325             if (node.getUserObject().type == Token.Condition) {
326                 conditionStack.push(node);
327             } else if (node.getUserObject().type == Token.And || node.getUserObject().type == Token.Or) {
328                 Node<Token> left = conditionStack.pop();
329                 Node<Token> right = conditionStack.pop();
330                 node.addNode(left);
331                 node.addNode(right);
332                 conditionStack.push(node);
333             }
334         }
335 
336         return conditionStack.pop();
337     }
338 
339     /**
340      * If higher push to stack, else pop till less than or equal, add to list push to stack if ( push to stack if ) pop to
341      * list till (.
342      * 
343      * http://en.wikipedia.org/wiki/Reverse_Polish_notation
344      * 
345      */
346     private List<Node<Token>> getRPN(List<Node<Token>> nodeList) {
347         List<Node<Token>> rpnList = new ArrayList<Node<Token>>();
348         Stack<Node<Token>> operatorStack = new Stack<Node<Token>>();
349 
350         for (Node<Token> node : nodeList) {
351             if (node.getUserObject().type == Token.Condition) {
352                 rpnList.add(node);
353 
354             } else if (node.getUserObject().type == Token.And) {
355                 operatorStack.push(node);
356             } else if (node.getUserObject().type == Token.StartParenthesis) {
357                 operatorStack.push(node);
358             } else if (node.getUserObject().type == Token.Or) {
359 
360                 if (operatorStack.isEmpty() == false && operatorStack.peek().getUserObject().type == Token.And) {
361                     do {
362                         rpnList.add(operatorStack.pop());
363                     } while (operatorStack.isEmpty() == false && operatorStack.peek().getUserObject().type == Token.And);
364                 }
365 
366                 operatorStack.push(node);
367             } else if (node.getUserObject().type == Token.EndParenthesis) {
368                 while (operatorStack.peek().getUserObject().type != Token.StartParenthesis) {
369                     rpnList.add(operatorStack.pop());
370                 }
371                 operatorStack.pop();// pop the (
372             }
373         }
374         if (operatorStack.isEmpty() == false) {
375             do {
376                 rpnList.add(operatorStack.pop());
377             } while (operatorStack.isEmpty() == false);
378         }
379         return rpnList;
380     }
381 
382     private int findNodeIndex(List<Node<Token>> nodeList, int type) {
383         int index = -1;
384         for (Node<Token> node : nodeList) {
385             index++;
386             if (node.getUserObject().type == type) {
387                 return index;
388             }
389         }
390         return index;
391     }
392 
393     private boolean hasParenthesis(List<Node<Token>> nodeList) {
394         boolean has = false;
395         for (Node<Token> node : nodeList) {
396             if (node.getUserObject().type == Token.StartParenthesis) {
397                 return true;
398             }
399         }
400         return has;
401     }
402 
403     private List<Node<Token>> toNodeList(List<Token> tokenList) {
404         List<Node<Token>> nodeList = new ArrayList<Node<Token>>();
405         for (Token token : tokenList) {
406             Node<Token> node = new Node<Token>();
407             node.setUserObject(token);
408             nodeList.add(node);
409         }
410         return nodeList;
411     }
412 
413     private void errorCheck(List<Token> tokenList) {
414         if (tokenList.size() == 0) {
415             errorMessageList.add("empty input");
416             return;
417         }
418         if (tokenList.size() <= 2) {
419             errorMessageList.add("input not complete");
420             return;
421         }
422         if ((tokenList.get(0).type == Token.StartParenthesis 
423                 || tokenList.get(0).type == Token.Condition) == false) {
424             errorMessageList.add("must start with ( or condition");
425             return;
426         }
427         int lastIndex = tokenList.size() - 1;
428         if ((tokenList.get(lastIndex).type == Token.EndParenthesis || tokenList.get(lastIndex).type == Token.Condition) == false) {
429             errorMessageList.add("must end with ) or condition");
430             return;
431         }
432         if (countToken(tokenList, Token.StartParenthesis) != countToken(tokenList, Token.EndParenthesis)) {
433             errorMessageList.add("() not in pair");
434             return;
435         }
436         // condition cannot duplicate
437         for (int i = 1; i < tokenList.size(); i++) {
438             Token token = tokenList.get(i);
439             if (token.type == Token.And) {
440                 checkAnd(tokenList, i);
441             } else if (token.type == Token.Or) {
442                 checkOr(tokenList, i);
443             } else if (token.type == Token.StartParenthesis) {
444                 checkStartParenthesis(tokenList, i);
445             } else if (token.type == Token.EndParenthesis) {
446                 checkEndParenthesis(tokenList, i);
447             } else if (token.type == Token.Condition) {
448                 checkCondition(tokenList, i);
449             }
450         }
451     }
452 
453     private int countToken(List<Token> tokenList, int type) {
454         int count = 0;
455         for (Token t : tokenList) {
456             if (t.type == type) {
457                 count++;
458             }
459         }
460         return count;
461     }
462 
463     private void checkAnd(List<Token> tokenList, int currentIndex) {
464         if ((tokenList.get(currentIndex - 1).type == Token.Condition || tokenList.get(currentIndex - 1).type == Token.EndParenthesis) == false) {
465             errorMessageList.add("only ) and condition could sit before and");
466         }
467         if (currentIndex == tokenList.size() - 1) {
468             return;
469         }
470         if ((tokenList.get(currentIndex + 1).type == Token.Condition || tokenList.get(currentIndex + 1).type == Token.StartParenthesis) == false) {
471             errorMessageList.add("only ( and condition could sit after and");
472         }
473 
474     }
475 
476     private void checkOr(List<Token> tokenList, int currentIndex) {
477         if ((tokenList.get(currentIndex - 1).type == Token.Condition || tokenList.get(currentIndex - 1).type == Token.EndParenthesis) == false) {
478             errorMessageList.add("only ) and condition could sit before or");
479         }
480         if (currentIndex == tokenList.size() - 1) {
481             return;
482         }
483         if ((tokenList.get(currentIndex + 1).type == Token.Condition || tokenList.get(currentIndex + 1).type == Token.StartParenthesis) == false) {
484             errorMessageList.add("only ( and condition could sit after or");
485         }
486     }
487 
488     private void checkStartParenthesis(List<Token> tokenList, int currentIndex) {
489         if ((tokenList.get(currentIndex - 1).type == Token.And || tokenList.get(currentIndex - 1).type == Token.Or || tokenList.get(currentIndex - 1).type == Token.StartParenthesis) == false) {
490             errorMessageList.add("only and, or, ( could sit before (");
491         }
492         if (currentIndex == tokenList.size() - 1) {
493             return;
494         }
495         if ((tokenList.get(currentIndex + 1).type == Token.Condition || tokenList.get(currentIndex + 1).type == Token.StartParenthesis) == false) {
496             errorMessageList.add("only ( and condition could sit after (");
497         }
498 
499     }
500 
501     private void checkEndParenthesis(List<Token> tokenList, int currentIndex) {
502         if ((tokenList.get(currentIndex - 1).type == Token.Condition || tokenList.get(currentIndex - 1).type == Token.EndParenthesis) == false) {
503             errorMessageList.add("only condition and ) could sit before )");
504         }
505         if (currentIndex == tokenList.size() - 1) {
506             return;
507         }
508         if ((tokenList.get(currentIndex + 1).type == Token.Or || tokenList.get(currentIndex + 1).type == Token.And || tokenList.get(currentIndex + 1).type == Token.EndParenthesis) == false) {
509             errorMessageList.add("only ), and, or could sit after )");
510         }
511 
512     }
513 
514     private void checkCondition(List<Token> tokenList, int currentIndex) {
515         if ((tokenList.get(currentIndex - 1).type == Token.And || tokenList.get(currentIndex - 1).type == Token.Or || tokenList.get(currentIndex - 1).type == Token.StartParenthesis) == false) {
516             errorMessageList.add("only and, or could sit before condition");
517         }
518         if (currentIndex == tokenList.size() - 1) {
519             return;
520         }
521         if ((tokenList.get(currentIndex + 1).type == Token.Or || tokenList.get(currentIndex + 1).type == Token.And || tokenList.get(currentIndex + 1).type == Token.EndParenthesis) == false) {
522             errorMessageList.add("only ), and, or could sit after condition");
523         }
524 
525     }
526 
527     private List<Token> getTokenList(List<String> tokenValueList) {
528         List<Token> tokenList = new ArrayList<Token>();
529         for (String value : tokenValueList) {
530             if (value.isEmpty()) {
531                 continue;
532             }
533             if ("(".equals(value)) {
534                 Token t = new Token();
535                 t.type = Token.StartParenthesis;
536                 tokenList.add(t);
537             } else if (")".equals(value)) {
538                 Token t = new Token();
539                 t.type = Token.EndParenthesis;
540                 tokenList.add(t);
541 
542             } else if ("and".equals(value)) {
543                 Token t = new Token();
544                 t.type = Token.And;
545                 tokenList.add(t);
546 
547             } else if ("or".equals(value)) {
548                 Token t = new Token();
549                 t.type = Token.Or;
550                 tokenList.add(t);
551 
552             } else {
553                 Token t = new Token();
554                 t.type = Token.Condition;
555                 t.value = value;
556                 tokenList.add(t);
557 
558             }
559         }
560         return tokenList;
561     }
562 
563     private List<String> getTokenValue(String expression) {
564         expression = expression.toLowerCase();
565         List<String> tokenValueList = new ArrayList<String>();
566         StringBuffer tokenValue = new StringBuffer();
567         for (int i = 0; i < expression.length(); i++) {
568 
569             char ch = expression.charAt(i);
570             if (ch == ' ') {
571                 tokenValueList.add(tokenValue.toString());
572                 tokenValue = new StringBuffer();
573             } else if (ch == '(' || ch == ')') {
574                 tokenValueList.add(tokenValue.toString());
575                 tokenValue = new StringBuffer();
576                 tokenValueList.add(String.valueOf(ch));
577             } else {
578                 tokenValue.append(ch);
579             }
580         }
581         tokenValueList.add(tokenValue.toString());
582         return tokenValueList;
583     }
584 }
585