KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > mmbase > bridge > implementation > BasicNode


1 /*
2
3 This software is OSI Certified Open Source Software.
4 OSI Certified is a certification mark of the Open Source Initiative.
5
6 The license (Mozilla version 1.0) can be read at the MMBase site.
7 See http://www.MMBase.org/license
8
9 */

10
11 package org.mmbase.bridge.implementation;
12
13 import java.util.*;
14
15 import org.mmbase.security.*;
16 import org.mmbase.bridge.*;
17 import org.mmbase.bridge.util.Queries;
18 import org.mmbase.bridge.util.BridgeCollections;
19 import org.mmbase.datatypes.DataType;
20 import org.mmbase.storage.search.*;
21 import org.mmbase.module.core.*;
22 import org.mmbase.module.corebuilders.*;
23 import org.mmbase.util.functions.*;
24 import org.mmbase.util.logging.*;
25 import org.mmbase.util.*;
26
27 import org.w3c.dom.Document JavaDoc;
28
29 /**
30  * Basic implementation of Node. Wraps MMObjectNodes, adds security.
31  *
32  * @author Rob Vermeulen
33  * @author Pierre van Rooden
34  * @author Michiel Meeuwissen
35  * @version $Id: BasicNode.java,v 1.210.2.4 2006/11/28 13:48:45 johannes Exp $
36  * @see org.mmbase.bridge.Node
37  * @see org.mmbase.module.core.MMObjectNode
38  */

