properties)
{
properties.remove(ContentModel.PROP_STORE_PROTOCOL);
properties.remove(ContentModel.PROP_STORE_IDENTIFIER);
properties.remove(ContentModel.PROP_NODE_UUID);
properties.remove(ContentModel.PROP_NODE_DBID);
}
/**
* Adds all properties used by the
* {@link ContentModel#ASPECT_REFERENCEABLE referencable aspect}.
*
* This method can be used to ensure that the values used by the aspect
* are present as node properties.
*
* This method also ensures that the {@link ContentModel#PROP_NAME name property}
* is always present as a property on a node.
*
* @param node the node with the values
* @param nodeRef the node reference containing the values required
* @param properties the node properties
*/
private void addIntrinsicProperties(Pair nodePair, Map properties)
{
Long nodeId = nodePair.getFirst();
NodeRef nodeRef = nodePair.getSecond();
properties.put(ContentModel.PROP_STORE_PROTOCOL, nodeRef.getStoreRef().getProtocol());
properties.put(ContentModel.PROP_STORE_IDENTIFIER, nodeRef.getStoreRef().getIdentifier());
properties.put(ContentModel.PROP_NODE_UUID, nodeRef.getId());
properties.put(ContentModel.PROP_NODE_DBID, nodeId);
// add the ID as the name, if required
if (properties.get(ContentModel.PROP_NAME) == null)
{
properties.put(ContentModel.PROP_NAME, nodeRef.getId());
}
}
public Serializable getProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
{
Long nodeId = getNodePairNotNull(nodeRef).getFirst();
// Spoof referencable properties
if (qname.equals(ContentModel.PROP_STORE_PROTOCOL))
{
return nodeRef.getStoreRef().getProtocol();
}
else if (qname.equals(ContentModel.PROP_STORE_IDENTIFIER))
{
return nodeRef.getStoreRef().getIdentifier();
}
else if (qname.equals(ContentModel.PROP_NODE_UUID))
{
return nodeRef.getId();
}
else if (qname.equals(ContentModel.PROP_NODE_DBID))
{
return nodeId;
}
Serializable property = nodeDaoService.getNodeProperty(nodeId, qname);
// check if we need to provide a spoofed name
if (property == null && qname.equals(ContentModel.PROP_NAME))
{
return nodeRef.getId();
}
// done
return property;
}
public Map getProperties(NodeRef nodeRef) throws InvalidNodeRefException
{
Pair nodePair = getNodePairNotNull(nodeRef);
return getPropertiesImpl(nodePair);
}
/**
* Gets, converts and adds the intrinsic properties to the current node's properties
*/
private Map getPropertiesImpl(Pair nodePair) throws InvalidNodeRefException
{
Long nodeId = nodePair.getFirst();
Map nodeProperties = nodeDaoService.getNodeProperties(nodeId);
// spoof referencable properties
addIntrinsicProperties(nodePair, nodeProperties);
// done
return nodeProperties;
}
/**
* Find any aspects that are missing for the node, given the properties before and after an update.
*/
private void addMissingAspects(
Pair nodePair,
Map propertiesBefore,
Map propertiesAfter)
{
Long nodeId = nodePair.getFirst();
NodeRef nodeRef = nodePair.getSecond();
Set aspectQNamesToAdd = new HashSet(5);
Set newProperties = new HashSet(propertiesAfter.keySet());
newProperties.removeAll(propertiesBefore.entrySet());
Set existingAspectsQNames = nodeDaoService.getNodeAspects(nodeId);
for (QName newPropertyQName : newProperties)
{
PropertyDefinition propDef = dictionaryService.getProperty(newPropertyQName);
if (propDef == null)
{
continue; // Ignore undefined properties
}
if (!propDef.getContainerClass().isAspect())
{
continue;
}
QName containerClassQName = propDef.getContainerClass().getName();
// Remove this aspect - it is there
if (existingAspectsQNames.contains(containerClassQName))
{
// Already there
continue;
}
aspectQNamesToAdd.add(containerClassQName);
}
// Add the aspects and any missing, default properties
if (aspectQNamesToAdd.size() > 0)
{
for (QName aspectQNameToAdd : aspectQNamesToAdd)
{
invokeBeforeAddAspect(nodeRef, aspectQNameToAdd);
}
nodeDaoService.addNodeAspects(nodeId, aspectQNamesToAdd);
// Add the aspects and then their appropriate default values.
for (QName aspectQNameToAdd : aspectQNamesToAdd)
{
addDefaultProperties(nodePair, propertiesAfter, aspectQNameToAdd);
addDefaultAspects(nodePair, aspectQNameToAdd);
}
for (QName aspectQNameToAdd : aspectQNamesToAdd)
{
invokeOnAddAspect(nodeRef, aspectQNameToAdd);
}
}
}
/**
* Find any aspects that are missing for the node, given the association type.
*/
private void addMissingAspects(
Pair nodePair,
QName assocTypeQName)
{
Long nodeId = nodePair.getFirst();
NodeRef nodeRef = nodePair.getSecond();
Set existingAspectsQNames = nodeDaoService.getNodeAspects(nodeId);
AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
if (assocDef == null)
{
return; // Ignore undefined properties
}
if (!assocDef.getSourceClass().isAspect())
{
return;
}
QName aspectQNameToAdd = assocDef.getSourceClass().getName();
// Remove this aspect - it is there
if (existingAspectsQNames.contains(aspectQNameToAdd))
{
// Already there
return;
}
// Add the aspects and any missing, default properties
invokeBeforeAddAspect(nodeRef, aspectQNameToAdd);
nodeDaoService.addNodeAspects(nodeId, Collections.singleton(aspectQNameToAdd));
// Add the aspects and then their appropriate default values.
addDefaultProperties(nodePair, aspectQNameToAdd);
addDefaultAspects(nodePair, aspectQNameToAdd);
invokeOnAddAspect(nodeRef, aspectQNameToAdd);
}
/**
* Gets the properties map, sets the value (null is allowed) and checks that the new set
* of properties is valid.
*
* @see DbNodeServiceImpl.NullPropertyValue
*/
public void setProperty(NodeRef nodeRef, QName qname, Serializable value) throws InvalidNodeRefException
{
Assert.notNull(qname);
// get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
// Ensure that we are not setting intrinsic properties
Map properties = new HashMap(1, 1.0F);
properties.put(qname, value);
extractIntrinsicProperties(properties);
// Shortcut if nothing is left
if (properties.size() == 0)
{
return;
}
// Get the properties from before
Map propertiesBefore = getPropertiesImpl(nodePair);
invokeBeforeUpdateNode(nodeRef);
// Update the properties
setPropertyImpl(nodeId, qname, value);
// Policy callbacks
Map propertiesAfter = getPropertiesImpl(nodePair);
invokeOnUpdateNode(nodeRef);
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
// Add any missing aspects
addMissingAspects(nodePair, propertiesBefore, propertiesAfter);
// Index
nodeIndexer.indexUpdateNode(nodeRef);
}
/**
* Sets the property, taking special care to handle intrinsic properties and cm:name properly
*/
private void setPropertyImpl(Long nodeId, QName qname, Serializable value)
{
if (qname.equals(ContentModel.PROP_NODE_UUID))
{
throw new IllegalArgumentException("The node UUID cannot be changed.");
}
else
{
// cm:name special handling
if (qname.equals(ContentModel.PROP_NAME))
{
Pair primaryParentAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
if (primaryParentAssocPair != null)
{
String oldName = extractNameProperty(nodeDaoService.getNodeProperties(nodeId));
String newName = DefaultTypeConverter.INSTANCE.convert(String.class, value);
setChildNameUnique(primaryParentAssocPair, newName, oldName);
}
}
// Set the property
nodeDaoService.addNodeProperty(nodeId, qname, value);
}
}
/**
* Ensures that all required properties are present on the node and copies the
* property values to the Node
.
*
* To remove a property, remove it from the map before calling this method.
* Null-valued properties are allowed.
*
* If any of the values are null, a marker object is put in to mimic nulls. They will be turned back into
* a real nulls when the properties are requested again.
*
* @see Node#getProperties()
*/
public void setProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException
{
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
extractIntrinsicProperties(properties);
// Invoke policy behaviours
Map propertiesBefore = getPropertiesImpl(nodePair);
invokeBeforeUpdateNode(nodeRef);
// Do the set properties
setPropertiesImpl(nodeId, properties);
// Invoke policy behaviours
Map propertiesAfter = getPropertiesImpl(nodePair);
invokeOnUpdateNode(nodeRef);
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
// Add any missing aspects
addMissingAspects(nodePair, propertiesBefore, propertiesAfter);
// Index
nodeIndexer.indexUpdateNode(nodeRef);
}
public void addProperties(NodeRef nodeRef, Map properties) throws InvalidNodeRefException
{
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
extractIntrinsicProperties(properties);
// Invoke policy behaviours
Map propertiesBefore = getPropertiesImpl(nodePair);
invokeBeforeUpdateNode(nodeRef);
// Change each property
for (Map.Entry entry : properties.entrySet())
{
QName propertyQName = entry.getKey();
Serializable propertyValue = entry.getValue();
setPropertyImpl(nodeId, propertyQName, propertyValue);
}
// Invoke policy behaviours
Map propertiesAfter = getPropertiesImpl(nodePair);
invokeOnUpdateNode(nodeRef);
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
// Add any missing aspects
addMissingAspects(nodePair, propertiesBefore, propertiesAfter);
// Index
nodeIndexer.indexUpdateNode(nodeRef);
}
private void setPropertiesImpl(Long nodeId, Map properties)
{
// Get the cm:name and uuid for special handling
if (properties.containsKey(ContentModel.PROP_NAME))
{
Serializable name = properties.get(ContentModel.PROP_NAME);
setPropertyImpl(nodeId, ContentModel.PROP_NAME, name);
}
if (properties.containsKey(ContentModel.PROP_NODE_UUID))
{
throw new IllegalArgumentException("The node UUID cannot be set");
}
// Now remove special properties
extractIntrinsicProperties(properties);
// Update the node
nodeDaoService.setNodeProperties(nodeId, properties);
}
public void removeProperty(NodeRef nodeRef, QName qname) throws InvalidNodeRefException
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
// Invoke policy behaviours
invokeBeforeUpdateNode(nodeRef);
// Get the values before
Map propertiesBefore = getPropertiesImpl(nodePair);
// cm:name special handling
if (qname.equals(ContentModel.PROP_NAME))
{
Pair primaryParentAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
String oldName = extractNameProperty(nodeDaoService.getNodeProperties(nodeId));
String newName = null;
setChildNameUnique(primaryParentAssocPair, newName, oldName);
}
// Remove
nodeDaoService.removeNodeProperties(nodeId, Collections.singleton(qname));
// Invoke policy behaviours
Map propertiesAfter = getPropertiesImpl(nodePair);
invokeOnUpdateNode(nodeRef);
invokeOnUpdateProperties(nodeRef, propertiesBefore, propertiesAfter);
// Index
nodeIndexer.indexUpdateNode(nodeRef);
}
public Collection getParents(NodeRef nodeRef) throws InvalidNodeRefException
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
// Get the assocs pointing to it
Collection> parentAssocPairs = nodeDaoService.getParentAssocs(nodeId);
// list of results
Collection results = new ArrayList(parentAssocPairs.size());
for (Pair assocPair : parentAssocPairs)
{
NodeRef parentNodeRef = assocPair.getSecond().getParentRef();
results.add(parentNodeRef);
}
// done
return results;
}
/**
* Filters out any associations if their qname is not a match to the given pattern.
*/
public List getParentAssocs(NodeRef nodeRef, QNamePattern typeQNamePattern, QNamePattern qnamePattern)
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
// Get the assocs pointing to it
Collection> parentAssocPairs = nodeDaoService.getParentAssocs(nodeId);
// list of results
List results = new ArrayList(parentAssocPairs.size());
for (Pair assocPair : parentAssocPairs)
{
ChildAssociationRef assocRef = assocPair.getSecond();
QName assocTypeQName = assocRef.getTypeQName();
QName assocQName = assocRef.getQName();
if (!qnamePattern.isMatch(assocQName) || !typeQNamePattern.isMatch(assocTypeQName))
{
// No match
continue;
}
results.add(assocRef);
}
// done
return results;
}
/**
* Filters out any associations if their qname is not a match to the given pattern.
*/
public List getChildAssocs(NodeRef nodeRef, final QNamePattern typeQNamePattern, final QNamePattern qnamePattern)
{
return getChildAssocs(nodeRef, typeQNamePattern, qnamePattern, true) ;
}
/**
* Filters out any associations if their qname is not a match to the given pattern.
*/
public List getChildAssocs(NodeRef nodeRef, final QNamePattern typeQNamePattern, final QNamePattern qnamePattern, final boolean preload)
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
final List results = new ArrayList(100);
abstract class BaseCallback implements NodeDaoService.ChildAssocRefQueryCallback
{
public boolean preLoadNodes()
{
return preload;
}
}
if (qnamePattern instanceof QName)
{
// Both explicit QNames
if (typeQNamePattern instanceof QName)
{
NodeDaoService.ChildAssocRefQueryCallback callback = new BaseCallback()
{
public boolean handle(Pair childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
results.add(childAssocPair.getSecond());
return false;
}
};
// Get all child associations with the specific qualified name
nodeDaoService.getChildAssocsByTypeQNameAndQName(nodeId, (QName) typeQNamePattern,
(QName) qnamePattern, callback);
}
// Type is explicit, local qname is pattern
else
{
NodeDaoService.ChildAssocRefQueryCallback callback;
if (typeQNamePattern.equals(RegexQNamePattern.MATCH_ALL))
{
callback = new BaseCallback()
{
public boolean handle(Pair childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
results.add(childAssocPair.getSecond());
return false;
}
};
}
else
{
callback = new BaseCallback()
{
public boolean handle(Pair childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
ChildAssociationRef assocRef = childAssocPair.getSecond();
QName assocTypeQName = assocRef.getTypeQName();
if (!typeQNamePattern.isMatch(assocTypeQName))
{
// No match
return false;
}
results.add(assocRef);
return false;
}
};
}
// Get all child associations with the specific qualified name
nodeDaoService.getChildAssocs(nodeId, (QName) qnamePattern, callback);
}
}
else
{
// Local qname is pattern, type name is explicit
if (typeQNamePattern instanceof QName)
{
NodeDaoService.ChildAssocRefQueryCallback callback;
// if the type is the wildcard type, and the qname is not a search, then use a shortcut query
if (qnamePattern.equals(RegexQNamePattern.MATCH_ALL))
{
callback = new BaseCallback()
{
public boolean handle(Pair childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
results.add(childAssocPair.getSecond());
return false;
}
};
}
else
{
callback = new BaseCallback()
{
public boolean handle(Pair childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
ChildAssociationRef assocRef = childAssocPair.getSecond();
QName assocQName = assocRef.getQName();
if (!qnamePattern.isMatch(assocQName))
{
// No match
return false;
}
results.add(assocRef);
return false;
}
};
}
// Get all child associations with the specific type qualified name
nodeDaoService.getChildAssocsByTypeQNames(nodeId, Collections.singletonList((QName) typeQNamePattern),
callback);
}
// Local qname is pattern, type name is pattern
else
{
NodeDaoService.ChildAssocRefQueryCallback callback = new BaseCallback()
{
public boolean handle(Pair childAssocPair,
Pair parentNodePair, Pair childNodePair)
{
ChildAssociationRef assocRef = childAssocPair.getSecond();
QName assocTypeQName = assocRef.getTypeQName();
QName assocQName = assocRef.getQName();
if (!qnamePattern.isMatch(assocQName) || !typeQNamePattern.isMatch(assocTypeQName))
{
// No match
return false;
}
results.add(assocRef);
return false;
}
};
// Get all child associations
nodeDaoService.getChildAssocs(nodeId, callback, false);
}
}
// sort the results
List orderedList = reorderChildAssocs(results);
// done
return orderedList;
}
public List getChildAssocs(NodeRef nodeRef, Set childNodeTypeQNames)
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
final List results = new ArrayList(100);
NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
{
public boolean handle(
Pair childAssocPair,
Pair parentNodePair,
Pair childNodePair)
{
results.add(childAssocPair.getSecond());
return false;
}
public boolean preLoadNodes()
{
return true;
}
};
// Get all child associations with the specific qualified name
nodeDaoService.getChildAssocsByChildTypes(nodeId, childNodeTypeQNames, callback);
// Sort the results
List orderedList = reorderChildAssocs(results);
// Done
return orderedList;
}
private List reorderChildAssocs(Collection childAssocRefs)
{
// shortcut if there are no assocs
if (childAssocRefs.size() == 0)
{
return Collections.emptyList();
}
// sort results
ArrayList orderedList = new ArrayList(childAssocRefs);
Collections.sort(orderedList);
// list of results
int nthSibling = 0;
Iterator iterator = orderedList.iterator();
while(iterator.hasNext())
{
ChildAssociationRef childAssocRef = iterator.next();
childAssocRef.setNthSibling(nthSibling);
nthSibling++;
}
// done
return orderedList;
}
public NodeRef getChildByName(NodeRef nodeRef, QName assocTypeQName, String childName)
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
Pair childAssocPair = nodeDaoService.getChildAssoc(nodeId, assocTypeQName, childName);
if (childAssocPair != null)
{
return childAssocPair.getSecond().getChildRef();
}
else
{
return null;
}
}
public List getChildrenByName(NodeRef nodeRef, QName assocTypeQName, Collection childNames)
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
final List results = new ArrayList(100);
NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
{
public boolean handle(
Pair childAssocPair,
Pair parentNodePair,
Pair childNodePair)
{
results.add(childAssocPair.getSecond());
return false;
}
public boolean preLoadNodes()
{
return true;
}
};
// Get all child associations with the specific qualified name
nodeDaoService.getChildAssocs(nodeId, assocTypeQName, childNames, callback);
// Sort the results
List orderedList = reorderChildAssocs(results);
// Done
return orderedList;
}
public ChildAssociationRef getPrimaryParent(NodeRef nodeRef) throws InvalidNodeRefException
{
// Get the node
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
// get the primary parent assoc
Pair assocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
// done - the assoc may be null for a root node
ChildAssociationRef assocRef = null;
if (assocPair == null)
{
assocRef = new ChildAssociationRef(null, null, null, nodeRef);
}
else
{
assocRef = assocPair.getSecond();
}
return assocRef;
}
public AssociationRef createAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
throws InvalidNodeRefException, AssociationExistsException
{
Pair sourceNodePair = getNodePairNotNull(sourceRef);
long sourceNodeId = sourceNodePair.getFirst();
Pair targetNodePair = getNodePairNotNull(targetRef);
long targetNodeId = targetNodePair.getFirst();
// we are sure that the association doesn't exist - make it
Pair assocPair = nodeDaoService.newNodeAssoc(sourceNodeId, targetNodeId, assocTypeQName);
AssociationRef assocRef = assocPair.getSecond();
// Invoke policy behaviours
invokeOnCreateAssociation(assocRef);
// Add missing aspects
addMissingAspects(sourceNodePair, assocTypeQName);
return assocRef;
}
public Collection getChildAssocsWithoutParentAssocsOfType(NodeRef parent, QName assocTypeQName)
{
// Get the parent node
Pair nodePair = getNodePairNotNull(parent);
Long parentNodeId = nodePair.getFirst();
final List results = new ArrayList(100);
NodeDaoService.ChildAssocRefQueryCallback callback = new NodeDaoService.ChildAssocRefQueryCallback()
{
public boolean handle(Pair childAssocPair, Pair parentNodePair,
Pair childNodePair)
{
results.add(childAssocPair.getSecond());
return true;
}
public boolean preLoadNodes()
{
return false;
}
};
// Get the child associations that meet the criteria
nodeDaoService.getChildAssocsWithoutParentAssocsOfType(parentNodeId, assocTypeQName, callback);
// done
return results;
}
public void removeAssociation(NodeRef sourceRef, NodeRef targetRef, QName assocTypeQName)
throws InvalidNodeRefException
{
Pair sourceNodePair = getNodePairNotNull(sourceRef);
long sourceNodeId = sourceNodePair.getFirst();
Pair targetNodePair = getNodePairNotNull(targetRef);
long targetNodeId = targetNodePair.getFirst();
// get the association
Pair assocPair = nodeDaoService.getNodeAssoc(sourceNodeId, targetNodeId, assocTypeQName);
if (assocPair == null)
{
// nothing to remove
return;
}
AssociationRef assocRef = assocPair.getSecond();
// delete it
nodeDaoService.deleteNodeAssoc(assocPair.getFirst());
// Invoke policy behaviours
invokeOnDeleteAssociation(assocRef);
}
public List getTargetAssocs(NodeRef sourceRef, QNamePattern qnamePattern)
{
Pair sourceNodePair = getNodePairNotNull(sourceRef);
long sourceNodeId = sourceNodePair.getFirst();
// get all assocs to target
Collection> assocPairs = nodeDaoService.getTargetNodeAssocs(sourceNodeId);
List nodeAssocRefs = new ArrayList(assocPairs.size());
for (Pair assocPair : assocPairs)
{
AssociationRef assocRef = assocPair.getSecond();
// check qname pattern
if (!qnamePattern.isMatch(assocRef.getTypeQName()))
{
continue; // the assoc name doesn't match the pattern given
}
nodeAssocRefs.add(assocRef);
}
// done
return nodeAssocRefs;
}
public List getSourceAssocs(NodeRef targetRef, QNamePattern qnamePattern)
{
Pair targetNodePair = getNodePairNotNull(targetRef);
long targetNodeId = targetNodePair.getFirst();
// get all assocs to target
Collection> assocPairs = nodeDaoService.getSourceNodeAssocs(targetNodeId);
List nodeAssocRefs = new ArrayList(assocPairs.size());
for (Pair assocPair : assocPairs)
{
AssociationRef assocRef = assocPair.getSecond();
// check qname pattern
if (!qnamePattern.isMatch(assocRef.getTypeQName()))
{
continue; // the assoc name doesn't match the pattern given
}
nodeAssocRefs.add(assocRef);
}
// done
return nodeAssocRefs;
}
/**
* @see #getPaths(NodeRef, boolean)
* @see #prependPaths(Node, Path, Collection, Stack, boolean)
*/
public Path getPath(NodeRef nodeRef) throws InvalidNodeRefException
{
List paths = getPaths(nodeRef, true); // checks primary path count
if (paths.size() == 1)
{
return paths.get(0); // we know there is only one
}
throw new RuntimeException("Primary path count not checked"); // checked by getPaths()
}
/**
* When searching for primaryOnly == true
, checks that there is exactly
* one path.
* @see #prependPaths(Node, Path, Collection, Stack, boolean)
*/
public List getPaths(NodeRef nodeRef, boolean primaryOnly) throws InvalidNodeRefException
{
// get the starting node
Pair nodePair = getNodePairNotNull(nodeRef);
// create storage for the paths - only need 1 bucket if we are looking for the primary path
List paths = new ArrayList(primaryOnly ? 1 : 10);
// create an empty current path to start from
Path currentPath = new Path();
// create storage for touched associations
Stack assocIdStack = new Stack();
// call recursive method to sort it out
nodeDaoService.prependPaths(nodePair, null, currentPath, paths, assocIdStack, primaryOnly);
// check that for the primary only case we have exactly one path
if (primaryOnly && paths.size() != 1)
{
throw new RuntimeException("Node has " + paths.size() + " primary paths: " + nodeRef);
}
// done
if (loggerPaths.isDebugEnabled())
{
StringBuilder sb = new StringBuilder(256);
if (primaryOnly)
{
sb.append("Primary paths");
}
else
{
sb.append("Paths");
}
sb.append(" for node ").append(nodeRef);
for (Path path : paths)
{
sb.append("\n").append(" ").append(path);
}
loggerPaths.debug(sb);
}
return paths;
}
private void archiveNode(NodeRef nodeRef, StoreRef archiveStoreRef)
{
Pair nodePair = getNodePairNotNull(nodeRef);
Long nodeId = nodePair.getFirst();
Pair primaryParentAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeId);
Set newAspects = new HashSet(5);
Map existingProperties = nodeDaoService.getNodeProperties(nodeId);
Map newProperties = new HashMap(11);
// add the aspect
newAspects.add(ContentModel.ASPECT_ARCHIVED);
newProperties.put(ContentModel.PROP_ARCHIVED_BY, AuthenticationUtil.getFullyAuthenticatedUser());
newProperties.put(ContentModel.PROP_ARCHIVED_DATE, new Date());
newProperties.put(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC, primaryParentAssocPair.getSecond());
Serializable originalOwner = existingProperties.get(ContentModel.PROP_OWNER);
Serializable originalCreator = existingProperties.get(ContentModel.PROP_CREATOR);
if (originalOwner != null || originalCreator != null)
{
newProperties.put(
ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER,
originalOwner != null ? originalOwner : originalCreator);
}
// change the node ownership
newAspects.add(ContentModel.ASPECT_OWNABLE);
newProperties.put(ContentModel.PROP_OWNER, AuthenticationUtil.getFullyAuthenticatedUser());
// Set the aspects and properties
nodeDaoService.addNodeProperties(nodeId, newProperties);
nodeDaoService.addNodeAspects(nodeId, newAspects);
// move the node
Pair archiveStoreRootNodePair = nodeDaoService.getRootNode(archiveStoreRef);
moveNode(
nodeRef,
archiveStoreRootNodePair.getSecond(),
ContentModel.ASSOC_CHILDREN,
QName.createQName(NamespaceService.SYSTEM_MODEL_1_0_URI, "archivedItem"));
}
public NodeRef restoreNode(NodeRef archivedNodeRef, NodeRef destinationParentNodeRef, QName assocTypeQName, QName assocQName)
{
Pair archivedNodePair = getNodePairNotNull(archivedNodeRef);
Long archivedNodeId = archivedNodePair.getFirst();
Set existingAspects = nodeDaoService.getNodeAspects(archivedNodeId);
Set newAspects = new HashSet(5);
Map existingProperties = nodeDaoService.getNodeProperties(archivedNodeId);
Map newProperties = new HashMap(11);
// the node must be a top-level archive node
if (!existingAspects.contains(ContentModel.ASPECT_ARCHIVED))
{
throw new AlfrescoRuntimeException("The node to restore is not an archive node");
}
ChildAssociationRef originalPrimaryParentAssocRef = (ChildAssociationRef) existingProperties.get(
ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC);
Serializable originalOwner = existingProperties.get(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER);
// remove the archived aspect
Set removePropertyQNames = new HashSet(11);
removePropertyQNames.add(ContentModel.PROP_ARCHIVED_ORIGINAL_PARENT_ASSOC);
removePropertyQNames.add(ContentModel.PROP_ARCHIVED_BY);
removePropertyQNames.add(ContentModel.PROP_ARCHIVED_DATE);
removePropertyQNames.add(ContentModel.PROP_ARCHIVED_ORIGINAL_OWNER);
nodeDaoService.removeNodeProperties(archivedNodeId, removePropertyQNames);
nodeDaoService.removeNodeAspects(archivedNodeId, Collections.singleton(ContentModel.ASPECT_ARCHIVED));
// restore the original ownership
if (originalOwner != null)
{
newAspects.add(ContentModel.ASPECT_OWNABLE);
newProperties.put(ContentModel.PROP_OWNER, originalOwner);
}
if (destinationParentNodeRef == null)
{
// we must restore to the original location
destinationParentNodeRef = originalPrimaryParentAssocRef.getParentRef();
}
// check the associations
if (assocTypeQName == null)
{
assocTypeQName = originalPrimaryParentAssocRef.getTypeQName();
}
if (assocQName == null)
{
assocQName = originalPrimaryParentAssocRef.getQName();
}
// move the node to the target parent, which may or may not be the original parent
ChildAssociationRef newChildAssocRef = moveNode(
archivedNodeRef,
destinationParentNodeRef,
assocTypeQName,
assocQName);
// the node reference has changed due to the store move
NodeRef restoredNodeRef = newChildAssocRef.getChildRef();
// done
if (logger.isDebugEnabled())
{
logger.debug("Restored node: \n" +
" original noderef: " + archivedNodeRef + "\n" +
" restored noderef: " + restoredNodeRef + "\n" +
" new parent: " + destinationParentNodeRef);
}
return restoredNodeRef;
}
/**
* Drops the old primary association and creates a new one
*/
public ChildAssociationRef moveNode(
NodeRef nodeToMoveRef,
NodeRef newParentRef,
QName assocTypeQName,
QName assocQName)
{
Pair nodeToMovePair = getNodePairNotNull(nodeToMoveRef);
Pair parentNodePair = getNodePairNotNull(newParentRef);
Long nodeToMoveId = nodeToMovePair.getFirst();
QName nodeToMoveTypeQName = nodeDaoService.getNodeType(nodeToMoveId);
NodeRef oldNodeToMoveRef = nodeToMovePair.getSecond();
Long parentNodeId = parentNodePair.getFirst();
NodeRef parentNodeRef = parentNodePair.getSecond();
StoreRef oldStoreRef = oldNodeToMoveRef.getStoreRef();
StoreRef newStoreRef = parentNodeRef.getStoreRef();
NodeRef newNodeToMoveRef = new NodeRef(newStoreRef, oldNodeToMoveRef.getId());
Pair newNodeToMovePair = new Pair(nodeToMoveId, newNodeToMoveRef);
// Get the primary parent association
Pair oldParentAssocPair = nodeDaoService.getPrimaryParentAssoc(nodeToMoveId);
if (oldParentAssocPair == null)
{
// The node doesn't have parent. Moving it is not possible.
throw new IllegalArgumentException("Node " + nodeToMoveId + " doesn't have a parent. Use 'addChild' instead of move.");
}
Long oldParentAssocId = oldParentAssocPair.getFirst();
ChildAssociationRef oldParentAssocRef = oldParentAssocPair.getSecond();
// Shortcut this whole process if nothing has changed
if (EqualsHelper.nullSafeEquals(oldParentAssocRef.getParentRef(), newParentRef) &&
EqualsHelper.nullSafeEquals(oldParentAssocRef.getTypeQName(), assocTypeQName) &&
EqualsHelper.nullSafeEquals(oldParentAssocRef.getQName(), assocQName))
{
// It's all just the same
return oldParentAssocRef;
}
boolean movingStore = !oldStoreRef.equals(newStoreRef);
// Handle store conflicts
if (movingStore)
{
handleStoreMoveConflicts(nodeToMovePair, newStoreRef);
}
// Invoke policy behaviour
if (movingStore)
{
invokeBeforeDeleteNode(nodeToMoveRef);
invokeBeforeCreateNode(newParentRef, assocTypeQName, assocQName, nodeToMoveTypeQName);
}
else
{
invokeBeforeDeleteChildAssociation(oldParentAssocRef);
invokeBeforeCreateChildAssociation(newParentRef, nodeToMoveRef, assocTypeQName, assocQName, false);
}
// Handle store moves
if (movingStore)
{
Pair newNodePair = nodeDaoService.moveNodeToStore(nodeToMoveId, newStoreRef);
if (!newNodePair.equals(newNodeToMovePair))
{
throw new RuntimeException("Store-moved pair isn't expected: " + newNodePair + " != " + newNodeToMovePair);
}
}
// Get the new node's cm:name
Map newNodeProperties = nodeDaoService.getNodeProperties(nodeToMoveId);
String newNodeChildName = extractNameProperty(newNodeProperties);
// Modify the association directly. We do this AFTER the change of the node's store so that
// the association reference returned is correct.
Pair