/* -*-             c-basic-offset: 4; indent-tabs-mode: nil; -*-  //------100-columns-wide------>|*/
// for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)

package org.codehaus.plexus.util.xml.pull;

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

/**
 * Define an interface to serialization of XML Infoset. This interface abstracts away if serialized XML is XML 1.0
 * compatible text or other formats of XML 1.0 serializations (such as binary XML for example with WBXML).
 * <p>
 * <b>PLEASE NOTE:</b> This interface will be part of XmlPull 1.2 API. It is included as basis for discussion. It may
 * change in any way.
 * <p>
 * Exceptions that may be thrown are: IOException or runtime exception (more runtime exceptions can be thrown but are
 * not declared and as such have no semantics defined for this interface):
 * <ul>
 * <li><em>IllegalArgumentException</em> - for almost all methods to signal that argument is illegal
 * <li><em>IllegalStateException</em> - to signal that call has good arguments but is not expected here (violation of
 * contract) and for features/properties when requesting setting unimplemented feature/property
 * (UnsupportedOperationException would be better but it is not in MIDP)
 * </ul>
 * <p>
 * <b>NOTE:</b> writing CDSECT, ENTITY_REF, IGNORABLE_WHITESPACE, PROCESSING_INSTRUCTION, COMMENT, and DOCDECL in some
 * implementations may not be supported (for example when serializing to WBXML). In such case IllegalStateException will
 * be thrown and it is recommended to use an optional feature to signal that implementation is not supporting this kind
 * of output.
 */

public interface XmlSerializer
{

    /**
     * Set feature identified by name (recommended to be URI for uniqueness). Some well known optional features are
     * defined in <a href="http://www.xmlpull.org/v1/doc/features.html">
     * http://www.xmlpull.org/v1/doc/features.html</a>. If feature is not recognized or can not be set then
     * IllegalStateException MUST be thrown.
     *
     * @exception IllegalStateException If the feature is not supported or can not be set
     */
    void setFeature( String name, boolean state )
        throws IllegalArgumentException, IllegalStateException;

    /**
     * Return the current value of the feature with given name.
     * <p>
     * <strong>NOTE:</strong> unknown properties are <strong>always</strong> returned as null
     *
     * @param name The name of feature to be retrieved.
     * @return The value of named feature.
     * @exception IllegalArgumentException if feature string is null
     */
    boolean getFeature( String name );

    /**
     * Set the value of a property. (the property name is recommended to be URI for uniqueness). Some well known
     * optional properties are defined in <a href="http://www.xmlpull.org/v1/doc/properties.html">
     * http://www.xmlpull.org/v1/doc/properties.html</a>. If property is not recognized or can not be set then
     * IllegalStateException MUST be thrown.
     *
     * @exception IllegalStateException if the property is not supported or can not be set
     */
    void setProperty( String name, Object value )
        throws IllegalArgumentException, IllegalStateException;

    /**
     * Look up the value of a property. The property name is any fully-qualified URI. I
     * <p>
     * <strong>NOTE:</strong> unknown properties are <string>always</strong> returned as null
     *
     * @param name The name of property to be retrieved.
     * @return The value of named property.
     */
    Object getProperty( String name );

    /**
     * Set to use binary output stream with given encoding.
     */
    void setOutput( OutputStream os, String encoding )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Set the output to the given writer.
     * <p>
     * <b>WARNING</b> no information about encoding is available!
     */
    void setOutput( Writer writer )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Write &lt;&#63;xml declaration with encoding (if encoding not null) and standalone flag (if standalone not null)
     * This method can only be called just after setOutput.
     */
    void startDocument( String encoding, Boolean standalone )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Finish writing. All unclosed start tags will be closed and output will be flushed. After calling this method no
     * more output can be serialized until next call to setOutput()
     */
    void endDocument()
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Binds the given prefix to the given namespace. This call is valid for the next element including child elements.
     * The prefix and namespace MUST be always declared even if prefix is not used in element (startTag() or
     * attribute()) - for XML 1.0 it must result in declaring <code>xmlns:prefix='namespace'</code> (or
     * <code>xmlns:prefix="namespace"</code> depending what character is used to quote attribute value).
     * <p>
     * <b>NOTE:</b> this method MUST be called directly before startTag() and if anything but startTag() or setPrefix()
     * is called next there will be exception.
     * <p>
     * <b>NOTE:</b> prefixes "xml" and "xmlns" are already bound and can not be redefined see:
     * <a href="http://www.w3.org/XML/xml-names-19990114-errata#NE05">Namespaces in XML Errata</a>.
     * <p>
     * <b>NOTE:</b> to set default namespace use as prefix empty string.
     *
     * @param prefix must be not null (or IllegalArgumentException is thrown)
     * @param namespace must be not null
     */
    void setPrefix( String prefix, String namespace )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Return namespace that corresponds to given prefix If there is no prefix bound to this namespace return null but
     * if generatePrefix is false then return generated prefix.
     * <p>
     * <b>NOTE:</b> if the prefix is empty string "" and default namespace is bound to this prefix then empty string
     * ("") is returned.
     * <p>
     * <b>NOTE:</b> prefixes "xml" and "xmlns" are already bound will have values as defined
     * <a href="http://www.w3.org/TR/REC-xml-names/">Namespaces in XML specification</a>
     */
    String getPrefix( String namespace, boolean generatePrefix )
        throws IllegalArgumentException;

    /**
     * Returns the current depth of the element. Outside the root element, the depth is 0. The depth is incremented by 1
     * when startTag() is called. The depth is decremented after the call to endTag() event was observed.
     *
     * <pre>
     * &lt;!-- outside --&gt;     0
     * &lt;root&gt;               1
     *   sometext                 1
     *     &lt;foobar&gt;         2
     *     &lt;/foobar&gt;        2
     * &lt;/root&gt;              1
     * &lt;!-- outside --&gt;     0
     * </pre>
     */
    int getDepth();