39 public class BasicNode extends org.mmbase.bridge.util.AbstractNode implements Node, Comparable JavaDoc, SizeMeasurable {
40
41
42     private static final Logger log = Logging.getLoggerInstance(BasicNode.class);
43
44     /**
45      * Reference to the NodeManager
46      */

47     protected BasicNodeManager nodeManager;
48
49     /**
50      * Reference to the Cloud.
51      */

52     final protected BasicCloud cloud;
53
54
55     /**
56      * Reference to actual MMObjectNode object.
57      */

58     protected MMObjectNode noderef;
59
60     /**
61      * Temporary node ID.
62      * This is necessary since there is otherwise no sure (and quick) way to determine
63      * whether a node is in 'edit' mode (i.e. has a temporary node).
64      * Basically, a temporarynodeid is either -1 (invalid), or a negative number smaller than -1
65      * (a temporary number assigend by the system).
66      */

67     private int temporaryNodeId = -1;
68
69     /**
70      * The account this node is edited under.
71      * This is needed to check whether people have not switched users during an edit.
72      */

73     private String JavaDoc account = null;
74
75
76
77     BasicNode(BasicCloud cloud) {
78         this.cloud = cloud;
79     }
80     /**
81      * Instantiates a node, linking it to a specified node manager.
82      * Use this constructor if the node you create uses a NodeManager that is not readily available
83      * from the cloud (such as a temporary nodemanager for a result list).
84      * @param node the MMObjectNode to base the node on
85      * @param nodeManager the NodeManager to use for administrating this Node
86      * @throws IllegalArgumentException If node is null
87      */

88     BasicNode(MMObjectNode node, BasicNodeManager nodeManager) {
89         cloud = nodeManager.cloud;
90         this.nodeManager = nodeManager;
91         setNode(node);
92         init();
93     }
94
95     /**
96      * Instantiates a node, linking it to a specified cloud
97      * The NodeManager for the node is requested from the Cloud.
98      * @param node the MMObjectNode to base the node on
99      * @param cloud the cloud to which this node belongs
100      * @throws IllegalArgumentException If node is null
101      */

102     BasicNode(MMObjectNode node, BasicCloud cloud) {
103         this.cloud = cloud;
104         setNode(node);
105         setNodeManager(node);
106         init();
107     }
108
109     /**
110      * Instantiates a new node (for insert), using a specified nodeManager.
111      * @param node a temporary MMObjectNode that is the base for the node
112      * @param cloud the cloud to create the node in
113      * @param id the id of the node in the temporary cloud
114      */

115     BasicNode(MMObjectNode node, BasicCloud cloud, int id) {
116         this.cloud = cloud;
117         setNode(node);
118         setNodeManager(node);
119         temporaryNodeId = id;
120         init();
121         edit(ACTION_CREATE);
122     }
123
124     /**
125      * @since MMBase-1.8
126      */

127     protected void setNodeManager(MMObjectNode node) {
128         nodeManager = cloud.getBasicNodeManager(node.getBuilder());
129         assert(nodeManager != null);
130     }
131
132     /**
133      * Initializes state in case of a transaction.
134      */

135     protected void init() {
136         // check whether the node is currently in transaction
137
// and intialize temporaryNodeId if that is the case
138
if (temporaryNodeId == -1 && cloud.contains(getNode())) {
139             temporaryNodeId = getNode().getNumber();
140         }
141     }
142
143
144     public int getByteSize() {
145         return getByteSize(new SizeOf());
146     }
147
148     public int getByteSize(SizeOf sizeof) {
149         return sizeof.sizeof(getNode());
150     }
151
152     /**
153      * Obtains a reference to the underlying MMObjectNode.
154      * If the underlying node was deleted, this returns a virtual node with
155      * no info except the (original) node number.
156      * @return the underlying MMObjectNode
157      * @throws NotFoundException if no node was specified.
158      */

159     protected final MMObjectNode getNode() {
160         return noderef;
161     }
162
163     /**
164      * Invalidates the reference to the underlying MMObjectNode,
165      * replacing it with a virtual node that only inherits the number field.
166      * @since MMBase-1.6.4
167      */

168     protected void invalidateNode() {
169         org.mmbase.module.core.VirtualNode n = new org.mmbase.module.core.VirtualNode(noderef.getBuilder());
170         n.setValue("number", noderef.getNumber());
171         n.clearChanged();
172         noderef = n;
173     }
174
175     /**
176      * Sets the reference to the underlying MMObjectNode.
177      * @param n the node to set a reference to.
178      * @throws IllegalArgumentException is n is null
179      * @since MMBase-1.6.4
180      */

181     protected void setNode(MMObjectNode n) {
182         if (n == null) {
183             throw new IllegalArgumentException JavaDoc("Passed Node is null");
184         }
185         noderef = n;
186     }
187
188     public Cloud getCloud() {
189         return cloud;
190     }
191
192     public NodeManager getNodeManager() {
193         return nodeManager;
194     }
195
196     public int getNumber() {
197         int i = getNode().getNumber();
198         // new node, thus return temp id.
199
// note that temp id is equal to "number" if the node is edited
200
if (i == -1) {
201             i = temporaryNodeId;
202         }
203         return i;
204     }
205
206     /**
207      * Returns whether this is a new (not yet committed) node.
208      * @return is a new node
209      */

210     public boolean isNew() {
211         return getNode().isNew();
212     }
213
214     public boolean isChanged(String JavaDoc fieldName) {
215         return getNode().getChanged().contains(fieldName);
216     }
217
218     public boolean isChanged() {
219         return getNode().isChanged();
220     }
221     public Set getChanged() {
222         return Collections.unmodifiableSet(getNode().getChanged());
223     }
224
225     /**
226      * Edit this node.
227      * Check whether edits are allowed and prepare a node for edits if needed.
228      * The type of edit is determined by the action specified, and one of:<br />
229      * ACTION_CREATE (create a node),<br />
230      * ACTION_EDIT (edit node, or change aliasses),<br />
231      * ACTION_DELETE (delete node),<br />
232      * ACTION_COMMIT (commit a node after changes)
233      *
234      * @param action The action to perform.
235      */

236     // there is little common for the 4 actions (code is full of if/else code), I think it would
237
// perhaps be clearer to remove this method, and simply resolve it on the places where it is
238
// used.
239
protected void edit(int action) {
240         if (account == null) {
241             account = cloud.getAccount();
242         } else if (!account.equals(cloud.getAccount())) {
243             throw new BridgeException("User context changed. Cannot proceed to edit this node .");
244         }
245
246         int realnumber = getNode().getNumber();
247         if (realnumber != -1) {
248             if (action == ACTION_DELETE) {
249                 cloud.verify(Operation.DELETE, realnumber);
250             } else if ((action == ACTION_EDIT) && (temporaryNodeId == -1)) {
251                 cloud.verify(Operation.WRITE, realnumber);
252             }
253         }
254
255         // check for the existence of a temporary node
256
if (temporaryNodeId == -1) {
257             // when committing a temporary node id must exist (otherwise fail).
258
if (action == ACTION_COMMIT) {
259                 // throw new BasicBridgeException("This node cannot be comitted (not changed).");
260
}
261             // when adding a temporary node id must exist (otherwise fail).
262
// this should not occur (hence internal error notice), but we test it anyway.
263

264             if (action == ACTION_CREATE) {
265                 throw new BridgeException("This node cannot be added. It was not correctly instantiated (internal error).");
266             }
267
268             // when editing a temporary node id must exist (otherwise create one)
269
// This also applies if you remove a node in a transaction (as the transction manager requires a temporary node)
270
//
271
// XXX: If you edit a node outside a transaction, but do not commit or cancel the edits,
272
// the temporarynode will not be removed. This is left to be fixed (i.e.through a time out mechanism?)
273
if ((action == ACTION_EDIT) || ((action == ACTION_DELETE) && (getCloud() instanceof BasicTransaction))) {
274                 int id = getNumber();
275                 String JavaDoc currentObjectContext = BasicCloudContext.tmpObjectManager.getObject(account, "" + id, "" + id);
276                 // store new temporary node in transaction
277
cloud.add(currentObjectContext);
278                 setNode(BasicCloudContext.tmpObjectManager.getNode(account, "" + id));
279                 // check nodetype afterwards?
280
temporaryNodeId = id;
281             }
282         }
283     }
284
285     /**
286      * Protected method to be able to set rnumber when creating a relation.
287      * @param fieldName name of field
288      * @param value new value of field
289      * @since MMBase-1.7
290      */

291     protected void setValueWithoutChecks(String JavaDoc fieldName, Object JavaDoc value) {
292         String JavaDoc result = BasicCloudContext.tmpObjectManager.setObjectField(account, "" + temporaryNodeId, fieldName, value);
293         if (TemporaryNodeManager.UNKNOWN == result) {
294             throw new BridgeException("Can't change unknown field '" + fieldName + "', of node " + getNumber() + " of nodeManager '" + getNodeManager().getName() +"'");
295         } else if (TemporaryNodeManager.INVALID_VALUE == result) {
296             noderef.storeValue(fieldName, value); // commit() will throw that invalid.
297
}
298     }
299     protected Integer JavaDoc toNodeNumber(Object JavaDoc v) {
300         if (v == null) {
301             return null;
302         } else if (v instanceof Node) {
303             return new Integer JavaDoc(((Node)v).getNumber());
304         } else if (v instanceof MMObjectNode) {
305             return new Integer JavaDoc(((MMObjectNode)v).getNumber());
306         } else {
307             // giving up
308
return new Integer JavaDoc(cloud.getNode(v.toString()).getNumber());
309         }
310     }
311
312     protected void setSize(String JavaDoc fieldName, long size) {
313         getNode().setSize(fieldName, size);
314     }
315
316     public boolean isNull(String JavaDoc fieldName) {
317         return noderef.isNull(fieldName);
318     }
319
320     public long getSize(String JavaDoc fieldName) {
321         return noderef.getSize(fieldName);
322     }
323
324     /**
325      * Like getObjectValue, but skips any processing that MMBase would normally perform on a field.
326      * You can use this to get data from a field for validation purposes.
327      * @param fieldName name of field
328      * @since MMBase-1.8
329      */

330     public Object JavaDoc getValueWithoutProcess(String JavaDoc fieldName) {
331         // an exception is made for 'owner' field in setValueWithoutProcess, so for symmetry, we
332
// must make the same exception here (and also in (getStringValue).
333
if ("owner".equals(fieldName)) {
334             return getContext();
335         }
336         Object JavaDoc result = getNode().getValue(fieldName);
337         if (result instanceof MMObjectNode) {
338             MMObjectNode mmnode = (MMObjectNode) result;
339             result = cloud.makeNode(mmnode, "" + mmnode.getNumber());
340         }
341         return result;
342     }
343     //TODO, silly get-methods could be removed (because in AbstractNode), (calling
344
//getValueWithoutProcess) but they depend on noderef now, so I don't dare to do that right ahead.
345

346     public boolean getBooleanValue(String JavaDoc fieldName) {
347         Boolean JavaDoc result = Boolean.valueOf(noderef.getBooleanValue(fieldName));
348         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
349
Field field = nodeManager.getField(fieldName);
350             result = (Boolean JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_BOOLEAN).process(this, field, result);
351         }
352         return result.booleanValue();
353     }
354
355     public Date getDateValue(String JavaDoc fieldName) {
356         Date result = noderef.getDateValue(fieldName);
357         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
358
Field field = nodeManager.getField(fieldName);
359             result = (Date) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_DATETIME).process(this, field, result);
360         }
361         return result;
362     }
363
364     public List getListValue(String JavaDoc fieldName) {
365         List result = noderef.getListValue(fieldName);
366         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
367
Field field = nodeManager.getField(fieldName);
368             result = (List) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_LIST).process(this, field, result);
369         }
370
371         return result;
372     }
373
374
375     public Node getNodeValue(String JavaDoc fieldName) {
376         if (fieldName == null || fieldName.equals("number")) {
377             return this;
378         }
379         Node result = null;
380         MMObjectNode mmobjectNode = getNode().getNodeValue(fieldName);
381         if (mmobjectNode != null) {
382             MMObjectBuilder builder = mmobjectNode.getBuilder();
383             if (builder instanceof TypeDef) {
384                 result = new BasicNodeManager(mmobjectNode, cloud);
385             } else if (builder instanceof RelDef || builder instanceof TypeRel) {
386                 result = new BasicRelationManager(mmobjectNode, cloud);
387             } else if (builder instanceof InsRel) {
388                 result = new BasicRelation(mmobjectNode, cloud); //.getNodeManager(noderes.getBuilder().getTableName()));
389
} else if (builder instanceof VirtualBuilder) {
390                 result = new VirtualNode((org.mmbase.module.core.VirtualNode)mmobjectNode, cloud);
391             } else {
392                 result = new BasicNode(mmobjectNode, cloud); //.getNodeManager(noderes.getBuilder().getTableName()));
393
}
394         }
395         if (nodeManager.hasField(fieldName)) { // only if this is actually a field of this node-manager, otherewise it might be e.g. a request for an 'element' of a cluster node
396
Field field = nodeManager.getField(fieldName);
397             result = (Node) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_NODE).process(this, field, result);
398         }
399
400         return result;
401     }
402
403     public int getIntValue(String JavaDoc fieldName) {
404         Integer JavaDoc result = new Integer JavaDoc(getNode().getIntValue(fieldName));
405         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
406
Field field = nodeManager.getField(fieldName);
407             result = (Integer JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_INTEGER).process(this, field, result);
408         }
409         return result.intValue();
410
411     }
412
413     public float getFloatValue(String JavaDoc fieldName) {
414         Float JavaDoc result = new Float JavaDoc(getNode().getFloatValue(fieldName));
415         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
416
Field field = nodeManager.getField(fieldName);
417             result = (Float JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_FLOAT).process(this, field, result);
418         }
419         return result.floatValue();
420     }
421
422     public long getLongValue(String JavaDoc fieldName) {
423         Long JavaDoc result = new Long JavaDoc(getNode().getLongValue(fieldName));
424         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
425
Field field = nodeManager.getField(fieldName);
426             result = (Long JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_LONG).process(this, field, result);
427         }
428         return result.longValue();
429     }
430
431     public double getDoubleValue(String JavaDoc fieldName) {
432         Double JavaDoc result = new Double JavaDoc(getNode().getDoubleValue(fieldName));
433         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
434
Field field = nodeManager.getField(fieldName);
435             result = (Double JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_DOUBLE).process(this, field, result);
436         }
437         return result.doubleValue();
438     }
439
440     public byte[] getByteValue(String JavaDoc fieldName) {
441         byte[] result = getNode().getByteValue(fieldName);
442         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
443
Field field = nodeManager.getField(fieldName);
444             result = (byte[]) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_BINARY).process(this, field, result);
445         }
446         return result;
447     }
448     public java.io.InputStream JavaDoc getInputStreamValue(String JavaDoc fieldName) {
449         java.io.InputStream JavaDoc result = getNode().getInputStreamValue(fieldName);
450         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
451
Field field = nodeManager.getField(fieldName);
452             result = (java.io.InputStream JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_BINARY).process(this, field, result);
453         }
454         return result;
455     }
456
457     public String JavaDoc getStringValue(String JavaDoc fieldName) {
458         if ("owner".equals(fieldName)) {
459             return getContext();
460         }
461         String JavaDoc result = getNode().getStringValue(fieldName);
462         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
463
Field field = nodeManager.getField(fieldName);
464             result = (String JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_STRING).process(this, field, result);
465         }
466         return result;
467     }
468
469     public Document JavaDoc getXMLValue(String JavaDoc fieldName) {
470         Document JavaDoc result = getNode().getXMLValue(fieldName);
471         if (nodeManager.hasField(fieldName)) { // gui(..) stuff could not work.
472
Field field = nodeManager.getField(fieldName);
473             result = (Document JavaDoc) field.getDataType().getProcessor(DataType.PROCESS_GET, Field.TYPE_XML).process(this, field, result);
474         }
475         return result;
476     }
477
478
479
480     public void commit() {
481         if (isNew()) {
482             cloud.verify(Operation.CREATE, BasicCloudContext.mmb.getTypeDef().getIntValue(getNodeManager().getName()));
483         }
484         edit(ACTION_COMMIT);
485
486         Collection errors = validate();
487         if (errors.size() > 0) {
488             String JavaDoc mes = "node " + getNumber() + noderef.getChanged() + ", builder '" + nodeManager.getName() + "' " + errors.toString();
489             noderef.cancel();
490             throw new IllegalArgumentException JavaDoc(mes);
491         }
492         processCommit();
493         if (log.isDebugEnabled()) {
494             log.debug("committing " + noderef.getChanged());
495         }
496         // ignore commit in transaction (transaction commits)
497
if (!(cloud instanceof Transaction)) { // sigh sigh sigh.
498
MMObjectNode node = getNode();
499             if (isNew()) {
500                 node.insert(cloud.getUser());
501                 // cloud.createSecurityInfo(getNumber());
502
} else {
503                 node.commit(cloud.getUser());
504                 //cloud.updateSecurityInfo(getNumber());
505
}
506             // remove the temporary node
507
BasicCloudContext.tmpObjectManager.deleteTmpNode(account, "" + temporaryNodeId);
508             temporaryNodeId = -1;
509         }
510     }
511
512     public void cancel() {
513         edit(ACTION_COMMIT);
514         // when in a transaction, let the transaction cancel
515
if (cloud instanceof Transaction) {
516             ((Transaction)cloud).cancel();
517         } else {
518             // remove the temporary node
519
BasicCloudContext.tmpObjectManager.deleteTmpNode(account, "" + temporaryNodeId);
520             if (isNew()) {
521                 invalidateNode();
522             } else {
523                 noderef.cancel();
524             }
525             temporaryNodeId = -1;
526         }
527     }
528
529
530     public void delete(boolean deleteRelations) {
531         edit(ACTION_DELETE);
532         if (isNew()) {
533             // remove from the Transaction
534
// note that the node is immediately destroyed !
535
// possibly older edits will fail if they refernce this node
536
cloud.remove("" + temporaryNodeId);
537
538             // remove a temporary node (no true instantion yet, no relations)
539
BasicCloudContext.tmpObjectManager.deleteTmpNode(account, "" + temporaryNodeId);
540         } else {
541             // remove a node that is edited, i.e. that already exists
542
// check relations first!
543
if (deleteRelations) {
544                 // option set, remove relations
545
deleteRelations(-1);
546             } else {
547                 // option unset, fail if any relations exit
548
if (getNode().hasRelations()) {
549                     throw new BridgeException("This node (" + getNode().getNumber() + ") cannot be deleted. It still has relations attached to it.");
550                 }
551             }
552             // remove aliases
553
deleteAliases(null);
554             // in transaction:
555
if (cloud instanceof BasicTransaction) {
556                 // let the transaction remove the node (as well as its temporary counterpart).
557
// note that the node still exists until the transaction completes
558
// a getNode() will still retrieve the node and make edits possible
559
// possibly 'older' edits will fail if they reference this node
560
((BasicTransaction)cloud).delete("" + temporaryNodeId);
561             } else {
562                 // remove the node
563
if (temporaryNodeId != -1) {
564                     BasicCloudContext.tmpObjectManager.deleteTmpNode(account, "" + temporaryNodeId);
565                 }
566                 MMObjectNode node = getNode();
567                 //node.getBuilder().removeNode(node);
568
node.remove(cloud.getUser());
569                 //cloud.removeSecurityInfo(number);
570
}
571         }
572         // the node does not exist anymore, so invalidate all references.
573
temporaryNodeId = -1;
574         invalidateNode();
575     }
576
577     public String JavaDoc toString() {
578         //return getNode().toString() + "(" + getNode().getClass().getName() + ")";
579
return getNode().toString();
580         //return "" + super.toString() + " " + getNode().getNumber();
581
}
582
583     /**
584      * Removes all relations of a certain type.
585      *
586      * @param type the type of relation (-1 = don't care)
587      */

