* NOTE: Any part of the source subtree can still terminate the recursion,
         *              so this is mainly useful where the subtree contains the default
         *              behaviour.
         */
        FORCE_RECURSE,
    }
    
    /**
     * A simple bean class to convey information to the callback methods dealing with
     * copying of associations.
     *
     * @see CopyBehaviourCallback#getAssociationCopyAction(QName, CopyDetails, CopyAssociationDetails)
     * 
     * @author Derek Hulley
     * @since 3.3SP3
     */
    public static final class CopyAssociationDetails
    {
        private final AssociationRef assocRef;
        private final NodeRef copyTarget;
        private final boolean copyTargetIsNew;
        
        public CopyAssociationDetails(
                AssociationRef assocRef,
                NodeRef copyTarget,
                boolean copyTargetIsNew)
        {
            this.assocRef = assocRef;
            this.copyTarget = copyTarget;
            this.copyTargetIsNew = copyTargetIsNew;
        }
        
        @Override
        public String toString()
        {
            StringBuilder sb = new StringBuilder(256);
            sb.append("CopyChildAssociationDetails ")
              .append("[ assocRef=").append(assocRef)
              .append(", copyTarget=").append(copyTarget)
              .append(", copyTargetIsNew=").append(copyTargetIsNew)
              .append("]");
            return sb.toString();
        }
        /**
         * @return          Returns the association being examined
         */
        public final AssociationRef getAssocRef()
        {
            return assocRef;
        }
        /**
         * @return          Returns the node that will be the
         *                  new source if the association is copied
         */
        public final NodeRef getCopyTarget()
        {
            return copyTarget;
        }
        /**
         * 
         * @return          Returns true if the {@link #getCopyTarget() copy target node}
         *                  has been newly created by the copy process or false if it
         *                  is a node that existed prior to the copy
         */
        public final boolean getCopyTargetIsNew()
        {
            return copyTargetIsNew;
        }
    }
    
    /**
     * A simple bean class to convey information to the callback methods dealing with
     * copying of child associations.
     *
     * @see CopyBehaviourCallback#getChildAssociationCopyAction(QName, CopyDetails, CopyChildAssociationDetails)
     * @see CopyBehaviourCallback#getChildAssociationRecurseAction(QName, CopyDetails, CopyChildAssociationDetails)
     * 
     * @author Derek Hulley
     * @since 3.2
     */
    public static final class CopyChildAssociationDetails
    {
        private final ChildAssociationRef childAssocRef;
        private final NodeRef copyTarget;
        private final boolean copyTargetIsNew;
        private final boolean copyChildren;
        
        public CopyChildAssociationDetails(
                ChildAssociationRef childAssocRef,
                NodeRef copyTarget,
                boolean copyTargetIsNew,
                boolean copyChildren)
        {
            this.childAssocRef = childAssocRef;
            this.copyTarget = copyTarget;
            this.copyTargetIsNew = copyTargetIsNew;
            this.copyChildren = copyChildren;
        }
        
        @Override
        public String toString()
        {
            StringBuilder sb = new StringBuilder(256);
            sb.append("CopyChildAssociationDetails ")
              .append("[ childAssocRef=").append(childAssocRef)
              .append(", copyTarget=").append(copyTarget)
              .append(", copyTargetIsNew=").append(copyTargetIsNew)
              .append(", copyChildren=").append(copyChildren)
              .append("]");
            return sb.toString();
        }
        /**
         * @return          Returns the association being examined
         */
        public final ChildAssociationRef getChildAssocRef()
        {
            return childAssocRef;
        }
        /**
         * @return          Returns the node that will be the
         *                  new parent if the association is copied
         */
        public final NodeRef getCopyTarget()
        {
            return copyTarget;
        }
        /**
         * 
         * @return          Returns true if the {@link #getCopyTarget() target node}
         *                  has been newly created by the copy process or false if it
         *                  is a node that existed prior to the copy
         */
        public final boolean getCopyTargetIsNew()
        {
            return copyTargetIsNew;
        }
        /**
         * Get the current recursion behaviour.  This can be ignored and even altered, if required.
         * 
         * @return          Returns true if the copy process is currently recursing to
         *                  child associations or false if not.
         */
        public final boolean isCopyChildren()
        {
            return copyChildren;
        }
    }
    
    /**
     * Determine if this type or aspect must be copied.  If the callback is for a type
     * (not aspect) then this determines if the node is copied at all.  If the callback
     * is for an aspect, then this determines if the aspect is copied.
     * 
     * @param classQName            the name of the class that this is being invoked for
     * @param copyDetails           the source node's copy details for quick reference
     * @return                      true if the type or aspect that this behaviour
     *                              represents must be copied.
     */
    boolean getMustCopy(QName classQName, CopyDetails copyDetails);
    
    /**
     * Determine the copy behaviour associated with a given peer association.
     * 
     * @param classQName            the name of the class that this is being invoked for
     * @param copyDetails           the source node's copy details for quick reference
     * @param assocCopyDetails      all other details relating to the association
     * @return                      Returns the copy actions
     *                              ({@link AssocCopySourceAction source} and {@link AssocCopySourceAction target})
     *                              to take with the given association
     */
    Pair 
     * This is called regardless of whether 'cascade' copy has been selected by the client
     * of the copy.  Some type and aspect behaviour will mandate a copy of the child
     * associations regardless of whether recursion is on.
     * 
     * @param classQName            the name of the class that this is being invoked for
     * @param copyDetails           the source node's copy details for quick reference
     * @param childAssocCopyDetails all other details relating to the child association
     * @return                      Returns the copy {@link ChildAssocCopyAction action} to take
     *                              with the given child association
     */
    ChildAssocCopyAction getChildAssociationCopyAction(
            QName classQName,
            CopyDetails copyDetails,
            CopyChildAssociationDetails childAssocCopyDetails);
    
    /**
     * Once the child association copy action has been chosen, the policy callback can
     * dictate whether or not to force further recursion.  This cannot prevent
     * behaviour further down the hierarchy from stopping the copy.
     * 
     * @param classQName            the name of the class that this is being invoked for
     * @param copyDetails           the source node's copy details for quick reference
     * @param childAssocCopyDetails all other details relating to the child association
     * @return                      Returns the type of {@link ChildAssocRecurseAction recursion}
     *                              to perform after having copied the child association
     * 
     * @see #getChildAssociationCopyAction(QName, CopyDetails, ChildAssociationRef, boolean)
     */
    ChildAssocRecurseAction getChildAssociationRecurseAction(
            QName classQName,
            CopyDetails copyDetails,
            CopyChildAssociationDetails childAssocCopyDetails);
    
    /**
     * Modify the properties that are copied across.
     * 
     * @param classQName            the name of the class that this is being invoked for
     * @param copyDetails           the source node's copy details for quick reference
     * @param properties            the type- or aspect-specific properties that can be copied.
     *                              The map can be manipulated and returned as required.
     * @return                      Returns the type or aspect properties that should be copied.
     */
    Map