Package client.net.sf.saxon.ce.dom

Source Code of client.net.sf.saxon.ce.dom.HTMLWriter

package client.net.sf.saxon.ce.dom;

import java.util.logging.Logger;

import client.net.sf.saxon.ce.Configuration;
import client.net.sf.saxon.ce.Controller;
import client.net.sf.saxon.ce.Controller.APIcommand;
import client.net.sf.saxon.ce.event.PipelineConfiguration;
import client.net.sf.saxon.ce.event.Receiver;
import client.net.sf.saxon.ce.lib.NamespaceConstant;
import client.net.sf.saxon.ce.om.NamePool;
import client.net.sf.saxon.ce.om.NamespaceBinding;
import client.net.sf.saxon.ce.trans.XPathException;
import client.net.sf.saxon.ce.value.Whitespace;

import com.google.gwt.core.client.JavaScriptException;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.Node;
import com.google.gwt.dom.client.NodeList;
import com.google.gwt.dom.client.Text;


/**
  * DOMWriter is a Receiver that attaches the result tree to a specified Node in the HTML DOM Document
  */

public class HTMLWriter implements Receiver {

    private PipelineConfiguration pipe;
    private NamePool namePool;
    private Node currentNode;
    private Document document;
    private Node nextSibling;
    private int level = 0;
    private String systemId;
    private Node containerNode;
    private static Logger logger = Logger.getLogger("XSLT20Processor");

    /**
     * Native Javascript method to create a namespaced element. Not available in GWT because
     * it's not supported in IE. But needed for SVG/mathML support
     * @param ns the namespace URI
     * @param name the local name
     * @return the constructed element or null if method not available
     */

    private native Element createElementNS(Document doc,
                                           final String ns,
                                           final String name) /*-{
        if (doc.createElementNS) {
          return doc.createElementNS(ns, name);
        }
        return null;
    }-*/;
   
    private native Node createProcessingInstruction(Document doc,
            final String target,
            final String data) /*-{
       return doc.createProcessingInstruction(target, data);
    }-*/;
     
    private native Node createComment(Document doc,
            final String data) /*-{
         return doc.createComment(data);
   }-*/;
   
    private static native boolean attNSSupported(Document doc) /*-{
         return (typeof doc.createNode == "function" || typeof doc.createAttributeNS == "function");
   }-*/;
   
    public static void setAttribute(Document doc,
        Element element,
        String name,
        String URI,
        String value,
        WriteMode wMode) {
      // fix for IE issue with colspan etc #1570

        name = tableAttributeFix(name, wMode);

      if (attNSSupported(doc)) {
        setAttributeJs(doc,element,name,URI,value);
      } else {
          String prefix = name.substring(0, name.indexOf(":"));
          String x = "xmlns";
          String nsDeclaraction = (prefix.length() == 0)? x : x + ":" + prefix;
        if (!element.hasAttribute(nsDeclaraction)) {
          addNamespace(element, prefix, URI);
        }
          element.setAttribute(name, value);
            setAttributeProps(element, name, value);
      }
    }
   
    public static String tableAttributeFix(String name, WriteMode wMode){
      if (wMode != WriteMode.XML && Configuration.getIeVersion() > 0 && name.length() > 5){
    if (name.equals("rowspan")){
      name = "rowSpan";
    } else if (name.equals("colspan")){
      name = "colSpan";
    } else if (name.equals("cellpadding")){
      name = "cellPadding";
    } else if (name.equals("cellppacing")){
      name = "cellSpacing";
    }
      }
    return name;
    }
    
   
    //  This throws an exception in IE7 that must be handled, in IE, you can only create a namespace-qualified
    //  attribute using the createNode method of the DOMDocument.
    /**
     *  Creates an attribute with a namespace.
     *  This throws an exception in IE7 that (it seems) must be handled by the caller,
     *  in IE, you can only create a namespace-qualified attribute using the createNode method
     *  of the DOMDocument.
     *
     */
    public static native void setAttributeJs(Document doc,
        final Node element,
        final String name,
        final String URI,
            final String value) /*-{
        var att;
        if (doc.createNode) {
            att = doc.createNode(2, name, URI);
            att.value = value;
        } else {
            att = doc.createAttributeNS(URI, name);
            att.nodeValue = value;
        }
        element.setAttributeNode(att);       
   }-*/;
   

    /**
    * Set the pipelineConfiguration
    */

