/*
 * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved.
 *
 * The contents of this file are subject to the CCM Public
 * License (the "License"); you may not use this file except in
 * compliance with the License. You may obtain a copy of the
 * License at http://www.redhat.com/licenses/ccmpl.html.
 *
 * Software distributed under the License is distributed on an
 * "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express
 * or implied. See the License for the specific language
 * governing rights and limitations under the License.
 *
 */
package com.arsdigita.cms;

import com.arsdigita.domain.DomainObject;
import com.arsdigita.kernel.ACSObject;
import com.arsdigita.persistence.OID;
import com.arsdigita.persistence.metadata.Property;
import org.apache.log4j.Logger;

/**
 * Recursively copies a DataObject, and all of its composite sub-objects.
 * This class implements <code>ItemCopier</code> by maintaining a Map of
 * OID-&gt;DataObject of all objects copied so far. This ensures that no
 * infinite recursion loops ever occur.
 * <p>
 * The {@link #copyItem(ContentItem)} method attempts to copy an item by
 * using the following steps:
 * <ol>
 *   <li>Copy the scalar attributes of the item</li>
 *   <li>Copy the 1..1 associations of the item</li>
 *   <li>Save the item</li>
 *   <li>Copy the 0..n associations of the item</li>
 *   <li>Save the item</li>
 *   <li>Transfer all services (such as categorization) from the original item to
 *     the copy</li>
 * </ol>
 * At each copying step, this class will call
 * {@link ContentItem#copyProperty(ContentItem, String, ItemCopier)}
 * in order to give the item a chance to override the default copying behavior.
 * This class will pass itself as the third argument.
 * <p>
 * <code>ObjectCopier</code> has package-scope since it will only be accessed from
 * {@link ContentItem}, as part of the internal implementation of the
 * {@link ContentItem#copy()} method. Classes outside the package should not
 * attempt to call methods on this class directly, since the results may
 * prove dangerous.
 * <p>
 *
 * @author <a href="mailto:sfreidin@arsdigita.com">Stanislav Freidin</a>
 * @version $Id: //cms/dev/src/com/arsdigita/cms/ObjectCopier.java#20 $
 * @see ContentItem
 * @see ContentItem#copyProperty(ContentItem, String, ItemCopier)
 */
class ObjectCopier extends DomainCopier implements ItemCopier {
    public static final String versionId =
        "$Id: //cms/dev/src/com/arsdigita/cms/ObjectCopier.java#20 $" +
        "$Author: dennis $" +
        "$DateTime: 2004/04/07 16:07:11 $";

    private static Logger s_log = Logger.getLogger(ObjectCopier.class);

    /**
     * This copier is used to create plain copies for items
     */
    public int getCopyType() {
        return ItemCopier.PLAIN_COPY;
    }

    /**
     * Copy a {@link ContentItem}, along with all of its component
     * sub-objects, and return the copy. Note that the categories to
     * which the source item belongs are not automatically transferred
     * to the copy; the user must call {@link #copyServices(ContentItem,
     * ContentItem)} in order to transfer the categories and other
     * services.
     *
     * @param item the item to be copied
     * @return a copy of the item
     */
    public ContentItem copyItem(ContentItem item) {
        final ContentItem copy = (ContentItem) copy(item);
        return copy;
    }

    /**
     * Copies properties.  This method is called from {@link
     * #copy(DomainObject)} for each property of the object being
     * copied.
     *
     * This implementation calls {@link CustomCopy#copyProperty(CustomCopy,
     * Property, itemCopier)} if <code>source</code> and
     * <code>target</code> implement <code>CustomCopy</code>, and only
     * call <code>super.copyProperty</code> if the above returns false.
     *
     *
     * @param source the <code>DomainObject</code> being copied
     * @param target the new copy
     * @param prop the <code>Property</code> currently under
     * consideration
     */
    protected void copyProperty(final DomainObject source,
                                final DomainObject target,
                                final Property prop) {
        m_trace.enter("copyProperty", source, target, prop);

        if (target instanceof CustomCopy) {

            final CustomCopy sitem = (CustomCopy) source;
            final CustomCopy titem = (CustomCopy) target;

            if (titem.copyProperty(sitem, prop, this)) {
                s_log.debug("The target item overrides the default " +
                            "behavior to propogate the property; " +
                            "skipping it");

                m_trace.exit("copyProperty");

                return;
            }
        }

        super.copyProperty(source, target, prop);

        m_trace.exit("copyProperty");
    }

    /**
     * Creates a copy, by reference or by value, of the property
     * represented in <code>object</code>.
     *
     * This implementation returns the result of {@link
     * #copy(DomainObject)} if the property is a component and simply
     * returns <code>object</code> if it is not.
     *
     * @param source the <code>CustomCopy</code> source (original)
     * object to which this property belongs
     * @param target the new <code>CustomCopy</code> copy to which
     * the return value of this method will be attached
     * @param object the <code>DomainObject</code> property being
     * copied
     * @param prop a <code>Property</code> representing
     * <code>object</code>
     * @return <code>object</code> if <code>prop</code> is not a
     * component or a copy of <code>object</code> it is a component
     * @throws UnsupportedOperationException if either source or
     * target are not <code>DomainObjects</code>  
     */
    public DomainObject copy(final CustomCopy source,
			     final CustomCopy target,
			     final DomainObject object,
			     final Property prop) {
	if ((source instanceof DomainObject) && 
	    (target instanceof DomainObject)) {
	    return copy((DomainObject) source, (DomainObject) target, 
			object, prop);
	} else {
	    throw new UnsupportedOperationException
		("CustomCopy implementations should be DomainObjects");
	}
    }

    /**
     * Transfer services from one {@link ContentItem} to another. For
     * example, this method will be called to transfer the categories
     * from the draft version of the item to the live version of the
     * item.
     *
     * Note that the target item must have a valid item ID before this
     * method is called. If the target item is new, it should be saved
     * prior to calling <code>copyServices</code>.
     *
     * @param target the target item
     * @param source the source item
     */
    public static void copyServices(ContentItem target, ContentItem source) {
        if (target.copyServices(source)) {
            s_log.debug("The target item has opted to do its own " +
                        "service propogation");
        } else {
            // What goes here?
        }
    }
}
