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

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import com.arsdigita.util.Assert;
import com.arsdigita.xml.Element;

/**
 * A basic implementation of the {@link Container} interface.
 * <p>By default,
 * renders all of its children directly, without wrapping them in any
 * kind of tag.
 * However, the {@link #SimpleContainer(String, String)} constructor
 * and/or the {@link #setTag(String)} method can be used to cause the container
 * to wrap the XML for its children in an arbitrary tag. This functionality
 * is useful for XSL templating.
 * For example, a template rule might be
 * written to arrange the children of this component in paragraphs:
 *
 * <blockquote><pre><code>// Java Code:
 * m_container = new SimpleContainer("cms:foo", CMS_XML_NS);
 *
 * // XSL code:
 * &lt;xsl:template match="cms:foo"&gt;
 *   &lt;xsl:for-each select="*"&gt;
 *     &lt;p&gt;
 *     &lt;xsl:apply-templates select="."/&gt;
 *     &lt;/p&gt;
 *   &lt;/xsl:for-each&gt;
 * &lt;/xsl:template&gt;
 * </code></pre></blockquote>
 *
 * @author David Lutterkort  
 * @author Stanislav Freidin 
 * @author Rory Solomon 
 * @author Uday Mathur 
 *
 * @version $Id: //core-platform/dev/src/com/arsdigita/bebop/SimpleContainer.java#12 $*/

public class SimpleContainer extends BlockStylable implements Container {

    public static final String versionId = "$Id: //core-platform/dev/src/com/arsdigita/bebop/SimpleContainer.java#12 $ by $Author: dennis $, $DateTime: 2004/04/07 16:07:11 $";

    private List m_components;
    private String m_tag, m_ns;

    /**
     * Constructs a new, empty <code>SimpleContainer</code>.
     */
    public SimpleContainer() {
        this(null, null);
    }

    /**
     * Constructs a new, empty <code>SimpleContainer</code> that will
     * wrap its children in the specified tag.
     *
     * @param tag the name of the XML element that will be used to wrap the
     *   children of this container
     * @param ns the namespace for the tag
     */
    public SimpleContainer(String tag, String ns) {
        super();
        m_components = new ArrayList();
        m_tag = tag;
        m_ns = ns;
    }

    /**
     * Adds a component to this container.
     *
     * @param pc the component to be added
     */
    public void add(Component pc) {
        Assert.assertNotLocked(this);
        Assert.exists(pc);
        m_components.add(pc);
    }

    /**
     * Adds a component to this container.
     *
     * @param pc the component to be added
     * @param constraints this parameter is ignored. Child classes should
     *   override the add method if they wish to provide special handling
     *   of constraints.
     */
    public void add(Component c, int constraints) {
        add(c);
    }

    /**
     * Determines membership.
     * @return <code>true</code> if the specified object is in this container;
     * <code>false</code> otherwise.
     * @param o the object type, typically a component. Type 
     * Object allows slicker code when o comes from any kind of collection.
     */
    public boolean contains(Object o) {
        return m_components.contains(o);
    }

    /**
     * Determines whether the container is empty.
     *
     * @return <code>false</code> if the container has any children;
     * <code>true</code> otherwise.
     */
    public boolean isEmpty() {
        return m_components.isEmpty();
    }

    /**
     *
     * 
     *
     */
    public int indexOf(Component pc) {
        return m_components.indexOf(pc);
    }

    /**
     * Returns the number of children inside this container.
     * @return the number of children inside this container.
     */
    public int size() {
        return m_components.size();
    }

    /**
     *
     * 
     *
     */
    public Component get(int index) {
        return (Component) m_components.get(index);
    }

    /**
     * Returns all the components of this container.
     * @return all the components of this container.
     */
    public Iterator children() {
        return m_components.iterator();
    }

    /**
     * Sets the XML tag that will be used to wrap the children of
     * this container.
     *
     * @param tag the XML tag, or null if children will not be wrapped
     *   in any manner.
     */
    protected final void setTag(String tag) {
        Assert.assertNotLocked(this);
        m_tag = tag;
    }

    /**
     * Sets the XML namespace for the tag that will be used to wrap
     * the children of this container.
     *
     * @param ns the XML namespace
     */
    protected final void setNamespace(String ns) {
        Assert.assertNotLocked(this);
        m_ns = ns;
    }

    /**
     * Retrieves the name of the XML tag that will be used to
     * wrap the child components.
     *
     * @return the name of the XML tag that will be used to
     *   wrap the child components, or null if no tag was specified.
     */
    public final String getTag() {
        return m_tag;
    }

    /**
     * Retrieves the name of the XML namespace for the tag that will be used to
     * wrap the child components.
     *
     * @return the name of the XML namespace for the tag that will be used to
     *   wrap the child components, or null if no namespace was specified.
     */
    public final String getNamespace() {
        return m_ns;
    }

    /**
     * Generates the containing element.  It is added with this
     * component's tag below the specified parent element. If the passed in
     * element is null, the method
     * passes through p.

     * @param p the parent XML element
     * @return the element to which the children will be added.
     */
    protected Element generateParent(Element p) {
        String tag = getTag();
        if (tag == null) {
            return p;
        }
        Element parent = p.newChildElement(tag, getNamespace());
        exportAttributes(parent);
        return parent;
    }

    /**
     * Generates the XML for this container. If the tag property
     * is nonempty, wraps the children in the specified XML tag.
     *
     * @param state represents the current request
     * @param p the parent XML element
     * @see #setTag(String)
     * @see #setNamespace(String)
     */
    public void generateChildrenXML(PageState state, Element p) {
        for (Iterator i = children(); i.hasNext(); ) {
            Component c = (Component) i.next();
            
            // XXX this seems to be a redundant vis check
            if ( c.isVisible(state) ) {
                c.generateXML(state, p);
            }
        }
    }

    /**
     * Generates the XML for this container. If the tag property
     * is nonempty, wraps the children in the specified XML tag.
     *
     * @param state represents the current request
     * @param p the parent XML element
     * @see #setTag(String)
     * @see #setNamespace(String)
     */
    public void generateXML(PageState state, Element p) {
        if ( isVisible(state) ) {
            Element parent = generateParent(p);
            generateChildrenXML(state, parent);
        }
    }
}
