/*
 * Copyright (C) 2002-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.domain.DomainObjectFactory;
import com.arsdigita.domain.DomainService;
import com.arsdigita.domain.InstantiatorNotFoundException;
import com.arsdigita.persistence.DataAssociation;
import com.arsdigita.persistence.DataAssociationCursor;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.metadata.ObjectType;
import com.arsdigita.persistence.metadata.Property;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

/**
 * Recursively updates the "version" attribute of a content item to a
 * new value. For internal use only; users should never have to know
 * or care about this class.
 *
 * @author <a href="mailto:sfreidin@arsdigita.com">Stanislav Freidin</a>
 * @version $Id: //cms/dev/src/com/arsdigita/cms/VersionUpdater.java#9 $
 */

class VersionUpdater extends DomainService {

    public static final String versionId = "$Id: //cms/dev/src/com/arsdigita/cms/VersionUpdater.java#9 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    private static org.apache.log4j.Logger s_log =
        org.apache.log4j.Logger.getLogger(VersionUpdater.class);

    // A map of visited OIDs. Helps prevent infinite recursion.
    // all objects copied so far.
    protected Set m_visited;

    // The new version tag for the items
    String m_newVersion;

    /**
     * Construct a new <code>VersionUpdater</code>
     */
    protected VersionUpdater(String newVersion) {
        m_visited = new HashSet();
        m_newVersion = newVersion;
    }

    /**
     * Recursively set the version of a ContentItem
     *
     * @param item the item
     */
    public void updateItemVersion(ContentItem item) {

        // This seems wasteful, but in fact we need to specialize
        // the item anyway, in order to cache the right OID
        updateSingleObject(getDataObject(item));
    }

    /**
     * Recursively traverse the object's children and set their versions
     * to the right thing
     *
     * @param src the parent object
     */
    protected void updateChildren(DataObject src) {
        ObjectType type = src.getOID().getObjectType();

        for(Iterator i = type.getProperties(); i.hasNext(); ) {
            Property prop = (Property) i.next();

            // Skip scalars
            if (!prop.isRole()) continue;

            // Skip non-composites
            if (!prop.isComponent()) continue;

            String propName = prop.getName();

            // Get the value of the property, skip nulls
            Object data = src.get(propName);
            if (data == null)
                continue;

            if(data instanceof DataObject) {
                updateSingleObject((DataObject)data);
            } else if (data instanceof DataAssociation) {
                // Association - iterate over it
                DataAssociationCursor daCursor =
                    ((DataAssociation)data).getDataAssociationCursor();

                while (daCursor.next()) {
                    updateSingleObject(daCursor.getDataObject());
                }
            } else {
                throw new IllegalStateException("Warning: unsupported property type " +
                                                data.getClass().getName());
            }
        }
    }

    /**
     * Update the version of a single object, if it happens to be
     * a content item
     */
    protected void updateSingleObject(DataObject src) {
        // Prevent infinite recursion
        if(m_visited.contains(src.getOID()))
            return;

        m_visited.add(src.getOID());

        ContentItem item = null;

        try {
            DomainObject obj = DomainObjectFactory.newInstance(src);
            if(obj instanceof ContentItem)
                item = (ContentItem)obj;
        } catch (InstantiatorNotFoundException e) {
            // do nothing
        }

        if(item != null) {
            // Prevent unnecessary updates
            if(! (m_newVersion.equals(item.getVersion()))) {

                // Update the current item's version; this will
                // also have the effect of setting/unsetting the live version
                // for the master
                item.setVersion(m_newVersion);
                item.save();
            }
        }

        updateChildren(src);
    }


}
