KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > alfresco > repo > node > db > DbNodeServiceImpl


1 /*
2  * Copyright (C) 2005 Alfresco, Inc.
3  *
4  * Licensed under the Mozilla Public License version 1.1
5  * with a permitted attribution clause. You may obtain a
6  * copy of the License at
7  *
8  * http://www.alfresco.org/legal/license.txt
9  *
10  * Unless required by applicable law or agreed to in writing,
11  * software distributed under the License is distributed on an
12  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13  * either express or implied. See the License for the specific
14  * language governing permissions and limitations under the
15  * License.
16  */

17 package org.alfresco.repo.node.db;
18
19 import java.io.Serializable JavaDoc;
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.HashMap JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.List JavaDoc;
26 import java.util.Map JavaDoc;
27 import java.util.Set JavaDoc;
28 import java.util.Stack JavaDoc;
29
30 import org.alfresco.model.ContentModel;
31 import org.alfresco.repo.domain.ChildAssoc;
32 import org.alfresco.repo.domain.Node;
33 import org.alfresco.repo.domain.NodeAssoc;
34 import org.alfresco.repo.domain.NodeKey;
35 import org.alfresco.repo.domain.NodeStatus;
36 import org.alfresco.repo.domain.PropertyValue;
37 import org.alfresco.repo.domain.Store;
38 import org.alfresco.repo.node.AbstractNodeServiceImpl;
39 import org.alfresco.repo.policy.PolicyComponent;
40 import org.alfresco.repo.transaction.AlfrescoTransactionSupport;
41 import org.alfresco.service.cmr.dictionary.AspectDefinition;
42 import org.alfresco.service.cmr.dictionary.ClassDefinition;
43 import org.alfresco.service.cmr.dictionary.DataTypeDefinition;
44 import org.alfresco.service.cmr.dictionary.DictionaryService;
45 import org.alfresco.service.cmr.dictionary.InvalidAspectException;
46 import org.alfresco.service.cmr.dictionary.InvalidTypeException;
47 import org.alfresco.service.cmr.dictionary.PropertyDefinition;
48 import org.alfresco.service.cmr.dictionary.TypeDefinition;
49 import org.alfresco.service.cmr.repository.AssociationExistsException;
50 import org.alfresco.service.cmr.repository.AssociationRef;
51 import org.alfresco.service.cmr.repository.ChildAssociationRef;
52 import org.alfresco.service.cmr.repository.CyclicChildRelationshipException;
53 import org.alfresco.service.cmr.repository.InvalidChildAssociationRefException;
54 import org.alfresco.service.cmr.repository.InvalidNodeRefException;
55 import org.alfresco.service.cmr.repository.InvalidStoreRefException;
56 import org.alfresco.service.cmr.repository.NodeRef;
57 import org.alfresco.service.cmr.repository.Path;
58 import org.alfresco.service.cmr.repository.StoreExistsException;
59 import org.alfresco.service.cmr.repository.StoreRef;
60 import org.alfresco.service.cmr.repository.NodeRef.Status;
61 import org.alfresco.service.namespace.QName;
62 import org.alfresco.service.namespace.QNamePattern;
63 import org.springframework.util.Assert;
64
65 /**
66  * Node service using database persistence layer to fulfill functionality
67  *
68  * @author Derek Hulley
69  */

