/*
 * Copyright (C) 2003-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.domain;

import com.arsdigita.persistence.DataAssociation;
import com.arsdigita.persistence.DataAssociationCursor;
import com.arsdigita.persistence.DataObject;
import com.arsdigita.persistence.PersistenceException;
import com.arsdigita.persistence.metadata.Property;

import java.util.Iterator;

import org.apache.log4j.Logger;

/**
 * <code>DomainObjectObserver</code> which checks that a delete will not fail
 * before trying to perform it. If it will, it returns a
 * <code>DeleteException</code> containing information about which objects
 * and associations would cause the failure.
 *
 * @author <a href="mailto:mbooth@redhat.com">Matthew Booth</a>
 */
public class DeleteCheckObserver
    extends AbstractDomainObjectObserver
{
    private static final Logger s_log =
        Logger.getLogger( DeleteCheckObserver.class );

    private static final DeleteCheckObserver s_observer =
        new DeleteCheckObserver();

    private DeleteCheckObserver() { }

    public static final void observe( ObservableDomainObject obj ) {
        obj.addObserver( s_observer );
    }

    /**
     * Check there are no associated objects which would cause a failure if
     * this object were deleted.
     *
     * @param obj the domain object to be deleted
     */
    public void beforeDelete( DomainObject obj ) throws PersistenceException {
        if( s_log.isDebugEnabled() ) {
            s_log.debug( "Checking delete of " + obj.getOID() );
        }

        DeleteException ex = new DeleteException( obj );

        DataObject dObj = obj.getDataObject();

        boolean error = false;
        Iterator properties = dObj.getObjectType().getProperties();
        while( properties.hasNext() ) {
            Property property = (Property) properties.next();
            String propName = property.getName();

            if( s_log.isDebugEnabled() ) {
                s_log.debug( "Checking property " + propName );
            }

            // If this is a component association, persistence should
            // delete it automatically
            if( property.isComponent() ) continue;

            // If this object is required by a non-component object, that's
            // a problem
            Property assocProp = property.getAssociatedProperty();
            if( null != assocProp && assocProp.isRequired() ) {
                if( s_log.isDebugEnabled() ) {
                    s_log.debug( propName + " would cause failure" );
                }

                Object assocObj = dObj.get( property.getName() );

                if( assocObj instanceof DataAssociation ) {
                    DataAssociation assoc = (DataAssociation) assocObj;
                    DataAssociationCursor cursor = assoc.cursor();

                    while( cursor.next() ) {
                        error = true;
                        ex.addDependency( propName,
                                          cursor.getDataObject() );

                        if( s_log.isDebugEnabled() ) {
                            s_log.debug( "Dependent object " + cursor.getDataObject().getOID() );
                        }
                    }
                }

                else if( null != assocObj ) {
                    error = true;
                    DataObject assocDObj = (DataObject) assocObj;
                    ex.addDependency( propName, assocDObj );

                    if( s_log.isDebugEnabled() ) {
                        s_log.debug( "Dependent object " + assocDObj.getOID() );
                    }
                }
            }
        }

        if( error ) throw ex;
    }
}