588     private void deleteRelations(int type) {
589         List relations = null;
590         try {
591             if (type == -1) {
592                 relations = BasicCloudContext.mmb.getInsRel().getRelationNodes(getNode().getNumber(), false);
593             } else {
594                 relations = BasicCloudContext.mmb.getInsRel().getRelationNodes(getNode().getNumber());
595             }
596         } catch (SearchQueryException sqe) {
597             log.error(sqe.getMessage()); // should not happen
598
}
599         if (relations != null) {
600             // check first
601
for (Iterator i = relations.iterator(); i.hasNext();) {
602                 MMObjectNode node = (MMObjectNode)i.next();
603                 cloud.verify(Operation.DELETE, node.getNumber());
604             }
605             // then delete
606
for (Iterator i = relations.iterator(); i.hasNext();) {
607                 MMObjectNode node = (MMObjectNode)i.next();
608                 if ((type == -1) || (node.getIntValue("rnumber") == type)) {
609                     if (cloud instanceof Transaction) {
610                         String JavaDoc oMmbaseId = "" + node.getValue("number");
611                         String JavaDoc currentObjectContext = BasicCloudContext.tmpObjectManager.getObject(account, "" + oMmbaseId, oMmbaseId);
612                         ((BasicTransaction)cloud).add(currentObjectContext);
613                         ((BasicTransaction)cloud).delete(currentObjectContext);
614                     } else {
615                         node.remove(cloud.getUser());
616                     }
617                 }
618             }
619         }
620     }
621
622     public void deleteRelations(String JavaDoc type) throws NotFoundException {
623         if ("object".equals(type)) {
624             deleteRelations(-1);
625         }
626         else {
627             RelDef reldef = BasicCloudContext.mmb.getRelDef();
628             int rType = reldef.getNumberByName(type);
629             if (rType == -1) {
630                 throw new NotFoundException("Relation with role : " + type + " does not exist.");
631             } else {
632                 deleteRelations(rType);
633             }
634         }
635     }
636
637
638     public RelationList getRelations(String JavaDoc role, String JavaDoc otherNodeManager) throws NotFoundException {
639         if (isNew()) {
640             // new nodes have no relations
641
return BridgeCollections.EMPTY_RELATIONLIST;
642         }
643         
644         if ("".equals(otherNodeManager)) otherNodeManager = null;
645         NodeManager otherManager = otherNodeManager == null ? cloud.getNodeManager("object") : cloud.getNodeManager(otherNodeManager);
646
647         TypeRel typeRel = BasicCloudContext.mmb.getTypeRel();
648         RelationList r1 = BridgeCollections.EMPTY_RELATIONLIST;
649         RelationList r2 = BridgeCollections.EMPTY_RELATIONLIST;
650         if (role == null) {
651             int allowedOtherNumber = "object".equals(otherManager.getName()) ? 0 : otherManager.getNumber();
652             if (!typeRel.getAllowedRelations(nodeManager.getNumber(), allowedOtherNumber, 0,
653                     RelationStep.DIRECTIONS_DESTINATION).isEmpty())
654                 r1 = getRelations(role, otherManager, "destination");
655             if (!typeRel.getAllowedRelations(nodeManager.getNumber(), allowedOtherNumber, 0,
656                     RelationStep.DIRECTIONS_SOURCE).isEmpty())
657                 r2 = getRelations(role, otherManager, "source");
658         }
659         else {
660             RelDef relDef = BasicCloudContext.mmb.getRelDef();
661             int rnumber = relDef.getNumberByName(role);
662             if (typeRel.contains(nodeManager.getNumber(), otherManager.getNumber(), rnumber, TypeRel.INCLUDE_PARENTS_AND_DESCENDANTS))
663                 r1 = getRelations(role, otherManager, "destination");
664             if (typeRel.contains(otherManager.getNumber(), nodeManager.getNumber(), rnumber, TypeRel.INCLUDE_PARENTS_AND_DESCENDANTS))
665                 r2 = getRelations(role, otherManager, "source");
666         }
667
668
669         if (r2.size() == 0) {
670             return r1;
671         } else if (r1.size() == 0) {
672             return r2;
673         } else {
674             // perhaps it would be better for performance to have some 'ChainedRelationList' implementation.
675
RelationList result = cloud.getCloudContext().createRelationList();
676             result.addAll(r1);
677             result.addAll(r2);
678             return result;
679         }
680     }
681
682     /**
683      * Returns a list of relations of the given node.
684      * @param role role of the relation
685      * @param nodeManager node manager on the other side of the relation
686      * @param searchDir direction of the relation
687      * @return list of relations
688      * @throws NotFoundException
689      *
690      * @see Queries#createRelationNodesQuery Should perhaps be implemented with that
691      */