    public void setPipelineConfiguration(PipelineConfiguration pipe) {
        this.pipe = pipe;
        namePool = pipe.getConfiguration().getNamePool();
    }

    /**
    * Get the pipeline configuration used for this document
    */

    public PipelineConfiguration getPipelineConfiguration() {
        return pipe;
    }

    /**
     * Set the System ID of the destination tree
     */

    public void setSystemId(String systemId) {
        this.systemId = systemId;
    }

    /**
     * Get the system identifier that was set with setSystemId.
     *
     * @return The system identifier that was set with setSystemId,
     *         or null if setSystemId was not called.
     */
    public String getSystemId() {
        return systemId;
    }

    /**
    * Start of the document.
    */

    public void open () {}

    /**
    * End of the document.
    */

    public void close () {}

    /**
     * Start of a document node.
    */

    public void startDocument() throws XPathException {
      // not required - setNode is called instead:
      //document = XMLDOM.createDocument();
    }

    /**
     * Notify the end of a document node
     */

    public void endDocument() throws XPathException {}

    /**
    * Start of an element.
    */

    public void startElement(int nameCode, int properties) throws XPathException {
        String localName = namePool.getLocalName(nameCode);
        String prefix = namePool.getPrefix(nameCode);
        String uri = namePool.getURI(nameCode);
       
        // TODO: For XML Writer it should write prefixes in a
        // way compliant with the XSLT2.0 specification - using xsl:output attributes
        Element element = null;
        if (uri != null && !uri.isEmpty()) {
          if(mode == WriteMode.XML && !prefix.equals("")) {
            element =  createElementNS(document, uri, prefix+":"+localName);
           
          } else {
            // no svg specific prefix now used, for compliance with HTML5
            element = createElementNS(document, uri, localName);
          }
        }
       
        // if there's no namespace - or no namespace support
        if (element == null) {
            element = document.createElement(localName);
        }
        // special case for html element: write to the document node
        Controller controller = pipe.getController();
        if (controller != null && controller.getApiCommand() == APIcommand.UPDATE_HTML
            && (localName.equals("html") || localName.equals("head") || localName.equals("body"))) {
          if (localName.equals("html")){
            element = (Element)document.getFirstChild();
          } else {
            element = (Element)document.getElementsByTagName(localName.toUpperCase()).getItem(0);
            NodeList<Node> nodes = element.getChildNodes();
            for (int n = 0; n < nodes.getLength(); n++) {
              Node node = nodes.getItem(n);
              node.removeFromParent();
            }
          }
          currentNode = element;
          level++;
          return;
        }
        if (nextSibling != null && level == 0) {
            currentNode.insertBefore(element, nextSibling);
        } else  {
          try {
            currentNode.appendChild(element);
          } catch(JavaScriptException err) {
            if(uri.equals(NamespaceConstant.IXSL)) {
                  XPathException xpe = new XPathException("Error on adding IXSL element to the DOM, the IXSL namespace should be added to the 'extension-element-prefixes' list.");
                  throw(xpe);
            } else {            
              throw(new XPathException(err.getMessage()));
            }
          } catch(Exception exc) {
            XPathException xpe = new XPathException("Error on startElement in HTMLWriter for element '" + localName + "': " + exc.getMessage());
            throw(xpe);
          }
        }
        currentNode = element;
        level++;
    }

    private static void addNamespace(Element element, String prefix, String uri) {
        String attName = (prefix.isEmpty() ? "xmlns" : "xmlns:" + prefix);
        element.setAttribute(attName, uri);
    }

    public void namespace (NamespaceBinding nsBinding, int properties) throws XPathException {
       if(mode == WriteMode.XML) {
          String prefix = nsBinding.getPrefix();
        String uri = nsBinding.getURI();
        Element element = (Element)currentNode;
            if (!(uri.equals(NamespaceConstant.XML))) {
                addNamespace(element, prefix, uri);
            }
       }
      
    }

