/* * Copyright (C) 2001-2004 Red Hat Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.arsdigita.cms; import com.arsdigita.cms.lifecycle.Lifecycle; import com.arsdigita.cms.lifecycle.LifecycleService; import com.arsdigita.domain.DomainObject; import com.arsdigita.kernel.ACSObject; import com.arsdigita.persistence.metadata.Property; import com.arsdigita.persistence.OID; import com.arsdigita.util.Assert; import org.apache.log4j.Logger; import java.util.HashSet; /** * Extends * ObjectCopier to create a live version for an item. Clones the * item, making sure that live versions of all subitems point only to other live * versions. * * @author Stanislav Freidin * @version $Id: VersionCopier.java 2140 2011-01-16 12:04:20Z pboy $ */ class VersionCopier extends ObjectCopier { private static Logger s_log = Logger.getLogger(VersionCopier.class); private final Lifecycle m_lifecycle; private boolean m_once = false; private long m_start = 0; private final HashSet m_traversedComponents; private OID m_topLevelSourceOID = null; /** * Creates a new * VersionCopier with a given Lifecycle * * @param lifecycle The Lifecycle */ VersionCopier(final Lifecycle lifecycle) { m_traversedComponents = new HashSet(); m_lifecycle = lifecycle; } /** * Creates a new * VersionCopier with no Lifecycle */ VersionCopier() { m_traversedComponents = new HashSet(); m_lifecycle = null; } /** * 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 */ @Override public ContentItem copyItem(final ContentItem item) { m_trace.enter("copyItem", item); if (s_log.isInfoEnabled()) { s_log.info("Publishing " + item); m_start = System.currentTimeMillis(); } if (Assert.isEnabled()) { //Assert.isFalse(item instanceof ContentBundle); //Assert.isFalse(item instanceof Folder); Assert.isFalse(m_once); m_once = true; } m_topLevelSourceOID = item.getOID(); final ContentItem version = createVersion(item); //Assert.isTrue(m_topLevelSourceOID == null, // "CopyItem should be called only once for a given copier instance"); if (m_lifecycle != null) { LifecycleService.setLifecycle(version, m_lifecycle); } m_trace.exit("copyItem", version); if (Assert.isEnabled()) { // Reset the reentrance detection. m_once = false; } if (s_log.isInfoEnabled()) { s_log.info("Done publishing " + item + " (" + (System. currentTimeMillis() - m_start) + " millis)"); } return version; } /** * This copier is used to create published copies of items */ @Override public int getCopyType() { return ItemCopier.VERSION_COPY; } /** * Kicks off the copying process. Creates a copy by value of * source and then traverses its associations and repeats the * process. * * @param source the * DomainObject from which to copy */ @Override public DomainObject copy(final DomainObject object) { if (object != null) { m_traversedComponents.add(object); } return super.copy(object); } /** * Copies properties. This method is called from {@link * #copy(DomainObject)} for each property of the object being copied. * * Copying behavior depends on the following:
* object instanceof ContentItem && prop.isComponent()
*
Copy ContentItem recursively, setting the version attribute, * etc.
* object instanceof ContentItem && * !prop.isComponent() (and target is an already-traversed * component of the top-level item )
Return reference to * already-created copy of this item
* object instanceof ContentItem && * !prop.isComponent() && prop.isRequired (and target is not an already-traversed * component of the top-level item )
Assert failure: * required top-level item associations are not allowed here.
* object instanceof ContentItem && * !prop.isComponent() && !prop.isRequired (and target is not an already-traversed * component of the top-level item )
Create PublishedLink * for association; return null
* !object instanceof ContentItem
object is not a * ContentItem, rely on default DomainCopier behavior
* * @param source the * DomainObject being copied * @param target the new copy * @param prop the * Property currently under consideration */ @Override protected DomainObject copy(final DomainObject source, final DomainObject target, final DomainObject object, final Property prop) { m_trace.enter("copy", object, prop); if (s_log.isDebugEnabled()) { s_log.debug("Copying property " + prop + " of " + object); } Assert.exists(source, DomainObject.class); Assert.exists(target, DomainObject.class); Assert.exists(object, DomainObject.class); Assert.exists(prop, Property.class); if (object instanceof ContentItem) { s_log.debug("The property is a content item"); final ContentItem item = (ContentItem) object; if (Assert.isEnabled()) { item.assertDraft(); } if (prop.isComponent()) { s_log.debug("The property is a component; creating a " + "live or pending version"); final ContentItem copy = createVersion(item); m_trace.exit("copy", copy); return copy; } else if (m_traversedComponents.contains(object)) { final DomainObject copy = copy(object); m_trace.exit("copy", copy); return copy; } else if (prop.isRequired()) { Assert.fail( "1..1 associations to non-component top-level ContentItems are not allowed"); return null; } else { s_log.debug("The property is not a component; creating " + "PublishedLink for the item"); if (source instanceof ContentItem) { PublishedLink.create( (ContentItem) getCopy(m_topLevelSourceOID), target, prop.getName(), item, (ContentItem) source); } else { PublishedLink.create( (ContentItem) getCopy(m_topLevelSourceOID), target, prop.getName(), item, null); } m_trace.exit("copy", null); return null; } } else { s_log.debug("The property is not a content item; using " + "domain object copier"); final DomainObject copy = super.copy(source, target, object, prop); m_trace.exit("copy", copy); return copy; } } private ContentItem createVersion(final ContentItem item) { m_trace.enter("createVersion", item); if (Assert.isEnabled()) { item.assertDraft(); } final ContentItem version = (ContentItem) copy(item); s_log.debug("The copy is pending; associating it with " + "the draft item"); version.setVersion(ContentItem.PENDING); item.addPendingVersion(version); version.copyServicesFrom(item); m_trace.exit("createVersion", version); return version; } }