70 public class DbNodeServiceImpl extends AbstractNodeServiceImpl
71 {
72     private final DictionaryService dictionaryService;
73     private final NodeDaoService nodeDaoService;
74     
75     public DbNodeServiceImpl(
76             PolicyComponent policyComponent,
77             DictionaryService dictionaryService,
78             NodeDaoService nodeDaoService)
79     {
80         super(policyComponent);
81         
82         this.dictionaryService = dictionaryService;
83         this.nodeDaoService = nodeDaoService;
84     }
85
86     /**
87      * Performs a null-safe get of the node
88      *
89      * @param nodeRef the node to retrieve
90      * @return Returns the node entity (never null)
91      * @throws InvalidNodeRefException if the referenced node could not be found
92      */

93     private Node getNodeNotNull(NodeRef nodeRef) throws InvalidNodeRefException
94     {
95         String JavaDoc protocol = nodeRef.getStoreRef().getProtocol();
96         String JavaDoc identifier = nodeRef.getStoreRef().getIdentifier();
97         Node unchecked = nodeDaoService.getNode(protocol, identifier, nodeRef.getId());
98         if (unchecked == null)
99         {
100             throw new InvalidNodeRefException("Node does not exist: " + nodeRef, nodeRef);
101         }
102         return unchecked;
103     }
104
105     public boolean exists(StoreRef storeRef)
106     {
107         Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
108         boolean exists = (store != null);
109         // done
110
return exists;
111     }
112     
113     public boolean exists(NodeRef nodeRef)
114     {
115         StoreRef storeRef = nodeRef.getStoreRef();
116         Node node = nodeDaoService.getNode(storeRef.getProtocol(),
117                 storeRef.getIdentifier(),
118                 nodeRef.getId());
119         boolean exists = (node != null);
120         // done
121
return exists;
122     }
123     
124     public Status getNodeStatus(NodeRef nodeRef)
125     {
126         NodeStatus nodeStatus = nodeDaoService.getNodeStatus(
127                 nodeRef.getStoreRef().getProtocol(),
128                 nodeRef.getStoreRef().getIdentifier(),
129                 nodeRef.getId());
130         if (nodeStatus == null) // node never existed
131
{
132             return null;
133         }
134         else
135         {
136             return new NodeRef.Status(
137                     nodeStatus.getChangeTxnId(),
138                     nodeStatus.isDeleted());
139         }
140     }
141
142     /**
143      * @see NodeDaoService#getStores()
144      */

145     public List JavaDoc<StoreRef> getStores()
146     {
147         List JavaDoc<Store> stores = nodeDaoService.getStores();
148         List JavaDoc<StoreRef> storeRefs = new ArrayList JavaDoc<StoreRef>(stores.size());
149         for (Store store : stores)
150         {
151             storeRefs.add(store.getStoreRef());
152         }
153         // done
154
return storeRefs;
155     }
156     
157     /**
158      * Defers to the typed service
159      * @see StoreDaoService#createWorkspace(String)
160      */

161     public StoreRef createStore(String JavaDoc protocol, String JavaDoc identifier)
162     {
163         StoreRef storeRef = new StoreRef(protocol, identifier);
164         // check that the store does not already exist
165
Store store = nodeDaoService.getStore(protocol, identifier);
166         if (store != null)
167         {
168             throw new StoreExistsException("Unable to create a store that already exists",
169                     new StoreRef(protocol, identifier));
170         }
171         
172         // invoke policies
173
invokeBeforeCreateStore(ContentModel.TYPE_STOREROOT, storeRef);
174         
175         // create a new one
176
store = nodeDaoService.createStore(protocol, identifier);
177         // get the root node
178
Node rootNode = store.getRootNode();
179         // assign the root aspect - this is expected of all roots, even store roots
180
addAspect(rootNode.getNodeRef(),
181                 ContentModel.ASPECT_ROOT,
182                 Collections.<QName, Serializable JavaDoc>emptyMap());
183         
184         // invoke policies
185
invokeOnCreateStore(rootNode.getNodeRef());
186         
187         // done
188
if (!store.getStoreRef().equals(storeRef))
189         {
190             throw new RuntimeException JavaDoc("Incorrect store reference");
191         }
192         return storeRef;
193     }
194
195     public NodeRef getRootNode(StoreRef storeRef) throws InvalidStoreRefException
196     {
197         Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
198         if (store == null)
199         {
200             throw new InvalidStoreRefException("Store does not exist", storeRef);
201         }
202         // get the root
203
Node node = store.getRootNode();
204         if (node == null)
205         {
206             throw new InvalidStoreRefException("Store does not have a root node", storeRef);
207         }
208         NodeRef nodeRef = node.getNodeRef();
209         // done
210
return nodeRef;
211     }
212
213     /**
214      * @see #createNode(NodeRef, QName, QName, QName, Map)
215      */

216     public ChildAssociationRef createNode(
217             NodeRef parentRef,
218             QName assocTypeQName,
219             QName assocQName,
220             QName nodeTypeQName)
221     {
222         return this.createNode(parentRef, assocTypeQName, assocQName, nodeTypeQName, null);
223     }
224
225     /**
226      * @see org.alfresco.service.cmr.repository.NodeService#createNode(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, org.alfresco.service.namespace.QName, java.util.Map)
227      */

228     public ChildAssociationRef createNode(
229             NodeRef parentRef,
230             QName assocTypeQName,
231             QName assocQName,
232             QName nodeTypeQName,
233             Map JavaDoc<QName, Serializable JavaDoc> properties)
234     {
235         Assert.notNull(parentRef);
236         Assert.notNull(assocTypeQName);
237         Assert.notNull(assocQName);
238         
239         // null property map is allowed
240
if (properties == null)
241         {
242             properties = new HashMap JavaDoc<QName, Serializable JavaDoc>();
243         }
244         else
245         {
246             // Copy the incomming property map since we may need to modify it later
247
properties = new HashMap JavaDoc<QName, Serializable JavaDoc>(properties);
248         }
249         
250         // Invoke policy behaviour
251
invokeBeforeUpdateNode(parentRef);
252         invokeBeforeCreateNode(parentRef, assocTypeQName, assocQName, nodeTypeQName);
253         
254         // get the store that the parent belongs to
255
StoreRef storeRef = parentRef.getStoreRef();
256         Store store = nodeDaoService.getStore(storeRef.getProtocol(), storeRef.getIdentifier());
257         if (store == null)
258         {
259             throw new RuntimeException JavaDoc("No store found for parent node: " + parentRef);
260         }
261         
262         // check the node type
263
TypeDefinition nodeTypeDef = dictionaryService.getType(nodeTypeQName);
264         if (nodeTypeDef == null)
265         {
266             throw new InvalidTypeException(nodeTypeQName);
267         }
268         
269         // get/generate an ID for the node
270
String JavaDoc newId = generateGuid(properties);
271         
272         // create the node instance
273
Node node = nodeDaoService.newNode(store, newId, nodeTypeQName);
274         
275         // get the parent node
276
Node parentNode = getNodeNotNull(parentRef);
277         
278         // create the association - invoke policy behaviour
279
ChildAssoc childAssoc = nodeDaoService.newChildAssoc(parentNode, node, true, assocTypeQName, assocQName);
280         ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
281         
282         // Set the default property values
283
addDefaultPropertyValues(nodeTypeDef, properties);
284         
285         // Add the default aspects to the node
286
addDefaultAspects(nodeTypeDef, node, childAssocRef.getChildRef(), properties);
287         
288         // set the properties - it is a new node so only set properties if there are any
289
if (properties.size() > 0)
290         {
291             this.setProperties(node.getNodeRef(), properties);
292         }
293
294         // Invoke policy behaviour
295
invokeOnCreateNode(childAssocRef);
296         invokeOnUpdateNode(parentRef);
297         
298         // done
299
return childAssocRef;
300     }
301     
302     /**
303      * Add the default aspects to a given node
304      *
305      * @param nodeTypeDef
306      */

307     private void addDefaultAspects(ClassDefinition classDefinition, Node node, NodeRef nodeRef, Map JavaDoc<QName, Serializable JavaDoc> properties)
308     {
309         // get the mandatory aspects for the node type
310
List JavaDoc<AspectDefinition> defaultAspectDefs = classDefinition.getDefaultAspects();
311         
312         // add all the aspects to the node
313
Set JavaDoc<QName> nodeAspects = node.getAspects();
314         for (AspectDefinition defaultAspectDef : defaultAspectDefs)
315         {
316             invokeBeforeAddAspect(nodeRef, defaultAspectDef.getName());
317             nodeAspects.add(defaultAspectDef.getName());
318             addDefaultPropertyValues(defaultAspectDef, properties);
319             invokeOnAddAspect(nodeRef, defaultAspectDef.getName());
320             
321             // Now add any default aspects for this aspect
322
addDefaultAspects(defaultAspectDef, node, nodeRef, properties);
323         }
324     }
325     
326     /**
327      * Sets the default property values
328      *
329      * @param classDefinition
330      * @param properties
331      */

332     private void addDefaultPropertyValues(ClassDefinition classDefinition, Map JavaDoc<QName, Serializable JavaDoc> properties)
333     {
334         for (Map.Entry JavaDoc<QName, Serializable JavaDoc> entry : classDefinition.getDefaultValues().entrySet())
335         {
336             if (properties.containsKey(entry.getKey()))
337             {
338                 // property is present
339
continue;
340             }
341             Serializable JavaDoc value = entry.getValue();
342             
343             // Check the type of the default property
344
PropertyDefinition prop = this.dictionaryService.getProperty(entry.getKey());
345             if (prop == null)
346             {
347                 // dictionary doesn't have a default value present
348
continue;
349             }
350
351             // TODO what other conversions are nessesary here for other types of default values ?
352

353             // ensure that we deliver the property in the correct form
354
if (DataTypeDefinition.BOOLEAN.equals(prop.getDataType().getName()) == true)
355             {
356                 if (value instanceof String JavaDoc)
357                 {
358                     if (((String JavaDoc)value).toUpperCase().equals("TRUE") == true)
359                     {
360                         value = Boolean.TRUE;
361                     }
362                     else if (((String JavaDoc)value).toUpperCase().equals("FALSE") == true)
363                     {
364                         value = Boolean.FALSE;
365                     }
366                 }
367             }
368             
369             // Set the default value of the property
370
properties.put(entry.getKey(), value);
371         }
372     }
373     
374     /**
375      * Drops the old primary association and creates a new one
376      */

377     public ChildAssociationRef moveNode(
378             NodeRef nodeToMoveRef,
379             NodeRef newParentRef,
380             QName assocTypeQName,
381             QName assocQName)
382             throws InvalidNodeRefException
383     {
384         Assert.notNull(nodeToMoveRef);
385         Assert.notNull(newParentRef);
386         Assert.notNull(assocTypeQName);
387         Assert.notNull(assocQName);
388         
389         // check the node references
390
Node nodeToMove = getNodeNotNull(nodeToMoveRef);
391         Node newParentNode = getNodeNotNull(newParentRef);
392         // get the primary parent assoc
393
ChildAssoc oldAssoc = nodeDaoService.getPrimaryParentAssoc(nodeToMove);
394         ChildAssociationRef oldAssocRef = oldAssoc.getChildAssocRef();
395         // get the old parent
396
Node oldParentNode = oldAssoc.getParent();
397         
398         // Invoke policy behaviour
399
invokeBeforeDeleteChildAssociation(oldAssocRef);
400         invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName);
401         invokeBeforeUpdateNode(oldParentNode.getNodeRef()); // old parent will be updated
402
invokeBeforeUpdateNode(newParentRef); // new parent ditto
403

404         // remove the child assoc from the old parent
405
// don't cascade as we will still need the node afterwards
406
nodeDaoService.deleteChildAssoc(oldAssoc, false);
407         // create a new assoc
408
ChildAssoc newAssoc = nodeDaoService.newChildAssoc(newParentNode, nodeToMove, true, assocTypeQName, assocQName);
409         
410         // check that no cyclic relationships have been created
411
getPaths(nodeToMoveRef, false);
412         
413         // invoke policy behaviour
414
invokeOnCreateChildAssociation(newAssoc.getChildAssocRef());
415         invokeOnDeleteChildAssociation(oldAssoc.getChildAssocRef());
416         invokeOnUpdateNode(oldParentNode.getNodeRef());
417         invokeOnUpdateNode(newParentRef);
418         
419         // update the node status
420
NodeStatus nodeStatus = nodeToMove.getStatus();
421         nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
422         
423         // done
424
return newAssoc.getChildAssocRef();
425     }
426
427     public void setChildAssociationIndex(ChildAssociationRef childAssocRef, int index)
428     {
429         // get nodes
430
Node parentNode = getNodeNotNull(childAssocRef.getParentRef());
431         Node childNode = getNodeNotNull(childAssocRef.getChildRef());
432         
433         ChildAssoc assoc = nodeDaoService.getChildAssoc(
434                 parentNode,
435                 childNode,
436                 childAssocRef.getTypeQName(),
437                 childAssocRef.getQName());
438         if (assoc == null)
439         {
440             throw new InvalidChildAssociationRefException("Unable to set child association index: \n" +
441                     " assoc: " + childAssocRef + "\n" +
442                     " index: " + index,
443                     childAssocRef);
444         }
445         // set the index
446
assoc.setIndex(index);
447     }
448
449     public QName getType(NodeRef nodeRef) throws InvalidNodeRefException
450     {
451         Node node = getNodeNotNull(nodeRef);
452         return node.getTypeQName();
453     }
454     
455     /**
456      * @see org.alfresco.service.cmr.repository.NodeService#setType(org.alfresco.service.cmr.repository.NodeRef, org.alfresco.service.namespace.QName)
457      */

