/*
 * 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.domain;

import org.apache.log4j.Logger;
import com.arsdigita.persistence.metadata.Property;
import java.util.HashSet;

/**
 * This is a general purpose traversal adaptor
 * that allows/denies processing of a property
 * based on the path to the property, and its 
 * presence in an inclusion/exclusion set.
 *
 * Instances of this class can be configured using
 * the DomainObjectTraversalInitializer
 *
 * <p>See <code>com.arsdigita.cms.installer.DomainObjectTraversalInitializer</code>.
 */
public class SimpleDomainObjectTraversalAdapter 
    implements  DomainObjectTraversalAdapter {
    
    private static final Logger s_log = 
        Logger.getLogger(SimpleDomainObjectTraversalAdapter.class);
    
    /**
     * Rule that indicates the set of properties should be treated
     * as an inclusion list. ie, don't allow any properties except
     * those listed. This is the default for associations
     */
    public final static int RULE_INCLUDE = 0;
    /**
     * Rule that indicates the set of properties should be treated
     * as an exclusion list. ie, allow through all properties,
     * except those listed. This is the default for attributes.
     */
    public final static int RULE_EXCLUDE = 1;

    private HashSet m_attr = new HashSet();
    private HashSet m_assoc = new HashSet();

    private int m_attrRule = RULE_EXCLUDE;
    private int m_assocRule = RULE_INCLUDE;

    private SimpleDomainObjectTraversalAdapter m_parent;
    
    /**
     * Creates a new traversal adapter, with no parent
     * delegate. If no explicit rule is present it will
     * return false if RULE_INCLUDE is set, or true if
     * RULE_EXCLUDE is set.
     */
    public SimpleDomainObjectTraversalAdapter() {
        this(null);
    }

    /**
     * Creates a new traversal adapter, extending the rules
     * defined by a parent. If there is no explicit rule
     * for the property questioned, it will delegate the
     * query to the parent.
     * @param parent the parent adapter to delegate to
     */
    public SimpleDomainObjectTraversalAdapter(
        SimpleDomainObjectTraversalAdapter parent
    ) {
        m_parent = parent;
    }

    /**
     * Set the rule for processing attributes
     *
     * @param rule the new processing rule
     */
    public void setAttributeRule(int rule) {
        m_attrRule = rule;
    }

    /**
     * Set the rule for processing associations
     *
     * @param rule the new processing rule
     */
    public void setAssociationRule(int rule) {
        m_assocRule = rule;
    }
    
    /**
     * Add a property to the attribute property set.
     *
     * @param path the full path to the property
     */
    public void addAttributeProperty(String prop) {
        m_attr.add(prop);
    }

    /**
     * Add a property to the association property set.
     *
     * @param path the full path to the property
     */
    public void addAssociationProperty(String prop) {
        m_assoc.add(prop);
    }
    
    /**
     * Determines whether or not to allow processing
     * of a property, based on the property set and
     * the processing rule
     */
    public boolean processProperty(DomainObject obj,
                                   String path,
                                   Property prop,
                                   String context) {
        if (prop.isAttribute()) {
            boolean result = m_attr.contains(path);
            if (s_log.isDebugEnabled()) {
                s_log.debug("Check attr " + path + " contains " + 
                            result + " " + m_attrRule);
            }
            if (!result && m_parent != null) {
                if (s_log.isDebugEnabled()) {
                    s_log.debug("No explicit rule, delegating to parent");
                }
                return m_parent.processProperty(obj, path, prop, context);
            }
            return m_attrRule == RULE_INCLUDE ? result : !result;
        } else {
            boolean result = m_assoc.contains(path);
            if (s_log.isDebugEnabled()) {
                s_log.debug("Check assoc " + path + " contains " + 
                            result + " " + m_attrRule);
            }
            if (!result && m_parent != null) {
                if (s_log.isDebugEnabled()) {
                    s_log.debug("No explicit rule, delegating to parent");
                }
                return m_parent.processProperty(obj, path, prop, context);
            }
            return m_assocRule == RULE_INCLUDE ? result : !result;
        }
    }
}
