/*
 * $Id: XhtmlDomDocument.java,v 1.1.1.1 2002/09/30 15:08:51 smartine Exp $
 * Copyright (C) 1999-2000 David Brownell
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package xml.dom.xhtml;

import org.w3c.dom.*;
import org.w3c.dom.html.HTMLCollection;
import org.w3c.dom.html.HTMLDocument;
import org.w3c.dom.html.HTMLElement;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;

import xml.dom.*;


// $Id: XhtmlDomDocument.java,v 1.1.1.1 2002/09/30 15:08:51 smartine Exp $

/**
 * <p> XHTML subclass of "DomDocument", supporting the HTML DOM interfaces
 * that make the most sense in the XHTML world.
 *
 * <p> This is basically just an XML DOM which does a little bit of error
 * checking for nodes in the XHTML namespace.  It doesn't support many of
 * the HTML DOM custom element subclasses, and while it enforces case in
 * some ways, it does so using the XHTML rules not those of the HTML DOM.
 * In short, it's an XML DOM that you can safely use anywhere, but it has
 * some added intelligence about the XHTML namespace, as well as basic
 * HTML DOM support.  </p>
 *
 * <p> Note that a number of the HTMLDocument methods are not implemented,
 * and invoking them will cause a DOMException to be thrown.  These include
 * all methods returning HTMLCollection objects, which are superfluous since
 * the L2 iterators can do the same things more generically.  They also
 * include the methods which require interaction with a parser, since those
 * exist primarily to support JavaScript code that writes literal HTML text
 * to the parser, instead of supporting generally useful functionality such
 * as loading a document given its URI. </p>
 *
 * @author David Brownell 
 * @version $Date: 2002/09/30 15:08:51 $
 */
public class XhtmlDomDocument extends DomDocument implements HTMLDocument
{
    /**
     * Constructs a Document node, associating it with an instance
     * of the XhtmlDomImpl class.
     *
     * @see XhtmlDomImpl
     */
    public XhtmlDomDocument ()
    {
	super (new XhtmlDomImpl ());
    }

    /**
     * Constructs a Document node, associating it with the specified
     * XhtmlDomImpl instance.
     */
    protected XhtmlDomDocument (XhtmlDomImpl impl)
    {
	super (impl);
    }

    
    /**
     * <b>DOM L2</b>
     * Returns a newly created attribute with the specified name
     * and namespace information.
     */
    public Attr createAttributeNS (String namespaceURI, String name)
    {
	// XXX provide XHTML-specific attribute code
	return super.createAttributeNS (namespaceURI, name);
    }


    /**
     * <b>DOM L2</b>
     * Returns a newly created element with the specified name and namespace
     * information; if this is the XHTML namespace, it requires that the name
     * be an XHTML name (after any prefix is removed)
     */
    public Element createElementNS (String namespaceURI, String name)
    {
	if (XhtmlDomImpl.xhtmlNamespace.equals (namespaceURI))
	    return new XhtmlDomElement (this, name);
	return super.createElementNS (namespaceURI, name);
    }


    /**
     * <b>DOM L1 (HTML)</b>
     * Returns the first "body" or "frameset" element in the document.
     */
    public HTMLElement getBody ()
    {
	Element		temp = getDocumentElement ();
	NodeList	children;

	if (temp == null)
	    return null;
	children = temp.getChildNodes ();
	for (int i = 0; (temp = (Element) children.item (i)) != null; i++) {
	    if (temp.getNodeType () != Node.ELEMENT_NODE)
		continue;
	    if (!XhtmlDomImpl.xhtmlNamespace.equals (temp.getNamespaceURI ()))
		continue;
	    if ("body".equals (temp.getLocalName ())
		    || "frameset".equals (temp.getLocalName ()))
		return (HTMLElement) temp;
	}
	return null;
    }
    