    /**
     * Returns the namespace URI of the current element as set by startTag().
     * <p>
     * <b>NOTE:</b> that means in particular that:
     * <ul>
     * <li>if there was startTag("", ...) then getNamespace() returns ""
     * <li>if there was startTag(null, ...) then getNamespace() returns null
     * </ul>
     *
     * @return namespace set by startTag() that is currently in scope
     */
    String getNamespace();

    /**
     * Returns the name of the current element as set by startTag(). It can only be null before first call to startTag()
     * or when last endTag() is called to close first startTag().
     *
     * @return namespace set by startTag() that is currently in scope
     */
    String getName();

    /**
     * Writes a start tag with the given namespace and name. If there is no prefix defined for the given namespace, a
     * prefix will be defined automatically. The explicit prefixes for namespaces can be established by calling
     * setPrefix() immediately before this method. If namespace is null no namespace prefix is printed but just name. If
     * namespace is empty string then serializer will make sure that default empty namespace is declared (in XML 1.0
     * xmlns='') or throw IllegalStateException if default namespace is already bound to non-empty string.
     */
    XmlSerializer startTag( String namespace, String name )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Write an attribute. Calls to attribute() MUST follow a call to startTag() immediately. If there is no prefix
     * defined for the given namespace, a prefix will be defined automatically. If namespace is null or empty string no
     * namespace prefix is printed but just name.
     */
    XmlSerializer attribute( String namespace, String name, String value )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Write end tag. Repetition of namespace and name is just for avoiding errors.
     * <p>
     * <b>Background:</b> in kXML endTag had no arguments, and non matching tags were very difficult to find... If
     * namespace is null no namespace prefix is printed but just name. If namespace is empty string then serializer will
     * make sure that default empty namespace is declared (in XML 1.0 xmlns='').
     */
    XmlSerializer endTag( String namespace, String name )
        throws IOException, IllegalArgumentException, IllegalStateException;

    // /**
    // * Writes a start tag with the given namespace and name.
    // * <br />If there is no prefix defined (prefix == null) for the given namespace,
    // * a prefix will be defined automatically.
    // * <br />If explicit prefixes is passed (prefix != null) then it will be used
    // *and namespace declared if not already declared or
    // * throw IllegalStateException the same prefix was already set on this
    // * element (setPrefix()) and was bound to different namespace.
    // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown.
    // * <br />If namespace is null then no namespace prefix is printed but just name.
    // * <br />If namespace is empty string then serializer will make sure that
    // * default empty namespace is declared (in XML 1.0 xmlns='')
    // * or throw IllegalStateException if default namespace is already bound
    // * to non-empty string.
    // */
    // XmlSerializer startTag (String prefix, String namespace, String name)
    // throws IOException, IllegalArgumentException, IllegalStateException;
    //
    // /**
    // * Write an attribute. Calls to attribute() MUST follow a call to
    // * startTag() immediately.
    // * <br />If there is no prefix defined (prefix == null) for the given namespace,
    // * a prefix will be defined automatically.
    // * <br />If explicit prefixes is passed (prefix != null) then it will be used
    // * and namespace declared if not already declared or
    // * throw IllegalStateException the same prefix was already set on this
    // * element (setPrefix()) and was bound to different namespace.
    // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown.
    // * <br />If namespace is null then no namespace prefix is printed but just name.
    // * <br />If namespace is empty string then serializer will make sure that
    // * default empty namespace is declared (in XML 1.0 xmlns='')
    // * or throw IllegalStateException if default namespace is already bound
    // * to non-empty string.
    // */
    // XmlSerializer attribute (String prefix, String namespace, String name, String value)
    // throws IOException, IllegalArgumentException, IllegalStateException;
    //
    // /**
    // * Write end tag. Repetition of namespace, prefix, and name is just for avoiding errors.
    // * <br />If namespace or name arguments are different from corresponding startTag call
    // * then IllegalArgumentException is thrown, if prefix argument is not null and is different
    // * from corresponding starTag then IllegalArgumentException is thrown.
    // * <br />If namespace is null then prefix must be null too or IllegalStateException is thrown.
    // * <br />If namespace is null then no namespace prefix is printed but just name.
    // * <br />If namespace is empty string then serializer will make sure that
    // * default empty namespace is declared (in XML 1.0 xmlns='').
    // * <p><b>Background:</b> in kXML endTag had no arguments, and non matching tags were
    // * very difficult to find...</p>
    // */
    // ALEK: This is really optional as prefix in end tag MUST correspond to start tag but good for error checking
    // XmlSerializer endTag (String prefix, String namespace, String name)
    // throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Writes text, where special XML chars are escaped automatically
     */
    XmlSerializer text( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Writes text, where special XML chars are escaped automatically
     */
    XmlSerializer text( char[] buf, int start, int len )
        throws IOException, IllegalArgumentException, IllegalStateException;

    void cdsect( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    void entityRef( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    void processingInstruction( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    void comment( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    void docdecl( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    void ignorableWhitespace( String text )
        throws IOException, IllegalArgumentException, IllegalStateException;

    /**
     * Write all pending output to the stream. If method startTag() or attribute() was called then start tag is closed
     * (final &gt;) before flush() is called on underlying output stream.
     * <p>
     * <b>NOTE:</b> if there is need to close start tag (so no more attribute() calls are allowed) but without flushing
     * output call method text() with empty string (text("")).
     */
    void flush()
        throws IOException;

}
