/*
 * Copyright (c) 2007-2008, Dennis M. Sosnoski All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
 * following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of conditions and the following
 * disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
 * following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of
 * JiBX nor the names of its contributors may be used to endorse or promote products derived from this software without
 * specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.jibx.binding.model;

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

import org.jibx.schema.support.LazyList;
import org.jibx.util.HolderBase;

/**
 * External data for a binding definition. This tracks references to other bindings, along with the associated namespace
 * information.
 * 
 * TODO: replace the lists for individual child element types with filtered lists on the binding element
 * 
 * @author Dennis M. Sosnoski
 */
public class BindingHolder extends HolderBase
{
    /** Directory managing this holder. */
    private final BindingDirectory m_directory;
    
    /** Actual binding definition. */
    private BindingElement m_binding;
    
    /** Binding finalized flag. */
    private boolean m_finished;
    
    /** List of format definitions in binding. */
    private final LazyList m_formats;
    
    /** List of includes for binding. */
    private final LazyList m_includes;
    
    /** List of mapping definitions in binding. */
    private final LazyList m_mappings;
    
    /**
     * Constructor.
     * 
     * @param uri (<code>null</code> if no-namespace binding)
     * @param index binding index number
     * @param dir directory managing this holder
     */
    public BindingHolder(String uri, int index, BindingDirectory dir) {
        super(uri);
        m_directory = dir;
        m_binding = new BindingElement();
        m_formats = new LazyList();
        m_includes = new LazyList();
        m_mappings = new LazyList();
        if (uri != null) {
            NamespaceElement ns = new NamespaceElement();
            ns.setDefaultName("elements");
            ns.setUri(uri);
            ns.setPrefix("ns" + index);
            m_binding.addTopChild(ns);
            m_binding.addNamespaceDecl("tns", uri);
        }
    }
    
    /**
     * Get the binding directory managing this holder.
     *
     * @return directory
     */
    public BindingDirectory getDirectory() {
        return m_directory;
    }
    
    /**
     * Get the binding element.
     * 
     * @return binding
     */
    public BindingElement getBinding() {
        return m_binding;
    }
    
    /**
     * Set the binding element. This method is provided so that the generated binding element can be replaced by one
     * which has been read in from a file after being written.
     * 
     * @param bind
     */
    public void setBinding(BindingElement bind) {
        m_binding = bind;
    }
    
    /**
     * Internal check method to verify that the binding is still modifiable.
     */
    private void checkModifiable() {
        if (m_finished) {
            throw new IllegalStateException("Internal error - attempt to modify binding after finalized");
        }
    }
    
    /**
     * Implementation method to handle adding a namespace declaration. This sets up the namespace declaration for output
     * in the generated XML.
     * 
     * @param prefix
     * @param uri
     */
    protected void addNamespaceDecl(String prefix, String uri) {
        checkModifiable();
        m_binding.addNamespaceDecl(prefix, uri);
    }
    
    /**
     * Add a format definition to the binding.
     *
     * @param format
     */
    public void addFormat(FormatElement format) {
        checkModifiable();
        m_formats.add(format);
    }
    
    /**
     * Add an include to the binding.
     *
     * @param include
     */
    public void addInclude(IncludeElement include) {
        checkModifiable();
        m_includes.add(include);
    }
    
    /**
     * Add a mapping definition to the binding.
     *
     * @param mapping
     */
    public void addMapping(MappingElement mapping) {
        checkModifiable();
        m_mappings.add(mapping);
    }
    
    /**
     * Add dependency on another binding.
     * 
     * @param uri namespace for binding of referenced component
     */
    public void addDependency(String uri) {
        checkModifiable();
        m_directory.addDependency(uri, this);
    }
    
    /**
     * Complete the construction of the binding. This includes processing references to other bindings, as well as the
     * actual mappings and formats which are included in the binding. This method must be called after all references
     * and binding components have been added.
     */
    public void finish() {
        if (!m_finished) {
            for (Iterator iter = getReferences().iterator(); iter.hasNext();) {
                getPrefix(((BindingHolder)iter.next()).getNamespace());
            }
            ArrayList topchilds = m_binding.topChildren();
            topchilds.addAll(m_formats);
            topchilds.addAll(m_includes);
            topchilds.addAll(m_mappings);
            m_finished = true;
        }
    }
}