View Javadoc

1   /* ===================================================================
2    * Copyright 2002-04 SRI International.
3    * Released under the MOZILLA PUBLIC LICENSE Version 1.1
4    * which can be obtained at http://www.mozilla.org/MPL/MPL-1.1.html
5    * This software is distributed on an "AS IS"
6    * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
7    * See the License for the specific language governing rights and
8    * limitations under the License.
9    * =================================================================== */
10  package com.sri.emo.controller;
11  
12  import com.jcorporate.expresso.core.controller.*;
13  import com.jcorporate.expresso.core.db.DBException;
14  import com.jcorporate.expresso.core.registry.RequestRegistry;
15  import com.jcorporate.expresso.services.controller.ui.DefaultAutoElement;
16  import com.sri.common.controller.AbstractDBController;
17  import com.sri.common.taglib.InputTag;
18  import com.sri.emo.dbobj.*;
19  import org.apache.log4j.Logger;
20  
21  import java.util.Collections;
22  import java.util.Comparator;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  
27  /***
28   * handle Picklist manipulation
29   *
30   * @author larry hamel
31   */
32  public class PicklistAction extends AbstractDBController {
33      // states
34  
35      /***
36  	 * 
37  	 */
38  	private static final long serialVersionUID = 1L;
39  
40  	/***
41       * edit picklist state
42       */
43      public static final String PROMPT_LIST = "promptEditPicklist";
44  
45      //    public static final String DO_EDIT_PICKLIST = "doEditPicklist";
46      public static final String PROMPT_EDIT_ITEM = "promptEditItem";
47  
48      /***
49       * Constant value for state 'Save Picklist Item'
50       */
51      public static final String DO_EDIT_ITEM = "doEditItem";
52  
53  
54      public static final String DO_INC_ITEM_ORDER = "doIncItemOrder";
55      public static final String DO_DEC_ITEM_ORDER = "doDecItemOrder";
56      public static final String PROMPT_ADD_ITEM = "promptAddItem";
57      public static final String DO_ADD_ITEM = "doAddItem";
58      public static final String SORT_BY_ALPHA = "sortByAlpha";
59      public static final String PROMPT_DELETE_ITEM = "promptDeleteItem";
60      public static final String DO_DELETE_ITEM = "doDeleteItem";
61  
62      /***
63       * Constant value for state 'List All Picklists'
64       */
65      public static final String LIST_PICKLISTS = "listPickLists";
66  
67      // prefix for input fields of new items
68      public static final String NEW_ITEM_PREFIX = "newprefix";
69      public static final String EXISTING_ITEM_PREFIX = "existprefix";
70      public static final String ASSOCIATE_PREFIX = "associateprefix";
71  
72      public PicklistAction() {
73          State state = new State(PROMPT_LIST, "Prompt to edit a picklist");
74          addState(state);
75  
76          //        State newState = new State(DO_EDIT_PICKLIST, "Edit a picklist");
77          //        addState(newState);
78          addState(new State(PROMPT_EDIT_ITEM, "Prompt edit single picklist item"));
79          addState(new State(DO_EDIT_ITEM,
80                  "submit editting of single picklist item"));
81  
82          addState(new State(DO_INC_ITEM_ORDER,
83                  "increment order number of pick item"));
84          addState(new State(DO_DEC_ITEM_ORDER,
85                  "decrement order number of pick item"));
86  
87          addState(new State(PROMPT_ADD_ITEM, "prompt add new pick item"));
88          addState(new State(DO_ADD_ITEM, "execute add new pick item"));
89  
90          addState(new State(PROMPT_DELETE_ITEM, "prompt delete pick item"));
91          addState(new State(DO_DELETE_ITEM, "execute delete pick item"));
92  
93          addState(new State(SORT_BY_ALPHA, "sort items by alpha"));
94  
95  
96          setInitialState(PROMPT_LIST);
97      }
98  
99      /***
100      * Returns the title of this controller
101      *
102      * @return java.lang.String
103      */
104     public String getTitle() {
105         return ("PicklistAction Controller");
106     }
107 
108     /***
109      * Prompt for edits.
110      *
111      * @param request  The ExpressoRequest object.
112      * @param response The ExpressoResponse object.
113      * @throws ControllerException upon error.
114      */
115     protected void runPromptEditPicklistState(ExpressoRequest request,
116                                               ExpressoResponse response) throws ControllerException {
117         try {
118             // make sure required params are here
119             PickList item = new PickList();
120             String[] required = {PickList.NODE_TYPE, PickList.ATTRIBUTE_TYPE};
121 
122             if (!isValidAndPopulated(item, required, null, request, response)) {
123                 Transition trans = new Transition(AddNodeAction.PROMPT_EDIT_NODE,
124                         "", AddNodeAction.class, AddNodeAction.PROMPT_EDIT_NODE);
125                 trans.addParam(Node.NODE_TYPE,
126                         request.getParameter(PickList.NODE_TYPE));
127                 trans.addParam(Node.NODE_ID, request.getParameter(Node.NODE_ID));
128                 trans.executeTransition(request, response);
129 
130                 return;
131             }
132 
133             // add hidden fields
134             Input hiddenInput = new Input(PickList.NODE_TYPE);
135             hiddenInput.setType(InputTag.TYPE_HIDDEN);
136             hiddenInput.setDefaultValue(item.getNodeType());
137             response.add(hiddenInput);
138             hiddenInput = new Input(PickList.ATTRIBUTE_TYPE);
139             hiddenInput.setType(InputTag.TYPE_HIDDEN);
140             hiddenInput.setDefaultValue(item.getPickAttribType());
141             response.add(hiddenInput);
142 
143             // add descriptive output for overall "name" of menu
144             Part part = PartsFactory.getAttribPart(item.getNodeType(), item.getPickAttribType());
145 
146             if (part == null) {
147                 // @todo handle this for lists not associated with node type
148                 throw new ControllerException(
149                         "Cannot find part corresponding to Node/Attribute: " + item.getNodeType() + "/" + item.getPickAttribType());
150             }
151             Output output = new Output(PickList.NODE_TYPE,
152                     part.getDisplayTitleOfParentNodeType(RequestRegistry.getUser().getUid()));
153             response.add(output);
154             output = new Output(PickList.ATTRIBUTE_TYPE, part.getPartLabel());
155             response.add(output);
156 
157             Block outerBlock = new Block(ROW_BLOCK);
158             response.add(outerBlock);
159 
160             // add existing picklist items
161             List list = item.searchAndRetrieveList(PickList.DISPLAY_IN_PICKLIST);
162             Collections.sort(list);
163 
164             int i = 0;
165 
166             for (Iterator iter = list.iterator(); iter.hasNext();) {
167                 PickList anItem = (PickList) iter.next();
168 
169                 Block rowBlock = new Block(ROW);
170                 outerBlock.add(rowBlock);
171 
172                 rowBlock.add(new Output(PickList.COMMENT, anItem.getComment()));
173                 rowBlock.add(new Output(PickList.LIST_ID, anItem.getID()));
174                 rowBlock.add(new Output(PickList.ORDER_NUM, anItem.getOrderNum()));
175                 rowBlock.add(new Output(Node.RELATED,
176                         "" +
177                                 getAttributesUsingPickItem(anItem).size()));
178 
179                 Transition trans = new Transition(anItem.getDisplayString(),
180                         getClass(), PROMPT_EDIT_ITEM);
181                 trans.addParam(PickList.LIST_ID, anItem.getID());
182                 rowBlock.add(trans);
183 
184                 trans = new Transition(PROMPT_DELETE_ITEM, this);
185                 trans.addParam(PickList.LIST_ID, anItem.getID());
186                 rowBlock.add(trans);
187 
188                 // add increment icon for all but last item
189                 if (i < (list.size() - 1)) {
190                     trans = new Transition(DO_INC_ITEM_ORDER + "_" +
191                             anItem.getID(), "", getClass(), DO_INC_ITEM_ORDER);
192                     trans.addParam(PickList.LIST_ID, anItem.getID());
193                     rowBlock.add(trans);
194                 }
195 
196                 // add decrement icon for all but first
197                 if (i > 0) {
198                     trans = new Transition(DO_DEC_ITEM_ORDER + "_" +
199                             anItem.getID(), "", getClass(), DO_DEC_ITEM_ORDER);
200                     trans.addParam(PickList.LIST_ID, anItem.getID());
201                     rowBlock.add(trans);
202                 }
203 
204                 i++;
205             }
206 
207             Transition trans = new Transition(PROMPT_ADD_ITEM, this);
208             trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
209             trans.addParam(PickList.NODE_TYPE, item.getNodeType());
210             response.add(trans);
211 
212             trans = new Transition(SORT_BY_ALPHA, this);
213             trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
214             trans.addParam(PickList.NODE_TYPE, item.getNodeType());
215             response.add(trans);
216         } catch (Exception e) {
217             throw new ControllerException(e);
218         }
219     }
220 
221     /***
222      * Prompt for editing single pick item.
223      *
224      * @param request  The ExpressoRequest object.
225      * @param response The ExpressoResponse object.
226      * @throws ControllerException upon error.
227      */
228     protected void runPromptEditItemState(ExpressoRequest request,
229                                           ExpressoResponse response) throws ControllerException {
230         try {
231             PickList item = getPicklistItem(request);
232             Block b = DefaultAutoElement.getAutoControllerElement()
233                     .createDBObjectBlock(request, response,
234                             item);
235             response.add(b);
236 
237             Transition trans = new Transition(item.getDisplayString(),
238                     getClass(), DO_EDIT_ITEM);
239             trans.addParam(PickList.LIST_ID, item.getID());
240             response.add(trans);
241 
242             // list of nodes which use this item
243             List list = getAttributesUsingPickItem(item);
244             addLinksForNodes(response, list);
245         } catch (Exception e) {
246             throw new ControllerException(e);
247         }
248     }
249 
250     private void addLinksForNodes(ExpressoResponse response, List list)
251             throws ControllerException, DBException {
252         Transition trans;
253         Block block = new Block(ROW_BLOCK);
254         response.add(block);
255 
256         for (Iterator iterator = list.iterator(); iterator.hasNext();) {
257             Attribute anAttrib = (Attribute) iterator.next();
258             Node node = anAttrib.getParentNode();
259             trans = new Transition(node.getNodeTitle(), AddNodeAction.class,
260                     AddNodeAction.VIEW_NODE);
261             trans.addParam(Node.NODE_ID, anAttrib.getParentNodeId());
262             block.add(trans);
263         }
264     }
265 
266     private List getAttributesUsingPickItem(
267             PickList item) throws DBException {
268         Attribute attrib = new Attribute();
269         attrib.setRequestingUser(RequestRegistry.getUser());
270         attrib.setAttributeType(item.getPickAttribType());
271         attrib.setParentNodeType(item.getNodeType());
272         attrib.setAttributeValue(item.getID());
273 
274         return (List) attrib.searchAndRetrieveList();
275     }
276 
277     private PickList getPicklistItem(ExpressoRequest request)
278             throws DBException {
279         String id = request.getParameter(PickList.LIST_ID);
280 
281         if (id == null) {
282             throw new DBException("Cannot find picklist ID parameter");
283         }
284 
285         PickList pick = new PickList();
286         pick.setID(id);
287         pick.retrieve();
288 
289         return pick;
290     }
291 
292     /***
293      * Do for editing single pick item.
294      *
295      * @param request  The ExpressoRequest object.
296      * @param response The ExpressoResponse object.
297      * @throws ControllerException upon error.
298      */
299     protected void runDoEditItemState(ExpressoRequest request,
300                                       ExpressoResponse response) throws ControllerException {
301         try {
302             PickList item = getPicklistItem(request);
303             ErrorCollection ec = new ErrorCollection();
304             item = (PickList) DefaultAutoElement.getAutoControllerElement()
305                     .parseDBObject(request, item, ec);
306 
307             if (!ec.isEmpty()) {
308                 response.saveErrors(ec);
309                 response.setFormCache();
310 
311                 Transition trans = new Transition(PROMPT_EDIT_ITEM, this);
312                 trans.addParam(PickList.LIST_ID, item.getID());
313                 trans.executeTransition(request, response);
314 
315                 return;
316             }
317 
318             item.update(true);
319 
320             Transition trans = getListTrans(item);
321             trans.redirectTransition(request, response);
322         } catch (Exception e) {
323             throw new ControllerException(e);
324         }
325     }
326 
327     private Transition getListTrans(PickList item) throws DBException {
328         Transition trans = new Transition(PROMPT_LIST, this);
329         trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
330         trans.addParam(PickList.NODE_TYPE, item.getNodeType());
331 
332         return trans;
333     }
334 
335     /***
336      * Prompt for deleting single pick item.
337      *
338      * @param request  The ExpressoRequest object.
339      * @param response The ExpressoResponse object.
340      * @throws ControllerException upon error.
341      */
342     protected void runPromptDeleteItemState(ExpressoRequest request,
343                                             ExpressoResponse response) throws ControllerException {
344         try {
345             PickList item = getPicklistItem(request);
346             response.add(new Output(PickList.DISPLAY_IN_PICKLIST,
347                     item.getDisplayString()));
348 
349             List list = getSortedList(item);
350             List usagelist = getAttributesUsingPickItem(item);
351 
352             // handle any existing items
353             if ((usagelist.size() > 0) && (list.size() <= 1)) {
354                 response.addError("Cannot delete last item in list because there are usages.");
355                 response.setFormCache();
356 
357                 Transition trans = new Transition(PROMPT_LIST, this);
358                 trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
359                 trans.addParam(PickList.NODE_TYPE, item.getNodeType());
360                 trans.executeTransition(request, response);
361 
362                 return;
363             } else {
364                 // list of nodes which use this item
365                 if (usagelist.size() > 0) {
366                     PickList changeto = (PickList) list.get(0);
367 
368                     if (item.getID().equals(changeto.getID())) {
369                         changeto = (PickList) list.get(1);
370                     }
371 
372                     addWarning(request,
373                             usagelist.size() +
374                                     " usages of this choice will be changed to choice: '" +
375                                     changeto.getDisplayString() +
376                                     "' after deletion. (See edit screen for item to see details on usages).");
377                 }
378             }
379 
380             Transition trans = new Transition("Delete", getClass(),
381                     DO_DELETE_ITEM);
382             trans.addParam(PickList.LIST_ID, item.getID());
383             response.add(trans);
384         } catch (Exception e) {
385             throw new ControllerException(e);
386         }
387     }
388 
389     /***
390      * Delete single pick item.
391      *
392      * @param request  The ExpressoRequest object.
393      * @param response The ExpressoResponse object.
394      * @throws ControllerException upon error.
395      */
396     protected void runDoDeleteItemState(ExpressoRequest request,
397                                         ExpressoResponse response) throws ControllerException {
398         try {
399             PickList item = getPicklistItem(request);
400 
401             List list = getSortedList(item);
402             List usagelist = getAttributesUsingPickItem(item);
403 
404             // handle any existing items
405             if ((usagelist.size() > 0) && (list.size() == 1)) {
406                 response.addError("Cannot delete last item in list because there are usages.");
407                 response.setFormCache();
408 
409                 Transition trans = new Transition(PROMPT_LIST, this);
410                 trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
411                 trans.addParam(PickList.NODE_TYPE, item.getNodeType());
412                 trans.executeTransition(request, response);
413 
414                 return;
415             } else {
416                 // move any existing nodes to another item
417                 PickList changeto = (PickList) list.get(0);
418 
419                 if (item.getID().equals(changeto.getID())) {
420                     changeto = (PickList) list.get(1);
421                 }
422 
423                 for (Iterator iterator = usagelist.iterator();
424                      iterator.hasNext();) {
425                     Attribute attribute = (Attribute) iterator.next();
426                     attribute.setAttributeValue(changeto.getID());
427                     attribute.update();
428                 }
429             }
430 
431             item.delete();
432 
433             // fix numbering of remainder
434             // first, where is deleted item in array?
435             int index = list.indexOf(item);
436 
437             // move all the upper neighbors down one
438             for (int i = index + 1; i < list.size(); i++) {
439                 PickList anItem = (PickList) list.get(i);
440                 anItem.setOrderNum(anItem.getOrderNumInt() - 1);
441                 anItem.update();
442             }
443 
444             Transition trans = getListTrans(item);
445             trans.redirectTransition(request, response);
446         } catch (Exception e) {
447             throw new ControllerException(e);
448         }
449     }
450 
451     /***
452      * Increment pick item order number.
453      *
454      * @param request  The ExpressoRequest object.
455      * @param response The ExpressoResponse object.
456      * @throws ControllerException upon error.
457      */
458     protected void runDoIncItemOrderState(ExpressoRequest request,
459                                           ExpressoResponse response) throws ControllerException {
460         try {
461             renumItems(request, response, true);
462         } catch (Exception e) {
463             throw new ControllerException(e);
464         }
465     }
466 
467     /***
468      * Decrement pick item order number.
469      *
470      * @param request  The ExpressoRequest object.
471      * @param response The ExpressoResponse object.
472      * @throws ControllerException upon error.
473      */
474     protected void runDoDecItemOrderState(ExpressoRequest request,
475                                           ExpressoResponse response) throws ControllerException {
476         try {
477             renumItems(request, response, false);
478         } catch (Exception e) {
479             throw new ControllerException(e);
480         }
481     }
482 
483     /***
484      * Prompt to add pick item.
485      *
486      * @param request  The ExpressoRequest object.
487      * @param response The ExpressoResponse object.
488      * @throws ControllerException upon error.
489      */
490     protected void runPromptAddItemState(ExpressoRequest request,
491                                          ExpressoResponse response) throws ControllerException {
492         try {
493             PickList item = new PickList();
494             String[] must = {PickList.NODE_TYPE, PickList.ATTRIBUTE_TYPE};
495             isValidAndPopulated(item, must, PROMPT_LIST, request, response);
496 
497             // assign order num
498             List list = getSortedList(item);
499             item.setOrderNum(list.size());
500 
501             // add auto inputs
502             Block b = DefaultAutoElement.getAutoControllerElement()
503                     .createDBObjectBlock(request, response,
504                             item);
505             response.add(b);
506 
507             Transition trans = new Transition("Add", getClass(), DO_ADD_ITEM);
508             trans.addParam(PickList.NODE_TYPE, item.getNodeType());
509             trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
510             response.add(trans);
511         } catch (Exception e) {
512             throw new ControllerException(e);
513         }
514     }
515 
516     /***
517      * Do to add pick item.
518      *
519      * @param request  The ExpressoRequest object.
520      * @param response The ExpressoResponse object.
521      * @throws ControllerException upon error.
522      */
523     protected void runDoAddItemState(ExpressoRequest request,
524                                      ExpressoResponse response) throws ControllerException {
525         try {
526             PickList item = new PickList();
527             ErrorCollection ec = new ErrorCollection();
528             item = (PickList) DefaultAutoElement.getAutoControllerElement()
529                     .parseDBObject(request, item, ec);
530 
531             if (!ec.isEmpty()) {
532                 response.saveErrors(ec);
533                 response.setFormCache();
534 
535                 Transition trans = new Transition(PROMPT_ADD_ITEM, this);
536                 trans.addParam(PickList.NODE_TYPE, item.getNodeType());
537                 trans.addParam(PickList.ATTRIBUTE_TYPE, item.getPickAttribType());
538                 trans.executeTransition(request, response);
539 
540                 return;
541             }
542 
543             item.add();
544 
545             Transition trans = getListTrans(item);
546             trans.redirectTransition(request, response);
547         } catch (Exception e) {
548             throw new ControllerException(e);
549         }
550     }
551 
552     /***
553      * Sort by alpha of display name of items.
554      *
555      * @param request  The ExpressoRequest object.
556      * @param response The ExpressoResponse object.
557      * @throws ControllerException upon error.
558      */
559     protected void runSortByAlphaState(ExpressoRequest request,
560                                        ExpressoResponse response) throws ControllerException {
561         try {
562             PickList item = new PickList();
563             String[] must = {PickList.NODE_TYPE, PickList.ATTRIBUTE_TYPE};
564             isValidAndPopulated(item, must, PROMPT_LIST, request, response);
565 
566             // assign order num
567             List list = getSortedList(item);
568             Collections.sort(list, new ItemNameComparator());
569 
570             int i = 0;
571 
572             for (Iterator iterator = list.iterator(); iterator.hasNext();) {
573                 PickList anItem = (PickList) iterator.next();
574                 anItem.setOrderNum(i);
575                 anItem.update();
576                 i++;
577             }
578 
579             Transition trans = getListTrans(item);
580             trans.redirectTransition(request, response);
581         } catch (Exception e) {
582             throw new ControllerException(e);
583         }
584     }
585 
586     private void renumItems(ExpressoRequest request,
587                             ExpressoResponse response, boolean isInc)
588             throws DBException, ControllerException {
589         PickList pick = getPicklistItem(request);
590 
591         renumItems(pick, isInc);
592 
593         Transition trans = getListTrans(pick);
594         trans.redirectTransition(request, response);
595     }
596 
597     /***
598      * Renumber item specified, and all neighbors as necessary.
599      *
600      * @param item  The Item we're changing the order for.
601      * @param isInc true if we're increasing the order number.
602      * @throws DBException upon error.
603      */
604     private void renumItems(PickList item, boolean isInc) throws DBException {
605         String order = item.getOrderNum();
606         int oldnum = 0;
607 
608         if (order.length() > 0) {
609             oldnum = Integer.parseInt(order);
610         }
611 
612         List list = getSortedList(item);
613 
614         if (isInc) {
615             item.setOrderNum(oldnum + 1);
616             item.update();
617 
618             // move upper neighbor down if nec.
619             if ((oldnum + 1) < list.size()) {
620                 PickList neighbor = (PickList) list.get(oldnum + 1);
621 
622                 if ((neighbor != null) &&
623                         (neighbor.getOrderNumInt() == (oldnum + 1))) {
624                     // move it down
625                     neighbor.setOrderNum(oldnum);
626                     neighbor.update();
627                 }
628             }
629         } else {
630             item.setOrderNum(oldnum - 1);
631             item.update();
632 
633             // move lower neighbor up if nec.
634             if (((oldnum - 1) >= 0) && (oldnum < list.size())) {
635                 PickList neighbor = (PickList) list.get(oldnum - 1);
636 
637                 if ((neighbor != null) &&
638                         (neighbor.getOrderNumInt() == (oldnum - 1))) {
639                     // move it down
640                     neighbor.setOrderNum(oldnum);
641                     neighbor.update();
642                 }
643             }
644         }
645     }
646 
647     private List getSortedList(PickList item)
648             throws DBException {
649         PickList all = new PickList();
650         all.setNodeType(item.getNodeType());
651         all.setPickAttribType(item.getPickAttribType());
652 
653         List list = all.searchAndRetrieveList();
654         Collections.sort(list);
655 
656         return list;
657     }
658 
659     private static class ItemNameComparator implements Comparator {
660         /***
661          * @param o1 the first object to be compared.
662          * @param o2 the second object to be compared.
663          * @return a negative integer, zero, or a positive integer as the
664          *         first argument is less than, equal to, or greater than the
665          *         second.
666          * @throws ClassCastException if the arguments' types prevent them from
667          *                            being compared by this Comparator.
668          */
669         public int compare(Object o1, Object o2) {
670             int result = 0;
671             PickList item1 = (PickList) o1;
672             PickList item2 = (PickList) o2;
673 
674             try {
675                 result = item1.getDisplayString().compareTo(item2.getDisplayString());
676             } catch (DBException e) {
677                 Logger.getLogger(ItemNameComparator.class).error("Problem comparing picklist items: ", e);
678             }
679 
680             return result;
681         }
682     }
683 }