692     public RelationList getRelations(String JavaDoc role, NodeManager nodeManager, String JavaDoc searchDir) throws NotFoundException {
693         if (isNew()) {
694             // new nodes have no relations
695
return org.mmbase.bridge.util.BridgeCollections.EMPTY_RELATIONLIST;
696         }
697         if (searchDir == null || "BOTH".equalsIgnoreCase(searchDir)) return getRelations(role, nodeManager);
698         if (nodeManager == null) nodeManager = cloud.getNodeManager("object");
699         NodeQuery query = Queries.createRelationNodesQuery(this, nodeManager, role, searchDir);
700         NodeManager nm = query.getNodeManager();
701         assert query.getNodeStep() instanceof RelationStep;
702         // assert nm instanceof RelationManager; cannot assert his, because if the role is null, no relation manager can be created (the nodemanager will be insrel).
703
return (RelationList) nm.getList(query);
704     }
705
706     public boolean hasRelations() {
707         return getNode().hasRelations();
708     }
709
710
711     public int countRelatedNodes(NodeManager otherNodeManager, String JavaDoc role, String JavaDoc direction) {
712         if (isNew()) return 0;
713         if (otherNodeManager == null || otherNodeManager.getName().equals("object")) {
714             // can be done on only insrel, which is often much quicker.
715
NodeManager insrel;
716             if (role != null) {
717                 insrel = cloud.getRelationManager(role);
718             } else {
719                 insrel = cloud.getNodeManager("insrel");
720             }
721             NodeQuery query = insrel.createQuery();
722
723             if (insrel instanceof BasicRelationManager) {
724                 MMObjectNode relDefNode = ((BasicRelationManager) insrel).relDefNode;
725                 if (relDefNode != null) {
726                     StepField rnumber = query.getStepField(insrel.getField("rnumber"));
727                     query.setConstraint(query.createConstraint(rnumber, new Integer JavaDoc(relDefNode.getNumber())));
728                 }
729             }
730
731             int dir = RelationStep.DIRECTIONS_BOTH;
732             if (direction != null) {
733                 dir = ClusterBuilder.getSearchDir(direction);
734             }
735
736             StepField snumber = query.getStepField(insrel.getField("snumber"));
737             StepField dnumber = query.getStepField(insrel.getField("dnumber"));
738
739             Integer JavaDoc number = new Integer JavaDoc(getNumber());
740
741             switch(dir) {
742             case RelationStep.DIRECTIONS_DESTINATION: {
743                 Queries.addConstraint(query, query.createConstraint(snumber, number));
744                 break;
745             }
746             case RelationStep.DIRECTIONS_SOURCE: {
747                 Queries.addConstraint(query, query.createConstraint(dnumber, number));
748                 break;
749             }
750             case RelationStep.DIRECTIONS_BOTH:
751             case RelationStep.DIRECTIONS_EITHER: {
752                 Constraint sourceConstraint = query.createConstraint(snumber, number);
753                 Constraint destinationConstraint = query.createConstraint(dnumber, number);
754                 Queries.addConstraint(query, query.createConstraint(sourceConstraint, CompositeConstraint.LOGICAL_OR, destinationConstraint));
755                 break;
756             }
757             default:
758                 log.debug("Unknown relation direction" + dir);
759                 break;
760             }
761             return Queries.count(query);
762         } else {
763             BasicQuery count = (BasicQuery) cloud.createAggregatedQuery();
764             count.addStep(nodeManager);
765             Step step = count.addRelationStep(otherNodeManager, role, direction, false).getPrevious();
766             count.addNode(step, this);
767             count.addAggregatedField(step, nodeManager.getField("number"), AggregatedField.AGGREGATION_TYPE_COUNT);
768             Node result = (Node) cloud.getList(count).get(0);
769             return result.getIntValue("number");
770         }
771     }
772
773     /**
774      * @since MMBase-1.8.2
775      */

