1
2
3
4
5
6
7
8
9
10 package com.sri.emo.dbobj;
11
12 import java.util.ArrayList;
13 import java.util.Collections;
14 import java.util.Iterator;
15 import java.util.List;
16 import java.util.Map;
17 import com.jcorporate.expresso.core.controller.ControllerRequest;
18 import com.jcorporate.expresso.core.controller.ExpressoRequest;
19 import com.jcorporate.expresso.core.controller.Transition;
20 import com.jcorporate.expresso.core.db.DBConnection;
21 import com.jcorporate.expresso.core.db.DBConnectionPool;
22 import com.jcorporate.expresso.core.db.DBException;
23 import com.jcorporate.expresso.core.dbobj.DBField;
24 import com.jcorporate.expresso.core.dbobj.SecurDBObject;
25 import com.jcorporate.expresso.core.dbobj.SecuredDBObject;
26 import com.jcorporate.expresso.core.dbobj.ValidValue;
27 import com.jcorporate.expresso.core.misc.StringUtil;
28 import com.jcorporate.expresso.core.registry.RequestRegistry;
29 import com.jcorporate.expresso.core.security.SuperUser;
30 import com.jcorporate.expresso.core.security.User;
31 import com.jcorporate.expresso.core.security.filters.Filter;
32 import com.jcorporate.expresso.core.security.filters.RawFilter;
33 import com.sri.common.controller.AbstractDBController;
34 import com.sri.emo.controller.NodeAction;
35 import com.sri.emo.controller.PartAction;
36 import com.sri.emo.dbobj.model_tree.ModelVisitable;
37 import com.sri.emo.dbobj.model_tree.ModelVisitor;
38
39
40 /***
41 * A part of an "entity" (a Node object). Our modeling environment allows dynamic building
42 * of objects. Parts have a complex typing system because the "type"
43 * field is overloaded for two kinds of parts: owned attributes and shared nodes.
44 * <p/>
45 * For owned attributes, the Type field is a simple string, unique within
46 * the parent object.
47 * </p>
48 * <p/>
49 * For shared nodes, the Type field is the nodeType of the shared nodes. However,
50 * multiple parts within a single entity can be of "shared node" type AND share
51 * the same kind of node. For example, Is a kind of/These are kinds of me -- these
52 * two part labels apply to the same kind of shared node, so their part types
53 * are the same. Parts which are shared-node parts are distinguished (uniquely)
54 * by a combination of part type, relation type, and parent type.
55 * </p>
56 *
57 * @author larry hamel
58 */
59 public class Part extends SecurDBObject implements Comparable, ModelVisitable {
60 /***
61 *
62 */
63 private static final long serialVersionUID = 1L;
64 public static final String PART_TYPE_TABLE_NAME = "part2";
65 public static final String KEEP_DETAILS = "keepDetails";
66
67 public static final String DELIMITER = "_x_";
68
69 /***
70 * field names
71 */
72 public static final String PART_ID = "PART_ID";
73 public static final String PARENT_TYPE = "PARENT_TYPE";
74 public static final String PART_NUM = "PART_NUM";
75 public static final String PART_LABEL = "PART_LABEL";
76 public static final String SPECIAL_HANDLER = "SPECIAL_HANDLER";
77 public static final String PART_TYPE = "PART_TYPE";
78 public static final String NODE_PART_RELATION_TYPE = RelationType.RELATION_TYPE_NAME;
79 public static final String CARDINALITY = "CARDINALITY";
80 public static final String PART_DESCRIP = "PART_DESCRIP";
81 public static final String PART_IS_ATTRIB = "PART_IS_ATTRIB";
82
83 /***
84 * attribute limitation constants (could be in other "def" table)
85 */
86 public static final String MULTIPLE_ALLOWED = "MULTIPLE_ALLOWED";
87 public static final String SINGLE_ALLOWED = "SINGLE_ALLOWED";
88 public static final String SUMMARY_SINGLE_ALLOWED = "SUMMARY_SINGLE_ALLOWED";
89 public static final String PICKLIST_SINGLE_ALLOWED = "PICKLIST_SINGLE_ALLOWED";
90 public static final String NOT_SPECIFIED_ID = "NOT_SPECIFIED_ID";
91 public static final String[][] CARDINALITY_CHOICES = {{NOT_SPECIFIED_ID, PickList.NOT_SPECIFIED_DISPLAY},
92 {MULTIPLE_ALLOWED, "Multiple attributes allowed"}, {SINGLE_ALLOWED, "Single attribute only"},
93 {SUMMARY_SINGLE_ALLOWED, "Summary single attribute"}, {PICKLIST_SINGLE_ALLOWED, "Picklist single attribute"},
94 };
95 public static final String[] SINGLE_CARDINALITY_CHOICES = {
96 SINGLE_ALLOWED, SUMMARY_SINGLE_ALLOWED, PICKLIST_SINGLE_ALLOWED,
97 };
98 private String REFERENCES_DEFINITION = " are notes about relevant items, such as academic articles.";
99 private String ONLINE_RESOURCES_DEFINITION = " are relevant items that can be found online (URLs).";
100 public static final String PART_DISPLAY_NAME = "PART_DISPLAY_NAME";
101 public static final String PART_HELP_STRING = "PART_HELP_STRING";
102 public static final String REL_REF = "REL_REF";
103 public static final String SRC_NODE_REF = "SRC_NODE_REF";
104 public static final String DEST_NODE_REF = "DEST_NODE_REF";
105
106 /***
107 * data member, caching value
108 */
109 private transient IPartHandler mSpecialHandler = null;
110 private transient String mSpecialHandlerStr = null;
111
112 /***
113 * Default constructor for <code>Part</code>
114 * creates a new object of this type with no connection
115 * yet allocated.
116 *
117 * @throws DBException If the new object cannot be
118 * created
119 */
120 public Part() throws DBException {
121 }
122
123 /***
124 * @param request ControllerRequest
125 * @throws DBException
126 * @deprecated
127 */
128 public Part(ControllerRequest request) throws DBException {
129 super(request);
130 }
131
132 public Part(SecuredDBObject obj) throws DBException {
133 super();
134 setRequestingUser(this.getRequestingUser());
135 setDataContext(obj.getDataContext());
136 }
137
138 /***
139 * Defines the database table name and fields for this DB object
140 *
141 * @throws DBException if the operation cannot be performed
142 */
143 protected synchronized void setupFields() throws DBException {
144 setTargetTable(PART_TYPE_TABLE_NAME);
145 setDescription("Parts of Objects");
146 addField(PART_ID, DBField.AUTOINC_TYPE, 0, false, "Primary key id");
147 addField(PARENT_TYPE, DBField.VARCHAR_TYPE, 100, false,
148 "Parent node type");
149 addField(PART_NUM, DBField.INT_TYPE, 0, false,
150 "Order of part within parent");
151 addField(PART_LABEL, DBField.VARCHAR_TYPE, 255, false, "Part Label");
152 addField(PART_TYPE, DBField.VARCHAR_TYPE, 100, false,
153 "part type--either node type (if node part), or attribute key (if attribute part");
154 addField(PART_IS_ATTRIB, DBField.BOOLEAN_TYPE, 0, false,
155 "true if part is attribute (otherwise, it is a node)");
156
157
158 addField(CARDINALITY, DBField.VARCHAR_TYPE, 100, true,
159 "Attribute limits");
160 addField(NODE_PART_RELATION_TYPE, DBField.VARCHAR_TYPE, 100, true,
161 "Relation of part to parent (all node parts fill this field, AND attributes which are summaries)");
162 addField(SPECIAL_HANDLER, DBField.VARCHAR_TYPE, 255, true,
163 "Class name of handler class if any");
164 addField(PART_DESCRIP, DBField.LONGVARCHAR_TYPE, 0, true,
165 "Description of this part");
166
167 addKey(PART_ID);
168
169 addIndex("parent_idx", PARENT_TYPE, false);
170
171
172 addIndex("parent_parttype_idx",
173 PARENT_TYPE + "," + PART_TYPE + "," + NODE_PART_RELATION_TYPE, true);
174 }
175
176
177 public void setParentType(String type) throws DBException {
178 setField(PARENT_TYPE, type);
179 }
180
181 public String getPartLabel() throws DBException {
182 return getField(PART_LABEL);
183 }
184
185 public String getPartType() throws DBException {
186 return getField(PART_TYPE);
187 }
188
189 public boolean isOwnedAttribute() throws DBException {
190
191 return getFieldBoolean(PART_IS_ATTRIB);
192 }
193
194 public String getPartNum() throws DBException {
195 return getField(PART_NUM);
196 }
197
198 public String getSpecialHandlerName() throws DBException {
199 return getField(SPECIAL_HANDLER);
200 }
201
202 public String getCardinality() throws DBException {
203 return getField(CARDINALITY);
204 }
205
206 public String getPartDescription() throws DBException {
207 return getField(PART_DESCRIP);
208 }
209
210 public void setPartType(String partType) throws DBException {
211 setField(PART_TYPE, partType);
212 }
213
214 public String getParentType() throws DBException {
215 return getField(PARENT_TYPE);
216 }
217
218 /***
219 * Return code for node relation type.
220 *
221 * @return String, the node relation type.
222 * @throws DBException upon database exception error.
223 */
224 public String getNodeRelation() throws DBException {
225 return getField(NODE_PART_RELATION_TYPE);
226 }
227
228 public void setNodeRelation(String nodeRelation) throws DBException {
229 setField(NODE_PART_RELATION_TYPE, nodeRelation);
230 }
231
232 public NodeType getParentEntity() throws DBException {
233 NodeType parent = new NodeType();
234 parent.setRequestingUser(this.getRequestingUser());
235 parent.setDBName(getDataContext());
236 parent.setEntityName(getParentType());
237
238
239 if (!parent.find()) {
240 throw new DBException("cannot find NodeType with name: " +
241 getPartType());
242 }
243
244 return parent;
245 }
246
247 /***
248 * for definition page,
249 *
250 * @param isHTML signals whether to use HTML tags, or if false, to use Docbook tags
251 * @return true The definition, in Docbook or Html text
252 * @throws DBException upon database exception error.
253 */
254 public String getDefinition(boolean isHTML) throws DBException {
255 String result = getPartDescription();
256
257 if (!isOwnedAttribute() && (result.length() == 0)) {
258
259 if (getParentType().equals(getPartType())) {
260
261 RelationType relType = new RelationType();
262 relType.setRequestingUser(this.getRequestingUser());
263 relType.setDataContext(getDataContext());
264 relType.setRelType(getNodeRelation());
265 relType.retrieve();
266
267 Filter old = null;
268
269 if (!isHTML) {
270 old = relType.setFilterClass(new RawFilter());
271 }
272
273 result = relType.getRelTypeDescrip();
274
275 if (!isHTML) {
276 relType.setFilterClass(old);
277 }
278 } else {
279 NodeType type = new NodeType(getRequestingUser());
280 type.setEntityName(getPartType());
281
282
283 if (!type.find()) {
284 getLogger().error("cannot find NodeType with name: " +
285 getPartType());
286 result = "(unknown type)";
287 }
288
289 if (isHTML) {
290 result = " are associations with (potentially shared) objects of type: <a href=\"#" +
291 getPartType() + "\">" + type.getDisplayName() + "</a>.";
292 } else {
293
294 result = " are associations with (potentially shared) objects of type: " +
295 type.getDisplayName();
296 }
297 }
298 } else if (AbstractParts.REFERENCES.equals(getPartType())) {
299 result = REFERENCES_DEFINITION;
300 } else if (AbstractParts.ONLINE_RESOURCE.equals(getPartType())) {
301 result = ONLINE_RESOURCES_DEFINITION;
302 }
303
304 return result;
305 }
306
307 public void setId(String id) throws DBException {
308 setField(PART_ID, id);
309 }
310
311 public String getId() throws DBException {
312 return getField(PART_ID);
313 }
314
315 public void setPartNum(int i) throws DBException {
316 if (i < 0) {
317 i = 0;
318 }
319 setField(PART_NUM, i);
320 }
321
322 /***
323 * @return true if this attribute has a picklist
324 * @throws DBException upon database exception error.
325 */
326 public boolean hasPicklist() throws DBException {
327 return isOwnedAttribute() &&
328 Part.PICKLIST_SINGLE_ALLOWED.equals(getCardinality());
329 }
330
331 public boolean isHaveCustomHandler() throws DBException {
332 return getCustomHandler() != null;
333 }
334
335 /***
336 * @return the custom editting handler or null if this part does not have one
337 * @throws DBException upon database exception error.
338 */
339 public IPartHandler getCustomHandler() throws DBException {
340
341
342 if (mSpecialHandlerStr == null) {
343 mSpecialHandlerStr = getField(Part.SPECIAL_HANDLER);
344
345 if ((mSpecialHandlerStr != null) &&
346 (mSpecialHandlerStr.length() > 0)) {
347 try {
348 Class myclass = Class.forName(mSpecialHandlerStr);
349 mSpecialHandler = (IPartHandler) myclass.newInstance();
350 } catch (Exception e) {
351 throw new DBException(e);
352 }
353 }
354 }
355
356 IPartHandler result = mSpecialHandler;
357 if (result != null) {
358 try {
359 result = (IPartHandler) mSpecialHandler.clone();
360 } catch (CloneNotSupportedException e) {
361 throw new DBException("cannot clone: ", e);
362 }
363 }
364
365 return result;
366 }
367
368 public void setCustomHandler(String customHandler) throws DBException {
369 setField(Part.SPECIAL_HANDLER, customHandler);
370 }
371
372 /***
373 * Return display title of node type of parent node, or empty string "" if not found.
374 *
375 * @param requestingUid the requesting uid.
376 * @return The display title.
377 */
378 public String getDisplayTitleOfParentNodeType(int requestingUid) {
379 String result = "";
380
381 try {
382 NodeType nodeType = new NodeType();
383 nodeType.setRequestingUser(User.getUserFromId(requestingUid, getDataContext()));
384 nodeType.setField(NodeType.NODE_TYPE_NAME, getParentType());
385
386 if (!nodeType.find()) {
387 throw new DBException("cannot find node type: " +
388 getParentType());
389 }
390
391 result = nodeType.getField(NodeType.DISPLAY_TITLE);
392
393 if (result == null) {
394 result = "";
395 }
396 } catch (DBException e) {
397 e.printStackTrace();
398 }
399
400 return result;
401 }
402
403 /***
404 * @param request the ControllerRequest for parameters.
405 * @return array of values in picklist, with each row containing ID, display; null if attribute has no picklist
406 * @throws DBException upon database exception error.
407 */
408 public String[][] getPicklistArray(ExpressoRequest request) throws DBException {
409 if (!hasPicklist()) {
410 return null;
411 }
412
413 List found = getPicklist(request);
414
415 if (found == null) {
416 return new String[0][2];
417 }
418
419
420 if (found.size() > 0) {
421 Collections.sort(found);
422 }
423
424
425 String[][] result = new String[found.size()][2];
426 int i = 0;
427
428 for (Iterator iter = found.iterator(); iter.hasNext();) {
429 PickList pickList = (PickList) iter.next();
430 result[i][0] = pickList.getField(PickList.LIST_ID);
431 result[i][1] = pickList.getField(PickList.DISPLAY_IN_PICKLIST);
432 i++;
433 }
434
435 return result;
436 }
437
438 public List getPicklist(ExpressoRequest request) throws DBException {
439
440 return getPickList(request.getDataContext(), RequestRegistry.getUser().getUid());
441 }
442
443 public List getPickList(String dataContext, int uid) throws DBException {
444 if (!hasPicklist()) {
445 return null;
446 }
447
448 PickList listItem = new PickList(getParentType(), getPartType());
449 listItem.setRequestingUser(User.getUserFromId(uid, dataContext));
450
451 return (List) listItem.searchAndRetrieveList(PickList.LIST_ID);
452 }
453
454 /***
455 * @return list of PickList items; never null
456 */
457 public List getPickList() throws DBException {
458 if (!hasPicklist()) {
459 return null;
460 }
461
462 PickList listItem = new PickList();
463 listItem.setNodeType(getParentType());
464 listItem.setPickAttribType(getPartType());
465
466 return (List) listItem.searchAndRetrieveList(PickList.LIST_ID);
467 }
468
469 public ValidValue[] getPickListValidValues() throws DBException {
470 if (!hasPicklist()) {
471 return new ValidValue[0];
472 }
473
474 List list = this.getPickList();
475
476 ValidValue[] returnValue = new ValidValue[list.size()];
477 for (int i = 0; i < list.size(); i++) {
478 PickList oneItem = (PickList) list.get(i);
479 returnValue[i] = new ValidValue(oneItem.getField(PickList.LIST_ID),
480 oneItem.getField(PickList.DISPLAY_IN_PICKLIST));
481 }
482
483 return returnValue;
484 }
485
486
487 /***
488 * @return true if this attribute is a summary
489 * @throws DBException upon database exception error.
490 */
491 public boolean isSummary() throws DBException {
492 return isOwnedAttribute() &&
493 Part.SUMMARY_SINGLE_ALLOWED.equals(getCardinality());
494 }
495
496 /***
497 * @return true if this attribute allows multiple entries
498 * @throws DBException upon database exception error.
499 */
500 public boolean areMultipleAttributesAllowed() throws DBException {
501 return Part.MULTIPLE_ALLOWED.equals(getCardinality());
502 }
503
504 public int numDisplayLines() throws DBException {
505 int numLines = AbstractDBController.SINGLE_TEXTAREA_NUM_LINES;
506
507 if (areMultipleAttributesAllowed()) {
508 numLines = AbstractDBController.MULTIPLE_TEXTAREA_NUM_LINES;
509 }
510
511 return numLines;
512 }
513
514 public void refreshSpecialHandlerCache() {
515 mSpecialHandlerStr = null;
516 }
517
518 /***
519 * Delete a part;
520 * deletes all attribs or relations associated with this part, unless
521 * the part has a KEEP_DETAILS attribute (an expresso attribute associated with the dbobject)
522 *
523 * @throws DBException if delete is not allowed for the current user
524 * @see #setAttribute
525 * @see #KEEP_DETAILS
526 */
527 public synchronized void delete() throws DBException {
528
529 if (!isRowAllowed(SecuredDBObject.DELETE)) {
530 return;
531 }
532
533 if (getAttribute(KEEP_DETAILS) == null) {
534 if (isOwnedAttribute()) {
535
536 Attribute attribs = new Attribute(SuperUser.INSTANCE);
537 attribs.setDataContext(getDataContext());
538 attribs.setParentNodeType(getParentType());
539 attribs.setAttributeType(getPartType());
540 attribs.deleteAll();
541 } else {
542 deleteRelations();
543 }
544 }
545
546 super.delete();
547
548
549 PartsFactory.refreshCache(getParentType());
550 getParentEntity().incrementVersion();
551 }
552
553
554 /***
555 * override to use permissions of parent node type
556 *
557 * @param requestedFunction code for function -- Add, Update, Delete, Search (read)
558 * @return true if this function is allowed for this requesting user
559 * @throws SecurityException (unchecked) if not allowed
560 * @throws com.jcorporate.expresso.core.db.DBException
561 * for other data-related errors.
562 */
563 public boolean isRowAllowed(String requestedFunction) throws DBException {
564 if (User.isAdmin(getRequestingUid()) || SuperUser.SYSTEM_UID == getRequestingUid()) {
565 return true;
566 }
567 NodeType nodeType = this.getParentEntity();
568 return nodeType.isRowAllowed(requestedFunction);
569 }
570
571 /***
572 * See if the current user of this DB object is allowed to perform the
573 * requested function, given the function's code.
574 *
575 * @param requestedFunction The code of the requested function. The codes are:
576 * <ol><li>A: Add<li>
577 * <li>S: Search<li>
578 * <li>U: Update<li>
579 * <li>D: Delete<li>
580 * </ol>
581 * @throws com.jcorporate.expresso.core.db.DBException
582 * If the requested operation is not permitted to this user
583 * @throws SecurityException if the user is not allowed access to the object.
584 */
585 public void isAllowed(String requestedFunction) throws SecurityException, DBException {
586
587 if (SEARCH.equals(requestedFunction)) {
588 return;
589 }
590
591 if (getRequestingUser() == SuperUser.INSTANCE) {
592 return;
593 }
594
595 if (getRequestingUser().isAdmin()) {
596 return;
597 }
598
599 if (getPartType().length() == 0) {
600
601 return;
602 }
603 NodeType nodeType = this.getParentEntity();
604 nodeType.isAllowed(requestedFunction);
605 }
606
607
608 /***
609 * add cache maintenance
610 *
611 * @throws DBException upon database exception error.
612 */
613 public synchronized void update() throws DBException {
614 Part oldPart = new Part();
615 oldPart.setRequestingUser(getRequestingUser());
616 oldPart.setDataContext(getDataContext());
617 oldPart.setId(getId());
618 oldPart.retrieve();
619
620 super.update();
621 refreshSpecialHandlerCache();
622 PartsFactory.refreshCache(getParentType());
623
624 getParentEntity().incrementVersion();
625
626
627
628 if ((oldPart.getNodeRelation().length() > 0) &&
629 !oldPart.getNodeRelation().equals(getNodeRelation())) {
630 changeRelations(oldPart.getNodeRelation());
631 }
632
633
634 if (!oldPart.getPartType().equals(getPartType())) {
635
636 if (oldPart.isOwnedAttribute()) {
637 Attribute attribs = new Attribute();
638 attribs.setRequestingUser(getRequestingUser());
639 attribs.setParentNodeType(getParentType());
640 attribs.setAttributeType(getPartType());
641
642 List all = attribs.searchAndRetrieveList();
643
644 for (Iterator iter = all.iterator(); iter.hasNext();) {
645 Attribute attribute = (Attribute) iter.next();
646 attribute.setAttributeType(getPartType());
647 attribute.update();
648 }
649 } else {
650
651
652 deleteRelations();
653 }
654 }
655 }
656
657
658 /***
659 * we override not to check permissions (which is done at the table level
660 * by superclass) but rather to add default permissions
661 *
662 * @throws DBException upon database exception error.
663 */
664 public synchronized void add() throws DBException {
665 NodeType parent = getParentEntity();
666 if (!parent.canRequesterWrite()) {
667 throw new DBException("User " + User.getLoginFromId(this.getRequestingUid())
668 + " does not have permission to change model: "
669 + parent.getEntityName());
670 }
671 super.add();
672
673 getParentEntity().incrementVersion();
674
675
676 PartsFactory.refreshCache(getParentType());
677 }
678
679
680 public boolean isSharedNodeAttrib() throws DBException {
681 return !isOwnedAttribute();
682 }
683
684 /***
685 * @param nodeRelation the relationship to be queried; useful for getting old node relations for updating; returned Relation object only have primary key set--the relations are from a 'join', not retrieved from DB
686 * @return list of Relations specified by this part;
687 * @throws DBException upon database exception error.
688 */
689 private List getRelations(String nodeRelation) throws DBException {
690 if (isOwnedAttribute()) {
691
692 return new ArrayList();
693 }
694
695 String sql = "SELECT relation.* " +
696 " FROM node as src, node as dest, relation " +
697 " WHERE src.NODE_TYPE LIKE '" + getParentType() + "' " +
698 " AND dest.NODE_TYPE LIKE '" + getPartType() + "' " +
699 " AND relation.RELATION_TYPE LIKE '" + nodeRelation + "' " +
700 " AND relation.RELATION_SRC = src.NODE_ID " +
701 " AND relation.RELATION_DEST = dest.NODE_ID ";
702
703 ArrayList relations = new ArrayList();
704 DBConnection conn = DBConnectionPool.getInstance(getDataContext())
705 .getConnection();
706
707 try {
708 conn.execute(sql);
709
710 while (conn.next()) {
711 Relation rel = new Relation();
712 rel.setRequestingUser(getRequestingUser());
713 rel.setDataContext(getDataContext());
714 rel.setSrcId(conn.getString(Relation.RELATION_SRC));
715 rel.setDestId(conn.getString(Relation.RELATION_DEST));
716 rel.setRelationTypeName(conn.getString(Relation.RELATION_TYPE));
717
718 if (conn.getString(Relation.RELATION_ORDER) != null) {
719 rel.setOrder(conn.getString(Relation.RELATION_ORDER));
720 }
721
722 relations.add(rel);
723 }
724 } finally {
725 conn.release();
726 }
727
728 return relations;
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745 }
746
747 /***
748 * @return list of Relations specified by this part; Relation object retrieved from DB via custom SQL
749 * @throws DBException upon database exception error.
750 */
751 public List getRelations() throws DBException {
752 return getRelations(getNodeRelation());
753 }
754
755 /***
756 * Delete all relations associated with this part.
757 *
758 * @throws DBException upon database exception error.
759 */
760 public void deleteRelations() throws DBException {
761 List list = getRelations();
762
763 for (Iterator iterator = list.iterator(); iterator.hasNext();) {
764 Relation aRelation = (Relation) iterator.next();
765
766 getLogger().debug("deleting Relation src/type: " +
767 aRelation.getSrcNode().getNodeTitle() + "/" +
768 aRelation.getRelationTypeName());
769
770 aRelation.delete();
771 }
772 }
773
774 /***
775 * change all relations associated with this part
776 * IFF the nodeRelation changed
777 *
778 * @param oldNodeRelation the old relation; assumes new relation is in "this"
779 * @throws DBException upon database exception error.
780 */
781 public void changeRelations(String oldNodeRelation) throws DBException {
782
783 if (oldNodeRelation.equals(getNodeRelation())) {
784 return;
785 }
786
787 List list = getRelations(oldNodeRelation);
788
789 for (Iterator iterator = list.iterator(); iterator.hasNext();) {
790 Relation relation = (Relation) iterator.next();
791
792
793
794 relation.delete();
795
796 relation.setRelationTypeName(getNodeRelation());
797 relation.add();
798
799 getLogger().debug("changed Relation src/oldtype/newtype: " +
800 relation.getSrcNode().getNodeTitle() + "/" + oldNodeRelation +
801 "/" + relation.getRelationTypeName());
802 }
803 }
804
805 public int getPartNumInt() throws DBException {
806 return Integer.parseInt(getPartNum());
807 }
808
809 public void setPartLabel(String s) throws DBException {
810 setField(PART_LABEL, s);
811 }
812
813 /***
814 * @param card cardinality type; see constants in this file
815 * @throws DBException upon database exception error.
816 */
817 public void setPartCardinality(String card) throws DBException {
818 setField(CARDINALITY, card);
819 }
820
821 public void isOwnedAttribute(boolean attribute) throws DBException {
822 setField(PART_IS_ATTRIB, attribute);
823 }
824
825 /***
826 * @return either parentType + partType for owned attribs, or partType + relationship for shared
827 * @throws DBException upon database exception error.
828 */
829 public String getPartUniqueId() throws DBException {
830 String result = getParentType() + "|" + getPartType();
831
832 if (isSharedNodeAttrib()) {
833 result += ("|" + getNodeRelation());
834 }
835
836 return result;
837 }
838
839 /***
840 * Can be for attribute OR shared relation; simply describes cardinality.
841 *
842 * @return true if this part allows only a single item
843 * @throws DBException upon getField exceptions.
844 * @see #isMultipleAllowed()
845 */
846 public boolean isSingleValued() throws DBException {
847 boolean result = false;
848
849 for (int i = 0; i < SINGLE_CARDINALITY_CHOICES.length; i++) {
850 String cardinalityChoice = SINGLE_CARDINALITY_CHOICES[i];
851
852 if (cardinalityChoice.equals(getCardinality())) {
853 result = true;
854
855 break;
856 }
857 }
858
859 return result;
860 }
861
862 /***
863 * Retrieves the label for the cardinality.
864 *
865 * @return display label for this part's cardinality
866 * @throws DBException upon database exception error.
867 */
868 public String getCardinalityLabel() throws DBException {
869 String result = CARDINALITY_CHOICES[1][1];
870
871 for (int i = 0; i < CARDINALITY_CHOICES.length; i++) {
872 String[] choice = CARDINALITY_CHOICES[i];
873
874 if (choice[0].equals(getCardinality())) {
875 result = choice[1];
876
877 break;
878 }
879 }
880
881 return result;
882 }
883
884 /***
885 * @param request the ControllerRequest object.
886 * @return display label of node relation for this part
887 * @throws DBException upon database exception error.
888 */
889 public String getNodeRelationLabel(ExpressoRequest request) throws DBException {
890 String result = "";
891
892 String rel = getNodeRelation();
893 result = StringUtil.notNull(RelationType.getRelTypeDisplayName(rel));
894
895 return result;
896 }
897
898 /***
899 * @return the label of the type of node allowed for relation
900 * @throws DBException upon database exception error.
901 */
902 public String getNodeRelationObjectLabel() throws DBException {
903 String result = "";
904
905 NodeType nodeType = new NodeType();
906 nodeType.setDBName(getDataContext());
907 nodeType.setRequestingUser(getRequestingUser());
908 nodeType.setField(NodeType.NODE_TYPE_NAME, getPartType());
909
910 if (!nodeType.find()) {
911 throw new DBException("cannot find node type: " +
912 getParentType());
913 }
914
915 result = StringUtil.notNull(nodeType.getField(NodeType.DISPLAY_TITLE));
916
917 return result;
918 }
919
920 public void setPartDescrip(String s) throws DBException {
921 setField(PART_DESCRIP, s);
922 }
923
924 public void setSpecialHandler(String s) throws DBException {
925 setField(SPECIAL_HANDLER, s);
926 }
927
928 /***
929 * Compares this object with the specified object for order. Returns a
930 * negative integer, zero, or a positive integer as this object is less
931 * than, equal to, or greater than the specified object.<p>
932 * <p/>
933 * In the foregoing description, the notation
934 * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
935 * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
936 * <tt>0</tt>, or <tt>1</tt> according to whether the value of <i>expression</i>
937 * is negative, zero or positive.
938 * <p/>
939 * The implementor must ensure <tt>sgn(x.compareTo(y)) ==
940 * -sgn(y.compareTo(x))</tt> for all <tt>x</tt> and <tt>y</tt>. (This
941 * implies that <tt>x.compareTo(y)</tt> must throw an exception iff
942 * <tt>y.compareTo(x)</tt> throws an exception.)<p>
943 * <p/>
944 * The implementor must also ensure that the relation is transitive:
945 * <tt>(x.compareTo(y)>0 && y.compareTo(z)>0)</tt> implies
946 * <tt>x.compareTo(z)>0</tt>.<p>
947 * <p/>
948 * Finally, the implementer must ensure that <tt>x.compareTo(y)==0</tt>
949 * implies that <tt>sgn(x.compareTo(z)) == sgn(y.compareTo(z))</tt>, for
950 * all <tt>z</tt>.<p>
951 * <p/>
952 * It is strongly recommended, but <i>not</i> strictly required that
953 * <tt>(x.compareTo(y)==0) == (x.equals(y))</tt>. Generally speaking, any
954 * class that implements the <tt>Comparable</tt> interface and violates
955 * this condition should clearly indicate this fact. The recommended
956 * language is "Note: this class has a natural ordering that is
957 * inconsistent with equals."
958 *
959 * @param o the Object to be compared.
960 * @return a negative integer, zero, or a positive integer as this object
961 * is less than, equal to, or greater than the specified object.
962 * @throws ClassCastException if the specified object's type prevents it
963 * from being compared to this Object.
964 */
965 public int compareTo(Object o) {
966 int result = 0;
967 Part other = (Part) o;
968
969 try {
970 result = getParentType().compareTo(other.getParentType());
971
972 if (result == 0) {
973 result = getPartNum().compareTo(other.getPartNum());
974 }
975 } catch (DBException e) {
976 getLogger().error(e);
977 }
978
979 return result;
980 }
981
982 public String[][] getPicklistArrayAssumeSecure() throws DBException {
983 if (!hasPicklist()) {
984 return null;
985 }
986
987 List found = getPicklistAssumeSecure();
988
989 if (found == null) {
990 return new String[0][2];
991 }
992
993
994 if (found.size() > 0) {
995 Collections.sort(found);
996 }
997
998
999 String[][] result = new String[found.size()][3];
1000 int i = 0;
1001
1002 for (Iterator iter = found.iterator(); iter.hasNext();) {
1003 PickList pickList = (PickList) iter.next();
1004 result[i][0] = pickList.getID();
1005 result[i][1] = pickList.getDisplayString();
1006 result[i][2] = pickList.getOrderNum();
1007 i++;
1008 }
1009
1010 return result;
1011 }
1012
1013 private List getPicklistAssumeSecure() throws DBException {
1014 if (!hasPicklist()) {
1015 return null;
1016 }
1017
1018 PickList listItem = new PickList(SuperUser.INSTANCE);
1019 listItem.setField(PickList.NODE_TYPE, getParentType());
1020 listItem.setField(PickList.ATTRIBUTE_TYPE, getPartType());
1021
1022 return (List) listItem.searchAndRetrieveList(PickList.LIST_ID);
1023 }
1024
1025 public void setPartId(int partId) throws DBException {
1026 setField(PART_ID, partId);
1027 }
1028
1029 public void setPartId(String partId) throws DBException {
1030 setField(PART_ID, partId);
1031 }
1032
1033 public String getPartDescriptionRaw() throws DBException {
1034 Filter old = setFilterClass(new RawFilter());
1035 String raw = getPartDescription();
1036 setFilterClass(old);
1037
1038 return raw;
1039 }
1040
1041 /***
1042 * provide a transition for editing this part, suitable for creating an
1043 * HTTP link
1044 *
1045 * @param nodeId The parent node id.
1046 * @param params request parameters; assumes NODE_ID is within this map
1047 * @return transtion for viewing, including label for name of object; never null
1048 * @throws DBException upon DBObject-related error.
1049 */
1050 public Transition getEditTrans(String nodeId, Map params) throws DBException {
1051 Transition trans = null;
1052
1053 if (isHaveCustomHandler() && params != null) {
1054
1055
1056 String oldNodeId = (String) params.get(Node.NODE_ID);
1057 params.put(Node.NODE_ID, nodeId);
1058 trans = getCustomHandler().getEditTransition(params);
1059 if (trans != null) {
1060 trans.setName(NodeAction.PROMPT_EDIT_ATTRIB);
1061 trans.setLabel(getPartLabel());
1062 }
1063 params.put(Node.NODE_ID, oldNodeId);
1064 }
1065
1066
1067 if (!isHaveCustomHandler() || trans == null) {
1068 if (isOwnedAttribute()) {
1069 trans = new Transition(getPartLabel(), NodeAction.class, NodeAction.PROMPT_EDIT_ATTRIB);
1070 trans.addParam(Attribute.ATTRIBUTE_TYPE, getPartType());
1071 if (nodeId != null) {
1072 trans.addParam(Node.NODE_ID, nodeId);
1073 }
1074
1075 } else {
1076
1077 trans = new Transition(getPartLabel(), NodeAction.class, NodeAction.PROMPT_PICKLIST_NODE);
1078 trans.addParam(Node.NODE_TYPE, getPartType());
1079 trans.addParam(Relation.RELATION_TYPE, getNodeRelation());
1080 if (nodeId != null) {
1081 trans.addParam(Node.NODE_ID, nodeId);
1082 }
1083 }
1084 }
1085
1086 return trans;
1087 }
1088
1089 /***
1090 * provide a transition for viewing this object, suitable for creating an
1091 * HTTP link
1092 *
1093 * @return transtion for viewing, including label for name of part; never null
1094 * @throws DBException upon DBObject-related error.
1095 */
1096 public Transition getViewTrans() throws DBException {
1097 Transition trans = new Transition(getPartLabel(), PartAction.class, PartAction.PROMPT_EDIT_PART);
1098 trans.addParam(Part.PART_ID, getId());
1099 return trans;
1100 }
1101
1102 public void acceptVisitor(ModelVisitor visitor) {
1103 visitor.visitPart(this);
1104 }
1105
1106 /***
1107 * can be for attribute OR shared relation; simply describes cardinality
1108 *
1109 * @return true if this part allows multiple items
1110 * @throws DBException upon DBObject-related error.
1111 * @see #isSingleValued()
1112 */
1113 public boolean isMultipleAllowed() throws DBException {
1114 return !isSingleValued();
1115 }
1116
1117 /***
1118 * @return the relation type for this shared relation; null if owned attribute.
1119 * @throws DBException upon DBObject-related error.
1120 */
1121 public RelationType getNodeRelationEntity() throws DBException {
1122 if (isOwnedAttribute()) {
1123 return null;
1124 }
1125
1126 RelationType relType = new RelationType();
1127 relType.setRelType(getNodeRelation());
1128 relType.retrieve();
1129 return relType;
1130 }
1131
1132 public String getPicklistXMLName() throws DBException {
1133 return getParentType() + DELIMITER + getPartType();
1134 }
1135
1136 /***
1137 * @return part found; null if not found
1138 */
1139 public static Part getPartFromXMLName(String name) throws DBException {
1140 if (name == null || name.length() == 0) throw new DBException("Cannot find part for empty name.");
1141 String[] split = name.split(DELIMITER);
1142 if (split.length != 2) {
1143 return null;
1144 }
1145
1146 Part result = PartsFactory.getPart(split[0], split[1], null);
1147 if (result == null) {
1148 return null;
1149 }
1150
1151 return result;
1152 }
1153 }