    public void setBody (HTMLElement body)
    {
	HTMLElement	origBody = getBody ();

	if (origBody != null) {
	    origBody.getParentNode ().replaceChild (body, origBody);
	    return;
	}

	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "setBody", this, 0);
    }

    
    public String getTitle ()
    {
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getTitle", this, 0);
    }
    
    public void setTitle (String title)
    {
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "setTitle", this, 0);
    }



    /**
     * <b>DOM L1 (HTML)</b>
     * Returns the element with the specified "id" attribute.  If there
     * is no such element, and an element permitted to have a "name"
     * attribute has a matching value, returns that element.
     */
    public Element getElementById (String id)
    {
	NodeIterator	iter = createNodeIterator (getDocumentElement (),
			    NodeFilter.SHOW_ELEMENT, null, true);
	Element		temp;

	// match by "id"
	while ((temp = (Element) iter.nextNode ()) != null) {
	    if (!XhtmlDomImpl.xhtmlNamespace.equals (temp.getNamespaceURI ()))
		continue;
	    if (id.equals (temp.getAttribute ("id")))
		return temp;
	}

	// else if no such "id" attribute, try "name"
	while ((temp = (Element) iter.previousNode ()) != null) {
	    if (!XhtmlDomImpl.xhtmlNamespace.equals (temp.getNamespaceURI ()))
		continue;
	    if (!id.equals (temp.getAttribute ("name")))
		continue;

	    // ... restricting it to the right kinds of element!
	    String local = temp.getLocalName ();
	    if ("a".equals (local) || "applet".equals (local)
		    || "frame".equals (local) || "iframe".equals (local)
		    || "img".equals (local) || "map".equals (local))
		return temp;
	}

	return null;
    }

    /**
     * <b>DOM L1 (HTML)</b>
     * Same as the getElementsByTagname () method.
     */
    public NodeList getElementsByName (String name)
    {
	return getElementsByTagName (name);
    }


    //
    // Parser methods that make sense for any SAX parser to handle, and
    // which are useful in their own right, but which are not included
    // even with with these DOM APIs that already require a parser:
    //
    // public void load (String URL)
    // public void load (InputStream stream)
    // public void load (Reader reader)
    //


    //
    // The methods which require a parser are not supported since they
    // require not the standard capability of loading a full document
    // (which any SAX parser could support, if it were present) but the
    // nonstandard one of writing buffers of text to the parser.
    //
    
    public String getCookie ()
    {
	// REQUIRES PARSER AND HTTP
	// returns client cookies sent with server fetches
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getCookie", this, 0);
    }

    public void setCookie (String cookies)
    {
	// REQUIRES PARSER AND HTTP
	// assigns client cookies sent with server fetches
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "setCookie", this, 0);
    }

    public String getDomain ()
    {
	// REQUIRES PARSER
	// returns FQDN from URL used by parser
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getDomain", this, 0);
    }

    public String getReferrer ()
    {
	// REQUIRES PARSER AND USER INTERFACE
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getReferrer", this, 0);
    }

    public String getURL ()
    {
	// REQUIRES PARSER
	// returns URL used by parser
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getURL", this, 0);
    }

    public void open ()
    {
	// REQUIRES PARSER
	// clears document and sets up to parse a new one 
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "open", this, 0);
    }

    public void close ()
    {
	// REQUIRES PARSER AND USER INTERFACE
	// closes open doc and "forces rendering"
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "close", this, 0);
    }

    public void write (String xhtmlText)
    {
	// REQUIRES PARSER
	// writes to open doc, no newline
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "write", this, 0);
    }

    public void writeln (String xhtmlText)
    {
	// REQUIRES PARSER
	// writes to open doc, with newline
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "writeln", this, 0);
    }


    //
    // The methods that require an HTMLCollection aren't implemented
    // primarily since they're superfluous given utility layers on top
    // of DOM, such as the L1 getElementsByTagName.  They're also quirky
    // in terms of functionality, for legacy JavaScript support that
    // few Java programmers will ever care about.
    //

    public HTMLCollection getAnchors ()
    {
	// REQUIRES HTMLCollection SUPPORT
	// all "<a name='...'>" elements (not 'id=...' and not other
	// kinds of addressable node)
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getAnchors", this, 0);
    }

    public HTMLCollection getApplets ()
    {
	// REQUIRES HTMLCollection SUPPORT
	// all "<applet ... >" elements and also
	// "<object ...>" ones which are applets
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getApplets", this, 0);
    }

    public HTMLCollection getForms ()
    {
	// REQUIRES HTMLCollection SUPPORT
	// all "<form ... >" elements
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getForms", this, 0);
    }

    public HTMLCollection getImages ()
    {
	// REQUIRES HTMLCollection SUPPORT
	// all "<img ... >" elements (but no "<object ...>" ones)
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getImages", this, 0);
    }

    public HTMLCollection getLinks ()
    {
	// REQUIRES HTMLCollection SUPPORT
	// all "<area ...>" and "<a href='...'>" elements
	throw new DomEx (DomEx.NOT_SUPPORTED_ERR, "getLinks", this, 0);
    }
}