776     protected NodeList getRelatedNodes(NodeManager otherManager, String JavaDoc role) {
777
778         NodeList l1 = BridgeCollections.EMPTY_NODELIST;
779         NodeList l2 = BridgeCollections.EMPTY_NODELIST;
780        
781         TypeRel typeRel = BasicCloudContext.mmb.getTypeRel();
782         if (role == null) {
783             int allowedOtherNumber = otherManager == null || "object".equals(otherManager.getName()) ? 0 : otherManager.getNumber();
784             if (!typeRel.getAllowedRelations(nodeManager.getNumber(), allowedOtherNumber, 0,
785                     RelationStep.DIRECTIONS_DESTINATION).isEmpty())
786                 l1 = getRelatedNodes(otherManager, role, "destination");
787             if (!typeRel.getAllowedRelations(nodeManager.getNumber(), allowedOtherNumber, 0,
788                     RelationStep.DIRECTIONS_SOURCE).isEmpty())
789                 l2 = getRelatedNodes(otherManager, role, "source");
790         }
791         else {
792             RelDef relDef = BasicCloudContext.mmb.getRelDef();
793             int rnumber = relDef.getNumberByName(role);
794             if (typeRel.contains(nodeManager.getNumber(), otherManager.getNumber(), rnumber, TypeRel.INCLUDE_PARENTS_AND_DESCENDANTS))
795                 l1 = getRelatedNodes(otherManager, role, "destination");
796             if (typeRel.contains(otherManager.getNumber(), nodeManager.getNumber(), rnumber, TypeRel.INCLUDE_PARENTS_AND_DESCENDANTS))
797                 l2 = getRelatedNodes(otherManager, role, "source");
798         }
799         if (l2.size() == 0) {
800             return l1;
801         } else if (l1.size() == 0) {
802             return l2;
803         } else {
804             // perhaps it would be better for performance to have some 'ChainedRelationList' implementation.
805
NodeList result = cloud.getCloudContext().createNodeList();
806             result.addAll(l1);
807             result.addAll(l2);
808             return result;
809         }
810     }
811     /**
812      * @param otherManager node manager on the other side of the relation
813      * @param role role of the relation
814      * @param searchDir direction of the relation
815      * @return List of related nodes
816      * @see Queries#createRelatedNodesQuery Should perhaps be implemented with that.
817      * @since MMBase-1.6
818      */

