All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gargoylesoftware.htmlunit.javascript.host.xml.XMLDocument Maven / Gradle / Ivy

Go to download

Vaadin is a web application framework for Rich Internet Applications (RIA). Vaadin enables easy development and maintenance of fast and secure rich web applications with a stunning look and feel and a wide browser support. It features a server-side architecture with the majority of the logic running on the server. Ajax technology is used at the browser-side to ensure a rich and interactive user experience.

There is a newer version: 1.2.0
Show newest version
/*
 * Copyright (c) 2002-2011 Gargoyle Software Inc.
 *
 * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.gargoylesoftware.htmlunit.javascript.host.xml;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import net.sourceforge.htmlunit.corejs.javascript.Context;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Node;

import com.gargoylesoftware.htmlunit.BrowserVersionFeatures;
import com.gargoylesoftware.htmlunit.SgmlPage;
import com.gargoylesoftware.htmlunit.StringWebResponse;
import com.gargoylesoftware.htmlunit.WebRequest;
import com.gargoylesoftware.htmlunit.WebResponse;
import com.gargoylesoftware.htmlunit.WebWindow;
import com.gargoylesoftware.htmlunit.html.DomAttr;
import com.gargoylesoftware.htmlunit.html.DomCDataSection;
import com.gargoylesoftware.htmlunit.html.DomElement;
import com.gargoylesoftware.htmlunit.html.DomNode;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.javascript.SimpleScriptable;
import com.gargoylesoftware.htmlunit.javascript.host.Attr;
import com.gargoylesoftware.htmlunit.javascript.host.Document;
import com.gargoylesoftware.htmlunit.javascript.host.Element;
import com.gargoylesoftware.htmlunit.javascript.host.html.HTMLCollection;
import com.gargoylesoftware.htmlunit.xml.XmlPage;

/**
 * A JavaScript object for XMLDocument.
 *
 * @version $Revision: 6358 $
 * @author Ahmed Ashour
 * @author Marc Guillemot
 * @author Sudhan Moghe
 * @author Ronald Brill
 */
public class XMLDocument extends Document {

    private static final Log LOG = LogFactory.getLog(XMLDocument.class);

    private boolean async_ = true;
    private boolean preserveWhiteSpace_;
    private XMLDOMParseError parseError_;

    /**
     * Creates a new instance. JavaScript objects must have a default constructor.
     */
    public XMLDocument() {
        this(null);
    }

    /**
     * Creates a new instance, with associated XmlPage.
     * @param enclosingWindow the window
     */
    public XMLDocument(final WebWindow enclosingWindow) {
        if (enclosingWindow != null) {
            try {
                final XmlPage page = new XmlPage((WebResponse) null, enclosingWindow);
                setDomNode(page);
            }
            catch (final IOException e) {
                throw Context.reportRuntimeError("IOException: " + e);
            }
        }
    }

    /**
     * Sets the async attribute.
     * @param async Whether or not to send the request to the server asynchronously
     */
    public void jsxSet_async(final boolean async) {
        async_ = async;
    }

    /**
     * Returns Whether or not to send the request to the server asynchronously.
     * @return the async attribute
     */
    public boolean jsxGet_async() {
        return async_;
    }

