001/**
002 * Copyright 2005-2013 The Kuali Foundation
003 *
004 * Licensed under the Educational Community License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.opensource.org/licenses/ecl2.php
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.kuali.rice.krms.tree;
017
018import org.apache.commons.lang.StringEscapeUtils;
019import org.kuali.rice.core.api.util.tree.Node;
020import org.kuali.rice.core.api.util.tree.Tree;
021import org.kuali.rice.krad.util.ObjectUtils;
022import org.kuali.rice.krms.api.repository.proposition.PropositionType;
023import org.kuali.rice.krms.dto.PropositionEditor;
024import org.kuali.rice.krms.dto.RuleEditor;
025import org.kuali.rice.krms.tree.node.CompoundOpCodeNode;
026import org.kuali.rice.krms.tree.node.SimplePropositionEditNode;
027import org.kuali.rice.krms.tree.node.SimplePropositionNode;
028import org.kuali.rice.krms.tree.node.RuleEditorTreeNode;
029import org.kuali.rice.krms.util.KRMSConstants;
030
031/**
032 *
033 * @author Kuali Student Team
034 */
035public class RuleEditTreeBuilder extends AbstractTreeBuilder{
036
037    private static final long serialVersionUID = 1L;
038
039    public Tree buildTree(RuleEditor rule) {
040
041        Tree myTree = new Tree<RuleEditorTreeNode, String>();
042
043        Node<RuleEditorTreeNode, String> rootNode = new Node<RuleEditorTreeNode, String>();
044        rootNode.setNodeType(RuleEditorTreeNode.ROOT_TYPE);
045
046        myTree.setRootElement(rootNode);
047
048        if (rule.getPropositionEditor() != null){
049            Node firstNode = addChildNode(rule, rootNode, rule.getPropositionEditor());
050            firstNode.setNodeType(firstNode.getNodeType() + " " + RuleEditorTreeNode.ROOT_TYPE);
051        }
052
053        return myTree;
054    }
055
056    /**
057     * This method builds a propositionTree recursively walking through the children of the proposition.
058     *
059     * @param sprout - parent tree node
060     * @param prop   - PropositionBo for which to make the tree node
061     */
062    private Node addChildNode(RuleEditor rule, Node sprout, PropositionEditor prop) {
063        // Depending on the type of proposition (simple/compound), and the editMode,
064        // Create a treeNode of the appropriate type for the node and attach it to the
065        // sprout parameter passed in.
066        // If the prop is a compound proposition, calls itself for each of the compoundComponents
067        if (prop != null) {
068            // add a node for the description display with a child proposition node
069            Node<RuleEditorTreeNode, String> leaf = new Node<RuleEditorTreeNode, String>();
070            if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(prop.getPropositionTypeCode())) {
071                //Add the proposition with alpha code in the map if it doesn't already exist.
072                if (null == prop.getKey()) {
073                    prop.setKey((String) rule.getSimpleKeys().next());
074                }
075                // Simple Proposition: add a node for the description display with a child proposition node
076                if (prop.isEditMode()) {
077                    leaf.setNodeType(SimplePropositionEditNode.NODE_TYPE);
078                    PropositionEditor copy = (PropositionEditor) ObjectUtils.deepCopy(prop);
079                    leaf.setData(new SimplePropositionEditNode(copy));
080                } else {
081                    leaf.setNodeLabel(this.buildNodeLabel(rule, prop));
082                    leaf.setNodeType(SimplePropositionNode.NODE_TYPE);
083                    addNodeType(leaf, KRMSConstants.NODE_TYPE_SUBRULEELEMENT);
084                    leaf.setData(new SimplePropositionNode(prop));
085                }
086
087                sprout.getChildren().add(leaf);
088            } else if (PropositionType.COMPOUND.getCode().equalsIgnoreCase(prop.getPropositionTypeCode())) {
089                //Add the proposition with alpha code in the map if it doesn't already exist.
090                if (null == prop.getKey()) {
091                    prop.setKey((String) rule.getCompoundKeys().next());
092                }
093                // Compound Proposition: editMode has description as an editable field
094                leaf.setNodeLabel(this.getDescription(prop));
095                leaf.setNodeType(RuleEditorTreeNode.COMPOUND_NODE_TYPE);
096                leaf.setData(new RuleEditorTreeNode(prop));
097
098                sprout.getChildren().add(leaf);
099
100                int counter = 0;
101                for (PropositionEditor child : prop.getCompoundEditors()) {
102                    // add an opcode node in between each of the children.
103                    if (counter > 0) {
104                        addOpCodeNode(leaf, prop, counter);
105                    }
106                    // call to build the childs node
107                    Node childNode = addChildNode(rule, leaf, child);
108                    if (counter==0){
109                        addNodeType(childNode, RuleEditorTreeNode.FIRST_IN_GROUP);
110                    }
111                    if (counter==prop.getCompoundEditors().size()-1){
112                        addNodeType(childNode, RuleEditorTreeNode.LAST_IN_GROUP);
113                    }
114                    //Add flag to identify if child can move right, if child has sibling after it
115                    if((leaf.getData().getProposition().getCompoundEditors().size() - 1) != counter) {
116                        if(!leaf.getData().getProposition().getCompoundEditors().get(leaf.getData().getProposition().getCompoundEditors().indexOf(child) + 1).getPropositionTypeCode().equals("C")) {
117                            addNodeType(childNode, RuleEditorTreeNode.DISABLE_MOVE_IN);
118                        }
119                    } //Set flag for last child in leaf
120                    else {
121                        addNodeType(childNode, RuleEditorTreeNode.DISABLE_MOVE_IN);
122                    }
123                    counter++;
124                }
125            }
126            //Set move left disabled flag if simple proposition in the root compound
127            if(sprout.getData() != null) {
128                if(((RuleEditorTreeNode) sprout.getData()).getProposition().equals(rule.getProposition())) {
129                    addNodeType(leaf, RuleEditorTreeNode.DISABLE_MOVE_OUT);
130                }
131            }
132            return leaf;
133        }
134        return null;
135    }
136
137    private String buildNodeLabel(RuleEditor rule, PropositionEditor prop) {
138        //Build the node label.
139        String prefix = this.getPropositionPrefix(prop);
140        return prefix + this.getDescription(prop);
141    }
142
143    /**
144     * This method adds an opCode Node to separate components in a compound proposition.
145     *
146     * @param currentNode
147     * @param prop
148     * @return
149     */
150    private void addOpCodeNode(Node currentNode, PropositionEditor prop, int counter) {
151        //Create the node.
152        Node<CompoundOpCodeNode, String> aNode = new Node<CompoundOpCodeNode, String>();
153        aNode.setNodeType(RuleEditorTreeNode.COMPOUND_OP_NODE_TYPE);
154
155        //Add a dummy editor.
156        PropositionEditor editor = new PropositionEditor();
157        editor.setKey(prop.getKey() + counter);
158        editor.setCompoundOpCode(prop.getCompoundOpCode());
159
160        aNode.setData(new CompoundOpCodeNode(editor));
161        currentNode.getChildren().add(aNode);
162    }
163
164    public String getNaturalLanguageUsageKey(){
165        return null;
166    }
167
168}