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.dbobj;
11  
12  import com.jcorporate.expresso.core.controller.Transition;
13  import com.jcorporate.expresso.core.dataobjects.DataField;
14  import com.jcorporate.expresso.core.db.DBConnection;
15  import com.jcorporate.expresso.core.db.DBException;
16  import com.jcorporate.expresso.core.dbobj.DBField;
17  import com.jcorporate.expresso.core.dbobj.SecurDBObject;
18  import com.jcorporate.expresso.core.dbobj.ValidValue;
19  import com.jcorporate.expresso.core.security.filters.AllowedHtmlPlusURLFilter;
20  import com.jcorporate.expresso.core.security.filters.Filter;
21  import com.jcorporate.expresso.core.security.filters.RawFilter;
22  import com.sri.emo.controller.SelectionWizardManager;
23  import com.sri.emo.wizard.WizardException;
24  import com.sri.emo.wizard.WizardPage;
25  import com.sri.emo.wizard.defaults.EmoWizardMetadata;
26  import com.sri.emo.wizard.defaults.EmoWizardPage;
27  import com.sri.emo.wizard.selection.management.PromptStepType;
28  
29  import java.util.*;
30  
31  
32  /***
33   * WizStep is a definition of each step in the wizard.
34   * This definition represents "Single Table Inheritance" for
35   * all the various types of Wizard Steps out there.  While this creates some
36   * complexity and wasted fields here, it saves trying to distribute all the data
37   * evenly across all the different tables and performing different joins.  It
38   * may be necessary to refactor later on.
39   *
40   * @author Mike Rimov
41   */
42  public class WizStep extends SecurDBObject implements IViewable {
43  
44      /***
45  	 * 
46  	 */
47  	private static final long serialVersionUID = 1L;
48  
49  	public static final String TABLE_NAME = "EMOWIZSTEPS";
50  
51      /***
52       * Public constant for access to field
53       * "Item ID".
54       */
55      public static final String FLD_ID = "StepId";
56  
57      /***
58       * Public constant for access to field
59       * "Wizard Id".
60       */
61      public static final String FLD_WIZID = "WizId";
62  
63      /***
64       * Public constant for access to field
65       * "New Field".
66       */
67      public static final String FLD_TITLE = "Title";
68  
69      /***
70       * Public constant for access to field
71       * "New Field".
72       */
73      public static final String FLD_DIRECTIVE = "Directive";
74  
75      /***
76       * Public constant for access to field
77       * "Help Text".
78       */
79      public static final String FLD_HELPTEXT = "HelpText";
80  
81      /***
82       * Public constant for access to field
83       * "Step Type".
84       */
85      public static final String FLD_STEP_TYPE = "StepType";
86  
87  
88      /***
89       * Public constant for access to field
90       * "Page Order List".
91       */
92      public static final String FLD_ORDER = "StepOrder";
93  
94      /***
95       * Valid value constant for text type attribute Undefined.
96       */
97      public static final String TEXT_VV_UNDEFINED = "0";
98      /***
99       * Valid value constant for text type attribute.
100      */
101     public static final String TEXT_VV_SINGLELINE = "1";
102 
103     /***
104      * Valid value constant for text type atttribute for multiline entry.
105      */
106     public static final String TEXT_VV_MULTILINE = "2";
107 
108     /***
109      * Constant for steps that have a per-model picklist.
110      */
111     public static final int MODEL_PICKLIST = 1;
112 
113     /***
114      * Constant for steps that have a per-instance picklist.
115      */
116     public static final int INSTANCE_PICKLIST = 2;
117 
118     /***
119      * Constant for steps that have a custom picklist.
120      */
121     public static final int CUSTOM_PICKLIST = 3;
122 
123     /***
124      * Constant for steps that have custom text entry.
125      */
126     public static final int TEXT_ENTRY = 4;
127 
128     /***
129      * Constant for steps that have custom instructions only.
130      */
131     public static final int INSTRUCTIONS_ONLY = 5;
132 
133     public static final String[] FIELDS_INDICATING_MATRIX_NEEDS_UPDATING =
134             {
135                     FLD_WIZID,
136                     FLD_STEP_TYPE,
137                     FLD_ORDER,
138             };
139     /***
140      * List of attributes.
141      */
142     private Map stepAttributes = null;
143 
144 
145     /***
146      * Creates an instance of WizStep.
147      *
148      * @throws DBException upon initialization exception.
149      * @see com.jcorporate.expresso.core.dbobj.SecuredDBObject#SecuredDBObject
150      */
151     public WizStep() throws DBException {
152     }
153 
154 
155     /***
156      * Creates an instance of WizStep that uses an already grabbed DBConnection
157      * object.  This is for use inside transactions.
158      *
159      * @param dbConnection com.jcorporate.expresso.core.db.DBConnection
160      * @throws DBException upon construction error
161      */
162     public WizStep(DBConnection dbConnection) throws DBException {
163         super(dbConnection);
164     }
165 
166     /***
167      * Retrieve a list of a valid value objects for use in the list of
168      * attributes.
169      *
170      * @return List
171      * @throws DBException upon database error.
172      */
173     public List getPartsValidValues() throws DBException {
174         NodeType nodeType = getNodeType();
175 
176         Part allParts[] = nodeType.getParts();
177 
178         List returnValue = new ArrayList(allParts.length);
179         for (int i = 0; i < allParts.length; i++) {
180             Part onePart = allParts[i];
181             if (onePart.hasPicklist()) {
182                 returnValue.add(new ValidValue(onePart.getId(),
183                         onePart.getPartLabel() + ": "
184                                 + onePart.getId()));
185             }
186         }
187 
188         return returnValue;
189     }
190 
191 
192     /***
193      * Get the part that is associated with this step.
194      *
195      * @return Part instance or null if not found.
196      * @throws DBException upon database error.
197      */
198     public Part getPart() throws DBException {
199         Part allParts[] = getNodeType().getParts();
200 
201         for (int i = 0; i < allParts.length; i++) {
202             Part onePart = allParts[i];
203             String id = onePart.getId();
204             if (id.equals(getStepAttribute(StepAttributes.ATTRIBUTE_MODEL_FIELD))) {
205                 return onePart;
206             }
207         }
208 
209         return null;
210     }
211 
212     /***
213      * Retrieve a map of all attributes associated with this step.
214      *
215      * @return Map of Strings keyed to Strings
216      * @throws DBException upon error.
217      */
218     public Map getStepAttributes() throws DBException {
219         if (stepAttributes == null) {
220             List results = getStepAttributeList();
221             Map returnValue = new HashMap(results.size());
222             for (int i = 0; i < results.size(); i++) {
223                 StepAttributes oneAttribute = (StepAttributes) results.get(i);
224                 returnValue.put(oneAttribute.getAttributeKey(), oneAttribute.getAttributeValue());
225             }
226             stepAttributes = returnValue;
227         }
228 
229         return stepAttributes;
230     }
231 
232     /***
233      * Retrieves the type of step.
234      *
235      * @return int
236      * @throws DBException
237      */
238     public int getStepType() throws DBException {
239         return getFieldInt(WizStep.FLD_STEP_TYPE);
240     }
241 
242     /***
243      * Retrieve a list of attributes associated with this step.
244      *
245      * @return List
246      * @throws DBException upon error.
247      */
248     public List getStepAttributeList() throws DBException {
249         //Set Step Attribtues
250         StepAttributes sa = new StepAttributes();
251         sa.setField(StepAttributes.FLD_STEPID, getId());
252 
253         //Load query list to map
254         return (List) sa.searchAndRetrieveList();
255     }
256 
257     /***
258      * Retrieve a a value for a step attribute.
259      *
260      * @param key String the attribute key.
261      * @return String
262      * @throws DBException
263      */
264     public String getStepAttribute(String key) throws DBException {
265         return (String) getStepAttributes().get(key);
266     }
267 
268     /***
269      * Add an attribute for the step.
270      *
271      * @param key   String the attribute key.
272      * @param value The new value for the step attribute.
273      * @throws DBException upon error
274      */
275     public void addStepAttribute(String key, String value) throws DBException {
276         StepAttributes sa = new StepAttributes();
277         sa.setField(StepAttributes.FLD_STEPID, getId());
278         sa.setField(StepAttributes.FLD_ATTRIBUTEKEY, key);
279         sa.setField(StepAttributes.FLD_ATTRIBUTEVALUE, value);
280         sa.add();
281         clearAttribMap();
282     }
283 
284     private void clearAttribMap() {
285         stepAttributes = null;
286     }
287 
288     /***
289      * Update an attribute for the step.
290      *
291      * @param key   String the attribute key.
292      * @param value The new value for the step attribute.
293      * @throws DBException upon error
294      */
295     public void updateStepAttribute(String key, String value) throws DBException {
296         StepAttributes sa = new StepAttributes();
297         sa.setField(StepAttributes.FLD_STEPID, getId());
298         sa.setField(StepAttributes.FLD_ATTRIBUTEKEY, key);
299         sa.setField(StepAttributes.FLD_ATTRIBUTEVALUE, value);
300         sa.update();
301         clearAttribMap();
302     }
303 
304     /***
305      * Sets the step attributes.
306      *
307      * @param attributes Map
308      * @throws DBException
309      */
310     public void setStepAttributes(Map attributes) throws DBException {
311         stepAttributes = attributes;
312     }
313 
314     /***
315      * Remove an attribute to be associated with the step.
316      *
317      * @param key String the attribute key.
318      * @throws DBException upon delete error.
319      */
320     public void removeStepAttribute(String key) throws DBException {
321         StepAttributes sa = new StepAttributes();
322         sa.setField(StepAttributes.FLD_STEPID, getId());
323         sa.setField(StepAttributes.FLD_ATTRIBUTEKEY, key);
324         if (sa.find()) {
325             sa.delete();
326         }
327         clearAttribMap();
328     }
329 
330     /***
331      * Delete all attributes.
332      *
333      * @throws DBException
334      */
335     public void removeAllAttributes() throws DBException {
336         StepAttributes sa = new StepAttributes();
337         sa.setField(StepAttributes.FLD_STEPID, getId());
338         sa.deleteAll();
339         clearAttribMap();
340     }
341 
342     /***
343      * Get the description to the valid value portion for the current field
344      * settting for FLD_MODELFIELD.
345      *
346      * @return String the description for the current setting
347      * @throws DBException upon database access error.
348      */
349     public String getMenuAttribLabel() throws DBException {
350 
351         if (getStepType() == PromptStepType.MODEL_PICKLIST) {
352             String fieldValue = getStepAttribute(StepAttributes.ATTRIBUTE_MODEL_FIELD);
353             List vv = getPartsValidValues();
354             for (int i = 0; i < vv.size(); i++) {
355                 ValidValue oneValue = (ValidValue) vv.get(i);
356                 if (fieldValue.equals(oneValue.getKey())) {
357                     String val = oneValue.getDescription();
358                     return val.substring(0, val.lastIndexOf(":"));
359                 }
360             }
361 
362         } else if (getStepType() == PromptStepType.INSTANCE_PICKLIST) {
363             String partId = getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_FIELD);
364             String nodeId = getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID);
365             Part part = new Part();
366             part.setPartId(partId);
367             part.retrieve();
368 
369             Node node = new Node(nodeId);
370             node.retrieve();
371             return node.getNodeTitle() + " - " + part.getPartLabel();
372         } else if (getStepType() == PromptStepType.CUSTOM_PICKLIST) {
373             return "PickList Id: " + getStepAttribute(StepAttributes.ATTRIBUTE_PICKLIST_ID);
374         }
375         return null;
376     }
377 
378     /***
379      * Return the node type associated with this step.
380      *
381      * @return NodeType or null if none is defined by the step.
382      * @throws DBException upon database access OR if we can't find
383      *                     the associated node type.
384      */
385     public NodeType getNodeType() throws DBException {
386         if (getModelId() > 0) {
387             NodeType returnValue = new NodeType();
388             returnValue.setField(NodeType.NODE_TYPE_ID, getStepAttribute(StepAttributes.ATTRIBUTE_MODEL));
389             returnValue.retrieve();
390             return returnValue;
391         } else if (getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID) != null && getStepAttribute(
392                 StepAttributes.ATTRIBUTE_INSTANCE_ID).length() > 0) {
393             Node node = new Node();
394             node.setNodeId(getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID));
395             node.retrieve();
396             NodeType nt = new NodeType();
397             nt.setEntityName(node.getNodeType());
398             if (!nt.find()) {
399                 throw new DBException("Unable to locate Node type of name: " + node.getNodeTitle());
400             }
401             return nt;
402         } else {
403             return null;
404         }
405     }
406 
407     /***
408      * Returns a model id or -1 if none defined.
409      *
410      * @return int
411      * @throws DBException
412      */
413     private int getModelId() throws DBException {
414         String val = getStepAttribute(StepAttributes.ATTRIBUTE_MODEL);
415         if (val == null) {
416             return -1;
417         } else {
418             return Integer.parseInt(val);
419         }
420     }
421 
422     /***
423      * Retrieve a list of valid value items for the picklist menu that
424      * may be associated with the wizsteps.
425      *
426      * @param attribType The attribute type.
427      * @return List of Picklist objects
428      * @throws DBException if no menu is defined for this step.
429      */
430     public List getPicklistMenu(String attribType) throws DBException {
431         if (attribType == null || attribType.length() == 0) {
432             return new ArrayList(0);
433         }
434 
435         PickList pick = new PickList();
436         pick.setPickAttribType(attribType);
437         return (List) pick.searchAndRetrieveList(PickList.ORDER_NUM);
438     }
439 
440     /***
441      * Retrieve the menu items as a valid value array.
442      *
443      * @return ValidValue[], never null, can be empty
444      */
445     public ValidValue[] getMenuItems() {
446 
447         try {
448             switch (getStepType()) {
449                 case PromptStepType.INSTANCE_PICKLIST: {
450 
451                     Attribute[] attribs = getAttributes(getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID)
452                             + "|"
453                             + getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_FIELD));
454 
455                     ValidValue returnValue[] = new ValidValue[attribs.length];
456                     for (int i = 0; i < attribs.length; i++) {
457                         returnValue[i] = new ValidValue(attribs[i].getAttribId(), attribs[i].getAttribValue());
458                     }
459 
460                     return returnValue;
461                 }
462 
463                 case PromptStepType.CUSTOM_PICKLIST: {
464                     String attribType = getStepAttribute(StepAttributes.ATTRIBUTE_PICKLIST_ID);
465 
466                     List list = getPicklistMenu(attribType);
467                     ValidValue[] returnValue = new ValidValue[list.size()];
468                     for (int i = 0; i < list.size(); i++) {
469                         PickList oneItem = (PickList) list.get(i);
470                         returnValue[i] = new ValidValue(oneItem.getField(PickList.LIST_ID),
471                                 oneItem.getField(PickList.DISPLAY_IN_PICKLIST));
472                     }
473 
474                     return returnValue;
475                 }
476 
477 
478                 case PromptStepType.MODEL_PICKLIST: {
479                     String attribType = getStepAttribute(StepAttributes.ATTRIBUTE_MODEL_FIELD);
480                     Part p = new Part();
481                     p.setPartId(attribType);
482                     p.retrieve();
483 
484                     List list = getPicklistMenu(p.getPartType());
485                     ValidValue[] returnValue = new ValidValue[list.size()];
486                     for (int i = 0; i < list.size(); i++) {
487                         PickList oneItem = (PickList) list.get(i);
488                         returnValue[i] = new ValidValue(oneItem.getField(PickList.LIST_ID),
489                                 oneItem.getField(PickList.DISPLAY_IN_PICKLIST));
490                     }
491 
492                     return returnValue;
493                 }
494 
495                 case PromptStepType.INSTRUCTIONS_ONLY:
496                     return new ValidValue[0];
497 
498                 case PromptStepType.TEXT_ENTRY:
499                     return new ValidValue[0];
500 
501                 default:
502                     throw new IllegalStateException(
503                             "InvalidInput Value: " + WizStep.FLD_STEP_TYPE + "=" + getStepType());
504             }
505         } catch (DBException e) {
506             getLogger().error("Problem getting menu: ", e);
507             return new ValidValue[0];
508         }
509 
510     }
511 
512     /***
513      * Retrieve the attributes.  This is
514      *
515      * @param key String formated as <nodeId>|<partid>
516      * @return Attribute[]
517      * @throws DBException upon database access error.
518      */
519     private Attribute[] getAttributes(String key) throws DBException {
520 
521         if (getStepType() != PromptStepType.INSTANCE_PICKLIST) {
522             throw new DBException("Cannot retrieve a multi-attribute when hooked into a particular model");
523         }
524 
525         StringTokenizer stok = new StringTokenizer(key, "|");
526         String nodeId = stok.nextToken();
527         String partId = stok.nextToken();
528 
529         Part part = new Part();
530         part.setPartId(Integer.parseInt(partId));
531         part.retrieve();
532 
533         Attribute at = new Attribute();
534         at.setParentNodeId(nodeId);
535         at.setAttributeType(part.getPartType());
536         List l = at.searchAndRetrieveList(Attribute.ATTRIBUTE_ORDER);
537         return (Attribute[]) l.toArray(new Attribute[l.size()]);
538     }
539 
540     /***
541      * Builds a wizard page given an instance of the metadata. [that has all
542      * links properly set]
543      *
544      * @param metadata the emo metadata instance.
545      * @return instance of a WizardPage
546      * @throws WizardException upon construction error.
547      */
548     public WizardPage getWizardPage(EmoWizardMetadata metadata) throws WizardException {
549         try {
550             metadata.setDirective(getField(com.sri.emo.dbobj.WizStep.FLD_DIRECTIVE));
551             metadata.setHelpText(getField(com.sri.emo.dbobj.WizStep.FLD_HELPTEXT));
552             metadata.setTitle(getField(com.sri.emo.dbobj.WizStep.FLD_TITLE));
553 
554             ValidValue[] menu = null;
555             if (getStepType() == PromptStepType.INSTRUCTIONS_ONLY) {
556                 metadata.setEntry(false);
557             } else {
558                 metadata.setEntry(true);
559                 menu = getMenuItems();
560             }
561 
562             return new EmoWizardPage(new Integer(this
563                     .getFieldInt(WizStep.FLD_ID)),
564                     metadata, menu);
565         } catch (DBException ex) {
566             throw new WizardException("Error constructing page", ex);
567         }
568     }
569 
570     /***
571      * Allows up navigation to the owner hierarchy.
572      *
573      * @return WizDefinition instance associated with this Wizard Step.
574      * @throws DBException upon access error.
575      */
576     public WizDefinition getWizardDefinition() throws DBException {
577         WizDefinition wizDef = new WizDefinition();
578         wizDef.setField(WizDefinition.FLD_ID, getField(WizStep.FLD_WIZID));
579         wizDef.retrieve();
580         return wizDef;
581     }
582 
583     /***
584      * One time intiialization of all the field types.  Set all specifications
585      * here such as field names, table name, friendly name of the data object,
586      * characterset, etc.
587      *
588      * @throws DBException upon error
589      */
590     protected void setupFields() throws DBException {
591         if (getLogger().isDebugEnabled()) {
592             getLogger().debug("Initializing field members");
593         }
594 
595         super.setupFields();
596         setTargetTable(TABLE_NAME);
597 
598         setDescription("Emo Wizard Steps");
599         setCharset("ISO-8859-1");
600 
601         addField(FLD_ID, DBField.AUTOINC_TYPE, 0, false, "Item ID");
602         addKey(FLD_ID);
603 
604         /***
605          * @todo It appears that Expresso is having problems with multi id'd
606          * keys where one id is auto-increment.  To workaround this, we're
607          * making a unique index for wizard id and Order
608          */
609         addField(FLD_WIZID, DBField.INTEGER_TYPE, 0, false, "Wizard Id");
610         addField(FLD_TITLE, DBField.VARCHAR_TYPE, 254, false, "Title");
611         setDefaultValue(FLD_TITLE, "New Step");
612 
613         addField(FLD_DIRECTIVE, DBField.VARCHAR_TYPE, 254, false, "Directive");
614         DBField fieldMeta = (DBField) getMetaData().getFieldMetadata(FLD_DIRECTIVE);
615         fieldMeta.setFilterClass(AllowedHtmlPlusURLFilter.class);
616 
617         addField(FLD_HELPTEXT, DBField.TEXT_TYPE, 0, true, "Help Text");
618         // field will have URLs rendered as <a> tags
619         fieldMeta = (DBField) getMetaData().getFieldMetadata(FLD_HELPTEXT);
620         fieldMeta.setFilterClass(AllowedHtmlPlusURLFilter.class);
621 
622         addField(FLD_ORDER, DBField.INTEGER_TYPE, 0, true, "Step Order");
623         setDefaultValue(FLD_ORDER, "0");
624 
625         addField(FLD_STEP_TYPE, DBField.INTEGER_TYPE, 0, false, "Step Type");
626         setDefaultValue(FLD_STEP_TYPE, Integer.toString(INSTANCE_PICKLIST));
627         setMultiValued(FLD_STEP_TYPE);
628 
629         //Allow for faster sorting on order.
630         //it must be not unique because when swapping orders, you will
631         //temporarily get two records with the same index.
632         addIndex("emo_wizsteporder", FLD_WIZID + "," + FLD_ORDER, false);
633 
634         addDetail(StepAttributes.class.getName(), FLD_ID, StepAttributes.FLD_STEPID);
635     }
636 
637 
638     /***
639      * Override to provide 'custom type' for value list.
640      * <p>{@inheritDoc}</p>
641      *
642      * @param fieldName the name of the field for values.
643      * @return Vector of valid values.
644      */
645     public synchronized Vector getValidValues(String fieldName) throws com.jcorporate.expresso.core.db.DBException {
646         if (FLD_STEP_TYPE.equals(fieldName)) {
647             Vector vv = new Vector(4);
648             vv.add(new ValidValue(Integer.toString(MODEL_PICKLIST), "Picklist from attribute of model"));
649             vv.add(new ValidValue(Integer.toString(INSTANCE_PICKLIST), "Picklist from attribute of instance"));
650             vv.add(new ValidValue(Integer.toString(CUSTOM_PICKLIST), "Picklist from customized list"));
651             vv.add(new ValidValue(Integer.toString(TEXT_ENTRY), "Text entry"));
652             vv.add(new ValidValue(Integer.toString(INSTRUCTIONS_ONLY), "No Input: Instructions Only"));
653             return vv;
654         } else {
655             return super.getValidValues(fieldName);
656         }
657     }
658 
659     public Vector getTextStyleValidValues() {
660         Vector validValues = new Vector(3);
661         validValues.add(new ValidValue(TEXT_VV_SINGLELINE, "Single Line"));
662         validValues.add(new ValidValue(TEXT_VV_MULTILINE, "Multiple Lines"));
663         return validValues;
664     }
665 
666     /***
667      * Overrides add to add attributes and to set the step order automatically if it hasn't
668      * been done so yet.
669      * <p>{@inheritDoc}</p>
670      *
671      * @throws DBException upon database error
672      */
673     public synchronized void add() throws DBException {
674         boolean hasLocalConn = true;
675         DBConnection transactionConn = getLocalConnection();
676         if (transactionConn == null) {
677             hasLocalConn = false;
678             transactionConn = getConnectionPool().getConnection("Add Wizard Step");
679             transactionConn.setAutoCommit(false);
680             setConnection(transactionConn); // make changes to us rollback-able
681         }
682         try {
683             if (getField(FLD_ORDER).length() == 0) {
684                 WizStep step = new WizStep();
685                 step.setWizId(getWizId());
686                 String max = step.getMax(WizStep.FLD_ORDER, true);
687                 if (max == null || max.length() == 0) {
688                     max = "-1";
689                 }
690                 int maxStep = Integer.parseInt(max);
691                 maxStep++;
692                 setField(WizStep.FLD_ORDER, maxStep);
693             }
694 
695             super.add();
696 
697             //Now save Attributes
698             StepAttributes attribs = new StepAttributes();
699             attribs.setConnection(transactionConn);
700             for (Iterator i = getStepAttributes().keySet().iterator(); i.hasNext();) {
701                 attribs.clear();
702                 String key = (String) i.next();
703                 String value = (String) getStepAttributes().get(key);
704 
705                 attribs.setField(StepAttributes.FLD_ATTRIBUTEKEY, key);
706                 attribs.setField(StepAttributes.FLD_ATTRIBUTEVALUE, value);
707                 attribs.setStepId(getId());
708                 attribs.add();
709             }
710 
711             if (!hasLocalConn) {
712                 transactionConn.commit();
713             }
714         } catch (DBException ex) {
715 
716             if (!hasLocalConn) {
717                 transactionConn.rollback();
718             }
719             throw ex;
720         } finally {
721             if (!hasLocalConn) {
722                 transactionConn.release();
723             }
724         }
725     }
726 
727     /***
728      * Returns true if we're using a multi-valued node attribute for this
729      * step.
730      *
731      * @return boolean true for attribute.
732      * @throws DBException upon getField() error,
733      */
734     public boolean isAttribute() throws DBException {
735         return getStepAttribute(StepAttributes.ATTRIBUTE_INSTANCE_ID) != null;
736     }
737 
738     /***
739      * Retrieve the model.
740      *
741      * @return String the name of the model.
742      * @throws DBException upon getField() error.
743      */
744     public String getModelName() throws DBException {
745         return getStepAttribute(StepAttributes.ATTRIBUTE_MODEL);
746     }
747 
748     /***
749      * Get the model field.
750      *
751      * @return String the name of the field of the model
752      * @throws DBException upon getFieldErorr();
753      */
754     public String getModelField() throws DBException {
755         return getStepAttribute(StepAttributes.ATTRIBUTE_MODEL_FIELD);
756     }
757 
758     /***
759      * Provide a transition for viewing this object, suitable for creating an
760      * HTTP link.
761      *
762      * @return transtion for viewing, including label for name of object; never null
763      * @throws DBException upon access error.
764      */
765     public Transition getViewTrans() throws DBException {
766         Transition trans = new Transition(getWizStepTitle(), SelectionWizardManager.class,
767                 SelectionWizardManager.STATE_PROMPT_EDIT);
768         trans.addParam(WizDefinition.FLD_ID, getId());
769         return trans;
770     }
771 
772     /***
773      * Retrieves the id field.
774      *
775      * @return String
776      * @throws DBException upon getField() error.
777      */
778     public String getId() throws DBException {
779         return getField(FLD_ID);
780     }
781 
782 
783     /***
784      * Retrieves the step title.
785      *
786      * @return String
787      * @throws DBException upon getField() error.
788      */
789     private String getWizStepTitle() throws DBException {
790         return getField(FLD_TITLE);
791     }
792 
793     /***
794      * Override of Normal Update to save all attributes to disk.
795      * <p>{@inheritDoc}</p>
796      *
797      * @throws DBException upon error.
798      */
799     public void update() throws DBException {
800 
801         boolean hasLocalConn = true;
802         DBConnection transactionConn = getLocalConnection();
803         if (transactionConn == null) {
804             hasLocalConn = false;
805             transactionConn = getConnectionPool().getConnection("Add Wizard Step");
806             transactionConn.setAutoCommit(false);
807             // set local transaction so this update will be rollback-able
808             setConnection(transactionConn);
809         }
810 
811         super.update(true);
812 
813         try {
814             //Now save Attributes
815             StepAttributes attribs = new StepAttributes();
816             attribs.setConnection(transactionConn);
817             attribs.setStepId(getId());
818             attribs.deleteAll();
819 
820             for (Iterator i = getStepAttributes().keySet().iterator(); i.hasNext();) {
821                 attribs.clear();
822                 attribs.setStepId(getId());
823 
824                 String key = (String) i.next();
825                 String value = (String) getStepAttributes().get(key);
826                 attribs.setField(StepAttributes.FLD_ATTRIBUTEKEY, key);
827                 attribs.setField(StepAttributes.FLD_ATTRIBUTEVALUE, value);
828                 attribs.add();
829             }
830 
831             if (!hasLocalConn) {
832                 transactionConn.commit();
833             }
834         } catch (DBException ex) {
835             transactionConn.rollback();
836             throw ex;
837         } finally {
838             if (!hasLocalConn) {
839                 setConnection(null); // clear connection
840                 transactionConn.release();
841             }
842         }
843     }
844 
845     public void setId(String id) throws DBException {
846         setField(FLD_ID, id);
847     }
848 
849     public void setId(int stepId) throws DBException {
850         setField(FLD_ID, stepId);
851     }
852 
853     public String getWizId() throws DBException {
854         return getField(FLD_WIZID);
855     }
856 
857     public void setWizId(String wizId) throws DBException {
858         setField(FLD_WIZID, wizId);
859     }
860 
861     /***
862      * Assumes that this object has been retrieved and then certain fields have
863      * been set from request parameters.  Therefore, we can use internal 'isChanged()'
864      * to determine if fields have been changed by input.
865      *
866      * @return true if this object has changed key fields that require a Decision Matrix update as well
867      * @throws DBException upon database access error.
868      */
869     public boolean shouldUpdateDecisionMatrix() throws DBException {
870         boolean result = false;
871 
872         for (int i = 0; i < FIELDS_INDICATING_MATRIX_NEEDS_UPDATING.length; i++) {
873             String fld = FIELDS_INDICATING_MATRIX_NEEDS_UPDATING[i];
874             DataField df = getDataField(fld);
875             if (df.isChanged()) {
876                 return true;
877             }
878         }
879         return result;
880     }
881 
882     public String getTitle() throws DBException {
883         return getField(FLD_TITLE);
884     }
885 
886     public String getOrder() throws DBException {
887         return getField(FLD_ORDER);
888     }
889 
890     public void setOrder(String order) throws DBException {
891         setField(FLD_ORDER, order);
892     }
893 
894     public int getType() throws DBException {
895         return getFieldInt(FLD_STEP_TYPE);
896     }
897 
898 
899     public String getHelpText() throws DBException {
900         return getField(FLD_HELPTEXT);
901     }
902 
903     public String getHelpTextRaw() throws DBException {
904         Filter old = setFilterClass(new RawFilter());
905         String raw = getHelpText();
906         setFilterClass(old);
907 
908         return raw;
909     }
910 }