458     public void setType(NodeRef nodeRef, QName typeQName) throws InvalidNodeRefException
459     {
460         // check the node type
461
TypeDefinition nodeTypeDef = dictionaryService.getType(typeQName);
462         if (nodeTypeDef == null)
463         {
464             throw new InvalidTypeException(typeQName);
465         }
466         
467         // Invoke policies
468
invokeBeforeUpdateNode(nodeRef);
469         
470         // Get the node and set the new type
471
Node node = getNodeNotNull(nodeRef);
472         node.setTypeQName(typeQName);
473         
474         // Add the default aspects to the node (update the properties with any new default values)
475
Map JavaDoc<QName, Serializable JavaDoc> properties = this.getProperties(nodeRef);
476         addDefaultAspects(nodeTypeDef, node, nodeRef, properties);
477         this.setProperties(nodeRef, properties);
478         
479         // Invoke policies
480
invokeOnUpdateNode(nodeRef);
481     }
482     
483     /**
484      * @see Node#getAspects()
485      */

486     public void addAspect(
487             NodeRef nodeRef,
488             QName aspectTypeQName,
489             Map JavaDoc<QName, Serializable JavaDoc> aspectProperties)
490             throws InvalidNodeRefException, InvalidAspectException
491     {
492         // check that the aspect is legal
493
AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
494         if (aspectDef == null)
495         {
496             throw new InvalidAspectException("The aspect is invalid: " + aspectTypeQName, aspectTypeQName);
497         }
498         
499         // Invoke policy behaviours
500
invokeBeforeUpdateNode(nodeRef);
501         invokeBeforeAddAspect(nodeRef, aspectTypeQName);
502         
503         Node node = getNodeNotNull(nodeRef);
504         
505         // attach the properties to the current node properties
506
Map JavaDoc<QName, Serializable JavaDoc> nodeProperties = getProperties(nodeRef);
507         
508         if (aspectProperties != null)
509         {
510             nodeProperties.putAll(aspectProperties);
511         }
512         
513         // Set any default property values that appear on the aspect
514
addDefaultPropertyValues(aspectDef, nodeProperties);
515         
516         // Add any dependant aspect
517
addDefaultAspects(aspectDef, node, nodeRef, nodeProperties);
518         
519         // Set the property values back on the node
520
setProperties(nodeRef, nodeProperties);
521         
522         // physically attach the aspect to the node
523
if (node.getAspects().add(aspectTypeQName) == true)
524         {
525             // Invoke policy behaviours
526
invokeOnUpdateNode(nodeRef);
527             invokeOnAddAspect(nodeRef, aspectTypeQName);
528             
529             // update the node status
530
NodeStatus nodeStatus = node.getStatus();
531             nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
532         }
533     }
534
535     /**
536      * @see Node#getAspects()
537      */

