use of org.alfresco.service.cmr.dictionary.AssociationDefinition in project alfresco-repository by Alfresco.
the class RemoveChildAssocCommand method processAssociationPersist.
/**
* Processes the given field data for persistence as an association.
*
* @param nodeRef The NodeRef to persist the associations on
* @param fieldData Data to persist for the associations
* @param assocCommands List of associations to be persisted
*/
protected void processAssociationPersist(NodeRef nodeRef, Map<QName, AssociationDefinition> assocDefs, Map<QName, ChildAssociationDefinition> childAssocDefs, FieldData fieldData, List<AbstractAssocCommand> assocCommands) {
if (getLogger().isDebugEnabled())
getLogger().debug("Processing field " + fieldData + " for association persistence");
String fieldName = fieldData.getName();
Matcher m = this.associationNamePattern.matcher(fieldName.replaceAll(DOT_CHARACTER_REPLACEMENT, DOT_CHARACTER));
if (m.matches()) {
String qNamePrefix = m.group(1);
String localName = m.group(2);
String assocSuffix = m.group(3);
QName fullQName = QName.createQName(qNamePrefix, localName, namespaceService);
// ensure that the association being persisted is defined in the model
AssociationDefinition assocDef = assocDefs.get(fullQName);
// then setting a value for this association should lead to the automatic application of the aspect defining it.
if (assocDef == null) {
// Is it defined on any other type?
AssociationDefinition assocDefFromDictionary = this.dictionaryService.getAssociation(fullQName);
// If the association is defined on any *aspect* type...
if (assocDefFromDictionary != null && assocDefFromDictionary.getSourceClass().isAspect()) {
// ... then it should be safe to proceed with applying the association value.
assocDef = assocDefFromDictionary;
} else {
// has been done with unhandled associations up to this point.
if (getLogger().isWarnEnabled()) {
getLogger().warn("Ignoring field '" + fieldName + "' as a valid association definition can not be found");
}
return;
}
}
String value = (String) fieldData.getValue();
String[] nodeRefs = value.split(",");
// with the current node.
for (String nextTargetNode : nodeRefs) {
if (nextTargetNode.length() > 0) {
if (NodeRef.isNodeRef(nextTargetNode)) {
if (assocSuffix.equals(ASSOC_DATA_ADDED_SUFFIX)) {
if (assocDef.isChild()) {
assocCommands.add(new AddChildAssocCommand(nodeRef, new NodeRef(nextTargetNode), fullQName));
} else {
assocCommands.add(new AddAssocCommand(nodeRef, new NodeRef(nextTargetNode), fullQName));
}
} else if (assocSuffix.equals(ASSOC_DATA_REMOVED_SUFFIX)) {
if (assocDef.isChild()) {
assocCommands.add(new RemoveChildAssocCommand(nodeRef, new NodeRef(nextTargetNode), fullQName));
} else {
assocCommands.add(new RemoveAssocCommand(nodeRef, new NodeRef(nextTargetNode), fullQName));
}
} else {
if (getLogger().isWarnEnabled()) {
StringBuilder msg = new StringBuilder();
msg.append("Ignoring 'fieldName ").append(fieldName).append("' as it does not have one of the expected suffixes (").append(ASSOC_DATA_ADDED_SUFFIX).append(" or ").append(ASSOC_DATA_REMOVED_SUFFIX).append(")");
getLogger().warn(msg.toString());
}
}
} else {
if (getLogger().isWarnEnabled()) {
StringBuilder msg = new StringBuilder();
msg.append("targetNode ").append(nextTargetNode).append(" is not a valid NodeRef and has been ignored.");
getLogger().warn(msg.toString());
}
}
}
}
} else if (getLogger().isWarnEnabled()) {
getLogger().warn("Ignoring unrecognised field '" + fieldName + "'");
}
}
use of org.alfresco.service.cmr.dictionary.AssociationDefinition in project alfresco-repository by Alfresco.
the class CopyServiceImpl method copyChildren.
/**
* @param copyChildren <tt>false</tt> if the client selected not to recurse
*/
private void copyChildren(CopyDetails copyDetails, QName classQName, NodeRef copyTarget, boolean copyTargetIsNew, boolean copyChildren, Map<NodeRef, NodeRef> copiesByOriginals, Set<NodeRef> copies, Map<QName, CopyBehaviourCallback> callbacks) {
NodeRef sourceNodeRef = copyDetails.getSourceNodeRef();
ClassDefinition classDef = dictionaryService.getClass(classQName);
if (classDef == null) {
// Ignore missing types
return;
}
// Check the behaviour
CopyBehaviourCallback callback = callbacks.get(classQName);
if (callback == null) {
throw new IllegalStateException("Source node class has no callback: " + classQName);
}
// Prepare storage for post-copy association handling
List<Pair<AssociationRef, AssocCopyTargetAction>> postCopyAssocs = TransactionalResourceHelper.getList(KEY_POST_COPY_ASSOCS);
// Handle peer associations.
for (Map.Entry<QName, AssociationDefinition> entry : classDef.getAssociations().entrySet()) {
QName assocTypeQName = entry.getKey();
AssociationDefinition assocDef = entry.getValue();
if (assocDef.isChild()) {
// Ignore child assocs
continue;
}
boolean haveRemovedFromCopyTarget = false;
// Get the associations
List<AssociationRef> assocRefs = nodeService.getTargetAssocs(sourceNodeRef, assocTypeQName);
for (AssociationRef assocRef : assocRefs) {
// Get the copy action for the association instance
CopyAssociationDetails assocCopyDetails = new CopyAssociationDetails(assocRef, copyTarget, copyTargetIsNew);
Pair<AssocCopySourceAction, AssocCopyTargetAction> assocCopyAction = callback.getAssociationCopyAction(classQName, copyDetails, assocCopyDetails);
// Consider the source side first
switch(assocCopyAction.getFirst()) {
case IGNORE:
// Do nothing
continue;
case COPY_REMOVE_EXISTING:
if (!copyTargetIsNew && !haveRemovedFromCopyTarget) {
// Only do this if we are copying over an existing node and we have NOT
// already cleaned up for this association type
haveRemovedFromCopyTarget = true;
for (AssociationRef assocToRemoveRef : internalNodeService.getTargetAssocs(copyTarget, assocTypeQName)) {
internalNodeService.removeAssociation(assocToRemoveRef.getSourceRef(), assocToRemoveRef.getTargetRef(), assocTypeQName);
}
}
// Fall through to copy
case COPY:
// Record the type of target behaviour that is expected
switch(assocCopyAction.getSecond()) {
case USE_ORIGINAL_TARGET:
case USE_COPIED_TARGET:
case USE_COPIED_OTHERWISE_ORIGINAL_TARGET:
// Have to save for later to see if the target node is copied, too
postCopyAssocs.add(new Pair<AssociationRef, AssocCopyTargetAction>(assocRef, assocCopyAction.getSecond()));
break;
default:
throw new IllegalStateException("Unknown association target copy action: " + assocCopyAction);
}
break;
default:
throw new IllegalStateException("Unknown association source copy action: " + assocCopyAction);
}
}
}
// Handle child associations. These need special attention due to their recursive nature.
for (Map.Entry<QName, ChildAssociationDefinition> childEntry : classDef.getChildAssociations().entrySet()) {
QName childAssocTypeQName = childEntry.getKey();
ChildAssociationDefinition childAssocDef = childEntry.getValue();
if (!childAssocDef.isChild()) {
// Ignore non-child assocs
continue;
}
// Get the child associations
List<ChildAssociationRef> childAssocRefs = nodeService.getChildAssocs(sourceNodeRef, childAssocTypeQName, RegexQNamePattern.MATCH_ALL);
for (ChildAssociationRef childAssocRef : childAssocRefs) {
NodeRef childNodeRef = childAssocRef.getChildRef();
QName assocQName = childAssocRef.getQName();
CopyChildAssociationDetails childAssocCopyDetails = new CopyChildAssociationDetails(childAssocRef, copyTarget, copyTargetIsNew, copyChildren);
// Handle nested copies
if (copies.contains(childNodeRef)) {
// of the same type. This is ignorable.
continue;
}
// Get the copy action for the association instance
ChildAssocCopyAction childAssocCopyAction = callback.getChildAssociationCopyAction(classQName, copyDetails, childAssocCopyDetails);
switch(childAssocCopyAction) {
case IGNORE:
break;
case COPY_ASSOC:
nodeService.addChild(copyTarget, childNodeRef, childAssocTypeQName, assocQName);
break;
case COPY_CHILD:
// Handle potentially cyclic relationships
if (copiesByOriginals.containsKey(childNodeRef)) {
// This is either a cyclic relationship or there are multiple different
// types of associations between the same parent and child.
// Just hook the child up with the association.
nodeService.addChild(copyTarget, childNodeRef, childAssocTypeQName, assocQName);
} else {
// Find out whether to force a recursion
ChildAssocRecurseAction childAssocRecurseAction = callback.getChildAssociationRecurseAction(classQName, copyDetails, childAssocCopyDetails);
switch(childAssocRecurseAction) {
case RESPECT_RECURSE_FLAG:
// Keep child copy flag the same
break;
case FORCE_RECURSE:
// Force recurse
copyChildren = true;
break;
default:
throw new IllegalStateException("Unrecognized enum");
}
// This copy may fail silently
copyImpl(childNodeRef, copyTarget, childAssocTypeQName, assocQName, // Keep child names for deep copies
copyChildren, // Keep child names for deep copies
false, copiesByOriginals, copies);
}
break;
default:
throw new IllegalStateException("Unrecognized enum");
}
}
}
}
use of org.alfresco.service.cmr.dictionary.AssociationDefinition in project alfresco-repository by Alfresco.
the class CopyServiceImpl method recursiveCopy.
/**
* Recursive copy algorithm
*
* @param dropName drop the name property when associations don't allow duplicately named children
*/
private NodeRef recursiveCopy(CopyDetails copyDetails, boolean copyChildren, boolean dropName, Map<NodeRef, NodeRef> copiesByOriginal, Set<NodeRef> copies, Map<QName, CopyBehaviourCallback> callbacks) {
NodeRef sourceNodeRef = copyDetails.getSourceNodeRef();
Set<QName> sourceNodeAspectQNames = copyDetails.getSourceNodeAspectQNames();
NodeRef targetParentNodeRef = copyDetails.getTargetParentNodeRef();
QName assocTypeQName = copyDetails.getAssocTypeQName();
QName assocQName = copyDetails.getAssocQName();
// Avoid duplicate and cyclic copies
if (copies.contains(sourceNodeRef)) {
throw new IllegalStateException("Nested copy prevention has failed: \n" + " " + copyDetails + "\n" + " Copies by original: " + copiesByOriginal);
} else if (copiesByOriginal.containsKey(sourceNodeRef)) {
throw new IllegalStateException("Multiple child assocs between same two nodes detected: \n" + " " + copyDetails + "\n" + " Copies by original: " + copiesByOriginal);
}
// Extract Type Definition
QName sourceNodeTypeQName = copyDetails.getSourceNodeTypeQName();
// Does this node get copied at all?
// The source node's type-bound behaviour has an effective veto.
CopyBehaviourCallback sourceTypeBehaviour = callbacks.get(sourceNodeTypeQName);
if (sourceTypeBehaviour == null) {
throw new IllegalStateException("Source node type has no callback: " + sourceNodeTypeQName);
}
if (!sourceTypeBehaviour.getMustCopy(sourceNodeTypeQName, copyDetails)) {
// Nothing to do
return null;
}
// Get the type properties to copy
Map<QName, Serializable> targetNodeProperties = buildCopyProperties(copyDetails, Collections.singleton(sourceNodeTypeQName), callbacks);
// Some aspects are going to be applied automatically. For efficiency, the initial node properties
// for these aspects should be provided.
Set<QName> defaultAspectQNames = getDefaultAspects(sourceNodeTypeQName);
Map<QName, Serializable> defaultAspectsProperties = buildCopyProperties(copyDetails, defaultAspectQNames, callbacks);
targetNodeProperties.putAll(defaultAspectsProperties);
// Drop the name property, if required. This prevents duplicate names and leaves it up to the client
// to assign a new name.
AssociationDefinition assocDef = dictionaryService.getAssociation(assocTypeQName);
if (!assocDef.isChild()) {
throw new AlfrescoRuntimeException("Association is not a child association: " + assocTypeQName);
} else {
ChildAssociationDefinition childAssocDef = (ChildAssociationDefinition) assocDef;
if (dropName && !childAssocDef.getDuplicateChildNamesAllowed()) {
// ALF-13949: A behaviour callback (e.g. WorkingCopyAspect) may already have generated a new name, so
// preserve the new name if it has changed
String newName = (String) targetNodeProperties.get(ContentModel.PROP_NAME);
if (newName != null && newName.equals(nodeService.getProperty(sourceNodeRef, ContentModel.PROP_NAME))) {
// duplicate children are not allowed.
targetNodeProperties.remove(ContentModel.PROP_NAME);
}
}
}
// Lastly, make sure the the Node UUID is set correctly; after all, the contract
// of the CopyDetails says that the targetNodeRef was already determined.
String targetNodeUuid = copyDetails.getTargetNodeRef().getId();
targetNodeProperties.put(ContentModel.PROP_NODE_UUID, targetNodeUuid);
// The initial node copy is good to go
ChildAssociationRef targetChildAssocRef = this.nodeService.createNode(targetParentNodeRef, assocTypeQName, assocQName, sourceNodeTypeQName, targetNodeProperties);
NodeRef copyTarget = targetChildAssocRef.getChildRef();
// Save the mapping for later
copiesByOriginal.put(sourceNodeRef, copyTarget);
copies.add(copyTarget);
// We now have a node, so fire the BeforeCopyPolicy
invokeBeforeCopy(sourceNodeRef, copyTarget);
// Work out which aspects still need copying. The source aspects less the default aspects
// will give this set.
Set<QName> remainingAspectQNames = new HashSet<QName>(sourceNodeAspectQNames);
remainingAspectQNames.removeAll(defaultAspectQNames);
// Prevent any rules being fired on the new destination node
this.ruleService.disableRules(copyTarget);
try {
// Apply the remaining aspects and properties
for (QName remainingAspectQName : remainingAspectQNames) {
copyProperties(copyDetails, copyTarget, remainingAspectQName, callbacks);
}
// Copy residual properties
copyResidualProperties(copyDetails, copyTarget);
// Link the new node to the original, but ensure that we only keep track of the last copy
List<AssociationRef> originalAssocs = internalNodeService.getTargetAssocs(copyTarget, ContentModel.ASSOC_ORIGINAL);
for (AssociationRef originalAssoc : originalAssocs) {
internalNodeService.removeAssociation(originalAssoc.getSourceRef(), originalAssoc.getTargetRef(), ContentModel.ASSOC_ORIGINAL);
}
// We create the link if the source is a cm:object and the not sys:pendingDelete
QName sourceTypeQName = internalNodeService.getType(sourceNodeRef);
if (dictionaryService.isSubClass(sourceTypeQName, ContentModel.TYPE_CMOBJECT) && !sourceNodeAspectQNames.contains(ContentModel.ASPECT_PENDING_DELETE)) {
internalNodeService.createAssociation(copyTarget, sourceNodeRef, ContentModel.ASSOC_ORIGINAL);
} else {
// We are not creating the association, so remove the associated aspect
internalNodeService.removeAspect(copyTarget, ContentModel.ASPECT_COPIEDFROM);
}
// Copy permissions
copyPermissions(sourceNodeRef, copyTarget);
// We present the recursion option regardless of what the client chooses
copyChildren(copyDetails, copyTarget, // We know that the node has been created
true, copyChildren, copiesByOriginal, copies, callbacks);
} finally {
this.ruleService.enableRules(copyTarget);
}
return copyTarget;
}
use of org.alfresco.service.cmr.dictionary.AssociationDefinition in project alfresco-repository by Alfresco.
the class PeerAssociatedNodeFinder method init.
public void init() {
super.init();
DictionaryService dictionaryService = serviceRegistry.getDictionaryService();
peerAssociationTypes.clear();
for (QName associationType : suppliedAssociationTypes) {
AssociationDefinition assocDef = dictionaryService.getAssociation(associationType);
if (assocDef != null && !assocDef.isChild()) {
peerAssociationTypes.add(associationType);
}
}
initialised = true;
}
use of org.alfresco.service.cmr.dictionary.AssociationDefinition in project alfresco-repository by Alfresco.
the class ViewParser method processStartElement.
/**
* Process start of xml element
*
* @param xpp XmlPullParser
* @param parserContext ParserContext
* @throws XmlPullParserException
* @throws IOException
*/
private void processStartElement(XmlPullParser xpp, ParserContext parserContext) throws XmlPullParserException, IOException {
// Extract qualified name
QName defName = getName(xpp);
// Process the element
Object element = parserContext.elementStack.peek();
// Handle special view directives
if (defName.equals(VIEW_METADATA)) {
MetaDataContext metaDataContext = new MetaDataContext(defName, (ElementContext) element);
parserContext.elementStack.push(metaDataContext);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + metaDataContext, parserContext.elementStack.size() - 1));
} else if (defName.equals(VIEW_ASPECTS) || defName.equals(VIEW_PROPERTIES) || defName.equals(VIEW_ASSOCIATIONS) || defName.equals(VIEW_ACL)) {
if (element instanceof NodeItemContext) {
throw new ImporterException("Cannot nest element " + defName + " within " + ((NodeItemContext) element).getElementName());
}
if (!(element instanceof NodeContext)) {
throw new ImporterException("Element " + defName + " can only be declared within a node");
}
NodeContext node = (NodeContext) element;
NodeItemContext nodeItemContext = new NodeItemContext(defName, node);
parserContext.elementStack.push(nodeItemContext);
if (logger.isDebugEnabled())
logger.debug(indentLog("Pushed " + nodeItemContext, parserContext.elementStack.size() - 1));
// process ACL specific attributes
if (defName.equals(VIEW_ACL)) {
processACL(xpp, parserContext);
}
} else {
if (element instanceof MetaDataContext) {
processMetaData(xpp, defName, parserContext);
} else if (element instanceof ParentContext) {
if (defName.equals(VIEW_REFERENCE)) {
// Process reference
processStartReference(xpp, defName, parserContext);
} else {
// Process type definition
TypeDefinition typeDef = dictionaryService.getType(defName);
if (typeDef == null) {
throw new ImporterException("Type " + defName + " has not been defined in the Repository dictionary");
}
processStartType(xpp, typeDef, parserContext);
}
return;
} else if (element instanceof NodeContext) {
// Process children of node
// Note: Process in the following order: aspects, properties and associations
Object def = ((NodeContext) element).determineDefinition(defName);
if (def == null) {
throw new ImporterException("Definition " + defName + " is not valid; cannot find in Repository dictionary");
}
if (def instanceof AspectDefinition) {
processAspect(xpp, (AspectDefinition) def, parserContext);
return;
} else if (def instanceof PropertyDefinition) {
processProperty(xpp, ((PropertyDefinition) def).getName(), parserContext);
return;
} else if (def instanceof AssociationDefinition) {
processStartAssoc(xpp, (AssociationDefinition) def, parserContext);
return;
}
} else if (element instanceof NodeItemContext) {
NodeItemContext nodeItem = (NodeItemContext) element;
NodeContext node = nodeItem.getNodeContext();
QName itemName = nodeItem.getElementName();
if (itemName.equals(VIEW_ASPECTS)) {
AspectDefinition def = node.determineAspect(defName);
if (def == null) {
throw new ImporterException("Aspect name " + defName + " is not valid; cannot find in Repository dictionary");
}
processAspect(xpp, def, parserContext);
} else if (itemName.equals(VIEW_PROPERTIES)) {
// Note: Allow properties which do not have a data dictionary definition
processProperty(xpp, defName, parserContext);
} else if (itemName.equals(VIEW_ASSOCIATIONS)) {
AssociationDefinition def = (AssociationDefinition) node.determineAssociation(defName);
if (def == null) {
throw new ImporterException("Association name " + defName + " is not valid; cannot find in Repository dictionary");
}
processStartAssoc(xpp, (AssociationDefinition) def, parserContext);
} else if (itemName.equals(VIEW_ACL)) {
processAccessControlEntry(xpp, parserContext);
}
}
}
}
Aggregations