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.DBConnection;
14  import com.jcorporate.expresso.core.db.DBConnectionPool;
15  import com.jcorporate.expresso.core.db.DBException;
16  import com.jcorporate.expresso.core.misc.StringUtil;
17  import com.jcorporate.expresso.core.registry.RequestRegistry;
18  import com.jcorporate.expresso.core.security.SuperUser;
19  import com.jcorporate.expresso.core.security.User;
20  import com.jcorporate.expresso.core.security.filters.Filter;
21  import com.jcorporate.expresso.core.security.filters.RawFilter;
22  import com.jcorporate.expresso.services.controller.ui.DefaultAutoElement;
23  import com.jcorporate.expresso.services.dbobj.RowPermissions;
24  import com.jcorporate.expresso.services.dbobj.Setup;
25  import com.sri.common.controller.AbstractDBController;
26  import com.sri.common.taglib.InputTag;
27  import com.sri.emo.dbobj.*;
28  import com.sri.emo.wizard.wizardgateway.ListWizards;
29  import com.sri.emo.wizard.wizardgateway.WizardGatewayController;
30  import org.dom4j.Document;
31  import org.dom4j.DocumentHelper;
32  import org.dom4j.Element;
33  import org.dom4j.io.SAXReader;
34  
35  import java.io.Reader;
36  import java.io.StringReader;
37  import java.util.Iterator;
38  import java.util.List;
39  import java.util.TreeSet;
40  
41  
42  /***
43   * manipulate the parts of an entity--the metadata handler
44   *
45   * @author larry hamel
46   */
47  public class PartAction extends AbstractDBController {
48      /***
49  	 * 
50  	 */
51  	private static final long serialVersionUID = 1L;
52  
53  	public static String CONTROLLER_NAME = "PartAction Controller";
54  
55      // states
56      public static final String LIST_ENTITIES = "listEntities";
57      public static final String PROMPT_ADD_PART = "promptAddPart";
58      public static final String DO_ADD_PART = "doAddPart";
59      public static final String PROMPT_ADD_ENTITY = "promptAddEntity";
60      public static final String DO_ADD_ENTITY = "doAddEntity";
61      public static final String PROMPT_EDIT_PART = "promptEditPart";
62      public static final String DO_EDIT_PART = "doEditPart";
63      public static final String PROMPT_EDIT_RELATION = "promptEditRelation";
64      public static final String DO_EDIT_RELATION = "doEditRelation";
65      public static final String PROMPT_ADD_RELATION = "promptAddRelation";
66      public static final String DO_ADD_RELATION = "doAddRelation";
67      public static final String CONFIRM_DELETE_RELATION = "confirmDeleteRelation";
68      public static final String DO_DELETE_RELATION = "doDeleteRelation";
69      public static final String LIST_RELATIONS = "listRelations";
70      public static final String PROMPT_EDIT_ENTITY = "promptEditEntity";
71      public static final String DO_EDIT_ENTITY = "doEditEntity";
72      public static final String GENERATE_ENTITY_XML_DOCS = "generateEntityXMLDocs";
73      public static final String DO_IMPORT_ENTITY = "doImportEntity";
74      public static final String PROMPT_IMPORT_ENTITY = "promptImportEntity";
75      public static final String PROMPT_DELETE_ENTITY = "confirmDeleteEntity";
76      public static final String DO_DELETE_ENTITY = "doDeleteEntity";
77      public static final String PROMPT_DELETE_PART = "confirmDeletePart";
78      public static final String DO_DELETE_PART = "doDeletePart";
79      public static final String DO_INC_PART_NUM = "doIncPartNum";
80      public static final String DO_DEC_PART_NUM = "doDecPartNum";
81      public static final String GENERATE_DOCS = "generateDocs";
82      public static final String GENERATE_GUIDE_DOCS = "genGuideDocs";
83      public static final String GENERATE_ALL_ENTITY_DOCBOOK = "generateAllEntityDocbook";
84  
85      // constants for action use
86      public static final String ATTRIB_SET_BLOCK = "ATTRIB_SET_BLOCK";
87      public static final String IS_OWNED = "IS_OWNED";
88      public static final String ATTRIB = "ATTRIB";
89      public static final String RELATED_ENTITY_TYPE = "RelatedEntityType";
90      public static final String PROMPT_ADD_PART_OWNED = "PROMPT_ADD_PART_OWNED";
91      public static final String PROMPT_ADD_PART_SHARED = "PROMPT_ADD_PART_SHARED";
92      public static final String ENTITY = "Entity";
93      public static final String PART = "Part";
94      public static final String WRITE_GROUP = "WRITE_GROUP";
95      public static final String READ_GROUP = "READ_GROUP";
96  
97      public static final String READ_GROUP_DEFAULT_SETUP_CODE = "read_group_default";
98      public static final String WRITE_GROUP_DEFAULT_SETUP_CODE = "write_group_default";
99      public static final String MODEL_EDIT_GROUP_DEFAULT_SETUP_CODE = "model_edit_default";
100 
101     public PartAction() {
102         addState(new State(LIST_ENTITIES, "List all entities"));
103 
104         addState(new State(PROMPT_ADD_PART, "Prompt to add a part"));
105         addState(new State(PROMPT_ADD_ENTITY, "Prompt to add an entity"));
106         addState(new State(DO_ADD_PART, "Submit adding a part"));
107         addState(new State(DO_ADD_ENTITY, "Submit adding an entity"));
108 
109         addState(new State(PROMPT_EDIT_ENTITY, "Prompt editing of an entity"));
110         addState(new State(DO_EDIT_ENTITY, "Submit editing of an entity"));
111 
112         addState(new State(PROMPT_EDIT_PART, "Prompt to edit a part"));
113         addState(new State(DO_EDIT_PART, "Edit a part"));
114 
115         addState(new State(PROMPT_DELETE_ENTITY, "Confirm deletion of an entity"));
116         addState(new State(DO_DELETE_ENTITY, "Delete an entity"));
117         addState(new State(DO_DELETE_PART, "Delete a part"));
118         addState(new State(PROMPT_DELETE_PART, "Confirm deletion of a part"));
119         addState(new State(DO_INC_PART_NUM, "Increment part number"));
120         addState(new State(DO_DEC_PART_NUM, "Decrement part number"));
121 
122         addState(new State(GENERATE_DOCS, "Generate docs from DB descriptions"));
123         addState(new State(GENERATE_GUIDE_DOCS, "Generate full guide"));
124         addState(new State(GENERATE_ENTITY_XML_DOCS, "Generate documentation of XML format"));
125         addState(new State(GENERATE_ALL_ENTITY_DOCBOOK, "Generate docbook output"));
126 
127         addState(new State(PROMPT_ADD_RELATION, "Prompt to add a relation"));
128         addState(new State(DO_ADD_RELATION, "Submit adding a relation"));
129         addState(new State(PROMPT_EDIT_RELATION, "Prompt to edit a relation"));
130         addState(new State(DO_EDIT_RELATION, "Submit editing a relation"));
131         addState(new State(CONFIRM_DELETE_RELATION, "Confirm deletion of a relation"));
132         addState(new State(DO_DELETE_RELATION, "Submit deletion of a relation"));
133 
134         addState(new State(LIST_RELATIONS, "List relations"));
135         addState(new State(PROMPT_IMPORT_ENTITY, "Prompt import entity XML"));
136         addState(new State(DO_IMPORT_ENTITY, "Import entity XML"));
137 
138         setInitialState(LIST_ENTITIES);
139     }
140 
141     /***
142      * Returns the title of this controller
143      *
144      * @return String: The Controller Name.
145      */
146     public String getTitle() {
147         return CONTROLLER_NAME;
148     }
149 
150     protected void runListEntitiesState(final ExpressoRequest request,
151                                         final ExpressoResponse response) throws ControllerException {
152         try {
153             Block allblock = new Block(ROW_BLOCK);
154             response.add(allblock);
155 
156             NodeType[] types = NodeType.getAllTypes();
157 
158             for (int i = 0; i < types.length; i++) {
159                 NodeType type = types[i];
160 
161                 Block row = new Block(ROW);
162                 allblock.add(row);
163 
164                 Output output = new Output(NodeType.NODE_TYPE_ID, type.getId());
165                 row.add(output);
166 
167                 output = new Output(NodeType.DISPLAY_TITLE,
168                         type.getDisplayName());
169                 row.add(output);
170 
171                 output = new Output(NodeType.NODE_TYPE_DESCRIP,
172                         type.getEntityDescription());
173                 row.add(output);
174 
175                 int uid = type.ownerID();
176                 String login = "Admin";
177                 if (uid != -1) {
178                     login = User.getLoginFromId(uid);
179                 }
180                 output = new Output(RowPermissions.OWNER_ID, login);
181                 row.add(output);
182 
183                 if (type.canRequesterWrite()) {
184                     // edit link
185                     Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
186                     trans.addParam(NodeType.NODE_TYPE_ID, type.getId());
187                     row.add(trans);
188 
189                     // delete link
190                     trans = new Transition("delete", getClass(),
191                             PartAction.PROMPT_DELETE_ENTITY);
192                     trans.addParam(NodeType.NODE_TYPE_ID, type.getId());
193                     row.add(trans);
194                 }
195 
196                 Transition trans = new Transition(GENERATE_ENTITY_XML_DOCS, this);
197                 trans.addParam(NodeType.NODE_TYPE_ID, type.getId());
198                 row.add(trans);
199 
200                 if (type.canRequesterAdministrate()) {
201                     row.add(getPermsTrans(type));
202                 }
203             }
204 
205             boolean canAdd = request.getUserInfo().
206                     isMember(Setup.getValueRequired(
207                             PartAction.MODEL_EDIT_GROUP_DEFAULT_SETUP_CODE));
208             if (canAdd) {
209                 Transition trans = new Transition(PROMPT_ADD_ENTITY, this);
210                 response.add(trans);
211 
212                 trans = new Transition("Import from XML", getClass(),
213                         PROMPT_IMPORT_ENTITY);
214                 response.add(trans);
215             }
216 
217             response.add(new Transition(LIST_RELATIONS, this));
218             response.add(new Transition(GENERATE_DOCS, this));
219 
220             Transition listWizards = new Transition(ListWizards.STATE_NAME, "Wizards", WizardGatewayController.class,
221                     ListWizards.STATE_NAME);
222             response.add(listWizards);
223 
224         } catch (Exception e) {
225             throw new ControllerException(e);
226         }
227     }
228 
229     /***
230      * Prompt for one entity.
231      *
232      * @param request  The ExpressoRequest Object: our parameters.
233      * @param response The ExpressoResponse object: what we populate.
234      * @throws ControllerException upon error.
235      * @throws DBException         upon database error.
236      */
237     protected void runPromptEditEntityState(final ExpressoRequest request,
238                                             final ExpressoResponse response) throws ControllerException, DBException {
239         try {
240             NodeType entity = getEntity(request);
241 
242             if (!entity.canRequesterWrite()) {
243                 response.addError("User " + RequestRegistry.getUser().getLoginName()
244                         + " does not have permission to change model: "
245                         + entity.getEntityName());
246                 transition(LIST_ENTITIES, request, response);
247                 return;
248             }
249 
250             Input input = new Input(NodeType.NODE_TYPE_NAME,
251                     "Entity Internal Name");
252             input.setType(InputTag.TYPE_TEXT);
253             input.setDefaultValue(entity.getEntityName());
254             response.add(input);
255 
256             input = new Input(NodeType.NODE_TYPE_ID);
257             input.setType(InputTag.TYPE_HIDDEN);
258             input.setDefaultValue(entity.getId());
259             response.add(input);
260 
261             input = new Input(NodeType.DISPLAY_TITLE, "Display title");
262             input.setType(InputTag.TYPE_TEXT);
263             input.setDefaultValue(entity.getDisplayName());
264             response.add(input);
265 
266             input = new Input(NodeType.NODE_TYPE_HANDLER,
267                     "Special handler class");
268             input.setType(InputTag.TYPE_TEXT);
269 
270             if (entity.hasCustomHandler()) {
271                 input.setDefaultValue(entity.getSpecialHandlerName());
272             }
273 
274             response.add(input);
275 
276             response.addOutput(new Output(NodeType.DISPLAY_TITLE,
277                     entity.getDisplayName()));
278             response.addOutput(new Output(NodeType.NODE_TYPE_ID, entity.getId()));
279 
280             input = new Input(NodeType.NODE_TYPE_DESCRIP, "Description");
281             input.setType(InputTag.TYPE_TEXTAREA);
282             input.setLines(MULTIPLE_TEXTAREA_NUM_LINES);
283             input.setDisplayLength(TEXTAREA_NUM_COLS);
284             input.setDefaultValue(entity.getEntityDescription());
285             response.add(input);
286 
287             Transition trans = new Transition(DO_EDIT_ENTITY, this);
288             trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
289             trans.setLabel("Save");
290             response.add(trans);
291 
292             trans = new Transition(GENERATE_ENTITY_XML_DOCS, this);
293             trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
294             trans.setLabel("View as XML");
295             response.add(trans);
296 
297             Part[] parts = PartsFactory.getParts(entity.getEntityName());
298             Block allparts = new Block(ROW_BLOCK);
299             response.add(allparts);
300 
301             for (int i = 0; i < parts.length; i++) {
302                 Part part = parts[i];
303                 Block row = new Block(ROW);
304                 allparts.add(row);
305 
306                 Output output = new Output(Part.PART_LABEL, part.getPartLabel());
307                 row.add(output);
308 
309                 output = new Output(Part.PART_NUM, part.getPartNum());
310                 row.add(output);
311 
312                 trans = new Transition(PROMPT_EDIT_PART, this);
313                 trans.addParam(Part.PART_ID, part.getId());
314                 row.add(trans);
315 
316                 trans = new Transition(PROMPT_DELETE_PART, this);
317                 trans.addParam(Part.PART_ID, part.getId());
318                 row.add(trans);
319 
320                 if (part.getPartNumInt() < (parts.length - 1)) {
321                     trans = new Transition(DO_INC_PART_NUM + i, "+",
322                             getClass(), DO_INC_PART_NUM);
323                     trans.addParam(Part.PART_ID, part.getId());
324                     row.add(trans);
325                 }
326 
327                 if (part.getPartNumInt() > 0) {
328                     trans = new Transition(DO_DEC_PART_NUM + i, "-",
329                             getClass(), DO_DEC_PART_NUM);
330                     trans.addParam(Part.PART_ID, part.getId());
331                     row.add(trans);
332                 }
333 
334                 trans = new Transition(PROMPT_ADD_PART_OWNED, "", getClass(),
335                         PROMPT_ADD_PART);
336                 trans.addParam(Part.PART_IS_ATTRIB, "Y");
337                 trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
338                 trans.addParam(Part.PART_NUM, part.getPartNum());
339                 row.add(trans);
340 
341                 trans = new Transition(PROMPT_ADD_PART_SHARED, "", getClass(),
342                         PROMPT_ADD_PART);
343                 trans.addParam(Part.PART_IS_ATTRIB, "N");
344                 trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
345                 trans.addParam(Part.PART_NUM, part.getPartNum());
346                 row.add(trans);
347 
348                 // add flag for sharedness
349                 if (part.isOwnedAttribute()) {
350                     row.add(new Output(Part.PART_IS_ATTRIB, "Y"));
351                 }
352             }
353 
354             // for new entities, at least include transitions to add parts
355             if (parts.length == 0) {
356                 trans = new Transition(PROMPT_ADD_PART_OWNED, "", getClass(),
357                         PROMPT_ADD_PART);
358                 trans.addParam(Part.PART_IS_ATTRIB, "Y");
359                 trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
360                 trans.addParam(Part.PART_NUM, "0");
361                 response.add(trans);
362 
363                 trans = new Transition(PROMPT_ADD_PART_SHARED, "", getClass(),
364                         PROMPT_ADD_PART);
365                 trans.addParam(Part.PART_IS_ATTRIB, "N");
366                 trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
367                 trans.addParam(Part.PART_NUM, "0");
368                 response.add(trans);
369             }
370         } catch (Exception e) {
371             throw new ControllerException(e);
372         }
373     }
374 
375     /***
376      * Do the edits.
377      *
378      * @param request  The ExpressoRequest Object: our parameters.
379      * @param response The ExpressoResponse object: what we populate.
380      * @throws ControllerException upon error.
381      */
382     protected void runDoEditEntityState(final ExpressoRequest request,
383                                         final ExpressoResponse response) throws ControllerException {
384         try {
385             String entityName = StringUtil.replaceAll(request.getParameter(NodeType.NODE_TYPE_NAME), " ", "_");
386             NodeType entity = getEntity(request);
387             String oldname = entity.getEntityName();
388 
389             String[] required = {NodeType.NODE_TYPE_ID};
390 
391             if (!isValidAndPopulated(entity, required, PROMPT_EDIT_ENTITY,
392                     request, response)) {
393                 return;
394             }
395 
396             entity.update();
397 
398             if ((entityName != null) && !entityName.equals(oldname)) {
399                 // big trouble-- must rename all existing records
400                 changeNodeTypeName(entity, oldname, entityName);
401             }
402 
403             transition(LIST_ENTITIES, request, response);
404         } catch (Exception e) {
405             throw new ControllerException(e);
406         }
407     }
408 
409     private void changeNodeTypeName(final NodeType entity, final String oldName,
410                                     String newName) throws DBException {
411         // change all existing attributes
412         String sql = "UPDATE attribute SET " + " NODE_TYPE = '" + newName +
413                 "'" + " WHERE   NODE_TYPE LIKE '" + oldName + "' ";
414 
415         DBConnection conn = DBConnectionPool.getInstance(entity.getDataContext()).getConnection();
416 
417         try {
418             conn.executeUpdate(sql);
419 
420             // change all nodes
421             sql = "UPDATE node SET " + " NODE_TYPE = '" + newName + "'" +
422                     " WHERE  NODE_TYPE LIKE '" + oldName + "' ";
423 
424             conn.executeUpdate(sql);
425 
426             // change part-to-entity relation
427             sql = "UPDATE part2 SET " + " PARENT_TYPE = '" + newName + "'" +
428                     " WHERE  PARENT_TYPE  LIKE '" + oldName + "' ";
429 
430             conn.executeUpdate(sql);
431         } finally {
432             conn.release();
433         }
434 
435         // now a tricky one.  for all parts (of any entity) with part
436         // which is SHARED and parttype = target type
437         Part part = new Part(entity);
438         part.isOwnedAttribute(false);
439         part.setPartType(oldName);
440 
441         List list = part.searchAndRetrieveList();
442 
443         for (Iterator iterator = list.iterator(); iterator.hasNext();) {
444             Part apart = (Part) iterator.next();
445             apart.setPartType(newName);
446             apart.update();
447         }
448 
449         PartsFactory.refreshCache(oldName);
450         PartsFactory.refreshCache(newName);
451     }
452 
453     /***
454      * Prompt for edits.
455      *
456      * @param request  The ExpressoRequest Object: our parameters.
457      * @param response The ExpressoResponse object: what we populate.
458      * @throws ControllerException upon error.
459      */
460     protected void runPromptEditPartState(final ExpressoRequest request,
461                                           final ExpressoResponse response) throws ControllerException {
462         try {
463             Part part = getPart(request);
464 
465             Input input = new Input(Part.PART_LABEL, "Part Label");
466             input.setType(InputTag.TYPE_TEXT);
467             input.setDefaultValue(part.getPartLabel());
468             response.add(input);
469 
470             Output output = new Output(Part.PART_LABEL, part.getPartLabel());
471             response.add(output);
472 
473             input = new Input(Part.PART_ID);
474             input.setType(InputTag.TYPE_HIDDEN);
475             input.setDefaultValue(part.getId());
476             response.add(input);
477 
478             input = new Input(Part.PART_NUM);
479             input.setType(InputTag.TYPE_TEXT);
480             input.setDefaultValue(part.getPartNum());
481             response.add(input);
482 
483             output = new Output(Part.PARENT_TYPE, part.getParentType());
484             response.add(output);
485 
486             input = new Input(Part.PART_TYPE, "Part Type");
487 
488             if (part.isOwnedAttribute()) {
489                 input.setType(InputTag.TYPE_TEXT);
490                 input.setDefaultValue(part.getPartType());
491             } else {
492                 createNodeTypeMenu(input, part.getPartType());
493             }
494 
495             response.add(input);
496 
497             if (!part.isOwnedAttribute()) {
498                 input = new Input(Part.NODE_PART_RELATION_TYPE,
499                         "Part Relationship");
500                 createNodeRelationMenu(input, part);
501                 response.add(input);
502 
503                 // output number of relations of this type in use
504                 List relationsInUse = part.getRelations();
505                 response.add(new Output(Part.REL_REF, relationsInUse.size() +
506                         ""));
507 
508                 // output sample of first 10 (max)
509                 int j = 0;
510 
511                 if (relationsInUse.size() > 0) {
512                     Block block = new Block(ROW_BLOCK);
513                     response.add(block);
514 
515                     for (Iterator iterator = relationsInUse.iterator();
516                          iterator.hasNext();) {
517                         Relation relation = (Relation) iterator.next();
518                         Node src = relation.getSrcNode();
519                         Transition trans = new Transition(src.getNodeTitle(),
520                                 AddNodeAction.class, AddNodeAction.VIEW_NODE);
521                         trans.addParam(Node.NODE_ID, src.getNodeId());
522                         block.add(trans);
523 
524                         if (++j >= 10) {
525                             break;
526                         }
527                     }
528                 }
529             } else if (part.hasPicklist()) {
530                 Transition trans = new Transition("", PicklistAction.class,
531                         PicklistAction.PROMPT_LIST);
532                 trans.addParam(PickList.ATTRIBUTE_TYPE, part.getPartType());
533                 trans.addParam(PickList.NODE_TYPE, part.getParentType());
534                 response.add(trans);
535             }
536 
537             output = new Output(Part.PART_NUM, part.getPartNum());
538             response.add(output);
539 
540             input = new Input(Part.SPECIAL_HANDLER,
541                     "Special Handler (class name for any special handling)");
542             input.setType(InputTag.TYPE_TEXT);
543             input.setDefaultValue(part.getSpecialHandlerName());
544             response.add(input);
545 
546             input = new Input(Part.PART_DESCRIP,
547                     "Description of this part");
548             input.setType(InputTag.TYPE_TEXTAREA);
549             input.setLines(3);
550             input.setDisplayLength(50);
551             input.setDefaultValue(part.getPartDescriptionRaw());
552             response.add(input);
553 
554             input = new Input(Part.CARDINALITY,
555                     "Cardinality (single or multiple allowed, etc.)");
556             createCardinalityMenu(input, part);
557             response.add(input);
558 
559             Transition trans = new Transition(DO_EDIT_PART, this);
560             trans.setLabel("Save");
561             response.add(trans);
562         } catch (Exception e) {
563             throw new ControllerException(e);
564         }
565     }
566 
567     /***
568      * Do the edits for parts.
569      *
570      * @param request  The ExpressoRequest Object: our parameters.
571      * @param response The ExpressoResponse object: what we populate.
572      * @throws ControllerException upon error.
573      */
574     protected void runDoEditPartState(final ExpressoRequest request,
575                                       final ExpressoResponse response) throws ControllerException {
576         try {
577             Part part = getPart(request);
578 
579             String newPartType = request.getParameter(Part.PART_TYPE);
580 
581             // translate menu choice into text if part is shared node
582             if (isNumeric(newPartType)) {
583                 int type_id = Integer.parseInt(newPartType);
584                 NodeType type = NodeType.getFromId(type_id);
585                 newPartType = type.getEntityName();
586 
587                 // reset in incoming params, which will land in new object
588                 request.setParameter(Part.PART_TYPE, newPartType);
589             }
590 
591             String[] required = {Part.PART_TYPE, Part.PARENT_TYPE};
592 
593             if (!isValidAndPopulated(part, required, PROMPT_EDIT_PART, request,
594                     response)) {
595                 response.setFormCache();
596                 return;
597             }
598 
599             // test custom handler; see if this throws; will be null (not throw) if nothing's entered
600             try {
601                 part.getCustomHandler();
602             } catch (DBException e) {
603                 String clazz = part.getField(Part.SPECIAL_HANDLER);
604                 response.addError("Cannot instantiate class: '" + clazz
605                         + "' because of error: " + e.getMessage());
606                 response.setFormCache();
607                 transition(PROMPT_EDIT_PART, request, response);
608                 return;
609             }
610 
611             part.update(); // handles relation updates too
612 
613             Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
614             trans.addParam(NodeType.NODE_TYPE_ID, part.getParentEntity().getId());
615             trans.setLabel("Save");
616             trans.redirectTransition(request, response);
617         } catch (Exception e) {
618             throw new ControllerException(e);
619         }
620     }
621 
622     private Part getPart(final ExpressoRequest request) throws ControllerException, DBException {
623         String id = request.getParameter(Part.PART_ID);
624 
625         if ((id == null) || (id.length() == 0)) {
626             throw new ControllerException("part id is a required parameter");
627         }
628 
629         Part part = new Part();
630         part.setRequestingUser(RequestRegistry.getUser());
631         part.setId(id);
632         part.retrieve();
633 
634         return part;
635     }
636 
637     /***
638      * Generate html doc for all entities, with their parts.
639      *
640      * @param request  The ExpressoRequest Object: our parameters.
641      * @param response The ExpressoResponse object: what we populate.
642      * @throws ControllerException upon error.
643      */
644     protected void runGenerateDocsState(final ExpressoRequest request,
645                                         final ExpressoResponse response) throws ControllerException {
646         NodeType type = null;
647         try {
648             NodeType[] types = NodeType.getAllTypes();
649 
650             // now all attributes per item
651             for (int i = 0; i < types.length; i++) {
652                 type = types[i];
653 
654                 Block attribBlock = new Block(ATTRIB_SET_BLOCK);
655                 response.add(attribBlock);
656 
657                 // provide header title and anchor
658                 Output output = new Output(NodeType.DISPLAY_TITLE,
659                         type.getDisplayName());
660                 attribBlock.add(output);
661 
662                 output = new Output(NodeType.NODE_TYPE_NAME,
663                         type.getEntityName());
664                 attribBlock.add(output);
665 
666                 output = new Output(NodeType.NODE_TYPE_DESCRIP,
667                         str(type.getEntityDescription()));
668                 attribBlock.add(output);
669 
670                 Part[] parts = PartsFactory.getParts(type.getEntityName());
671 
672                 for (int j = 0; j < parts.length; j++) {
673                     Part part = parts[j];
674 
675                     Block row = new Block(ATTRIB);
676                     attribBlock.add(row);
677 
678                     // part items
679                     output = new Output(Part.PART_LABEL, part.getPartLabel());
680                     row.add(output);
681 
682                     // add one to num for 1-based count
683                     output = new Output(Part.PART_NUM,
684                             "" + (Integer.parseInt(part.getPartNum()) + 1));
685                     row.add(output);
686 
687                     if (part.isOwnedAttribute()) {
688                         //                        output = new Output(Part.CARDINALITY, part.getCardinality());
689                         //                        row.add(output);
690                         // for anchor
691                         output = new Output(Part.PART_TYPE, part.getPartType());
692                         row.add(output);
693                     }
694 
695                     output = new Output(Part.PART_DESCRIP,
696                             str(part.getDefinition(true)));
697                     row.add(output);
698                 }
699             }
700 
701             // docs for relations between nodes
702             addAllRelationTypes(response);
703         } catch (Exception e) {
704             String msg = "";
705             if ( type != null ) try {
706                 msg = "While describing type: " + type.getEntityName();
707             } catch (DBException e1) {
708                 getLogger().error(e1);
709             }
710             throw new ControllerException(msg, e);
711         }
712     }
713 
714     /***
715      * Generate html doc for all entities, with their parts.
716      *
717      * @param request  The ExpressoRequest Object: our parameters.
718      * @param response The ExpressoResponse object: what we populate.
719      * @throws ControllerException upon error.
720      * @todo remove
721      */
722     protected void runGenGuideDocsState(final ExpressoRequest request,
723                                         final ExpressoResponse response) throws ControllerException {
724         runGenerateDocsState(request, response);
725     }
726 
727 
728     private void addAllRelationTypes(ExpressoResponse response) throws DBException, ControllerException {
729         RelationType query = new RelationType();
730         query.setRequestingUser(RequestRegistry.getUser());
731         List allRelationTypes = query.searchAndRetrieveList(RelationType.
732                 RELATION_TYPE_DISPLAY_NAME);
733 
734         for (Iterator iter = allRelationTypes.iterator(); iter.hasNext();) {
735             RelationType type = (RelationType) iter.next();
736 
737             // only include items with a description
738             if (type.getRelTypeDescrip().length() > 0) {
739                 Block relBlock = new Block(ROW_BLOCK);
740                 response.add(relBlock);
741                 relBlock.add(new Output(RelationType.RELATION_TYPE_NAME,
742                         type.getRelTypeName()));
743                 relBlock.add(new Output(RelationType.
744                         RELATION_TYPE_DISPLAY_NAME,
745                         type.getRelTypeDisplayName()));
746                 relBlock.add(new Output(RelationType.RELATION_TYPE_DESCRIP,
747                         type.getRelTypeDescrip()));
748             }
749         }
750     }
751 
752     private boolean isNumeric(final String partType) {
753         boolean result = true;
754 
755         try {
756             Integer.parseInt(partType);
757         } catch (NumberFormatException e) {
758             result = false;
759         }
760 
761         return result;
762     }
763 
764     private void createCardinalityMenu(final Input input, final Part part)
765             throws DBException {
766         input.setType(InputTag.TYPE_DROPDOWN);
767         input.setMaxLength(80);
768 
769         // determine previously selected
770         String selectedText = "";
771 
772         if (part != null) {
773             selectedText = part.getCardinality();
774         }
775 
776         boolean foundSelected = false;
777 
778         for (int i = 0; i < Part.CARDINALITY_CHOICES.length; i++) {
779             String[] choice = Part.CARDINALITY_CHOICES[i];
780             input.addValidValue(choice[0], choice[1]);
781 
782             if (selectedText.equals(choice[0])) {
783                 foundSelected = true;
784                 input.setDefaultValue(choice[0]);
785             }
786         }
787 
788         // use default if there is no previous selection
789         if (!foundSelected) {
790             input.setDefaultValue(Part.MULTIPLE_ALLOWED);
791         }
792     }
793 
794     /***
795      * Create a menu which lists all the possible node relations.
796      *
797      * @param input The Input we're populating with valid values.
798      * @param part  part for menu; used for default selection and UID and
799      *              db context; cannot be null
800      * @throws DBException upon error.
801      */
802     private void createNodeRelationMenu(final Input input, final Part part)
803             throws DBException {
804         input.setType(InputTag.TYPE_DROPDOWN);
805         input.setMaxLength(80);
806 
807         // determine previously selected
808         String selectedText = part.getNodeRelation();
809 
810         boolean foundSelected = false;
811         RelationType typeSearch = null;
812         typeSearch = new RelationType();
813         typeSearch.setRequestingUser(part.getRequestingUser());
814         typeSearch.setDataContext(part.getDataContext());
815 
816         List list = typeSearch.searchAndRetrieveList(RelationType.
817                 RELATION_TYPE_DESCRIP);
818 
819         for (Iterator iterator = list.iterator(); iterator.hasNext();) {
820             RelationType choice = (RelationType) iterator.next();
821             input.addValidValue(choice.getRelTypeName(),
822                     choice.getRelTypeDisplayName());
823 
824             if (selectedText.equals(choice.getRelTypeName())) {
825                 foundSelected = true;
826                 input.setDefaultValue(choice.getRelTypeName());
827             }
828         }
829 
830         if (!foundSelected) {
831             input.setDefaultValue(RelationType.DEST_IS_PART_OF_SRC);
832         }
833     }
834 
835     private void createNodeTypeMenu(final Input input, final String defaultType) throws DBException {
836         input.setType(InputTag.TYPE_DROPDOWN);
837 
838         // determine default node type id
839         String selected = "-1";
840 
841         if (defaultType != null) {
842             NodeType defaultNodeType = new NodeType(RequestRegistry.getUser());
843             defaultNodeType.setEntityName(defaultType);
844 
845             if (defaultNodeType.find()) {
846                 selected = defaultNodeType.getId();
847             }
848         }
849 
850         boolean foundSelected = false;
851         NodeType[] types = NodeType.getAllTypes();
852 
853         for (int i = 0; i < types.length; i++) {
854             NodeType type = types[i];
855             input.addValidValue(type.getId(), type.getEntityName());
856 
857             if (selected.equals(type.getId())) {
858                 foundSelected = true;
859                 input.setDefaultValue(selected);
860             }
861         }
862 
863         // use default if there is no previous selection
864         if (!foundSelected) {
865             input.setDefaultValue(types[0].getId());
866         }
867     }
868 
869     /***
870      * Generate XML format documentation for a given object.
871      *
872      * @param request  The ExpressoRequest Object: our parameters.
873      * @param response The ExpressoResponse object: what we populate.
874      * @throws ControllerException upon error.
875      */
876     protected void runGenerateEntityXMLDocsState(final ExpressoRequest request,
877                                                  final ExpressoResponse response)
878             throws ControllerException {
879         try {
880             NodeType entity = getEntity(request);
881             ModelXMLWriter writer = new ModelXMLWriter();
882             Document doc = writer.renderEntityAsXml(entity);
883 
884             response.setCustomResponse(true);
885 
886             if (doc != null) {
887                 outputXML(doc, request);
888             }
889         } catch (Exception e) {
890             throw new ControllerException(e);
891         }
892     }
893 
894 
895     /***
896      * Import XML for a given object type; update existing if found.
897      *
898      * @param request  The ExpressoRequest Object: our parameters.
899      * @param response The ExpressoResponse object: what we populate.
900      * @throws ControllerException upon error.
901      */
902     protected void runDoImportEntityState(final ExpressoRequest request,
903                                           final ExpressoResponse response) throws ControllerException {
904         try {
905             String xml = request.getParameter(ENTITY);
906             Element origroot = parseXML(new StringReader(xml));
907             ModelXMLReader reader = new ModelXMLReader();
908             NodeType entity = reader.parseEntityAndParts(origroot);
909 
910             Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
911             trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
912             trans.executeTransition(request, response);
913         } catch (Exception e) {
914             throw new ControllerException(e);
915         }
916     }
917 
918     /***
919      * Import XML for a given object type; update existing if found.
920      *
921      * @param request  The ExpressoRequest Object: our parameters.
922      * @param response The ExpressoResponse object: what we populate.
923      * @throws ControllerException upon error.
924      */
925     protected void runPromptImportEntityState(final ExpressoRequest request,
926                                               final ExpressoResponse response) throws ControllerException {
927         try {
928             Input input = new Input(ENTITY);
929             input.setType(InputTag.TYPE_TEXTAREA);
930             input.setDisplayLength(60);
931 
932             //            input.setMaxLength(INPUT_MAX_LENGTH);
933             input.setLines(20);
934 
935             // check request for default value, if it was passed in
936             String defaultValue = request.getParameter(ENTITY);
937             input.setDefaultValue(defaultValue);
938 
939             String formValue = response.getFormCache(ENTITY);
940 
941             if (formValue.length() == 0) {
942                 input.setDefaultValue(formValue);
943             }
944 
945             response.add(input);
946 
947             // Add transition to save
948             Transition trans = new Transition("Import", getClass(),
949                     DO_IMPORT_ENTITY);
950             response.add(trans);
951         } catch (Exception e) {
952             throw new ControllerException(e);
953         }
954     }
955 
956     private NodeType getEntity(final ExpressoRequest request) throws
957             ControllerException, DBException {
958         String id = request.getParameter(NodeType.NODE_TYPE_ID);
959 
960         if ((id == null) || (id.length() == 0)) {
961             throw new ControllerException("node type id is a required parameter");
962         }
963 
964         NodeType entity = new NodeType(RequestRegistry.getUser());
965         entity.setId(id);
966         entity.retrieve(); // will throw if not found
967 
968         return entity;
969     }
970 
971     /***
972      * Generate XML format documentation for a given object.
973      *
974      * @param request  The ExpressoRequest Object: our parameters.
975      * @param response The ExpressoResponse object: what we populate.
976      * @throws ControllerException upon error.
977      */
978     protected void runGenerateAllEntityDocbookState(final ExpressoRequest
979             request, final ExpressoResponse response) throws ControllerException {
980         Document doc = null;
981 
982         try {
983             TreeSet real = PartsFactory.getEntities();
984             List fake = PartsFactory.getFakeEntities();
985 
986             NodeType assessTask = new NodeType(RequestRegistry.getUser());
987             assessTask.setEntityName("ASSESSMENT_TASK");
988             assessTask.setDisplayName("Assessment Task");
989             assessTask.setEntityDescription("A task is a goal-directed human activity to be " +
990                     "pursued in a specified manner, context, or circumstance. Tasks may vary " +
991                     "from relatively simple (e.g., responding to a multiple-choice item) to " +
992                     "complex (e.g., conducting a symphony). [Haertel93]");
993             fake.add(assessTask);
994 
995             real.addAll(fake);
996 
997             doc = DocumentHelper.createDocument();
998 
999             Element root = doc.addElement("section");
1000 
1001             if (real.size() > 0) {
1002                 for (Iterator iterator = real.iterator(); iterator.hasNext();) {
1003                     NodeType entity = (NodeType) iterator.next();
1004 
1005                     // use raw field for default text value,
1006                     // since it does not need translation to HTML
1007                     Filter old = entity.setFilterClass(new RawFilter());
1008 
1009                     boolean isFake = fake.contains(entity);
1010                     String indexMarker = entity.getEntityName();
1011 
1012                     if (isFake) {
1013                         indexMarker += "2";
1014                     }
1015 
1016                     Element section = root.addElement("section");
1017                     section.addAttribute("id", indexMarker).addAttribute("xreflabel",
1018                             entity.getDisplayName());
1019 
1020                     section.addElement("title").addText(entity.getDisplayName());
1021 
1022                     section.addElement("indexterm")
1023                             .addAttribute("zone", indexMarker)
1024                             .addElement("primary").addText(entity.getDisplayName());
1025 
1026                     Element para = section.addElement("para");
1027 
1028                     if (entity.getEntityDescription().length() > 0) {
1029                         para.addText(entity.getEntityDescription());
1030                     } else {
1031                         para.addText("sample entity definition for this entity... FILL ME IN!");
1032                     }
1033 
1034                     Part[] parts = PartsFactory.getParts(entity.getEntityName());
1035 
1036                     if (!isFake && (parts.length > 0)) {
1037                         Element summarySection = section.addElement("section");
1038                         summarySection.addElement("title").addText("Attribute Summary");
1039 
1040                         summarySection.addElement("para").addText(
1041                                 "The attribute summary in the next table provides name and description, " +
1042                                         "and includes two additional items: " +
1043                                         " 1) an indication of whether the attribute is shared, " +
1044                                         "and 2) the cardinality for the attribute, which is " +
1045                                         "basically whether more than " +
1046                                         "one attribute instance can be entered for a given attribute type.");
1047 
1048                         Element table = summarySection.addElement("table");
1049                         table.addElement("title").addText("Attribute Descriptions for " +
1050                                 entity.getDisplayName());
1051 
1052                         Element tgroup = table.addElement("tgroup");
1053 
1054                         //                        tgroup.addProcessingInstruction("dbhtml", "cellspacing=\"0\"");
1055                         //                        tgroup.addProcessingInstruction("dbhtml", "cellpadding=\"5\"");
1056                         Element tbody = tgroup.addAttribute("cols", "4")
1057                                 .addElement("tbody");
1058 
1059                         tgroup.addElement("colspec").addAttribute("colnum", "1")
1060                                 .addAttribute("colname", "displayName")
1061                                 .addAttribute("colwidth", "1*").addAttribute("align",
1062                                 "left");
1063                         tgroup.addElement("colspec").addAttribute("colnum", "2")
1064                                 .addAttribute("colname", "descrip").addAttribute("colwidth",
1065                                 "2*");
1066                         tgroup.addElement("colspec").addAttribute("colnum", "3")
1067                                 .addAttribute("colname", "shared")
1068                                 .addAttribute("colwidth", "0.4*").addAttribute("align",
1069                                 "center");
1070                         tgroup.addElement("colspec").addAttribute("colnum", "4")
1071                                 .addAttribute("colname", "card")
1072                                 .addAttribute("colwidth", "0.6*").addAttribute("align",
1073                                 "left");
1074 
1075                         Element headerRow = tgroup.addElement("thead")
1076                                 .addElement("row");
1077                         headerRow.addElement("entry").addElement("para")
1078                                 .addText("Display Order and Name");
1079                         headerRow.addElement("entry").addElement("para")
1080                                 .addText("Description");
1081                         headerRow.addElement("entry").addElement("para")
1082                                 .addText("Shared?");
1083                         headerRow.addElement("entry").addElement("para")
1084                                 .addText("Cardinality");
1085 
1086                         boolean hasShared = false;
1087                         boolean hasOwned = false;
1088 
1089                         // now all parts per entity
1090                         for (int j = 0;
1091                              !isFake && (parts != null) &&
1092                                      (j < parts.length); j++) {
1093                             Part part = parts[j];
1094                             Filter partold = part.setFilterClass(new RawFilter());
1095 
1096                             Element row = tbody.addElement("row")
1097                                     .addAttribute("id",
1098                                             part.getPartUniqueId()).addAttribute("xreflabel",
1099                                     part.getPartLabel());
1100 
1101                             // row cannot contain an index term, but we can put this
1102                             // ANYWHERE since we are using the 'zone' feature
1103                             table.addElement("indexterm")
1104                                     .addAttribute("zone", part.getPartUniqueId())
1105                                     .addElement("primary").addText(part.
1106                                     getPartLabel() +
1107                                     " (" + entity.getDisplayName() + " attrib.)");
1108 
1109                             row.addElement("entry").addElement("para").addText(part.getPartNum() +
1110                                     ") " + part.getPartLabel());
1111                             row.addElement("entry").addElement("para").addText("\"" +
1112                                     part.getPartLabel() + "\" attributes " +
1113                                     part.getDefinition(false));
1114 
1115                             if (part.isSharedNodeAttrib()) {
1116                                 hasShared = true;
1117                                 row.addElement("entry").addElement("para")
1118                                         .addText("Yes");
1119                             } else {
1120                                 row.addElement("entry"); // get &nbsp; this way
1121                                 hasOwned = true;
1122                             }
1123 
1124                             row.addElement("entry").addElement("para").addText(part.getCardinalityLabel());
1125 
1126                             part.setFilterClass(partold);
1127                         } // all attribute definitions
1128 
1129                         // show shared items
1130                         if (hasShared) {
1131                             Element sharedSection = section.addElement("section");
1132                             sharedSection.addElement("title").addText("Shared Attribute Details");
1133 
1134                             sharedSection.addElement("para").addText("Additional " +
1135                                     "shared attribute information in the next table includes details " +
1136                                     " on the Object type which" +
1137                                     " is allowed for the given relation, as well as" +
1138                                     " the relation's label and internal name.");
1139                             table = sharedSection.addElement("table");
1140                             table.addElement("title").addText("Additional Shared Attribute Information for " +
1141                                     entity.getDisplayName());
1142 
1143                             //                            table.addProcessingInstruction("dbfo", "table-width=\"125\"");
1144                             tgroup = table.addElement("tgroup");
1145                             tbody = tgroup.addAttribute("cols", "3").addElement("tbody");
1146 
1147                             tgroup.addElement("colspec")
1148                                     .addAttribute("colnum", "1")
1149                                     .addAttribute("colname", "displayName")
1150                                     .addAttribute("colwidth", "0.75*")
1151                                     .addAttribute("align", "left");
1152 
1153                             tgroup.addElement("colspec")
1154                                     .addAttribute("colnum", "2")
1155                                     .addAttribute("colname", "typename")
1156                                     .addAttribute("colwidth", "1*");
1157                             tgroup.addElement("colspec")
1158                                     .addAttribute("colnum", "3")
1159                                     .addAttribute("colname", "relation")
1160                                     .addAttribute("colwidth", "2*").addAttribute("align",
1161                                     "left");
1162 
1163                             headerRow = tgroup.addElement("thead").addElement("row");
1164                             headerRow.addElement("entry").addElement("para")
1165                                     .addText("Display Order and Name");
1166                             headerRow.addElement("entry").addElement("para")
1167                                     .addText("Related Object Type");
1168                             headerRow.addElement("entry").addElement("para")
1169                                     .addText("Relation Label -- Internal Relation Name");
1170 
1171                             // now all parts per entity
1172                             for (int j = 0;
1173                                  !isFake && (parts != null) &&
1174                                          (j < parts.length); j++) {
1175                                 Part part = parts[j];
1176 
1177                                 if (part.isOwnedAttribute()) {
1178                                     continue; // just shared
1179                                 }
1180 
1181                                 Filter partold = part.setFilterClass(new
1182                                         RawFilter());
1183 
1184                                 Element row = tbody.addElement("row");
1185 
1186                                 row.addElement("entry").addElement("para")
1187                                         .addText(part.getPartNum() + ") " +
1188                                                 part.getPartLabel());
1189 
1190                                 String text = "";
1191 
1192                                 if (part.isOwnedAttribute()) {
1193                                     text = part.getPartType();
1194                                 } else {
1195                                     text = part.getNodeRelationObjectLabel();
1196                                 }
1197 
1198                                 row.addElement("entry").addElement("para")
1199                                         .addText(text);
1200 
1201                                 if (part.isSharedNodeAttrib()) {
1202                                     row.addElement("entry").addElement("para")
1203                                             .addText(part.getNodeRelationLabel(request) + " -- " +
1204                                                     part.getNodeRelation());
1205                                 } else {
1206                                     row.addElement("entry");
1207                                 }
1208 
1209                                 part.setFilterClass(partold);
1210                             } // all attributes
1211                         }
1212 
1213                         // show owned items
1214                         if (hasOwned) {
1215                             Element ownedSection = section.addElement("section");
1216                             ownedSection.addElement("title").addText("Owned Attribute Details");
1217 
1218                             ownedSection.addElement("para").addText("Additional " +
1219                                     "owned attribute information in the next table includes details " +
1220                                     " on the internal name of the attribute.");
1221                             table = ownedSection.addElement("table");
1222                             table.addElement("title").addText("Additional Owned Attribute Information for " +
1223                                     entity.getDisplayName());
1224 
1225                             //                            table.addProcessingInstruction("dbfo", "table-width=\"125\"");
1226                             tgroup = table.addElement("tgroup");
1227                             tbody = tgroup.addAttribute("cols", "2").addElement("tbody");
1228 
1229                             tgroup.addElement("colspec")
1230                                     .addAttribute("colnum", "1")
1231                                     .addAttribute("colname", "displayName")
1232                                     .addAttribute("colwidth", "0.6*")
1233                                     .addAttribute("align", "left");
1234 
1235                             tgroup.addElement("colspec")
1236                                     .addAttribute("colnum", "2")
1237                                     .addAttribute("colname", "typename")
1238                                     .addAttribute("colwidth", "1*");
1239 
1240                             headerRow = tgroup.addElement("thead").addElement("row");
1241                             headerRow.addElement("entry").addElement("para")
1242                                     .addText("Display Order and Name");
1243                             headerRow.addElement("entry").addElement("para")
1244                                     .addText("Internal Name");
1245 
1246                             // now all parts per entity
1247                             for (int j = 0;
1248                                  !isFake && (parts != null) &&
1249                                          (j < parts.length); j++) {
1250                                 Part part = parts[j];
1251 
1252                                 if (part.isSharedNodeAttrib()) {
1253                                     continue; // just shared
1254                                 }
1255 
1256                                 Filter partold = part.setFilterClass(new
1257                                         RawFilter());
1258 
1259                                 Element row = tbody.addElement("row");
1260 
1261                                 row.addElement("entry").addElement("para")
1262                                         .addText(part.getPartNum() + ") " +
1263                                                 part.getPartLabel());
1264 
1265                                 String text = "";
1266 
1267                                 if (part.isOwnedAttribute()) {
1268                                     text = part.getPartType();
1269                                 } else {
1270                                     text = part.getNodeRelationObjectLabel();
1271                                 }
1272 
1273                                 row.addElement("entry").addElement("para")
1274                                         .addText(text);
1275 
1276                                 part.setFilterClass(partold);
1277                             } // all attributes
1278                         } // shared
1279                     } // if there are any parts
1280 
1281                     entity.setFilterClass(old);
1282                 } // for each entity
1283             } // size > 0
1284 
1285             response.setCustomResponse(true);
1286 
1287             if (doc != null) {
1288                 outputXML(doc, request);
1289             }
1290         } catch (Exception e) {
1291             throw new ControllerException(e);
1292         }
1293     }
1294 
1295     /***
1296      * Prompt to add an Entity.
1297      *
1298      * @param request  The ExpressoRequest Object: our parameters.
1299      * @param response The ExpressoResponse object: what we populate.
1300      * @throws ControllerException upon error.
1301      */
1302     protected void runPromptAddEntityState(final ExpressoRequest request,
1303                                            final ExpressoResponse response) throws ControllerException {
1304 
1305         //Ok, this is lame, but at least the code checkers stop complaining
1306         //about an unused parameter!
1307         assert request != null;
1308 
1309         try {
1310             Input input = new Input(NodeType.NODE_TYPE_NAME);
1311             input.setType(InputTag.TYPE_TEXT);
1312             response.add(input);
1313 
1314             input = new Input(NodeType.NODE_TYPE_DESCRIP);
1315             input.setType(InputTag.TYPE_TEXTAREA);
1316             response.add(input);
1317 
1318             input = new Input(NodeType.DISPLAY_TITLE);
1319             input.setType(InputTag.TYPE_TEXT);
1320             response.add(input);
1321 
1322             Transition trans = new Transition(DO_ADD_ENTITY, this);
1323             trans.setLabel("Save");
1324             response.add(trans);
1325         } catch (Exception e) {
1326             throw new ControllerException(e);
1327         }
1328     }
1329 
1330     /***
1331      * Prompt to add an Entity.
1332      *
1333      * @param request  The ExpressoRequest Object: our parameters.
1334      * @param response The ExpressoResponse object: what we populate.
1335      * @throws ControllerException upon error.
1336      */
1337     protected void runDoAddEntityState(final ExpressoRequest request,
1338                                        final ExpressoResponse response) throws ControllerException {
1339         try {
1340             NodeType nodeType = new NodeType(RequestRegistry.getUser());
1341             String[] required = {NodeType.NODE_TYPE_NAME};
1342 
1343             if (!isValidAndPopulated(nodeType, required,
1344                     PROMPT_ADD_ENTITY, request, response)) {
1345                 return;
1346             }
1347 
1348             // test for spaces
1349             nodeType.setEntityName(StringUtil.replaceAll(nodeType.getEntityName()
1350                     , " ", "_"));
1351             nodeType.add();
1352 //            // default permissions
1353 //            setDefaultReadWriteGroups(nodeType);
1354 
1355             Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
1356             trans.addParam(NodeType.NODE_TYPE_ID, nodeType.getId());
1357             trans.redirectTransition(request, response);
1358         } catch (Exception e) {
1359             throw new ControllerException(e);
1360         }
1361     }
1362 
1363 //    public static void setDefaultReadWriteGroups(RowSecuredDBObject obj) throws DBException {
1364 //        String readGroup = Setup.getValueUnrequired(
1365 //                obj.getDataContext(), READ_GROUP_DEFAULT_SETUP_CODE);
1366 //
1367 //        obj.addGroupPerm(readGroup, RowPermissions.GROUP_READ_ONLY_PERMISSIONS);
1368 //
1369 //        String writeGroup = Setup.getValueUnrequired(
1370 //                obj.getDataContext(), WRITE_GROUP_DEFAULT_SETUP_CODE);
1371 //
1372 //        obj.addGroupPerm(writeGroup, RowPermissions.GROUP_ONLY_READWRITE_PERMISSIONS);
1373 //    }
1374 
1375     /***
1376      * Prompt to add an Part.
1377      *
1378      * @param request  The ExpressoRequest Object: our parameters.
1379      * @param response The ExpressoResponse object: what we populate.
1380      * @throws ControllerException upon error.
1381      */
1382     protected void runPromptAddPartState(final ExpressoRequest request,
1383                                          final ExpressoResponse response) throws ControllerException {
1384         try {
1385             NodeType entity = getEntity(request);
1386 
1387             String isOwnedStr = request.getParameter(Part.PART_IS_ATTRIB);
1388 
1389             if ((isOwnedStr == null) || (isOwnedStr.length() == 0)) {
1390                 throw new ControllerException("part_is_attrib is a required parameter");
1391             }
1392 
1393             boolean isOwned = isOwnedStr.equalsIgnoreCase("Y");
1394 
1395             Input input = new Input(Part.PART_LABEL, "Part Label");
1396             input.setType(InputTag.TYPE_TEXT);
1397             input.setDefaultValue(response.getFormCache(Part.PART_LABEL));
1398             response.add(input);
1399 
1400             // hiddens
1401             input = new Input(Part.PART_IS_ATTRIB);
1402             input.setType(InputTag.TYPE_HIDDEN);
1403             input.setDefaultValue(isOwned ? "Y" : "N");
1404             response.add(input);
1405 
1406             input = new Input(NodeType.NODE_TYPE_ID);
1407             input.setType(InputTag.TYPE_HIDDEN);
1408             input.setDefaultValue(entity.getId());
1409             response.add(input);
1410 
1411             response.add(new Output(Part.PARENT_TYPE, entity.getEntityName()));
1412 
1413             input = new Input(Part.PART_NUM);
1414             input.setType(InputTag.TYPE_TEXT);
1415 
1416             Part[] parts = PartsFactory.getParts(entity.getEntityName());
1417 
1418             // number may be request param
1419             String num = request.getParameter(Part.PART_NUM);
1420 
1421             if (num != null) {
1422                 input.setDefaultValue(num);
1423             } else {
1424                 if (response.getFormCache(Part.PART_NUM).length() > 0) {
1425                     input.setDefaultValue(response.getFormCache(Part.PART_NUM));
1426                 } else {
1427                     input.setDefaultValue("" + parts.length);
1428                 }
1429             }
1430 
1431             response.add(input);
1432 
1433             input = new Input(Part.PART_TYPE, "Part Type");
1434 
1435             if (isOwned) {
1436                 input.setType(InputTag.TYPE_TEXT);
1437                 input.setDefaultValue(response.getFormCache(Part.PART_TYPE));
1438             } else {
1439                 createNodeTypeMenu(input, null);
1440 
1441                 if (response.getFormCache(Part.PART_TYPE).length() > 0) {
1442                     input.setDefaultValue(response.getFormCache(Part.PART_TYPE));
1443                 }
1444             }
1445 
1446             response.add(input);
1447 
1448             if (!isOwned) {
1449                 input = new Input(Part.NODE_PART_RELATION_TYPE,
1450                         "Part Relationship");
1451                 createNodeRelationMenu(input, new Part());
1452 
1453                 if (response.getFormCache(Part.NODE_PART_RELATION_TYPE).length() > 0) {
1454                     input.setDefaultValue(response.getFormCache(Part.
1455                             NODE_PART_RELATION_TYPE));
1456                 }
1457 
1458                 response.add(input);
1459             }
1460 
1461             input = new Input(Part.SPECIAL_HANDLER,
1462                     "Special Handler (class name for any special handling)");
1463             input.setType(InputTag.TYPE_TEXT);
1464             input.setDefaultValue(response.getFormCache(Part.SPECIAL_HANDLER));
1465             response.add(input);
1466 
1467             input = new Input(Part.PART_DESCRIP,
1468                     "Description of this part, with respect to parent");
1469             input.setType(InputTag.TYPE_TEXTAREA);
1470             input.setDefaultValue(response.getFormCache(Part.PART_DESCRIP));
1471             input.setLines(3);
1472             input.setDisplayLength(50);
1473             response.add(input);
1474 
1475             input = new Input(Part.CARDINALITY,
1476                     "Cardinality (single or multiple allowed, etc.)");
1477             createCardinalityMenu(input, null);
1478 
1479             if (response.getFormCache(Part.CARDINALITY).length() > 0) {
1480                 input.setDefaultValue(response.getFormCache(Part.CARDINALITY));
1481             }
1482 
1483             response.add(input);
1484 
1485             Transition trans = new Transition(DO_ADD_PART, this);
1486             trans.setLabel("Save");
1487             response.add(trans);
1488         } catch (Exception e) {
1489             throw new ControllerException(e);
1490         }
1491     }
1492 
1493     /***
1494      * Do the addition for part.
1495      *
1496      * @param request  The ExpressoRequest Object: our parameters.
1497      * @param response The ExpressoResponse object: what we populate.
1498      * @throws ControllerException upon error.
1499      */
1500     protected void runDoAddPartState(final ExpressoRequest request,
1501                                      final ExpressoResponse response) throws ControllerException {
1502         String isOwnedStr = request.getParameter(Part.PART_IS_ATTRIB);
1503 
1504         if ((isOwnedStr == null) || (isOwnedStr.length() == 0)) {
1505             throw new ControllerException("part owned/shared boolean is a required parameter");
1506         }
1507 
1508         try {
1509             NodeType parentType = getEntity(request);
1510 
1511             Part part = new Part();
1512 
1513             String newPartType = request.getParameter(Part.PART_TYPE);
1514 
1515             // translate menu choice into text if necessary.  a shared node
1516             // will have menu for this item, made up of node types
1517             if (isNumeric(newPartType)) {
1518                 int type_id = Integer.parseInt(newPartType);
1519                 NodeType type = null;
1520 
1521                 try {
1522                     type = NodeType.getFromId(type_id);
1523                 } catch (DBException e) {
1524                     // assume that someone has entered a numeric name
1525                     //for an attrib type
1526                     response.addError("attribute names must not be numeric.  "
1527                             + "incoming numerics are assumed to be for existing "
1528                             + "attribute IDs, and no existing attributes found "
1529                             + "with id=" + type_id);
1530                     response.setFormCache();
1531                     transition(PROMPT_ADD_PART, request, response);
1532 
1533                     return;
1534                 }
1535 
1536                 newPartType = type.getEntityName();
1537                 request.setParameter(Part.PART_TYPE, newPartType);
1538             }
1539 
1540             String[] allrequired = {Part.PART_TYPE};
1541 
1542             if (!isValidAndPopulated(part, allrequired, PROMPT_ADD_PART,
1543                     request, response)) {
1544                 return;
1545             }
1546 
1547             // test custom handler; see if this throws; will be null (not throw) if nothing's entered
1548             try {
1549                 part.getCustomHandler();
1550             } catch (DBException e) {
1551                 String clazz = part.getField(Part.SPECIAL_HANDLER);
1552                 response.addError("Cannot instantiate class: '" + clazz
1553                         + "' because of error: " + e.getMessage());
1554                 response.setFormCache();
1555                 transition(PROMPT_ADD_PART, request, response);
1556                 return;
1557             }
1558 
1559             /***
1560              * @todo replace ' and " and other disallowed xml chars
1561              */
1562             part.setPartType(StringUtil.replaceAll(part.getPartType(), " ", "_"));
1563             part.setParentType(parentType.getEntityName());
1564 
1565             // are they duplicating?
1566             if (part.isOwnedAttribute()) {
1567                 Part testPart = new Part();
1568                 testPart.setPartType(part.getPartType());
1569                 testPart.setParentType(part.getParentType());
1570 
1571                 if (testPart.find()) {
1572                     response.addError("Found existing part with type name: "
1573                             + part.getPartType());
1574                     response.setFormCache();
1575                     transition(PROMPT_ADD_PART, request, response);
1576                 }
1577             } else {
1578                 Part testPart = new Part();
1579                 testPart.setPartType(part.getPartType());
1580                 testPart.setParentType(part.getParentType());
1581                 testPart.setNodeRelation(part.getNodeRelation());
1582 
1583                 if (testPart.find()) {
1584                     response.addError("Found existing part with type/parent/relation: " +
1585                             part.getPartType() + "/" + part.getParentType() + "/" +
1586                             part.getNodeRelation());
1587                     response.setFormCache();
1588                     transition(PROMPT_ADD_PART, request, response);
1589                 }
1590             }
1591 
1592             Part[] parts = PartsFactory.getParts(part.getParentEntity()
1593                     .getEntityName());
1594             int insertionNum = Integer.parseInt(part.getPartNum());
1595 
1596             // did they enter an impossible part num?
1597             if (insertionNum > parts.length) {
1598                 response.addError("Cannot add part with number this high: " +
1599                         part.getPartNum());
1600                 response.setFormCache();
1601                 transition(PROMPT_ADD_PART, request, response);
1602             }
1603 
1604             if (insertionNum < 0) {
1605                 response.addError("Cannot add part with number this low: " +
1606                         part.getPartNum());
1607                 response.setFormCache();
1608                 transition(PROMPT_ADD_PART, request, response);
1609             }
1610 
1611             // adjust other part numbers if necessary
1612             if (parts.length > insertionNum) {
1613                 // must adjust other parts after this insertion
1614                 for (int i = insertionNum; i < parts.length; i++) {
1615                     Part aPart = parts[i];
1616                     aPart.setPartNum(i + 1);
1617                     aPart.update();
1618                 }
1619             }
1620 
1621             part.add();
1622 
1623             Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
1624             trans.addParam(NodeType.NODE_TYPE_ID, part.getParentEntity().getId());
1625             trans.redirectTransition(request, response);
1626         } catch (Exception e) {
1627             throw new ControllerException(e);
1628         }
1629     }
1630 
1631     /***
1632      * Do the deletion of part.
1633      *
1634      * @param request  The ExpressoRequest Object: our parameters.
1635      * @param response The ExpressoResponse object: what we populate.
1636      * @throws ControllerException upon error.
1637      */
1638     protected void runDoDeletePartState(final ExpressoRequest request,
1639                                         final ExpressoResponse response) throws ControllerException {
1640         try {
1641             Part part = getPart(request);
1642             Part[] allParts = PartsFactory.getParts(part.getParentType());
1643 
1644             // flag to keep details if necessary
1645             if (request.getParameter(Part.KEEP_DETAILS) != null) {
1646                 part.setAttribute(Part.KEEP_DETAILS, "true");
1647             }
1648 
1649             part.delete();
1650 
1651             // fix numbering of remainder
1652             // first, where is deleted item in array?
1653             int index = 0;
1654 
1655             for (int i = 0; i < allParts.length; i++) {
1656                 Part aPart = allParts[i];
1657 
1658                 if (aPart == part) {
1659                     index = i;
1660 
1661                     break;
1662                 }
1663             }
1664 
1665             if (index <= (allParts.length - 2)) {
1666                 Part partToDec = allParts[index + 1];
1667                 renumPart(partToDec.getId(), false);
1668             }
1669 
1670             Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
1671             trans.addParam(NodeType.NODE_TYPE_ID, part.getParentEntity().getId());
1672             trans.redirectTransition(request, response);
1673         } catch (Exception e) {
1674             throw new ControllerException(e);
1675         }
1676     }
1677 
1678     /***
1679      * Prompt to confirm deletion an Part.
1680      *
1681      * @param request  The ExpressoRequest Object: our parameters.
1682      * @param response The ExpressoResponse object: what we populate.
1683      * @throws ControllerException upon error.
1684      */
1685     protected void runConfirmDeletePartState(final ExpressoRequest request,
1686                                              final ExpressoResponse response) throws ControllerException {
1687         try {
1688             Part part = getPart(request);
1689 
1690             response.add(new Output(Part.PART_LABEL, part.getPartLabel()));
1691 
1692             Input input = new Input(Part.KEEP_DETAILS);
1693             input.addValidValue(Part.KEEP_DETAILS,
1694                     "Keep old attributes despite removing this part");
1695             input.setType(InputTag.TYPE_CHECKBOX);
1696 
1697             //            input.setLabel("Keep old attributes despite removing this part");
1698             response.add(input);
1699 
1700             Transition trans = new Transition(DO_DELETE_PART, this);
1701             trans.addParam(Part.PART_ID, part.getId());
1702             trans.setLabel("Delete");
1703             response.add(trans);
1704         } catch (Exception e) {
1705             throw new ControllerException(e);
1706         }
1707     }
1708 
1709     /***
1710      * Increment Part number.
1711      *
1712      * @param request  The ExpressoRequest Object: our parameters.
1713      * @param response The ExpressoResponse object: what we populate.
1714      * @throws ControllerException upon error.
1715      */
1716     protected void runDoIncPartNumState(final ExpressoRequest request,
1717                                         final ExpressoResponse response) throws ControllerException {
1718         renumPart(request, response, true);
1719     }
1720 
1721     /***
1722      * Decrement Part number.
1723      *
1724      * @param request  The ExpressoRequest Object: our parameters.
1725      * @param response The ExpressoResponse object: what we populate.
1726      * @throws ControllerException upon error.
1727      */
1728     protected void runDoDecPartNumState(final ExpressoRequest request,
1729                                         final ExpressoResponse response) throws ControllerException {
1730         renumPart(request, response, false);
1731     }
1732 
1733     /***
1734      * Renumbering as called by a button calling a controller.
1735      *
1736      * @param request  The ExpressoRequest Object: our parameters.
1737      * @param response The ExpressoResponse object: what we populate.
1738      * @param isInc    are we incrementing or decrementing.
1739      * @throws ControllerException upon error.
1740      */
1741     private void renumPart(final ExpressoRequest request,
1742                            final ExpressoResponse response, final boolean isInc) throws ControllerException {
1743         try {
1744             String id = request.getParameter(Part.PART_ID);
1745 
1746             if ((id == null) || (id.length() == 0)) {
1747                 throw new ControllerException("part id is a required parameter");
1748             }
1749 
1750             NodeType parent = renumPart(id, isInc);
1751 
1752             Transition trans = new Transition(PROMPT_EDIT_ENTITY, this);
1753             trans.addParam(NodeType.NODE_TYPE_ID, parent.getId());
1754             trans.redirectTransition(request, response);
1755         } catch (Exception e) {
1756             throw new ControllerException(e);
1757         }
1758     }
1759 
1760     /***
1761      * The meat of the renumbering.
1762      *
1763      * @param id    The Part Id.
1764      * @param isInc True if we're incrementing the part order.
1765      * @return NodeType.
1766      * @throws DBException upon error.
1767      */
1768     private NodeType renumPart(final String id,
1769                                final boolean isInc) throws DBException {
1770         // todo add test for going below 0 or above N, but for
1771         // now, depend on UI to not provide for those mistakes
1772         Part part = new Part();
1773         part.setId(id);
1774         part.retrieve();
1775 
1776         NodeType parent = part.getParentEntity();
1777 
1778         // get old cached list; will be updated when we update part
1779         Part[] parts = PartsFactory.getParts(parent.getEntityName());
1780 
1781         int oldnum = part.getPartNumInt();
1782 
1783         if (isInc) {
1784             // part renumbering is super privileged
1785             part.setRequestingUser(SuperUser.INSTANCE);
1786             part.setPartNum(oldnum + 1);
1787             part.update();
1788 
1789             // move upper neighbor down if nec.
1790             // we use oldnum as index, so protect against neg. numbers
1791             if ((oldnum + 1 >= 0)
1792                     && parts[oldnum + 1].getPartNumInt() == (oldnum + 1)) {
1793                 parts[oldnum + 1].setPartNum(oldnum);
1794                 parts[oldnum + 1].setRequestingUser(SuperUser.INSTANCE);
1795                 parts[oldnum + 1].update();
1796             }
1797         } else {
1798             part.setPartNum(oldnum - 1);
1799             part.update();
1800 
1801             // move lower neighbor up if nec.
1802             // we use oldnum as index, so protect against neg. numbers
1803             if ((oldnum - 1 >= 0)
1804                     && parts[oldnum - 1].getPartNumInt() == (oldnum - 1)) {
1805                 parts[oldnum - 1].setPartNum(oldnum);
1806                 parts[oldnum - 1].setRequestingUser(SuperUser.INSTANCE);
1807                 parts[oldnum - 1].update();
1808             }
1809         }
1810 
1811         return parent;
1812     }
1813 
1814     /***
1815      * Prompt to confirm delete entity.
1816      *
1817      * @param request  The ExpressoRequest Object: our parameters.
1818      * @param response The ExpressoResponse object: what we populate.
1819      * @throws ControllerException upon error.
1820      */
1821     protected void runConfirmDeleteEntityState(final ExpressoRequest request,
1822                                                final ExpressoResponse response) throws ControllerException {
1823         try {
1824             NodeType entity = getEntity(request);
1825 
1826             Input input = new Input(NodeType.NODE_TYPE_ID);
1827             input.setType(InputTag.TYPE_HIDDEN);
1828             input.setDefaultValue(entity.getId());
1829             response.add(input);
1830 
1831             response.add(new Output(NodeType.DISPLAY_TITLE,
1832                     entity.getDisplayName()));
1833 
1834             Transition trans = new Transition(PartAction.DO_DELETE_ENTITY,
1835                     "Delete", getClass(), PartAction.DO_DELETE_ENTITY);
1836             trans.addParam(NodeType.NODE_TYPE_ID, entity.getId());
1837             response.add(trans);
1838         } catch (Exception e) {
1839             throw new ControllerException(e);
1840         }
1841     }
1842 
1843     /***
1844      * Prompt to confirm delete entity.
1845      *
1846      * @param request  The ExpressoRequest Object: our parameters.
1847      * @param response The ExpressoResponse object: what we populate.
1848      * @throws ControllerException upon error.
1849      */
1850     protected void runDoDeleteEntityState(final ExpressoRequest request,
1851                                           final ExpressoResponse response) throws ControllerException {
1852         try {
1853             NodeType entity = getEntity(request);
1854 
1855             entity.delete();
1856             PartsFactory.refreshCache(entity.getEntityName());
1857 
1858             Transition trans = new Transition(LIST_ENTITIES, this);
1859             trans.redirectTransition(request, response);
1860         } catch (Exception e) {
1861             throw new ControllerException(e);
1862         }
1863     }
1864 
1865     /***
1866      * Prompt to add an relation.
1867      *
1868      * @param request  The ExpressoRequest Object: our parameters.
1869      * @param response The ExpressoResponse object: what we populate.
1870      * @throws ControllerException upon error.
1871      */
1872     protected void runPromptAddRelationState(final ExpressoRequest request,
1873                                              final ExpressoResponse response) throws ControllerException {
1874         try {
1875             RelationType type = new RelationType();
1876             String[] must = {};
1877             isValidAndPopulated(type, must, LIST_ENTITIES, request, response);
1878 
1879             Block b = DefaultAutoElement.getAutoControllerElement().createDBObjectBlock(request, response, type);
1880             response.add(b);
1881 
1882             Transition create = new Transition("Save", getClass(),
1883                     DO_ADD_RELATION);
1884             response.add(create);
1885         } catch (Exception e) {
1886             throw new ControllerException(e);
1887         }
1888     }
1889 
1890     /***
1891      * Do the addition for part.
1892      *
1893      * @param request  The ExpressoRequest Object: our parameters.
1894      * @param response The ExpressoResponse object: what we populate.
1895      * @throws ControllerException    upon error.
1896      * @throws NonHandleableException upon fatal error.
1897      */
1898     protected void runDoAddRelationState(final ExpressoRequest request,
1899                                          final ExpressoResponse response) throws ControllerException,
1900             NonHandleableException {
1901         ErrorCollection ec = new ErrorCollection();
1902 
1903         try {
1904             RelationType type = new RelationType();
1905             type = (RelationType) DefaultAutoElement.getAutoControllerElement().parseDBObject(request, type, ec);
1906             type.add();
1907 
1908             Transition trans = new Transition("", getClass(), LIST_RELATIONS);
1909             trans.redirectTransition(request, response);
1910         } catch (Exception e) {
1911             ec.addError("Error adding assessment: " +
1912                     StringUtil.omitPackages(e) + ": " + e.getMessage());
1913             getLogger().debug("cannot add assessment: ", e);
1914 
1915             //            ec.addError(new ControllerException(ex));
1916             response.saveErrors(ec);
1917             response.setFormCache();
1918             transition(PROMPT_ADD_RELATION, request, response);
1919 
1920             return;
1921         }
1922     }
1923 
1924     /***
1925      * Prompt to edit a relation.
1926      *
1927      * @param request  The ExpressoRequest Object: our parameters.
1928      * @param response The ExpressoResponse object: what we populate.
1929      * @throws ControllerException upon error.
1930      */
1931     protected void runPromptEditRelationState(final ExpressoRequest request,
1932                                               final ExpressoResponse response) throws ControllerException {
1933         try {
1934             RelationType type = getRelationType(request);
1935             Block b = DefaultAutoElement.getAutoControllerElement().createDBObjectBlock(request, response, type);
1936             response.add(b);
1937 
1938             // find all parts which use this relation
1939             List list = type.getParts();
1940 
1941             if (list.size() > 0) {
1942                 Block rows = new Block(ROW_BLOCK);
1943                 response.add(rows);
1944 
1945                 for (Iterator iterator = list.iterator();
1946                      iterator.hasNext();) {
1947                     Part part = (Part) iterator.next();
1948                     Transition trans = new Transition(part.getParentType() +
1949                             ": " + part.getPartLabel(), getClass(), PROMPT_EDIT_PART);
1950                     trans.addParam(Part.PART_ID, part.getId());
1951                     rows.add(trans);
1952                 }
1953             }
1954 
1955             Transition save = new Transition("Save", getClass(), DO_EDIT_RELATION);
1956             save.addParam(RelationType.RELATION_TYPE_NAME, type.getRelTypeName());
1957             response.add(save);
1958         } catch (Exception e) {
1959             throw new ControllerException(e);
1960         }
1961     }
1962 
1963     private RelationType getRelationType(final ExpressoRequest request) throws
1964             DBException {
1965         RelationType type = new RelationType();
1966         type.setRelType(request.getParameter(RelationType.RELATION_TYPE_NAME));
1967         type.retrieve();
1968 
1969         return type;
1970     }
1971 
1972     /***
1973      * Do the addition for relation.
1974      *
1975      * @param request  The ExpressoRequest Object: our parameters.
1976      * @param response The ExpressoResponse object: what we populate.
1977      * @throws ControllerException    upon error.
1978      * @throws NonHandleableException upon fatal error.
1979      */
1980     protected void runDoEditRelationState(final ExpressoRequest request,
1981                                           final ExpressoResponse response) throws ControllerException,
1982             NonHandleableException {
1983         ErrorCollection ec = new ErrorCollection();
1984 
1985         try {
1986             RelationType type = new RelationType();
1987             type = (RelationType) DefaultAutoElement.getAutoControllerElement().parseDBObject(request, type, ec);
1988 
1989             RelationType orig = new RelationType();
1990             orig.setRelType(type.getRelTypeName());
1991             orig.retrieve();
1992 
1993             // it would be nice to handle all the inverse-relation stuff within
1994             // an overridden .update(), but the recursion
1995             // potential is too confusing,
1996             // so we handle it explicitly here only
1997             // handle inverse if necessary; if inverse is being changed,
1998             // delay updating until existing Relations have been deleted
1999             if (!orig.getInverseRel().equals(type.getInverseRel())) {
2000                 List rels = orig.getRelations();
2001 
2002                 // delete all of these, then re-add them after adjustments;
2003                 // this will automatically reset the proper inverse relations
2004                 for (Iterator iterator = rels.iterator(); iterator.hasNext();) {
2005                     Relation relation = (Relation) iterator.next();
2006 
2007                     // preserve orig. ordering, since every delete will
2008                     // reorder remaining items automatically
2009                     relation.setAttribute("origOrder", relation.getOrder());
2010                     relation.delete();
2011                 }
2012 
2013                 type.update();
2014 
2015                 // add new inverse
2016                 if (type.getInverseRel().length() > 0) {
2017                     RelationType newinv = type.getInverseRelType();
2018 
2019                     if (newinv != null) {
2020                         newinv.setInverseRel(type.getRelTypeName());
2021                         newinv.update();
2022                     }
2023                 }
2024 
2025                 // remove original inverse
2026                 if (orig.getInverseRel().length() > 0) {
2027                     RelationType origInv = orig.getInverseRelType();
2028 
2029                     if (origInv != null) {
2030                         origInv.setInverseRel("");
2031                         origInv.update();
2032                     }
2033                 }
2034 
2035                 // this will automatically reset the proper inverse relations
2036                 for (Iterator iterator = rels.iterator(); iterator.hasNext();) {
2037                     Relation relation = (Relation) iterator.next();
2038                     relation.setOrder((String) relation.getAttribute("origOrder"));
2039                     relation.add();
2040                 }
2041             } else {
2042                 type.update();
2043             }
2044 
2045             Transition trans = new Transition("", getClass(), LIST_RELATIONS);
2046             trans.redirectTransition(request, response);
2047         } catch (Exception e) {
2048             ec.addError("Error adding assessment: " +
2049                     StringUtil.omitPackages(e) + ": " + e.getMessage());
2050             getLogger().debug("cannot add assessment: ", e);
2051 
2052             //            ec.addError(new ControllerException(ex));
2053             response.saveErrors(ec);
2054             response.setFormCache();
2055             transition(PROMPT_EDIT_RELATION, request, response);
2056 
2057             return;
2058         }
2059     }
2060 
2061     /***
2062      * Do the addition for part.
2063      *
2064      * @param request  The ExpressoRequest Object: our parameters.
2065      * @param response The ExpressoResponse object: what we populate.
2066      * @throws ControllerException    upon error.
2067      * @throws NonHandleableException upon fatal error.
2068      */
2069     protected void runListRelationsState(final ExpressoRequest request,
2070                                          final ExpressoResponse response) throws ControllerException,
2071             NonHandleableException {
2072         try {
2073             RelationType search = new RelationType();
2074             response.setTitle("List of Relations");
2075 
2076             // todo use pagination
2077             List result = search.searchAndRetrieveList(RelationType.RELATION_TYPE_DISPLAY_NAME);
2078             Block records = new Block(ROW_BLOCK);
2079             response.add(records);
2080 
2081             for (Iterator i = result.iterator(); i.hasNext();) {
2082                 RelationType aType = (RelationType) i.next();
2083                 Block b = DefaultAutoElement.getAutoControllerElement().createDBObjectBlock(request, response, aType);
2084                 records.add(b);
2085 
2086                 if (aType.isReflexive()) {
2087                     b.add(new Output(RelationType.RELATION_INVERSE, "true"));
2088                 }
2089 
2090                 if (aType.canRequesterWrite()) {
2091                     Transition edit = new Transition("Edit", PartAction.class,
2092                             PartAction.PROMPT_EDIT_RELATION);
2093                     edit.addParam(RelationType.RELATION_TYPE_NAME, aType.getRelTypeName());
2094                     b.add(edit);
2095 
2096                     Transition delete = new Transition("Delete", getClass(), CONFIRM_DELETE_RELATION);
2097                     delete.addParam(RelationType.RELATION_TYPE_NAME, aType.getRelTypeName());
2098                     b.add(delete);
2099                 }
2100 
2101                 if (aType.canRequesterAdministrate()) {
2102                     b.add(getPermsTrans(aType));
2103                 }
2104 
2105             }
2106 
2107             RelationType testerType = new RelationType();
2108             if (testerType.canRequesterAdd()) {
2109                 Transition create = new Transition("Add Relation",
2110                         PartAction.class, PartAction.PROMPT_ADD_RELATION);
2111 
2112                 response.add(create);
2113             }
2114         } catch (DBException ex) {
2115             throw new ControllerException("Unable to list classes", ex);
2116         }
2117     }
2118 
2119     /***
2120      * Confirm deletion of a relation.
2121      *
2122      * @param request  The ExpressoRequest Object: our parameters.
2123      * @param response The ExpressoResponse object: what we populate.
2124      * @throws ControllerException upon error.
2125      */
2126     protected void runConfirmDeleteRelationState(final ExpressoRequest request,
2127                                                  final ExpressoResponse response) throws ControllerException {
2128         try {
2129             RelationType type = getRelationType(request);
2130             response.add(new Output(RelationType.RELATION_TYPE_DISPLAY_NAME, type.getRelTypeDisplayName()));
2131 
2132             Transition del = new Transition("Delete", getClass(), DO_DELETE_RELATION);
2133             del.addParam(RelationType.RELATION_TYPE_NAME, type.getRelTypeName());
2134             response.add(del);
2135 
2136             List list = type.getRelations();
2137 
2138             if (list.size() > 0) {
2139                 response.addError(list.size() +
2140                         " relations of this type exist in DB. "
2141                         + "Cannot delete while these are present.");
2142             }
2143         } catch (Exception e) {
2144             throw new ControllerException(e);
2145         }
2146     }
2147 
2148     /***
2149      * Do the addition for relation.
2150      *
2151      * @param request  The ExpressoRequest Object: our parameters.
2152      * @param response The ExpressoResponse object: what we populate.
2153      * @throws ControllerException    upon error.
2154      * @throws NonHandleableException upon fatal error.
2155      */
2156     protected void runDoDeleteRelationState(final ExpressoRequest request,
2157                                             final ExpressoResponse response) throws ControllerException,
2158             NonHandleableException {
2159         ErrorCollection ec = new ErrorCollection();
2160 
2161         try {
2162             RelationType type = new RelationType();
2163             type = (RelationType) DefaultAutoElement.getAutoControllerElement().parseDBObject(request, type, ec);
2164 
2165 
2166             if (!type.find()) {
2167                 response.addError("Cannot find relation type with name: " + type.getRelTypeName());
2168                 response.setFormCache();
2169 
2170                 Transition prompt = new Transition("", getClass(), CONFIRM_DELETE_RELATION);
2171                 prompt.addParam(RelationType.RELATION_TYPE_NAME, type.getRelTypeName());
2172                 prompt.executeTransition(request, response);
2173 
2174                 return;
2175             }
2176 
2177             List list = type.getRelations();
2178 
2179             if (list.size() > 0) {
2180                 response.addError(list.size()
2181                         + " relations of this type exist in DB. Cannot delete while"
2182                         + " these are present.");
2183                 response.setFormCache();
2184 
2185                 Transition prompt = new Transition("", getClass(), CONFIRM_DELETE_RELATION);
2186                 prompt.addParam(RelationType.RELATION_TYPE_NAME, type.getRelTypeName());
2187                 prompt.executeTransition(request, response);
2188 
2189                 return;
2190             }
2191 
2192             type.delete(); // inverse handled within delete();
2193 
2194             Transition trans = new Transition("", getClass(), LIST_RELATIONS);
2195             trans.redirectTransition(request, response);
2196         } catch (Exception e) {
2197             ec.addError("Error editing inverse relation: " +
2198                     StringUtil.omitPackages(e) + ": " + e.getMessage());
2199             getLogger().debug("Error editing inverse relation: ", e);
2200 
2201             //            ec.addError(new ControllerException(ex));
2202             response.saveErrors(ec);
2203             response.setFormCache();
2204             transition(CONFIRM_DELETE_RELATION, request, response);
2205 
2206             return;
2207         }
2208     }
2209 
2210     /***
2211      * Parses the XML into Elements.
2212      *
2213      * @param reader Reader the Xml Reader instance.
2214      * @return Element: Constructed XML
2215      * @throws Exception upon error.
2216      */
2217     public static org.dom4j.Element parseXML(final Reader reader) throws Exception {
2218         SAXReader xmlReader = new SAXReader();
2219         Document doc = xmlReader.read(reader);
2220 
2221         //        if (getLogger().isDebugEnabled()) {
2222         //            writeDoc(doc, System.out);
2223         //        }
2224 
2225         return doc.getRootElement();
2226     }
2227 }