538     public void removeAspect(NodeRef nodeRef, QName aspectTypeQName)
539             throws InvalidNodeRefException, InvalidAspectException
540     {
541         // Invoke policy behaviours
542
invokeBeforeUpdateNode(nodeRef);
543         invokeBeforeRemoveAspect(nodeRef, aspectTypeQName);
544         
545         // get the aspect
546
AspectDefinition aspectDef = dictionaryService.getAspect(aspectTypeQName);
547         if (aspectDef == null)
548         {
549             throw new InvalidAspectException(aspectTypeQName);
550         }
551         // get the node
552
Node node = getNodeNotNull(nodeRef);
553         
554         // check that the aspect may be removed
555
TypeDefinition nodeTypeDef = dictionaryService.getType(node.getTypeQName());
556         if (nodeTypeDef == null)
557         {
558             throw new InvalidNodeRefException("The node type is no longer valid: " + nodeRef, nodeRef);
559         }
560         List JavaDoc<AspectDefinition> defaultAspects = nodeTypeDef.getDefaultAspects();
561         if (defaultAspects.contains(aspectDef))
562         {
563             throw new InvalidAspectException(
564                     "The aspect is a default for the node's type and cannot be removed: " + aspectTypeQName,
565                     aspectTypeQName);
566         }
567         
568         // remove the aspect, if present
569
boolean removed = node.getAspects().remove(aspectTypeQName);
570         // if the aspect was present, remove the associated properties
571
if (removed)
572         {
573             Map JavaDoc<QName, PropertyValue> nodeProperties = node.getProperties();
574             Map JavaDoc<QName,PropertyDefinition> propertyDefs = aspectDef.getProperties();
575             for (QName propertyName : propertyDefs.keySet())
576             {
577                 nodeProperties.remove(propertyName);
578             }
579             
580             // Invoke policy behaviours
581
invokeOnUpdateNode(nodeRef);
582             invokeOnRemoveAspect(nodeRef, aspectTypeQName);
583             
584             // update the node status
585
NodeStatus nodeStatus = node.getStatus();
586             nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
587         }
588     }
589
590     /**
591      * Performs a check on the set of node aspects
592      *
593      * @see Node#getAspects()
594      */

595     public boolean hasAspect(NodeRef nodeRef, QName aspectRef) throws InvalidNodeRefException, InvalidAspectException
596     {
597         Node node = getNodeNotNull(nodeRef);
598         Set JavaDoc<QName> aspectQNames = node.getAspects();
599         boolean hasAspect = aspectQNames.contains(aspectRef);
600         // done
601
return hasAspect;
602     }
603
604     public Set JavaDoc<QName> getAspects(NodeRef nodeRef) throws InvalidNodeRefException
605     {
606         Node node = getNodeNotNull(nodeRef);
607         Set JavaDoc<QName> aspectQNames = node.getAspects();
608         // copy the set to ensure initialization
609
Set JavaDoc<QName> ret = new HashSet JavaDoc<QName>(aspectQNames.size());
610         ret.addAll(aspectQNames);
611         // done
612
return ret;
613     }
614
615     public void deleteNode(NodeRef nodeRef)
616     {
617         // Invoke policy behaviours
618
invokeBeforeDeleteNode(nodeRef);
619         
620         // get the node
621
Node node = getNodeNotNull(nodeRef);
622         // get the primary parent-child relationship before it is gone
623
ChildAssociationRef childAssocRef = getPrimaryParent(nodeRef);
624         // get type and aspect QNames as they will be unavailable after the delete
625
QName nodeTypeQName = node.getTypeQName();
626         Set JavaDoc<QName> nodeAspectQNames = node.getAspects();
627         // delete it
628
nodeDaoService.deleteNode(node, true);
629         
630         // Invoke policy behaviours
631
invokeOnDeleteNode(childAssocRef, nodeTypeQName, nodeAspectQNames);
632     }
633 // /**
634
// * Recursive method to ensure cascade-deletion works with full invocation of policy behaviours.
635
// * <p>
636
// * The recursion will first cascade down primary associations before deleting all regular and
637
// * child associations to and from it. After this, the node itself is deleted. This bottom-up
638
// * behaviour ensures that the policy invocation behaviour, which currently relies on being able
639
// * to inspect association source types, gets fired correctly.
640
// */
641
// public void deleteNode(NodeRef nodeRef)
642
// {
643
// // Invoke policy behaviours
644
// invokeBeforeDeleteNode(nodeRef);
645
//
646
// // get the node
647
// Node node = getNodeNotNull(nodeRef);
648
//
649
// // get node info (for invocation purposes) before any deletions occur
650
// // get the primary parent-child relationship before it is gone
651
// ChildAssociationRef primaryParentAssocRef = getPrimaryParent(nodeRef);
652
// // get type and aspect QNames as they will be unavailable after the delete
653
// QName nodeTypeQName = node.getTypeQName();
654
// Set<QName> nodeAspectQNames = node.getAspects();
655
//
656
// // get all associations, forcing a load of the collections
657
// Collection<ChildAssoc> childAssocs = new ArrayList<ChildAssoc>(node.getChildAssocs());
658
// Collection<ChildAssoc> parentAssocs = new ArrayList<ChildAssoc>(node.getParentAssocs());
659
// Collection<NodeAssoc> sourceAssocs = new ArrayList<NodeAssoc>(node.getSourceNodeAssocs());
660
// Collection<NodeAssoc> targetAssocs = new ArrayList<NodeAssoc>(node.getTargetNodeAssocs());
661
//
662
// // remove all child associations, including the primary one
663
// for (ChildAssoc childAssoc : childAssocs)
664
// {
665
// ChildAssociationRef childAssocRef = childAssoc.getChildAssocRef();
666
// // cascade into primary associations
667
// if (childAssoc.getIsPrimary())
668
// {
669
// NodeRef childNodeRef = childAssocRef.getChildRef();
670
// this.deleteNode(childNodeRef);
671
// }
672
//
673
// // one or more of these associations may have been dealt with when deleting the
674
// // child, so check that the association is valid
675
//
676
// // invoke pre-deletion behaviour
677
// invokeBeforeDeleteChildAssociation(childAssocRef);
678
// // remove it - cascade just to be sure
679
// nodeDaoService.deleteChildAssoc(childAssoc, true);
680
// // invoke post-deletion behaviour
681
// invokeOnDeleteChildAssociation(childAssocRef);
682
// }
683
// // remove all parent associations, including the primary one
684
// for (ChildAssoc parentAssoc : parentAssocs)
685
// {
686
// ChildAssociationRef parentAssocRef = parentAssoc.getChildAssocRef();
687
// // invoke pre-deletion behaviour
688
// invokeBeforeDeleteChildAssociation(parentAssocRef);
689
// // remove it - don't cascade as this is a parent assoc
690
// nodeDaoService.deleteChildAssoc(parentAssoc, false);
691
// // invoke post-deletion behaviour
692
// invokeOnDeleteChildAssociation(parentAssocRef);
693
// }
694
// // remove all source node associations
695
// for (NodeAssoc sourceAssoc : sourceAssocs)
696
// {
697
// AssociationRef sourceAssocRef = sourceAssoc.getNodeAssocRef();
698
// // remove it
699
// nodeDaoService.deleteNodeAssoc(sourceAssoc);
700
// // invoke post-deletion behaviour
701
// invokeOnDeleteAssociation(sourceAssocRef);
702
// }
703
// // remove all target node associations
704
// for (NodeAssoc targetAssoc : targetAssocs)
705
// {
706
// AssociationRef targetAssocRef = targetAssoc.getNodeAssocRef();
707
// // remove it
708
// nodeDaoService.deleteNodeAssoc(targetAssoc);
709
// // invoke post-deletion behaviour
710
// invokeOnDeleteAssociation(targetAssocRef);
711
// }
712
//
713
// // delete it
714
// // We cascade so that we are sure that any new children created by policy implementations are
715
// // removed. There won't be any noticiations for these, but it prevents the cascade and
716
// // notifications from chasing each other
717
// nodeDaoService.deleteNode(node, true);
718
//
719
// // Invoke policy behaviours
720
// invokeOnDeleteNode(primaryParentAssocRef, nodeTypeQName, nodeAspectQNames);
721
// }
722

