//*****************************************************************************
/*
** FILE:   xg_Element.java
**
** (c) 1997, 1998 Steve Withall.
**
** HISTORY:
**    13Oct97  stevew  Created.
*/
package xg;

import xa.xa_Keyword;
import xa.xa_NodeTypeChoiceList;

import eh.eh_Debug;

import com.sun.java.swing.text.AttributeSet;

import java.io.IOException;
import java.io.Writer;

//*****************************************************************************
/** An XML element.
 */
public class xg_Element extends xg_Node
{
    /** List of this element's attributes. */
    protected xg_AttList  ElementAttList     = new xg_AttList();

    /** If this node has whitespace prior to its end tag, this is it. */
    protected String      TrailingWhitespace = null;

    //*****************************************************************************
    /** Construct an element with no name.
     */
    public xg_Element()
    {
    }

    //*****************************************************************************
    /** Construct an element with a name.
     *
     *  @param  InputNodeName  The name of the element
     */
    public xg_Element(String InputNodeName)
    {
        super(InputNodeName);
    }

    //*****************************************************************************
    /** Save this element (and its children) in XML source form in InputWriter.
     *
     *  @param  InputWriter  The writer to which the XML will be written
     */
    public void save(Writer  InputWriter) throws IOException
    {
        eh_Debug.add(7, "xg_Element.save: Save element '" + NodeName + "'");  //TEMP
        saveStartTag(InputWriter);
        saveContent(InputWriter);
        saveEndTag(InputWriter);
    }

    //*****************************************************************************
    /** Save this element's start tag in XML source form in InputWriter.
     *
     *  @param  InputWriter  The writer to which the XML will be written
     */
    public void saveStartTag(Writer  InputWriter) throws IOException
    {
        // If there is preceding whitespace, save it.
        if (PrecedingWhitespace != null)
            InputWriter.write(PrecedingWhitespace);

        // Save the start of the start tag.
        InputWriter.write(xa_Keyword.TAG_START_CHAR);
        InputWriter.write(NodeName);

        // Save the attributes.
        ElementAttList.save(InputWriter);

        // If there is no content, identify it as an empty tag.
        if (getChildCount() == 0)
            InputWriter.write(xa_Keyword.SLASH_CHAR);

        InputWriter.write(xa_Keyword.TAG_END_CHAR);
    }

    //*****************************************************************************
    /** Save this element's content (ie. its children).
     *
     *  @param  InputWriter  The writer to which the XML will be written
     */
    public void saveContent(Writer  InputWriter) throws IOException
    {
        if (getChildCount() > 0)
            saveChildren(InputWriter);
    }

    //*****************************************************************************
    /** Save this element's end tag in XML source form in InputWriter. If this is
     *  an empty element, do nothing.
     *
     *  @param  InputWriter  The writer to which the XML will be written
     */
    public void saveEndTag(Writer  InputWriter) throws IOException
    {
        if (getChildCount() > 0)
        {
            // If there is whitespace before the end tag, save it.
            if (TrailingWhitespace != null)
                InputWriter.write(TrailingWhitespace);

            // Save the end tag itself.
            eh_Debug.add(7, "xg_Element.saveEndTag: Save end tag for '" + NodeName + "'");
            InputWriter.write(xa_Keyword.END_TAG_START_STRING);
            InputWriter.write(NodeName);

            //TBD Write any whitespace which preceded the end tag's end character.

            InputWriter.write(xa_Keyword.TAG_END_CHAR);
        }
    }

    //*****************************************************************************
    /** Add an attribute, given its name and value.
     *
     *  @param   InputAttName   The name of this attribute
     *  @param   InputAttValue  The value of this attribute
     *  @return  Flag indicating success (true) or failure. Failure only occurs if
     *            an attribute with this name already exists (which is an XML
     *            well-formedness error).
     */
    public boolean addAttribute(String  InputAttName,
                                String  InputAttValue)
    {
        return (addAttribute(new xg_Attribute(InputAttName, InputAttValue) ) );
    }

