View Javadoc
1   /**
2    * Copyright 2005-2015 The Kuali Foundation
3    *
4    * Licensed under the Educational Community License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.opensource.org/licenses/ecl2.php
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.kuali.rice.krms.impl.ui;
17  
18  import org.apache.commons.lang.StringUtils;
19  import org.kuali.rice.krad.data.KradDataServiceLocator;
20  import org.kuali.rice.krad.inquiry.InquiryController;
21  import org.kuali.rice.krad.uif.UifParameters;
22  import org.kuali.rice.krad.web.form.InquiryForm;
23  import org.kuali.rice.krad.web.form.UifFormBase;
24  import org.kuali.rice.krms.impl.repository.ActionBo;
25  import org.kuali.rice.krms.impl.repository.AgendaBo;
26  import org.kuali.rice.krms.impl.repository.AgendaItemBo;
27  import org.kuali.rice.krms.impl.repository.ContextBoService;
28  import org.kuali.rice.krms.impl.repository.KrmsRepositoryServiceLocator;
29  import org.kuali.rice.krms.impl.repository.RepositoryBoIncrementer;
30  import org.kuali.rice.krms.impl.repository.RuleBo;
31  import org.springframework.stereotype.Controller;
32  import org.springframework.web.bind.annotation.RequestMapping;
33  import org.springframework.web.servlet.ModelAndView;
34  
35  @Controller
36  @RequestMapping(value = org.kuali.rice.krms.impl.util.KrmsImplConstants.WebPaths.AGENDA_INQUIRY_PATH)
37  public class AgendaInquiryController  extends InquiryController {
38  
39      private static final RepositoryBoIncrementer ruleIdIncrementer = new RepositoryBoIncrementer(RuleBo.RULE_SEQ_NAME);
40  
41  
42      /**
43       * This method updates the existing rule in the agenda.
44       */
45      @RequestMapping(params = "methodToCall=" + "viewRule")
46      public ModelAndView viewRule(UifFormBase form) throws Exception {
47  
48          AgendaEditor agendaEditor = getAgendaEditor(form);
49          agendaEditor.setAddRuleInProgress(false);
50  
51          // this is the root of the tree:
52          AgendaItemBo firstItem = getFirstAgendaItem(agendaEditor.getAgenda());
53  
54          String selectedItemId = agendaEditor.getSelectedAgendaItemId();
55          AgendaItemBo node = getAgendaItemById(firstItem, selectedItemId);
56  
57          setAgendaItemLine(form, node);
58  
59          form.getActionParameters().put(UifParameters.NAVIGATE_TO_PAGE_ID, "AgendaEditorView-ViewRule-Page");
60          return super.navigate(form);
61      }
62  
63      /**
64       * @param form
65       * @return the {@link AgendaEditor} from the form
66       */
67      private AgendaEditor getAgendaEditor(UifFormBase form) {
68          InquiryForm inquiryForm = (InquiryForm) form;
69          return ((AgendaEditor)inquiryForm.getDataObject());
70      }
71  
72      /**
73       * This method finds and returns the first agenda item in the agenda, or null if there are no items presently
74       *
75       * @param agenda
76       * @return
77       */
78      private AgendaItemBo getFirstAgendaItem(AgendaBo agenda) {
79          AgendaItemBo firstItem = null;
80          if (agenda != null && agenda.getItems() != null) {
81              for (AgendaItemBo agendaItem : agenda.getItems()) {
82                  if (agenda.getFirstItemId().equals(agendaItem.getId())) {
83                      firstItem = agendaItem;
84                      break;
85                  }
86              }
87          }
88          return firstItem;
89      }
90  
91      /**
92       * Search the tree for the agenda item with the given id.
93       */
94      private AgendaItemBo getAgendaItemById(AgendaItemBo node, String agendaItemId) {
95          if (node == null) {
96              throw new IllegalArgumentException("node must be non-null");
97          }
98  
99          AgendaItemBo result = null;
100 
101         if (StringUtils.equals(node.getId(), agendaItemId)) {
102             result = node;
103         } else {
104             for (AgendaItemChildAccessor childAccessor : AgendaItemChildAccessor.linkedNodes) {
105                 AgendaItemBo child = childAccessor.getChild(node);
106                 if (child != null) {
107                     result = getAgendaItemById(child, agendaItemId);
108                     if (result != null) {
109                         break;
110                     }
111                 }
112             }
113         }
114         return result;
115     }
116 
117     /**
118      * This method sets the agendaItemLine for adding/editing AgendaItems.
119      * The agendaItemLine is a copy of the agendaItem so that changes are not applied when
120      * they are abandoned.  If the agendaItem is null a new empty agendaItemLine is created.
121      *
122      * @param form
123      * @param agendaItem
124      */
125     private void setAgendaItemLine(UifFormBase form, AgendaItemBo agendaItem) {
126         AgendaEditor agendaEditor = getAgendaEditor(form);
127         if (agendaItem == null) {
128             RuleBo rule = new RuleBo();
129             rule.setId(ruleIdIncrementer.getNewId());
130             if (StringUtils.isBlank(agendaEditor.getAgenda().getContextId())) {
131                 rule.setNamespace("");
132             } else {
133                 rule.setNamespace(getContextBoService().getContextByContextId(agendaEditor.getAgenda().getContextId()).getNamespace());
134             }
135             agendaItem = new AgendaItemBo();
136             agendaItem.setRule(rule);
137             agendaEditor.setAgendaItemLine(agendaItem);
138         } else {
139             agendaEditor.setAgendaItemLine(KradDataServiceLocator.getDataObjectService().copyInstance(agendaItem));
140         }
141 
142 
143         if (agendaItem.getRule().getActions().isEmpty()) {
144             ActionBo actionBo = new ActionBo();
145             actionBo.setTypeId("");
146             actionBo.setNamespace(agendaItem.getRule().getNamespace());
147             actionBo.setRule(agendaItem.getRule());
148             actionBo.setSequenceNumber(1);
149             agendaEditor.setAgendaItemLineRuleAction(actionBo);
150         } else {
151             agendaEditor.setAgendaItemLineRuleAction(agendaItem.getRule().getActions().get(0));
152         }
153 
154         agendaEditor.setCustomRuleActionAttributesMap(agendaEditor.getAgendaItemLineRuleAction().getAttributes());
155         agendaEditor.setCustomRuleAttributesMap(agendaEditor.getAgendaItemLine().getRule().getAttributes());
156     }
157 
158     /**
159      * <p>This class abstracts getting and setting a child of an AgendaItemBo, making some recursive operations
160      * require less boiler plate.</p>
161      *
162      * <p>The word 'child' in AgendaItemChildAccessor means child in the strict data structures sense, in that the
163      * instance passed in holds a reference to some other node (or null).  However, when discussing the agenda tree
164      * and algorithms for manipulating it, the meaning of 'child' is somewhat different, and there are notions of
165      * 'sibling' and 'cousin' that are tossed about too. It's probably worth explaining that somewhat here:</p>
166      *
167      * <p>General principals of relationships when talking about the agenda tree:
168      * <ul>
169      * <li>Generation boundaries (parent to child) are across 'When TRUE' and 'When FALSE' references.</li>
170      * <li>"Age" among siblings & cousins goes from top (oldest) to bottom (youngest).</li>
171      * <li>siblings are related by 'Always' references.</li>
172      * </ul>
173      * </p>
174      * <p>This diagram of an agenda tree and the following examples seek to illustrate these principals:</p>
175      * <img src="doc-files/AgendaEditorController-1.png" alt="Example Agenda Items"/>
176      * <p>Examples:
177      * <ul>
178      * <li>A is the parent of B, C, & D</li>
179      * <li>E is the younger sibling of A</li>
180      * <li>B is the older cousin of C</li>
181      * <li>C is the older sibling of D</li>
182      * <li>F is the younger cousin of D</li>
183      * </ul>
184      * </p>
185      */
186     protected static class AgendaItemChildAccessor {
187 
188         private enum Child { WHEN_TRUE, WHEN_FALSE, ALWAYS };
189 
190         private static final AgendaItemChildAccessor whenTrue = new AgendaItemChildAccessor(Child.WHEN_TRUE);
191         private static final AgendaItemChildAccessor whenFalse = new AgendaItemChildAccessor(Child.WHEN_FALSE);
192         private static final AgendaItemChildAccessor always = new AgendaItemChildAccessor(Child.ALWAYS);
193 
194         /**
195          * Accessors for all linked items
196          */
197         private static final AgendaItemChildAccessor [] linkedNodes = { whenTrue, whenFalse, always };
198 
199         /**
200          * Accessors for children (so ALWAYS is omitted);
201          */
202         private static final AgendaItemChildAccessor [] children = { whenTrue, whenFalse };
203 
204         private final Child whichChild;
205 
206         private AgendaItemChildAccessor(Child whichChild) {
207             if (whichChild == null) {
208                 throw new IllegalArgumentException("whichChild must be non-null");
209             }
210             this.whichChild = whichChild;
211         }
212 
213         /**
214          * @return the referenced child
215          */
216         public AgendaItemBo getChild(AgendaItemBo parent) {
217             switch (whichChild) {
218             case WHEN_TRUE: return parent.getWhenTrue();
219             case WHEN_FALSE: return parent.getWhenFalse();
220             case ALWAYS: return parent.getAlways();
221             default: throw new IllegalStateException();
222             }
223         }
224 
225         /**
226          * Sets the child reference and the child id
227          */
228         public void setChild(AgendaItemBo parent, AgendaItemBo child) {
229             switch (whichChild) {
230             case WHEN_TRUE:
231                 parent.setWhenTrue(child);
232                 parent.setWhenTrueId(child == null ? null : child.getId());
233                 break;
234             case WHEN_FALSE:
235                 parent.setWhenFalse(child);
236                 parent.setWhenFalseId(child == null ? null : child.getId());
237                 break;
238             case ALWAYS:
239                 parent.setAlways(child);
240                 parent.setAlwaysId(child == null ? null : child.getId());
241                 break;
242             default: throw new IllegalStateException();
243             }
244         }
245     }
246 
247     /**
248      * return the contextBoService
249      */
250     private ContextBoService getContextBoService() {
251         return KrmsRepositoryServiceLocator.getContextBoService();
252     }
253 
254 }