723     public ChildAssociationRef addChild(NodeRef parentRef, NodeRef childRef, QName assocTypeQName, QName assocQName)
724     {
725         // Invoke policy behaviours
726
invokeBeforeUpdateNode(parentRef);
727         invokeBeforeCreateChildAssociation(parentRef, childRef, assocTypeQName, assocQName);
728         
729         // check that both nodes belong to the same store
730
if (!parentRef.getStoreRef().equals(childRef.getStoreRef()))
731         {
732             throw new InvalidNodeRefException("Parent and child nodes must belong to the same store: \n" +
733                     " parent: " + parentRef + "\n" +
734                     " child: " + childRef,
735                     childRef);
736         }
737
738         // get the parent node and ensure that it is a container node
739
Node parentNode = getNodeNotNull(parentRef);
740         // get the child node
741
Node childNode = getNodeNotNull(childRef);
742         // make the association
743
ChildAssoc assoc = nodeDaoService.newChildAssoc(parentNode, childNode, false, assocTypeQName, assocQName);
744         ChildAssociationRef assocRef = assoc.getChildAssocRef();
745         NodeRef childNodeRef = assocRef.getChildRef();
746         
747         // check that the child addition of the child has not created a cyclic relationship
748
// this functionality is provided for free in getPath
749
getPaths(childNodeRef, false);
750
751         // Invoke policy behaviours
752
invokeOnCreateChildAssociation(assocRef);
753         invokeOnUpdateNode(parentRef);
754         
755         return assoc.getChildAssocRef();
756     }
757
758     public void removeChild(NodeRef parentRef, NodeRef childRef) throws InvalidNodeRefException
759     {
760         Node parentNode = getNodeNotNull(parentRef);
761         Node childNode = getNodeNotNull(childRef);
762         NodeKey childNodeKey = childNode.getKey();
763         
764         // get all the child assocs
765
ChildAssociationRef primaryAssocRef = null;
766         Collection JavaDoc<ChildAssoc> assocs = parentNode.getChildAssocs();
767         assocs = new HashSet JavaDoc<ChildAssoc>(assocs); // copy set as we will be modifying it
768
for (ChildAssoc assoc : assocs)
769         {
770             if (!assoc.getChild().getKey().equals(childNodeKey))
771             {
772                 continue; // not a matching association
773
}
774             ChildAssociationRef assocRef = assoc.getChildAssocRef();
775             // Is this a primary association?
776
if (assoc.getIsPrimary())
777             {
778                 // keep the primary associaton for last
779
primaryAssocRef = assocRef;
780             }
781             else
782             {
783                 // delete the association instance - it is not primary
784
invokeBeforeDeleteChildAssociation(assocRef);
785                 nodeDaoService.deleteChildAssoc(assoc, true); // cascade
786
invokeOnDeleteChildAssociation(assocRef);
787             }
788         }
789         // remove the child if the primary association was a match
790
if (primaryAssocRef != null)
791         {
792             deleteNode(primaryAssocRef.getChildRef());
793         }
794
795         // Invoke policy behaviours
796
invokeOnUpdateNode(parentRef);
797         
798         // done
799
}
800
801     public Map JavaDoc<QName, Serializable JavaDoc> getProperties(NodeRef nodeRef) throws InvalidNodeRefException
802     {
803         Node node = getNodeNotNull(nodeRef);
804         Map JavaDoc<QName, PropertyValue> nodeProperties = node.getProperties();
805         Map JavaDoc<QName, Serializable JavaDoc> ret = new HashMap JavaDoc<QName, Serializable JavaDoc>(nodeProperties.size());
806         // copy values
807
for (Map.Entry JavaDoc<QName, PropertyValue> entry: nodeProperties.entrySet())
808         {
809             QName propertyQName = entry.getKey();
810             PropertyValue propertyValue = entry.getValue();
811             // get the property definition
812
PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
813             // convert to the correct type
814
Serializable JavaDoc value = makeSerializableValue(propertyDef, propertyValue);
815             // copy across
816
ret.put(propertyQName, value);
817         }
818         // spoof referencable properties
819
addReferencableProperties(nodeRef, ret);
820         // done
821
return ret;
822     }
823     
824     public Serializable JavaDoc getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
825     {
826         // spoof referencable properties
827
if (qname.equals(ContentModel.PROP_STORE_PROTOCOL))
828         {
829             return nodeRef.getStoreRef().getProtocol();
830         }
831         else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER))
832         {
833             return nodeRef.getStoreRef().getIdentifier();
834         }
835         else if (qname.equals(ContentModel.PROP_NODE_UUID))
836         {
837             return nodeRef.getId();
838         }
839
840         // get the property from the node
841
Node node = getNodeNotNull(nodeRef);
842         Map JavaDoc<QName, PropertyValue> properties = node.getProperties();
843         PropertyValue propertyValue = properties.get(qname);
844         
845         // get the property definition
846
PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
847         // convert to the correct type
848
Serializable JavaDoc value = makeSerializableValue(propertyDef, propertyValue);
849         // done
850
return value;
851     }
852
853     /**
854      * Ensures that all required properties are present on the node and copies the
855      * property values to the <code>Node</code>.
856      * <p>
857      * To remove a property, <b>remove it from the map</b> before calling this method.
858      * Null-valued properties are allowed.
859      * <p>
860      * If any of the values are null, a marker object is put in to mimic nulls. They will be turned back into
861      * a real nulls when the properties are requested again.
862      *
863      * @see Node#getProperties()
864      */