819     public NodeList getRelatedNodes(NodeManager otherManager, String JavaDoc role, String JavaDoc searchDir) {
820         if (log.isDebugEnabled()) {
821             log.debug("type(" + otherManager.getName() + "), role(" + role + "), dir(" + searchDir + ")");
822         }
823         if (isNew()) {
824             // new nodes have no relations
825
return org.mmbase.bridge.util.BridgeCollections.EMPTY_NODELIST;
826         }
827         if (searchDir == null) searchDir = "BOTH";
828         if ("BOTH".equalsIgnoreCase(searchDir)) {
829             return getRelatedNodes(otherManager, role);
830         }
831         NodeQuery query = Queries.createRelatedNodesQuery(this, otherManager, role, searchDir);
832         return query.getNodeManager().getList(query);
833     }
834
835     public int countRelatedNodes(String JavaDoc type) {
836         if (isNew()) return 0;
837         return getNode().getRelationCount(type);
838     }
839
840     public StringList getAliases() {
841         NodeManager oalias = cloud.getNodeManager("oalias");
842         NodeQuery q = oalias.createQuery();
843         Constraint c = q.createConstraint(q.getStepField(oalias.getField("destination")), new Integer JavaDoc(getNumber()));
844         q.setConstraint(c);
845         NodeList aliases = oalias.getList(q);
846         StringList result = new BasicStringList();
847         NodeIterator i = aliases.nodeIterator();
848         while (i.hasNext()) {
849             Node alias = i.nextNode();
850             result.add(alias.getStringValue("name"));
851         }
852
853         // There might be aliases in temporary nodes
854
// This is quite a dirty (and probably also slow) hack
855
// for bug #6185.
856
// Usually the temporaryNodes hashtable shall not be
857
// too full.
858
if (cloud instanceof Transaction) {
859             Map tnodes = MMObjectBuilder.temporaryNodes;
860             for (Iterator e = tnodes.values().iterator(); e.hasNext();) {
861                 MMObjectNode mynode = (MMObjectNode)e.next();
862                 if (mynode.getName().equals("oalias")){
863                     String JavaDoc dest = mynode.getStringValue("_destination");
864                     if ((account + "_" + temporaryNodeId).equals(dest)) {
865                         result.add(mynode.getStringValue("name"));
866                     }
867                 }
868             }
869         }
870
871         return result;
872     }
873
874     public void createAlias(String JavaDoc aliasName) {
875         edit(ACTION_EDIT);
876         if (cloud instanceof Transaction) {
877             String JavaDoc aliasContext = BasicCloudContext.tmpObjectManager.createTmpAlias(aliasName, account, "a" + temporaryNodeId, "" + temporaryNodeId);
878             ((BasicTransaction)cloud).add(aliasContext); // sigh
879
} else if (isNew()) {
880             throw new BridgeException("Cannot add alias to a new node that has not been committed.");
881         } else {
882             String JavaDoc owner = cloud.getUser().getOwnerField();
883             if (!getNode().getBuilder().createAlias(getNumber(), aliasName, owner)) {
884                 Node otherNode = cloud.getNode(aliasName);
885                 if (otherNode != null) {
886                     throw new BridgeException("Alias " + aliasName + " could not be created. It is an alias for " + otherNode.getNodeManager().getName() + " node " + otherNode.getNumber() + " already");
887                 } else {
888                     throw new BridgeException("Alias " + aliasName + " could not be created.");
889                 }
890             }
891         }
892     }
893
894     /**
895      * Delete one or all aliases of this node
896      * @param aliasName the name of the alias (null means all aliases)
897      */