    //*****************************************************************************
    /** Add an attribute.
     *
     *  @param   InputAttribute  The attribute to be added
     *  @return  Flag indicating success (true) or failure. Failure only occurs if
     *            an attribute with this name already exists (which is an XML
     *            well-formedness error).
     */
    public boolean addAttribute(xg_Attribute  InputAttribute)
    {
        return (ElementAttList.addAttribute(InputAttribute) );
    }

    //*****************************************************************************
    /** Set the list of attributes. That is, replace the current attribute list
     *  with a completely new one. This is a rather dangerous practice, which
     *  should be done only if you know what you're doing. This method was added to
     *  support the case where some temporary changes are required, and for the
     *  original attribute list to be restored later.
     *
     *  @param  InputAttList  A replacement list of attributes
     */
    public void setAttList(xg_AttList  InputAttList)
    {
        ElementAttList = InputAttList;
    }

    //*****************************************************************************
    /** Set the value of the whitespace before the end tag.
     *
     *  @param  InputTrailingWhitespace   Value of the trailing whitespace
     */
    public void setTrailingWhitespace(String  InputTrailingWhitespace)
    {
        TrailingWhitespace = InputTrailingWhitespace;
    }

    //*****************************************************************************
    /** Set the parent of this node. This overrides the method in xg_Node in order
     *  to set the resolve parent (if the parent is an xg_Element).
     *
     *  @param  ParentNode   The parent of this node
     */
    public void setParentNode(xg_Node  InputParentNode)
    {
        super.setParentNode(InputParentNode);
        if (InputParentNode instanceof xg_Element)
            ElementAttList.setResolveParent(InputParentNode.getAttributes() );
    }

    //*****************************************************************************
    /** Get the type of this node.
     *
     *  @return  The type of this node
     */
    public int getType()
    {
        return xa_NodeTypeChoiceList.ELEMENT_TYPE;
    }

    //*****************************************************************************
    /** Get the list of attributes.
     *
     *  @return  The list of attributes
     */
    public xg_AttList getAttList()
    {
        return ElementAttList;
    }

    //*****************************************************************************
    /** Get the value of a named attribute.
     *
     *  @param   InputAttName  The name of the attribute required
     *  @return  The value of the named attribute (or null if there is no attribute
     *            with this name).
     */
    public String getAttributeValue(String  InputAttName)
    {
        String        AttValue = null;
        xg_Attribute  Att      = getAttribute(InputAttName);
        if (Att != null)
            AttValue = Att.getValue();
        return AttValue;
    }

    //*****************************************************************************
    /** Get the value of a named attribute, and throwing a verification exception
     *  if it is missing.
     *
     *  @param      InputAttName  The name of the attribute required
     *  @return     The value of the named attribute (or null if there is no
     *               attribute with this name).
     *  @exception  xg_VerificationException  The attribute is missing
     */
    public String getAttributeValueVerified(String  InputAttName)
                        throws xg_VerificationException
    {
        String  AttValue = getAttributeValue(InputAttName);
        if (AttValue == null)
            throw new xg_VerificationException(NodeName + " element does not contain a '"
                                                     + InputAttName + "' attribute",
                                               getStartOffset(),
                                               getEndOffset() );
        return AttValue;
    }

    //*****************************************************************************
    /** Get a named attribute.
     *
     *  @param   InputAttName  The name of the attribute required
     *  @return  The named attribute (or null if there is no attribute with this name).
     */
    public xg_Attribute getAttribute(String  InputAttName)
    {
        return ElementAttList.getAtt(InputAttName);
    }

    //*****************************************************************************
    /** Get the collection of attributes this element contains.
     *
     *  @return  The attributes for the element
     */
    public AttributeSet getAttributes()
    {
        return ElementAttList;
    }

    //*****************************************************************************
    /** Get the value of the whitespace before the end tag.
     *
     *  @return  String value or null
     */
    public String getTrailingWhitespace()
    {
        return TrailingWhitespace;
    }

    //*****************************************************************************
    /** Return a string representation of the element (intended for use as debug
     *  output).
     *
     *  @return  String representing the node name and its type.
     */
    public String toString()
    {
        return ("Element '" + NodeName + "'");
    }
}

//*****************************************************************************