865     public void setProperties(NodeRef nodeRef, Map JavaDoc<QName, Serializable JavaDoc> properties) throws InvalidNodeRefException
866     {
867         // Invoke policy behaviours
868
invokeBeforeUpdateNode(nodeRef);
869
870         if (properties == null)
871         {
872             throw new IllegalArgumentException JavaDoc("Properties may not be null");
873         }
874         
875         // remove referencable properties
876
removeReferencableProperties(properties);
877         
878         // find the node
879
Node node = getNodeNotNull(nodeRef);
880         // get the properties before
881
Map JavaDoc<QName, Serializable JavaDoc> propertiesBefore = getProperties(nodeRef);
882
883         // copy properties onto node
884
Map JavaDoc<QName, PropertyValue> nodeProperties = node.getProperties();
885         nodeProperties.clear();
886         
887         // check the property type and copy the values across
888
for (QName propertyQName : properties.keySet())
889         {
890             PropertyDefinition propertyDef = dictionaryService.getProperty(propertyQName);
891             Serializable JavaDoc value = properties.get(propertyQName);
892             // get a persistable value
893
PropertyValue propertyValue = makePropertyValue(propertyDef, value);
894             nodeProperties.put(propertyQName, propertyValue);
895         }
896         
897         // store the properties after the change
898
Map JavaDoc<QName, Serializable JavaDoc> propertiesAfter = Collections.unmodifiableMap(properties);
899
900         // Invoke policy behaviours
901
invokeOnUpdateNode(nodeRef);
902         invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
903         
904         // update the node status
905
NodeStatus nodeStatus = node.getStatus();
906         nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
907     }
908     
909     /**
910      * Gets the properties map, sets the value (null is allowed) and checks that the new set
911      * of properties is valid.
912      *
913      * @see DbNodeServiceImpl.NullPropertyValue
914      */

915     public void setProperty(NodeRef nodeRef, QName qname, Serializable JavaDoc value) throws InvalidNodeRefException
916     {
917         Assert.notNull(qname);
918         
919         // Invoke policy behaviours
920
invokeBeforeUpdateNode(nodeRef);
921         
922         // get the node
923
Node node = getNodeNotNull(nodeRef);
924         // get properties before
925
Map JavaDoc<QName, Serializable JavaDoc> propertiesBefore = getProperties(nodeRef);
926         
927         Map JavaDoc<QName, PropertyValue> properties = node.getProperties();
928         PropertyDefinition propertyDef = dictionaryService.getProperty(qname);
929         // get a persistable value
930
PropertyValue propertyValue = makePropertyValue(propertyDef, value);
931         properties.put(qname, propertyValue);
932
933         // get properties after the change
934
Map JavaDoc<QName, Serializable JavaDoc> propertiesAfter = getProperties(nodeRef);
935         
936         // Invoke policy behaviours
937
invokeOnUpdateNode(nodeRef);
938         invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
939         
940         // update the node status
941
NodeStatus nodeStatus = node.getStatus();
942         nodeStatus.setChangeTxnId(AlfrescoTransactionSupport.getTransactionId());
943     }
944     
945     /**
946      * Transforms {@link Node#getParentAssocs()} to a new collection
947      */

948     public Collection JavaDoc<NodeRef> getParents(NodeRef nodeRef) throws InvalidNodeRefException
949     {
950         Node node = getNodeNotNull(nodeRef);
951         // get the assocs pointing to it
952
Collection JavaDoc<ChildAssoc> parentAssocs = node.getParentAssocs();
953         // list of results
954
Collection JavaDoc<NodeRef> results = new ArrayList JavaDoc<NodeRef>(parentAssocs.size());
955         for (ChildAssoc assoc : parentAssocs)
956         {
957             // get the parent
958
Node parentNode = assoc.getParent();
959             results.add(parentNode.getNodeRef());
960         }
961         // done
962
return results;
963     }
964
965     /**
966      * Filters out any associations if their qname is not a match to the given pattern.
967      */

968     public List JavaDoc<ChildAssociationRef> getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
969     {
970         Node node = getNodeNotNull(nodeRef);
971         // get the assocs pointing to it
972
Collection JavaDoc<ChildAssoc> parentAssocs = node.getParentAssocs();
973         // shortcut if there are no assocs
974
if (parentAssocs.size() == 0)
975         {
976             return Collections.emptyList();
977         }
978         // list of results
979
List JavaDoc<ChildAssociationRef> results = new ArrayList JavaDoc<ChildAssociationRef>(parentAssocs.size());
980         for (ChildAssoc assoc : parentAssocs)
981         {
982             // does the qname match the pattern?
983
if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName()))
984             {
985                 // no match - ignore
986
continue;
987             }
988             results.add(assoc.getChildAssocRef());
989         }
990         // done
991
return results;
992     }
993
994     /**
995      * Filters out any associations if their qname is not a match to the given pattern.
996      */