    public void attribute(int nameCode, CharSequence value)
    throws XPathException {
        String localName = namePool.getLocalName(nameCode);
        String uri = namePool.getURI(nameCode);
        String val = value.toString();
        Element element = (Element)currentNode;

        // must be HTML write mode
        if (mode != WriteMode.XML && NamespaceConstant.HTML_PROP.equals(uri)) {
            element.setPropertyString(localName, val);
        } else if (mode != WriteMode.XML && NamespaceConstant.HTML_STYLE_PROP.equals(uri)) {
          // if localName starts with '_-' then remove the underscore e.g _-webkit-transition
          if(localName.length() > 1 && localName.charAt(0) == '_' && localName.charAt(1) == '-') {
            localName = localName.substring(1);
          }
          localName = HTMLWriter.getCamelCaseName(localName);
            element.getStyle().setProperty(localName, val);
        } else if (uri != null && !uri.isEmpty()){
            String fullname = namePool.getDisplayName(nameCode);
            setAttribute(document, element, fullname, uri, val, mode);
        } else {
          localName = tableAttributeFix(localName, mode);
            element.setAttribute(localName, val);
            setAttributeProps(element, localName, val);
        }
    }
   
    /**
     * Method for backward compatibility with IE8 and previous where
     * properties and attributes were handled separately
     */
    public static void setAttributePropsOriginal(Element element, String localName, String val){
      if (Configuration.getIeVersion() > 0 && Configuration.getIeVersion() < 9) {
          if (localName.length() == 5) {
              if (localName.equals("style")) {
                  // In IE, setting the style attribute dynamically has no effect on the individual style properties,
                  // and does not affect the rendition of the element. So we parse out the content of the attribute,
                  // and use it to set the individual properties.
                if (hasStyle(element)) {
                  setStyleProperties(element, val);
                }
              } else if (localName.equals("class")) {
                setClass(element, val);
              } else if (localName.equals("title")) {
                setTitle(element, val);
//              } else if (localName.equals("align") || localName.equals("width")) {
//                setElementProperty(element, localName, val);
            } else {           
              setElementProperty(element, localName, val);
            }
          } else if (localName.length() == 2 && localName.equals("id")) {
            setId(element, val);
//          } else if (localName.length() == 7 && localName.equals("colSpan") || localName.equals("rowSpan") ) {
//            setElementProperty(element, localName, val);
          } else {           
            setElementProperty(element, localName, val);
          }
      }
    }
   
    /**
     * following setElementProperty method call doesn't work consistently
     * because in IE some element are initially undefined?
     */
    public static void setAttributeProps(Element element, String localName, String val){
      if (Configuration.getIeVersion() > 0 && Configuration.getIeVersion() < 9) {
              if (localName.equals("style")) {
                if (hasStyle(element)) {
                  setStyleProperties(element, val);
                }
              }
              else {
                localName = (localName == "class")? "className" : localName;
                try {
                  setElementProperty(element, localName, val);
                } catch(Exception e) {
                  // some IE8 properties exist but appear to be read-only
                  logger.warning("Unable to set '" + localName + "' property for element.");
                }
              }
      }
    }
   
    private static native boolean hasStyle(Element element) /*-{
         return (typeof element.style !== "undefined");
    }-*/;
   
    private static native boolean setClass(Element element, String value) /*-{
        if (typeof element.className !== "undefined") {
          element.className = value;
        }
    }-*/;
   
    private static native boolean setId(Element element, String value) /*-{
     if (typeof element.id !== "undefined") {
       element.id = value;
     }
    }-*/;
   
    private static native boolean setTitle(Element element, String value) /*-{
     if (typeof element.title !== "undefined") {
       element.title = value;
     }
    }-*/;
   
    private static native void setElementProperty(Element element, String name, String value) /*-{
      if (typeof element[name] !== "undefined") {
      element[name] = value;
      }
    }-*/;
   
    /**
     * Parse the value of the style attribute and use it to set individual properties of the style object
     * @param element the element whose style properties are to be updated
     * @param styleAttribute the raw value of the style attribute
     * @throws XPathException
     */

