View Javadoc

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