997     public List JavaDoc<ChildAssociationRef> getChildAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
998     {
999         Node node = getNodeNotNull(nodeRef);
1000        // get the assocs pointing from it
1001
Collection JavaDoc<ChildAssoc> childAssocs = node.getChildAssocs();
1002        // shortcut if there are no assocs
1003
if (childAssocs.size() == 0)
1004        {
1005            return Collections.emptyList();
1006        }
1007        // sort results
1008
ArrayList JavaDoc<ChildAssoc> orderedList = new ArrayList JavaDoc<ChildAssoc>(childAssocs);
1009        Collections.sort(orderedList);
1010        
1011        // list of results
1012
List JavaDoc<ChildAssociationRef> results = new ArrayList JavaDoc<ChildAssociationRef>(childAssocs.size());
1013        int nthSibling = 0;
1014        for (ChildAssoc assoc : orderedList)
1015        {
1016            // does the qname match the pattern?
1017
if (!qnamePattern.isMatch(assoc.getQname()) || !typeQNamePattern.isMatch(assoc.getTypeQName()))
1018            {
1019                // no match - ignore
1020
continue;
1021            }
1022            ChildAssociationRef assocRef = assoc.getChildAssocRef();
1023            // slot the value in the right spot
1024
assocRef.setNthSibling(nthSibling);
1025            nthSibling++;
1026            // get the child
1027
results.add(assoc.getChildAssocRef());
1028        }
1029        // done
1030
return results;
1031    }
1032
1033    public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
1034    {
1035        Node node = getNodeNotNull(nodeRef);
1036        // get the primary parent assoc
1037
ChildAssoc assoc = nodeDaoService.getPrimaryParentAssoc(node);
1038
1039        // done - the assoc may be null for a root node
1040
ChildAssociationRef assocRef = null;
1041        if (assoc == null)
1042        {
1043            assocRef = new ChildAssociationRef(null, null, null, nodeRef);
1044        }
1045        else
1046        {
1047            assocRef = assoc.getChildAssocRef();
1048        }
1049        return assocRef;
1050    }
1051
1052    public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
1053            throws InvalidNodeRefException, AssociationExistsException
1054    {
1055        // Invoke policy behaviours
1056
invokeBeforeUpdateNode(sourceRef);
1057        
1058        Node sourceNode = getNodeNotNull(sourceRef);
1059        Node targetNode = getNodeNotNull(targetRef);
1060        // see if it exists
1061
NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName);
1062        if (assoc != null)
1063        {
1064            throw new AssociationExistsException(sourceRef, targetRef, assocTypeQName);
1065        }
1066        // we are sure that the association doesn't exist - make it
1067
assoc = nodeDaoService.newNodeAssoc(sourceNode, targetNode, assocTypeQName);
1068        AssociationRef assocRef = assoc.getNodeAssocRef();
1069
1070        // Invoke policy behaviours
1071
invokeOnUpdateNode(sourceRef);
1072        invokeOnCreateAssociation(assocRef);
1073        
1074        return assocRef;
1075    }
1076
1077    public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
1078            throws InvalidNodeRefException
1079    {
1080        // Invoke policy behaviours
1081
invokeBeforeUpdateNode(sourceRef);
1082        
1083        Node sourceNode = getNodeNotNull(sourceRef);
1084        Node targetNode = getNodeNotNull(targetRef);
1085        // get the association
1086
NodeAssoc assoc = nodeDaoService.getNodeAssoc(sourceNode, targetNode, assocTypeQName);
1087        AssociationRef assocRef = assoc.getNodeAssocRef();
1088        // delete it
1089
nodeDaoService.deleteNodeAssoc(assoc);
1090        
1091        // Invoke policy behaviours
1092
invokeOnUpdateNode(sourceRef);
1093        invokeOnDeleteAssociation(assocRef);
1094    }
1095
1096    public List JavaDoc<AssociationRef> getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
1097            throws InvalidNodeRefException
1098    {
1099        Node sourceNode = getNodeNotNull(sourceRef);
1100        // get all assocs to target
1101
Collection JavaDoc<NodeAssoc> assocs = sourceNode.getTargetNodeAssocs();
1102        List JavaDoc<AssociationRef> nodeAssocRefs = new ArrayList JavaDoc<AssociationRef>(assocs.size());
1103        for (NodeAssoc assoc : assocs)
1104        {
1105            // check qname pattern
1106
if (!qnamePattern.isMatch(assoc.getTypeQName()))
1107            {
1108                continue; // the assoc name doesn't match the pattern given
1109
}
1110            nodeAssocRefs.add(assoc.getNodeAssocRef());
1111        }
1112        // done
1113
return nodeAssocRefs;
1114    }
1115
1116    public List JavaDoc<AssociationRef> getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
1117            throws InvalidNodeRefException
1118    {
1119        Node sourceNode = getNodeNotNull(targetRef);
1120        // get all assocs to source
1121
Collection JavaDoc<NodeAssoc> assocs = sourceNode.getSourceNodeAssocs();
1122        List JavaDoc<AssociationRef> nodeAssocRefs = new ArrayList JavaDoc<AssociationRef>(assocs.size());
1123        for (NodeAssoc assoc : assocs)
1124        {
1125            // check qname pattern
1126
if (!qnamePattern.isMatch(assoc.getTypeQName()))
1127            {
1128                continue; // the assoc name doesn't match the pattern given
1129
}
1130            nodeAssocRefs.add(assoc.getNodeAssocRef());
1131        }
1132        // done
1133
return nodeAssocRefs;
1134    }
1135    
1136    /**
1137     * Recursive method used to build up paths from a given node to the root.
1138     * <p>
1139     * Whilst walking up the hierarchy to the root, some nodes may have a <b>root</b> aspect.
1140     * Everytime one of these is encountered, a new path is farmed off, but the method
1141     * continues to walk up the hierarchy.
1142     *
1143     * @param currentNode the node to start from, i.e. the child node to work upwards from
1144     * @param currentPath the path from the current node to the descendent that we started from
1145     * @param completedPaths paths that have reached the root are added to this collection
1146     * @param assocStack the parent-child relationships traversed whilst building the path.
1147     * Used to detected cyclic relationships.
1148     * @param primaryOnly true if only the primary parent association must be traversed.
1149     * If this is true, then the only root is the top level node having no parents.
1150     * @throws CyclicChildRelationshipException
1151     */