    public static void setStyleProperties(Element element, String styleAttribute) {
        int semi = styleAttribute.indexOf(';');
        String first = (semi < 0 ? styleAttribute : styleAttribute.substring(0, semi));
        int colon = first.indexOf(':');
        if (colon > 0 && colon < first.length() - 1) {
            String prop = first.substring(0, colon).trim();
            // Turn the style name into camelCase
            prop = getCamelCaseName(prop);
            String value = first.substring(colon+1).trim();
            try {
              element.getStyle().setProperty(prop, value);
            // IE throws illegal argument exception if property name is
               // not valid - ignore exception for consistency
            catch (JavaScriptException jex) {}
        }
        if (semi > 0 && semi < styleAttribute.length() - 2) {
          setStyleProperties(element, styleAttribute.substring(semi+1));
        }
    }
   
    public static String getCamelCaseName(String prop) {
        while (prop.contains("-")) {
            int h = prop.indexOf('-');
            if (h > 0) { // preserve first char
              String p = prop.substring(0, h) + Character.toUpperCase(prop.charAt(h+1));
              if (h+2 < prop.length()) {
                  p += prop.substring(h+2);
              }
              prop = p;
            }
        }
        return prop;
    }

    public void startContent() throws XPathException {}

    /**
    * End of an element.
    */

    public void endElement () throws XPathException {
        currentNode = currentNode.getParentNode();
        level--;
    }


    /**
    * Character data.
    */

    public void characters(CharSequence chars) throws XPathException
    {
        if (level == 0 && nextSibling == null && Whitespace.isWhite(chars)) {
            return; // no action for top-level whitespace
        }

        try {
          Text text = document.createTextNode(chars.toString());
          if (nextSibling != null && level == 0) {
              currentNode.insertBefore(text, nextSibling);
          } else {
              currentNode.appendChild(text);
          }
        } catch(Exception e) {
          String desc = (nextSibling != null && level == 0) ? "inserting" : "appending";
          throw(new XPathException("DOM error " + desc + " text node with value: '" + chars.toString() + "' to node with name: " + currentNode.getNodeName()));
        }
    }


    /**
    * Handle a processing instruction.
    */

    public void processingInstruction(String target, CharSequence data)
        throws XPathException
    {
        // Processing instructions not in HTML DOM - but added for XML DOM
      // TODO: Note that Node is a GWT HTML DOM object so an exception may be raised
      // by appending the wrong node type!
      if (mode == WriteMode.XML) {
        JavaScriptObject pi = createProcessingInstruction(document, target, data.toString());
        addNode(pi, "processing-instruction");
     
    }

    /**
    * Handle a comment.
    */

    public void comment(CharSequence chars) throws XPathException
    {
        // Added for XML compatibility
      if (mode == WriteMode.XML) {
        JavaScriptObject comment = createComment(document, chars.toString());
        addNode(comment, "comment");
      }
    }
   
    public void addNode(JavaScriptObject newNode, String nodeType) throws XPathException {
        try {
          if (nextSibling != null && level == 0) {
            insertBefore(nextSibling, newNode);
          } else {
              appendChild(currentNode, newNode);
          }
        } catch(Exception e) {
          String desc = (nextSibling != null && level == 0) ? "inserting" : "appending";
          throw(new XPathException("DOM error " + desc + " " + nodeType + " node to node with name: " + currentNode.getNodeName()));
        }
    }
   
    public final native JavaScriptObject appendChild(JavaScriptObject parent, JavaScriptObject newChild) /*-{
       return parent.appendChild(newChild);
    }-*/;
   
    public final native JavaScriptObject insertBefore(JavaScriptObject targetNode, JavaScriptObject newNode) /*-{
       return targetNode.insertBefore(newNode);
    }-*/;

    /**
     * Set the attachment point for the new subtree
     * @param node the node to which the new subtree will be attached
    */
   
    public enum WriteMode {
      NONE, XML, HTML
    }
   

   
    private WriteMode mode = WriteMode.NONE;

    public void setNode (Node node) {
        if (node == null) {
            return;
        }
        currentNode = node;
        if (node.getNodeType() == Node.DOCUMENT_NODE) {
            document = (Document)node;
        } else {
            document = currentNode.getOwnerDocument();           
        }
        if (mode == WriteMode.NONE) {
          Controller.APIcommand cmd = pipe.getController().getApiCommand();
          mode = (cmd == APIcommand.TRANSFORM_TO_DOCUMENT || cmd == APIcommand.TRANSFORM_TO_FRAGMENT)?
              WriteMode.XML : WriteMode.HTML;
        }
    }
   
    public Node getNode() {
      // though this is changed by startElement it's reset by endElement and therefore,
      // when called after close, should always return the initial container node
      // - the document node or the documentFragment node.
      return currentNode;
    }

    /**
     * Set next sibling
     * @param nextSibling the node, which must be a child of the attachment point, before which the new subtree
     * will be created. If this is null the new subtree will be added after any existing children of the
     * attachment point.
     */

    public void setNextSibling(Node nextSibling) {
        this.nextSibling = nextSibling;
    }

}

// This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
// This Source Code Form is “Incompatible With Secondary Licenses”, as defined by the Mozilla Public License, v. 2.0.
TOP

Related Classes of client.net.sf.saxon.ce.dom.HTMLWriter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.