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.kuali.rice.core.api.util.tree.Node;
019import org.kuali.rice.core.api.util.tree.Tree;
020import org.kuali.rice.krad.util.ObjectUtils;
021import org.kuali.rice.krms.api.repository.proposition.PropositionType;
022import org.kuali.rice.krms.dto.PropositionEditor;
023import org.kuali.rice.krms.dto.RuleEditor;
024import org.kuali.rice.krms.tree.node.CompoundOpCodeNode;
025import org.kuali.rice.krms.tree.node.SimplePropositionEditNode;
026import org.kuali.rice.krms.tree.node.SimplePropositionNode;
027import org.kuali.rice.krms.tree.node.RuleEditorTreeNode;
028import org.kuali.rice.krms.util.KRMSConstants;
029
030/**
031 *
032 * @author Kuali Student Team
033 */
034public class RuleEditTreeBuilder extends AbstractTreeBuilder{
035
036    private static final long serialVersionUID = 1L;
037
038    public Tree buildTree(RuleEditor rule) {
039
040        Tree myTree = new Tree<RuleEditorTreeNode, String>();
041
042        Node<RuleEditorTreeNode, String> rootNode = new Node<RuleEditorTreeNode, String>();
043        rootNode.setNodeType(KRMSConstants.ROOT_TYPE);
044
045        myTree.setRootElement(rootNode);
046
047        if (rule.getPropositionEditor() != null){
048            Node firstNode = addChildNode(rule, rootNode, rule.getPropositionEditor());
049            firstNode.setNodeType(firstNode.getNodeType() + " " + KRMSConstants.ROOT_TYPE);
050        }
051
052        return myTree;
053    }
054
055    /**
056     * This method builds a propositionTree recursively walking through the children of the proposition.
057     *
058     * @param sprout - parent tree node
059     * @param prop   - PropositionBo for which to make the tree node
060     */
061    private Node addChildNode(RuleEditor rule, Node sprout, PropositionEditor prop) {
062        // Depending on the type of proposition (simple/compound), and the editMode,
063        // Create a treeNode of the appropriate type for the node and attach it to the
064        // sprout parameter passed in.
065        // If the prop is a compound proposition, calls itself for each of the compoundComponents
066        if (prop != null) {
067            // add a node for the description display with a child proposition node
068            Node<RuleEditorTreeNode, String> leaf = new Node<RuleEditorTreeNode, String>();
069            if (PropositionType.SIMPLE.getCode().equalsIgnoreCase(prop.getPropositionTypeCode())) {
070                handleSimpleNode(rule, sprout, prop, leaf);
071            } else if (PropositionType.COMPOUND.getCode().equalsIgnoreCase(prop.getPropositionTypeCode())) {
072                handleCompoundNode(rule, sprout, prop, leaf);
073            }
074
075            //Set move left disabled flag if simple proposition in the root compound
076            if(sprout.getData() != null) {
077                if(((RuleEditorTreeNode) sprout.getData()).getProposition().equals(rule.getProposition())) {
078                    addNodeType(leaf, KRMSConstants.DISABLE_MOVE_OUT);
079                }
080            }
081            return leaf;
082        }
083        return null;
084    }
085
086    protected void handleCompoundNode(RuleEditor rule, Node sprout, PropositionEditor prop, Node<RuleEditorTreeNode, String> leaf) {
087        //Add the proposition with alpha code in the map if it doesn't already exist.
088        if (null == prop.getKey()) {
089            prop.setKey((String) rule.getCompoundKeys().next());
090        }
091        setupCompoundNode(rule, prop, leaf);
092        sprout.getChildren().add(leaf);
093
094        int counter = 0;
095        for (PropositionEditor child : prop.getCompoundEditors()) {
096            // add an opcode node in between each of the children.
097            if (counter > 0) {
098                addOpCodeNode(leaf, prop, counter);
099            }
100            // call to build the childs node
101            Node childNode = addChildNode(rule, leaf, child);
102            if (counter==0){
103                addNodeType(childNode, KRMSConstants.FIRST_IN_GROUP);
104            }
105            if (counter==prop.getCompoundEditors().size()-1){
106                addNodeType(childNode, KRMSConstants.LAST_IN_GROUP);
107            }
108            //Add flag to identify if child can move right, if child has sibling after it
109            if((leaf.getData().getProposition().getCompoundEditors().size() - 1) != counter) {
110                if(!leaf.getData().getProposition().getCompoundEditors().get(leaf.getData().getProposition().getCompoundEditors().indexOf(child) + 1).getPropositionTypeCode().equals("C")) {
111                    addNodeType(childNode, KRMSConstants.DISABLE_MOVE_IN);
112                }
113            } //Set flag for last child in leaf
114            else {
115                addNodeType(childNode, KRMSConstants.DISABLE_MOVE_IN);
116            }
117            counter++;
118        }
119    }
120
121    protected void setupCompoundNode(RuleEditor rule, PropositionEditor prop, Node<RuleEditorTreeNode, String> leaf) {
122        // Compound Proposition: editMode has description as an editable field
123        leaf.setNodeLabel(this.getDescription(prop));
124        leaf.setNodeType(KRMSConstants.COMPOUND_NODE_TYPE);
125        leaf.setData(new SimplePropositionNode(prop));
126    }
127
128    protected void handleSimpleNode(RuleEditor rule, Node sprout, PropositionEditor prop, Node<RuleEditorTreeNode, String> leaf) {
129        //Add the proposition with alpha code in the map if it doesn't already exist.
130        if (null == prop.getKey()) {
131            prop.setKey((String) rule.getSimpleKeys().next());
132        }
133        // Simple Proposition: add a node for the description display with a child proposition node
134        if (prop.isEditMode()) {
135            setupEditNode(rule, prop, leaf);
136        } else {
137            setupSimpleNode(rule, prop, leaf);
138        }
139
140        sprout.getChildren().add(leaf);
141    }
142
143    protected void setupEditNode(RuleEditor rule, PropositionEditor prop, Node<RuleEditorTreeNode, String> leaf) {
144        leaf.setNodeType(KRMSConstants.EDIT_NODE_TYPE);
145        PropositionEditor copy = (PropositionEditor) ObjectUtils.deepCopy(prop);
146        leaf.setData(new SimplePropositionEditNode(copy));
147    }
148
149    protected void setupSimpleNode(RuleEditor rule, PropositionEditor prop, Node<RuleEditorTreeNode, String> leaf) {
150        leaf.setNodeLabel(this.buildNodeLabel(rule, prop));
151        leaf.setNodeType(KRMSConstants.SIMPLE_NODE_TYPE);
152        addNodeType(leaf, KRMSConstants.NODE_TYPE_SUBRULEELEMENT);
153        leaf.setData(new SimplePropositionNode(prop));
154    }
155
156    protected String buildNodeLabel(RuleEditor rule, PropositionEditor prop) {
157        //Build the node label.
158        String prefix = this.getPropositionPrefix(prop);
159        return prefix + this.getDescription(prop);
160    }
161
162    /**
163     * This method adds an opCode Node to separate components in a compound proposition.
164     *
165     * @param currentNode
166     * @param prop
167     * @return
168     */
169    protected void addOpCodeNode(Node currentNode, PropositionEditor prop, int counter) {
170        //Create the node.
171        Node<CompoundOpCodeNode, String> aNode = new Node<CompoundOpCodeNode, String>();
172        aNode.setNodeType(KRMSConstants.COMPOUND_OP_NODE_TYPE);
173
174        //Add a dummy editor.
175        PropositionEditor editor = new PropositionEditor();
176        editor.setKey(prop.getKey() + counter);
177        editor.setCompoundOpCode(prop.getCompoundOpCode());
178
179        aNode.setData(new CompoundOpCodeNode(editor));
180        currentNode.getChildren().add(aNode);
181    }
182
183    public String getNaturalLanguageUsageKey(){
184        return null;
185    }
186
187}