1152    private void prependPaths(
1153            final Node currentNode,
1154            final Path currentPath,
1155            Collection JavaDoc<Path> completedPaths,
1156            Stack JavaDoc<ChildAssoc> assocStack,
1157            boolean primaryOnly)
1158        throws CyclicChildRelationshipException
1159    {
1160        NodeRef currentNodeRef = currentNode.getNodeRef();
1161        // get the parent associations of the given node
1162
Collection JavaDoc<ChildAssoc> parentAssocs = currentNode.getParentAssocs();
1163        // does the node have parents
1164
boolean hasParents = parentAssocs.size() > 0;
1165        // does the current node have a root aspect?
1166
boolean isRoot = hasAspect(currentNodeRef, ContentModel.ASPECT_ROOT);
1167        boolean isStoreRoot = currentNode.getTypeQName().equals(ContentModel.TYPE_STOREROOT);
1168        
1169        // look for a root. If we only want the primary root, then ignore all but the top-level root.
1170
if (isRoot && !(primaryOnly && hasParents)) // exclude primary search with parents present
1171
{
1172            // create a one-sided assoc ref for the root node and prepend to the stack
1173
// this effectively spoofs the fact that the current node is not below the root
1174
// - we put this assoc in as the first assoc in the path must be a one-sided
1175
// reference pointing to the root node
1176
ChildAssociationRef assocRef = new ChildAssociationRef(
1177                    null,
1178                    null,
1179                    null,
1180                    getRootNode(currentNode.getNodeRef().getStoreRef()));
1181            // create a path to save and add the 'root' assoc
1182
Path pathToSave = new Path();
1183            Path.ChildAssocElement first = null;
1184            for (Path.Element element: currentPath)
1185            {
1186                if (first == null)
1187                {
1188                    first = (Path.ChildAssocElement) element;
1189                }
1190                else
1191                {
1192                    pathToSave.append(element);
1193                }
1194            }
1195            if (first != null)
1196            {
1197                // mimic an association that would appear if the current node was below
1198
// the root node
1199
// or if first beneath the root node it will make the real thing
1200
ChildAssociationRef updateAssocRef = new ChildAssociationRef(
1201                       isStoreRoot ? ContentModel.ASSOC_CHILDREN : first.getRef().getTypeQName(),
1202                       getRootNode(currentNode.getNodeRef().getStoreRef()),
1203                       first.getRef().getQName(),
1204                       first.getRef().getChildRef());
1205                Path.Element newFirst = new Path.ChildAssocElement(updateAssocRef);
1206                pathToSave.prepend(newFirst);
1207            }
1208            
1209            Path.Element element = new Path.ChildAssocElement(assocRef);
1210            pathToSave.prepend(element);
1211            
1212            // store the path just built
1213
completedPaths.add(pathToSave);
1214        }
1215
1216        if (parentAssocs.size() == 0 && !isRoot)
1217        {
1218            throw new RuntimeException JavaDoc("Node without parents does not have root aspect: " +
1219                    currentNodeRef);
1220        }
1221        // walk up each parent association
1222
for (ChildAssoc assoc : parentAssocs)
1223        {
1224            // does the association already exist in the stack
1225
if (assocStack.contains(assoc))
1226            {
1227                // the association was present already
1228
throw new CyclicChildRelationshipException(
1229                        "Cyclic parent-child relationship detected: \n" +
1230                        " current node: " + currentNode + "\n" +
1231                        " current path: " + currentPath + "\n" +
1232                        " next assoc: " + assoc,
1233                        assoc);
1234            }
1235            // do we consider only primary assocs?
1236
if (primaryOnly && !assoc.getIsPrimary())
1237            {
1238                continue;
1239            }
1240            // build a path element
1241
NodeRef parentRef = assoc.getParent().getNodeRef();
1242            QName qname = assoc.getQname();
1243            NodeRef childRef = assoc.getChild().getNodeRef();
1244            boolean isPrimary = assoc.getIsPrimary();
1245            // build a real association reference
1246
ChildAssociationRef assocRef = new ChildAssociationRef(assoc.getTypeQName(), parentRef, qname, childRef, isPrimary, -1);
1247            // Ordering is not important here: We are building distinct paths upwards
1248
Path.Element element = new Path.ChildAssocElement(assocRef);
1249            // create a new path that builds on the current path
1250
Path path = new Path();
1251            path.append(currentPath);
1252            // prepend element
1253
path.prepend(element);
1254            // get parent node
1255
Node parentNode = assoc.getParent();
1256            
1257            // push the assoc stack, recurse and pop
1258
assocStack.push(assoc);
1259            prependPaths(parentNode, path, completedPaths, assocStack, primaryOnly);
1260            assocStack.pop();
1261        }
1262        // done
1263
}
1264
1265    /**
1266     * @see #getPaths(NodeRef, boolean)
1267     * @see #prependPaths(Node, Path, Collection, Stack, boolean)
1268     */

1269    public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException
1270    {
1271        List JavaDoc<Path> paths = getPaths(nodeRef, true); // checks primary path count
1272
if (paths.size() == 1)
1273        {
1274            return paths.get(0); // we know there is only one
1275
}
1276        throw new RuntimeException JavaDoc("Primary path count not checked"); // checked by getPaths()
1277
}
1278
1279    /**
1280     * When searching for <code>primaryOnly == true</code>, checks that there is exactly
1281     * one path.
1282     * @see #prependPaths(Node, Path, Collection, Stack, boolean)
1283     */

1284    public List JavaDoc<Path> getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException
1285    {
1286        // get the starting node
1287
Node node = getNodeNotNull(nodeRef);
1288        // create storage for the paths - only need 1 bucket if we are looking for the primary path
1289
List JavaDoc<Path> paths = new ArrayList JavaDoc<Path>(primaryOnly ? 1 : 10);
1290        // create an empty current path to start from
1291
Path currentPath = new Path();
1292        // create storage for touched associations
1293
Stack JavaDoc<ChildAssoc> assocStack = new Stack JavaDoc<ChildAssoc>();
1294        // call recursive method to sort it out
1295
prependPaths(node, currentPath, paths, assocStack, primaryOnly);
1296        
1297        // check that for the primary only case we have exactly one path
1298
if (primaryOnly && paths.size() != 1)
1299        {
1300            throw new RuntimeException JavaDoc("Node has " + paths.size() + " primary paths: " + nodeRef);
1301        }
1302        
1303        // done
1304
return paths;
1305    }
1306}
1307
Popular Tags