    /**
     * Loads an XML document from the specified location.
     *
     * @param xmlSource a string containing a URL that specifies the location of the XML file
     * @return true if the load succeeded; false if the load failed
     */
    public boolean jsxFunction_load(final String xmlSource) {
        if (async_) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("XMLDocument.load(): 'async' is true, currently treated as false.");
            }
        }
        try {
            final HtmlPage htmlPage = (HtmlPage) getWindow().getWebWindow().getEnclosedPage();
            final WebRequest request = new WebRequest(htmlPage.getFullyQualifiedUrl(xmlSource));
            final WebResponse webResponse = getWindow().getWebWindow().getWebClient().loadWebResponse(request);
            final XmlPage page = new XmlPage(webResponse, getWindow().getWebWindow(), false);
            setDomNode(page);
            return true;
        }
        catch (final IOException e) {
            final XMLDOMParseError parseError = jsxGet_parseError();
            parseError.setErrorCode(-1);
            parseError.setFilepos(1);
            parseError.setLine(1);
            parseError.setLinepos(1);
            parseError.setReason(e.getMessage());
            parseError.setSrcText("xml");
            parseError.setUrl(xmlSource);
            if (LOG.isDebugEnabled()) {
                LOG.debug("Error parsing XML from '" + xmlSource + "'", e);
            }
            return false;
        }
    }

    /**
     * Loads an XML document using the supplied string.
     *
     * @param strXML A string containing the XML string to load into this XML document object
     *        This string can contain an entire XML document or a well-formed fragment.
     * @return true if the load succeeded; false if the load failed
     */
    public boolean jsxFunction_loadXML(final String strXML) {
        try {
            final WebWindow webWindow = getWindow().getWebWindow();

            // build a dummy WebResponse
            final URL hackUrl = new URL("http://-htmlunit-internal/XMLDocument.loadXML"); // hack! better solution?
            final WebResponse webResponse = new StringWebResponse(strXML, hackUrl);

            final XmlPage page = new XmlPage(webResponse, webWindow);
            setDomNode(page);
            return true;
        }
        catch (final IOException e) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Error parsing XML\n" + strXML, e);
            }
            return false;
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public SimpleScriptable makeScriptableFor(final DomNode domNode) {
        final SimpleScriptable scriptable;

        // TODO: cleanup, getScriptObject() should be used!!!
        if (domNode instanceof DomElement && !(domNode instanceof HtmlElement)) {
            scriptable = new Element();
        }
        else if (domNode instanceof DomAttr) {
            final Attr attribute;
            if (getPage().getWebClient().getBrowserVersion().hasFeature(BrowserVersionFeatures.GENERATED_134)) {
                attribute = new XMLAttr();
            }
            else {
                attribute = new Attr();
            }
            scriptable = attribute;
        }
        else {
            return super.makeScriptableFor(domNode);
        }

        scriptable.setPrototype(getPrototype(scriptable.getClass()));
        scriptable.setParentScope(getParentScope());
        scriptable.setDomNode(domNode);
        return scriptable;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    protected void initParentScope(final DomNode domNode, final SimpleScriptable scriptable) {
        scriptable.setParentScope(getParentScope());
    }

    /**
     * Gets the JavaScript property "parseError" for the document.
     * @return the ParserError object for the document
     */
    public XMLDOMParseError jsxGet_parseError() {
        if (parseError_ == null) {
            parseError_ = new XMLDOMParseError();
            parseError_.setPrototype(getPrototype(parseError_.getClass()));
            parseError_.setParentScope(getParentScope());
        }
        return parseError_;
    }

    /**
     * Contains the XML representation of the node and all its descendants.
     * @return an XML representation of this node and all its descendants
     */
    @Override
    public String jsxGet_xml() {
        final XMLSerializer seralizer = new XMLSerializer();
        seralizer.setParentScope(getWindow());
        seralizer.setPrototype(getPrototype(seralizer.getClass()));
        return seralizer.jsxFunction_serializeToString(jsxGet_documentElement());
    }

    /**
     * Gets the current white space handling.
     * @return the current white space handling
     */
    public boolean jsxGet_preserveWhiteSpace() {
        return preserveWhiteSpace_;
    }

    /**
     * Specifies the white space handling.
     * @param preserveWhiteSpace white space handling
     */
    public void jsxSet_preserveWhiteSpace(final boolean preserveWhiteSpace) {
        preserveWhiteSpace_ = preserveWhiteSpace;
    }

    /**
     * This method is used to set
     * second-level properties
     * on the DOM object.
     *
     * @param name the name of the property to be set
     * @param value the value of the specified property
     */
    public void jsxFunction_setProperty(final String name, final String value) {
        //empty implementation
    }

    /**
     * Applies the specified XPath expression to this node's context and returns the generated list of matching nodes.
     * @param expression a string specifying an XPath expression
     * @return list of the found elements
     */
    public HTMLCollection jsxFunction_selectNodes(final String expression) {
        final boolean attributeChangeSensitive = expression.contains("@");
        final String description = "XMLDocument.selectNodes('" + expression + "')";
        final SgmlPage page = getPage();
        final HTMLCollection collection = new HTMLCollection(page.getDocumentElement(),
                attributeChangeSensitive, description) {
            protected List computeElements() {
                final List list = new ArrayList(page.getByXPath(expression));
                return list;
            }
        };
        return collection;
    }

    /**
     * Applies the specified pattern-matching operation to this node's context and returns the first matching node.
     * @param expression a string specifying an XPath expression
     * @return the first node that matches the given pattern-matching operation
     *         If no nodes match the expression, returns a null value.
     */
    public Object jsxFunction_selectSingleNode(final String expression) {
        final HTMLCollection collection = jsxFunction_selectNodes(expression);
        if (collection.jsxGet_length() > 0) {
            return collection.get(0, collection);
        }
        return null;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public HTMLCollection jsxFunction_getElementsByTagName(final String tagName) {
        final DomNode firstChild = this.getDomNodeOrDie().getFirstChild();
        if (firstChild == null) {
            return HTMLCollection.emptyCollection(getWindow());
        }

        final HTMLCollection collection = new HTMLCollection(getDomNodeOrDie(), false,
                "XMLDocument.getElementsByTagName") {
            protected boolean isMatching(final DomNode node) {
                return node.getLocalName().equals(tagName);
            }
        };

        return collection;
    }

    /**
     * Returns the element with the specified ID, as long as it is an HTML element; null otherwise.
     * @param id the ID to search for
     * @return the element with the specified ID, as long as it is an HTML element; null otherwise
     */
    public Object jsxFunction_getElementById(final String id) {
        final XmlPage xmlPage = (XmlPage) getDomNodeOrDie();
        final Object domElement = xmlPage.getFirstByXPath("//*[@id = \"" + id + "\"]");
        if (domElement == null) {
            return null;
        }

        if (domElement instanceof HtmlElement) {
            return ((HtmlElement) domElement).getScriptObject();
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("getElementById(" + id + "): no HTML DOM node found with this ID");
        }
        return null;
    }

    /**
     * Since we are not processing DTD, this method always returns null.
     * @param id the ID to search for
     * @return null
     */
    public Object jsxFunction_nodeFromID(final String id) {
        return null;
    }

    /**
     * Creates a new ProcessingInstruction.
     * @param target the target
     * @param data the data
     * @return the new ProcessingInstruction
     */
    public Object jsxFunction_createProcessingInstruction(final String target, final String data) {
        final DomNode node = ((XmlPage) getPage()).createProcessingInstruction(target, data);
        return getScriptableFor(node);
    }

    /**
     * Creates a new createCDATASection.
     * @param data the data
     * @return the new CDATASection
     */
    public Object jsxFunction_createCDATASection(final String data) {
        final DomCDataSection node = ((XmlPage) getPage()).createCDATASection(data);
        return getScriptableFor(node);
    }

    /**
     * Creates a node using the supplied type, name, and namespace.
     * @param type a value that uniquely identifies the node type
     * @param name the value for the new node's nodeName property
     * @param namespaceURI A string defining the namespace URI.
     *        If specified, the node is created in the context of the namespaceURI parameter
     *        with the prefix specified on the node name.
     *        If the name parameter does not have a prefix, this is treated as the default namespace.
     * @return the newly created node
     */
    public Object jsxFunction_createNode(final Object type, final String name, final Object namespaceURI) {
        switch((short) Context.toNumber(type)) {
            case Node.ELEMENT_NODE:
                return jsxFunction_createElementNS((String) namespaceURI, name);
            case Node.ATTRIBUTE_NODE:
                return jsxFunction_createAttribute(name);

            default:
                throw Context.reportRuntimeError("xmlDoc.createNode(): Unsupported type "
                        + (short) Context.toNumber(type));
        }
    }
}