package org.alfresco.repo.copy; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Map; import org.alfresco.repo.copy.CopyBehaviourCallback.AssocCopySourceAction; import org.alfresco.repo.copy.CopyBehaviourCallback.AssocCopyTargetAction; import org.alfresco.repo.transaction.TransactionalResourceHelper; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.namespace.QName; import org.alfresco.util.Pair; /** * Abstract implementation to allow for easier migration if the interface changes. * * @author Derek Hulley * @since 3.2 */ public abstract class AbstractCopyBehaviourCallback implements CopyBehaviourCallback { private static final String KEY_NODEREF_REPOINTING_PREFIX = "recordNodeRefPropertiesForRepointing-"; /** * @return Returns * {@link AssocCopySourceAction#COPY_REMOVE_EXISTING} and * {@link AssocCopyTargetAction#USE_COPIED_TARGET} */ @Override public Pair getAssociationCopyAction( QName classQName, CopyDetails copyDetails, CopyAssociationDetails assocCopyDetails) { return new Pair( AssocCopySourceAction.COPY_REMOVE_EXISTING, AssocCopyTargetAction.USE_COPIED_TARGET); } /** * @return Returns ChildAssocRecurseAction.RESPECT_RECURSE_FLAG */ public ChildAssocRecurseAction getChildAssociationRecurseAction( QName classQName, CopyDetails copyDetails, CopyChildAssociationDetails childAssocCopyDetails) { return ChildAssocRecurseAction.RESPECT_RECURSE_FLAG; } /** * @throws IllegalStateException always */ protected void throwExceptionForUnexpectedBehaviour(CopyDetails copyDetails, String ... otherDetails) { StringBuilder sb = new StringBuilder(512); sb.append("Behaviour should have been invoked: \n" + " Aspect: " + this.getClass().getName() + "\n" + " " + copyDetails + "\n"); for (String otherDetail : otherDetails) { sb.append(" ").append(otherDetail).append("\n"); } throw new IllegalStateException(sb.toString()); } /** * Helper method to transactionally record NodeRef properties so that they * can later be fixed up to point to the relative, after-copy locations. *

* When the copy has been completed, the second stage of the process can be applied. * * @param sourceNodeRef the node that is being copied * @param properties the node properties being copied * @param propertyQName the qualified name of the property to check * * @see #repointNodeRefs(NodeRef, NodeRef, QName, Map, NodeService) */ public void recordNodeRefsForRepointing( NodeRef sourceNodeRef, Map properties, QName propertyQName) { Serializable parameterValue = properties.get(propertyQName); if (parameterValue != null && (parameterValue instanceof Collection || parameterValue instanceof NodeRef)) { String key = KEY_NODEREF_REPOINTING_PREFIX + propertyQName.toString(); // Store it for later Map map = TransactionalResourceHelper.getMap(key); map.put(sourceNodeRef, parameterValue); } } /** * The second stage of the NodeRef repointing. Call this method to have * any NodeRef properties readjusted to reflect the copied node hierarchy. * Only use this method if it a requirement for the particular type or aspect that you * are coding for. * * @param sourceNodeRef the source node * @param propertyQName the target node i.e. the copy of the source node * @param copyMap the full hierarchy copy map of source to copies * * @see #recordNodeRefsForRepointing(NodeRef, Map, QName) */ @SuppressWarnings("unchecked") public void repointNodeRefs( NodeRef sourceNodeRef, NodeRef targetNodeRef, QName propertyQName, Map copyMap, NodeService nodeService) { String key = KEY_NODEREF_REPOINTING_PREFIX + propertyQName.toString(); Map map = TransactionalResourceHelper.getMap(key); Serializable value = map.get(sourceNodeRef); if (value == null) { // Don't bother. The source node did not have a NodeRef property return; } Serializable newValue = null; if (value instanceof Collection) { Collection oldList = (Collection) value; List newList = new ArrayList(oldList.size()); for (Serializable oldListValue : oldList) { Serializable newListValue = oldListValue; if (oldListValue instanceof NodeRef) { newListValue = repointNodeRef(copyMap, (NodeRef) oldListValue); } // Put the value in the new list even though the new list might be discarded newList.add(newListValue); // Check if the value changed if (!newListValue.equals(oldListValue)) { // The value changed, so the new list will have to be set onto the target node newValue = (Serializable) newList; } } } else if (value instanceof NodeRef) { NodeRef newNodeRef = repointNodeRef(copyMap, (NodeRef) value); if (!newNodeRef.equals(value)) { // The value changed, so the new list will have to be set onto the target node newValue = newNodeRef; } } else { throw new IllegalStateException("Should only have Collections and NodeRef values"); } // Fix the node property on the target, if necessary if (newValue != null) { nodeService.setProperty(targetNodeRef, propertyQName, newValue); } } private NodeRef repointNodeRef(Map copyMap, NodeRef pointerNodeRef) { NodeRef copiedPointerNodeRef = copyMap.get(pointerNodeRef); if (copiedPointerNodeRef == null) { return pointerNodeRef; } else { return copiedPointerNodeRef; } } /** * By default it is forbidden for top-level nodes to be renamed */ @Override public boolean isTopLevelCanBeRenamed(QName classQName, CopyDetails copyDetails) { return false; } }