898     private void deleteAliases(String JavaDoc aliasName) {
899         // A new node cannot have any aliases, except when in a transaction.
900
// However, there is no point in adding aliasses to a ndoe you plan to delete,
901
// so no attempt has been made to rectify this (cause its not worth all the trouble).
902
// If people remove a node for which they created aliases in the same transaction, that transaction will fail.
903
// Live with it.
904
if (!isNew()) {
905             NodeManager oalias = cloud.getNodeManager("oalias");
906             NodeQuery q = oalias.createQuery();
907             Constraint c = q.createConstraint(q.getStepField(oalias.getField("destination")), new Integer JavaDoc(getNumber()));
908             if (aliasName != null) {
909                 Constraint c2 = q.createConstraint(q.getStepField(oalias.getField("name")), aliasName);
910                 c = q.createConstraint (c,CompositeConstraint.LOGICAL_AND,c2);
911             }
912             q.setConstraint(c);
913             NodeList aliases = oalias.getList(q);
914             NodeIterator i = aliases.nodeIterator();
915             while (i.hasNext()) {
916                 Node alias = i.nextNode();
917                 alias.delete();
918             }
919         }
920     }
921
922     public void deleteAlias(String JavaDoc aliasName) {
923         edit(ACTION_EDIT);
924         deleteAliases(aliasName);
925     }
926
927
928     // javadoc inherited (from Node)
929
public void setContext(String JavaDoc context) {
930         // set the context on the node (run after insert).
931
getNode().setContext(cloud.getUser(), context, temporaryNodeId == -1);
932     }
933
934     // javadoc inherited (from Node)
935
public String JavaDoc getContext() {
936         return getNode().getContext(cloud.getUser());
937     }
938
939
940     // javadoc inherited (from Node)
941
public StringList getPossibleContexts() {
942         return new BasicStringList(getNode().getPossibleContexts(cloud.getUser()));
943     }
944
945     public boolean mayWrite() {
946         return isNew() || cloud.check(Operation.WRITE, getNode().getNumber());
947     }
948
949     public boolean mayDelete() {
950         return isNew() || cloud.check(Operation.DELETE, getNode().getNumber());
951     }
952
953     public boolean mayChangeContext() {
954         return isNew() || cloud.check(Operation.CHANGE_CONTEXT, getNode().getNumber());
955     }
956
957     /**
958      * Reverse the buffers, when changed and not stored...
959      */

960     protected void finalize() {
961         // When not commit-ed or cancelled, and the buffer has changed, the changes must be reversed.
962
// when not done it results in node-lists with changes which are not performed on the database...
963
// This is all due to the fact that Node doesnt make a copy of MMObjectNode, while editing...
964
// my opinion is that this should happen, as soon as edit-ting starts,..........
965
// when still has modifications.....
966
if (isChanged()) {
967             if (!(cloud instanceof Transaction)) {
968                 // cancel the modifications...
969
cancel();
970             }
971         }
972     }
973
974
975     /**
976      * @see java.lang.Object#hashCode()
977      *
978      * @since MMBase-1.6.2
979      */

980     public int hashCode() {
981         return getNode().hashCode();
982     }
983
984     public Collection getFunctions() {
985         return getNode().getFunctions();
986     }
987
988     protected Function getNodeFunction(String JavaDoc functionName) {
989         return getNode().getFunction(functionName);
990     }
991
992
993     public Parameters createParameters(String JavaDoc functionName) {
994         return getNode().getFunction(functionName).createParameters();
995     }
996     protected FieldValue createFunctionValue(Object JavaDoc result) {
997         return new BasicFunctionValue(getCloud(), result);
998     }
999
1000}
1001
Popular Tags