Package org.jbpm.xml

Source Code of org.jbpm.xml.DomBuilder

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
/*
* $Id: DOMBuilder.java 472634 2006-11-08 20:43:55Z jycli $
*/
package org.jbpm.xml;

import java.util.Stack;
import java.util.Vector;

import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.Text;
import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.Locator;
import org.xml.sax.ext.LexicalHandler;

/**
* This class takes SAX events (in addition to some extra events that SAX
* doesn't handle yet) and adds the result to a document or document fragment.
*/
public class DomBuilder implements ContentHandler, LexicalHandler {

  /** Root document */
  public Document m_doc;

  /** Current node */
  protected Node m_currentNode = null;

  /** The root node */
  protected Node m_root = null;

  /** The next sibling node */
  protected Node m_nextSibling = null;

  /** First node of document fragment or null if not a DocumentFragment */
  public DocumentFragment m_docFrag = null;

  /** Vector of element nodes */
  protected Stack m_elemStack = new Stack();

  /** Namespace support */
  protected Vector m_prefixMappings = new Vector();

  /** to obtain the line number information */
  protected Locator locator = null;

  /**
   * DOMBuilder instance constructor... it will add the DOM nodes to the
   * document fragment.
   *
   * @param doc
   *          Root document
   * @param node
   *          Current node
   */
  public DomBuilder(Document doc, Node node) {
    m_doc = doc;
    m_currentNode = m_root = node;

    if (node instanceof Element)
      m_elemStack.push(node);
  }

  /**
   * DOMBuilder instance constructor... it will add the DOM nodes to the
   * document fragment.
   *
   * @param doc
   *          Root document
   * @param docFrag
   *          Document fragment
   */
  public DomBuilder(Document doc, DocumentFragment docFrag) {
    m_doc = doc;
    m_docFrag = docFrag;
  }

  /**
   * DOMBuilder instance constructor... it will add the DOM nodes to the
   * document.
   *
   * @param doc
   *          Root document
   */
  public DomBuilder(Document doc) {
    m_doc = doc;
  }

  /**
   * Get the root document or DocumentFragment of the DOM being created.
   *
   * @return The root document or document fragment if not null
   */
  public Node getRootDocument() {
    return (null != m_docFrag) ? (Node) m_docFrag : (Node) m_doc;
  }

  /**
   * Get the root node of the DOM tree.
   */
  public Node getRootNode() {
    return m_root;
  }

  /**
   * Get the node currently being processed.
   *
   * @return the current node being processed
   */
  public Node getCurrentNode() {
    return m_currentNode;
  }

  /**
   * Set the next sibling node, which is where the result nodes should be
   * inserted before.
   *
   * @param nextSibling
   *          the next sibling node.
   */
  public void setNextSibling(Node nextSibling) {
    m_nextSibling = nextSibling;
  }

  /**
   * Return the next sibling node.
   *
   * @return the next sibling node.
   */
  public Node getNextSibling() {
    return m_nextSibling;
  }

  /**
   * Return null since there is no Writer for this class.
   *
   * @return null
   */
  public java.io.Writer getWriter() {
    return null;
  }

  /**
   * Append a node to the current container.
   *
   * @param newNode
   *          New node to append
   */
  protected void append(Node newNode) throws org.xml.sax.SAXException {

    Node currentNode = m_currentNode;

    if (null != currentNode) {
      if (currentNode == m_root && m_nextSibling != null)
        currentNode.insertBefore(newNode, m_nextSibling);
      else
        currentNode.appendChild(newNode);

      // System.out.println(newNode.getNodeName());
    } else if (null != m_docFrag) {
      if (m_nextSibling != null)
        m_docFrag.insertBefore(newNode, m_nextSibling);
      else
        m_docFrag.appendChild(newNode);
    } else {
      boolean ok = true;
      short type = newNode.getNodeType();

      if (type == Node.TEXT_NODE) {
        String data = newNode.getNodeValue();

        if ((null != data) && (data.trim().length() > 0)) {
          throw new org.xml.sax.SAXException("Warning: can't output text before document element!  Ignoring...");
        }

        ok = false;
      } else if (type == Node.ELEMENT_NODE) {
        if (m_doc.getDocumentElement() != null) {
          ok = false;

          throw new org.xml.sax.SAXException("Can't have more than one root on a DOM!");
        }
      }

      if (ok) {
        if (m_nextSibling != null)
          m_doc.insertBefore(newNode, m_nextSibling);
        else
          m_doc.appendChild(newNode);
      }
    }
  }

  /**
   * Receive an object for locating the origin of SAX document events.
   *
   * <p>
   * SAX parsers are strongly encouraged (though not absolutely required) to
   * supply a locator: if it does so, it must supply the locator to the
   * application by invoking this method before invoking any of the other
   * methods in the ContentHandler interface.
   * </p>
   *
   * <p>
   * The locator allows the application to determine the end position of any
   * document-related event, even if the parser is not reporting an error.
   * Typically, the application will use this information for reporting its own
   * errors (such as character content that does not match an application's
   * business rules). The information returned by the locator is probably not
   * sufficient for use with a search engine.
   * </p>
   *
   * <p>
   * Note that the locator will return correct information only during the
   * invocation of the events in this interface. The application should not
   * attempt to use it at any other time.
   * </p>
   *
   * @param locator
   *          An object that can return the location of any SAX document event.
   * @see org.xml.sax.Locator
   */
  public void setDocumentLocator(Locator locator) {
    this.locator = locator;
    // No action for the moment.
  }

  /**
   * Receive notification of the beginning of a document.
   *
   * <p>
   * The SAX parser will invoke this method only once, before any other methods
   * in this interface or in DTDHandler (except for setDocumentLocator).
   * </p>
   */
  public void startDocument() throws org.xml.sax.SAXException {

    // No action for the moment.
  }

  /**
   * Receive notification of the end of a document.
   *
   * <p>
   * The SAX parser will invoke this method only once, and it will be the last
   * method invoked during the parse. The parser shall not invoke this method
   * until it has either abandoned parsing (because of an unrecoverable error)
   * or reached the end of input.
   * </p>
   */
  public void endDocument() throws org.xml.sax.SAXException {

    // No action for the moment.
  }

  /**
   * Receive notification of the beginning of an element.
   *
   * <p>
   * The Parser will invoke this method at the beginning of every element in the
   * XML document; there will be a corresponding endElement() event for every
   * startElement() event (even when the element is empty). All of the element's
   * content will be reported, in order, before the corresponding endElement()
   * event.
   * </p>
   *
   * <p>
   * If the element name has a namespace prefix, the prefix will still be
   * attached. Note that the attribute list provided will contain only
   * attributes with explicit values (specified or defaulted): #IMPLIED
   * attributes will be omitted.
   * </p>
   *
   *
   * @param ns
   *          The namespace of the node
   * @param localName
   *          The local part of the qualified name
   * @param name
   *          The element name.
   * @param atts
   *          The attributes attached to the element, if any.
   * @see #endElement
   * @see org.xml.sax.Attributes
   */
  public void startElement(String ns, String localName, String name, Attributes atts) throws org.xml.sax.SAXException {

    Element elem;

    // Note that the namespace-aware call must be used to correctly
    // construct a Level 2 DOM, even for non-namespaced nodes.
    if ((null == ns) || (ns.length() == 0))
      elem = m_doc.createElementNS(null, name);
    else
      elem = m_doc.createElementNS(ns, name);

    append(elem);

    try {
      int nAtts = atts.getLength();

      if (0 != nAtts) {
        for (int i = 0; i < nAtts; i++) {

          // System.out.println("type " + atts.getType(i) + " name " +
          // atts.getLocalName(i) );
          // First handle a possible ID attribute
          if (atts.getType(i).equalsIgnoreCase("ID"))
            setIDAttribute(atts.getValue(i), elem);

          String attrNS = atts.getURI(i);

          if ("".equals(attrNS))
            attrNS = null; // DOM represents no-namespace as null

          // System.out.println("attrNS: "+attrNS+", localName:
          // "+atts.getQName(i)
          // +", qname: "+atts.getQName(i)+", value: "+atts.getValue(i));
          // Crimson won't let us set an xmlns: attribute on the DOM.
          String attrQName = atts.getQName(i);

          // In SAX, xmlns[:] attributes have an empty namespace, while in DOM
          // they
          // should have the xmlns namespace
          if (attrQName.startsWith("xmlns:") || attrQName.equals("xmlns")) {
            attrNS = "http://www.w3.org/2000/xmlns/";
          }

          // ALWAYS use the DOM Level 2 call!
          elem.setAttributeNS(attrNS, attrQName, atts.getValue(i));
        }
      }

      if (locator!=null) {
        int lineNumber = locator.getLineNumber();
        elem.setAttributeNS("http://jbpm.org/line-numbers", "line-number", Integer.toString(lineNumber));
        int columnNumber = locator.getColumnNumber();
        elem.setAttributeNS("http://jbpm.org/line-numbers", "column-number", Integer.toString(columnNumber));
      }

      /*
       * Adding namespace nodes to the DOM tree;
       */
      int nDecls = m_prefixMappings.size();

      String prefix, declURL;

      for (int i = 0; i < nDecls; i += 2) {
        prefix = (String) m_prefixMappings.elementAt(i);

        if (prefix == null)
          continue;

        declURL = (String) m_prefixMappings.elementAt(i + 1);

        elem.setAttributeNS("http://www.w3.org/2000/xmlns/", prefix, declURL);
      }

      m_prefixMappings.clear();

      // append(elem);

      m_elemStack.push(elem);

      m_currentNode = elem;

      // append(elem);
    } catch (java.lang.Exception de) {
      // de.printStackTrace();
      throw new org.xml.sax.SAXException(de);
    }

  }

  /**
   *
   *
   *
   * Receive notification of the end of an element.
   *
   * <p>
   * The SAX parser will invoke this method at the end of every element in the
   * XML document; there will be a corresponding startElement() event for every
   * endElement() event (even when the element is empty).
   * </p>
   *
   * <p>
   * If the element name has a namespace prefix, the prefix will still be
   * attached to the name.
   * </p>
   *
   *
   * @param ns
   *          the namespace of the element
   * @param localName
   *          The local part of the qualified name of the element
   * @param name
   *          The element name
   */
  public void endElement(String ns, String localName, String name) throws org.xml.sax.SAXException {
    m_elemStack.pop();
    m_currentNode = m_elemStack.isEmpty() ? null : (Node) m_elemStack.peek();
  }

  /**
   * Set an ID string to node association in the ID table.
   *
   * @param id
   *          The ID string.
   * @param elem
   *          The associated ID.
   */
  public void setIDAttribute(String id, Element elem) {

    // Do nothing. This method is meant to be overiden.
  }

  /**
   * Receive notification of character data.
   *
   * <p>
   * The Parser will call this method to report each chunk of character data.
   * SAX parsers may return all contiguous character data in a single chunk, or
   * they may split it into several chunks; however, all of the characters in
   * any single event must come from the same external entity, so that the
   * Locator provides useful information.
   * </p>
   *
   * <p>
   * The application must not attempt to read from the array outside of the
   * specified range.
   * </p>
   *
   * <p>
   * Note that some parsers will report whitespace using the
   * ignorableWhitespace() method rather than this one (validating parsers must
   * do so).
   * </p>
   *
   * @param ch
   *          The characters from the XML document.
   * @param start
   *          The start position in the array.
   * @param length
   *          The number of characters to read from the array.
   * @see #ignorableWhitespace
   * @see org.xml.sax.Locator
   */
  public void characters(char ch[], int start, int length) throws org.xml.sax.SAXException {
    if (isOutsideDocElem() && isWhiteSpace(ch, start, length))
      return; // avoid DOM006 Hierarchy request error

    if (m_inCData) {
      cdata(ch, start, length);

      return;
    }

    String s = new String(ch, start, length);
    Node childNode;
    childNode = m_currentNode != null ? m_currentNode.getLastChild() : null;
    if (childNode != null && childNode.getNodeType() == Node.TEXT_NODE) {
      ((Text) childNode).appendData(s);
    } else {
      Text text = m_doc.createTextNode(s);
      append(text);
    }
  }

  /**
   * If available, when the disable-output-escaping attribute is used, output
   * raw text without escaping. A PI will be inserted in front of the node with
   * the name "lotusxsl-next-is-raw" and a value of "formatter-to-dom".
   *
   * @param ch
   *          Array containing the characters
   * @param start
   *          Index to start of characters in the array
   * @param length
   *          Number of characters in the array
   */
  public void charactersRaw(char ch[], int start, int length) throws org.xml.sax.SAXException {
    if (isOutsideDocElem() && isWhiteSpace(ch, start, length))
      return; // avoid DOM006 Hierarchy request error

    String s = new String(ch, start, length);

    append(m_doc.createProcessingInstruction("xslt-next-is-raw", "formatter-to-dom"));
    append(m_doc.createTextNode(s));
  }

  /**
   * Report the beginning of an entity.
   *
   * The start and end of the document entity are not reported. The start and
   * end of the external DTD subset are reported using the pseudo-name "[dtd]".
   * All other events must be properly nested within start/end entity events.
   *
   * @param name
   *          The name of the entity. If it is a parameter entity, the name will
   *          begin with '%'.
   * @see #endEntity
   * @see org.xml.sax.ext.DeclHandler#internalEntityDecl
   * @see org.xml.sax.ext.DeclHandler#externalEntityDecl
   */
  public void startEntity(String name) throws org.xml.sax.SAXException {

    // Almost certainly the wrong behavior...
    // entityReference(name);
  }

  /**
   * Report the end of an entity.
   *
   * @param name
   *          The name of the entity that is ending.
   * @see #startEntity
   */
  public void endEntity(String name) throws org.xml.sax.SAXException {
  }

  /**
   * Receive notivication of a entityReference.
   *
   * @param name
   *          name of the entity reference
   */
  public void entityReference(String name) throws org.xml.sax.SAXException {
    append(m_doc.createEntityReference(name));
  }

  /**
   * Receive notification of ignorable whitespace in element content.
   *
   * <p>
   * Validating Parsers must use this method to report each chunk of ignorable
   * whitespace (see the W3C XML 1.0 recommendation, section 2.10):
   * non-validating parsers may also use this method if they are capable of
   * parsing and using content models.
   * </p>
   *
   * <p>
   * SAX parsers may return all contiguous whitespace in a single chunk, or they
   * may split it into several chunks; however, all of the characters in any
   * single event must come from the same external entity, so that the Locator
   * provides useful information.
   * </p>
   *
   * <p>
   * The application must not attempt to read from the array outside of the
   * specified range.
   * </p>
   *
   * @param ch
   *          The characters from the XML document.
   * @param start
   *          The start position in the array.
   * @param length
   *          The number of characters to read from the array.
   * @see #characters
   */
  public void ignorableWhitespace(char ch[], int start, int length) throws org.xml.sax.SAXException {
    if (isOutsideDocElem())
      return; // avoid DOM006 Hierarchy request error

    String s = new String(ch, start, length);

    append(m_doc.createTextNode(s));
  }

  /**
   * Tell if the current node is outside the document element.
   *
   * @return true if the current node is outside the document element.
   */
  private boolean isOutsideDocElem() {
    return (null == m_docFrag) && m_elemStack.size() == 0 && (null == m_currentNode || m_currentNode.getNodeType() == Node.DOCUMENT_NODE);
  }

  /**
   * Receive notification of a processing instruction.
   *
   * <p>
   * The Parser will invoke this method once for each processing instruction
   * found: note that processing instructions may occur before or after the main
   * document element.
   * </p>
   *
   * <p>
   * A SAX parser should never report an XML declaration (XML 1.0, section 2.8)
   * or a text declaration (XML 1.0, section 4.3.1) using this method.
   * </p>
   *
   * @param target
   *          The processing instruction target.
   * @param data
   *          The processing instruction data, or null if none was supplied.
   */
  public void processingInstruction(String target, String data) throws org.xml.sax.SAXException {
    append(m_doc.createProcessingInstruction(target, data));
  }

  /**
   * Report an XML comment anywhere in the document.
   *
   * This callback will be used for comments inside or outside the document
   * element, including comments in the external DTD subset (if read).
   *
   * @param ch
   *          An array holding the characters in the comment.
   * @param start
   *          The starting position in the array.
   * @param length
   *          The number of characters to use from the array.
   */
  public void comment(char ch[], int start, int length) throws org.xml.sax.SAXException {
    append(m_doc.createComment(new String(ch, start, length)));
  }

  /** Flag indicating that we are processing a CData section */
  protected boolean m_inCData = false;

  /**
   * Report the start of a CDATA section.
   *
   * @see #endCDATA
   */
  public void startCDATA() throws org.xml.sax.SAXException {
    m_inCData = true;
    append(m_doc.createCDATASection(""));
  }

  /**
   * Report the end of a CDATA section.
   *
   * @see #startCDATA
   */
  public void endCDATA() throws org.xml.sax.SAXException {
    m_inCData = false;
  }

  /**
   * Receive notification of cdata.
   *
   * <p>
   * The Parser will call this method to report each chunk of character data.
   * SAX parsers may return all contiguous character data in a single chunk, or
   * they may split it into several chunks; however, all of the characters in
   * any single event must come from the same external entity, so that the
   * Locator provides useful information.
   * </p>
   *
   * <p>
   * The application must not attempt to read from the array outside of the
   * specified range.
   * </p>
   *
   * <p>
   * Note that some parsers will report whitespace using the
   * ignorableWhitespace() method rather than this one (validating parsers must
   * do so).
   * </p>
   *
   * @param ch
   *          The characters from the XML document.
   * @param start
   *          The start position in the array.
   * @param length
   *          The number of characters to read from the array.
   * @see #ignorableWhitespace
   * @see org.xml.sax.Locator
   */
  public void cdata(char ch[], int start, int length) throws org.xml.sax.SAXException {
    if (isOutsideDocElem() && isWhiteSpace(ch, start, length))
      return; // avoid DOM006 Hierarchy request error

    String s = new String(ch, start, length);

    CDATASection section = (CDATASection) m_currentNode.getLastChild();
    section.appendData(s);
  }

  /**
   * Report the start of DTD declarations, if any.
   *
   * Any declarations are assumed to be in the internal subset unless otherwise
   * indicated.
   *
   * @param name
   *          The document type name.
   * @param publicId
   *          The declared public identifier for the external DTD subset, or
   *          null if none was declared.
   * @param systemId
   *          The declared system identifier for the external DTD subset, or
   *          null if none was declared.
   * @see #endDTD
   * @see #startEntity
   */
  public void startDTD(String name, String publicId, String systemId) throws org.xml.sax.SAXException {

    // Do nothing for now.
  }

  /**
   * Report the end of DTD declarations.
   *
   * @see #startDTD
   */
  public void endDTD() throws org.xml.sax.SAXException {

    // Do nothing for now.
  }

  /**
   * Begin the scope of a prefix-URI Namespace mapping.
   *
   * <p>
   * The information from this event is not necessary for normal Namespace
   * processing: the SAX XML reader will automatically replace prefixes for
   * element and attribute names when the http://xml.org/sax/features/namespaces
   * feature is true (the default).
   * </p>
   *
   * <p>
   * There are cases, however, when applications need to use prefixes in
   * character data or in attribute values, where they cannot safely be expanded
   * automatically; the start/endPrefixMapping event supplies the information to
   * the application to expand prefixes in those contexts itself, if necessary.
   * </p>
   *
   * <p>
   * Note that start/endPrefixMapping events are not guaranteed to be properly
   * nested relative to each-other: all startPrefixMapping events will occur
   * before the corresponding startElement event, and all endPrefixMapping
   * events will occur after the corresponding endElement event, but their order
   * is not guaranteed.
   * </p>
   *
   * @param prefix
   *          The Namespace prefix being declared.
   * @param uri
   *          The Namespace URI the prefix is mapped to.
   * @see #endPrefixMapping
   * @see #startElement
   */
  public void startPrefixMapping(String prefix, String uri) throws org.xml.sax.SAXException {
    if (null == prefix || prefix.equals(""))
      prefix = "xmlns";
    else
      prefix = "xmlns:" + prefix;
    m_prefixMappings.addElement(prefix);
    m_prefixMappings.addElement(uri);
  }

  /**
   * End the scope of a prefix-URI mapping.
   *
   * <p>
   * See startPrefixMapping for details. This event will always occur after the
   * corresponding endElement event, but the order of endPrefixMapping events is
   * not otherwise guaranteed.
   * </p>
   *
   * @param prefix
   *          The prefix that was being mapping.
   * @see #startPrefixMapping
   * @see #endElement
   */
  public void endPrefixMapping(String prefix) throws org.xml.sax.SAXException {
  }

  /**
   * Receive notification of a skipped entity.
   *
   * <p>
   * The Parser will invoke this method once for each entity skipped.
   * Non-validating processors may skip entities if they have not seen the
   * declarations (because, for example, the entity was declared in an external
   * DTD subset). All processors may skip external entities, depending on the
   * values of the http://xml.org/sax/features/external-general-entities and the
   * http://xml.org/sax/features/external-parameter-entities properties.
   * </p>
   *
   * @param name
   *          The name of the skipped entity. If it is a parameter entity, the
   *          name will begin with '%'.
   */
  public void skippedEntity(String name) throws org.xml.sax.SAXException {
  }

  /**
   * Returns whether the specified <var>ch</var> conforms to the XML 1.0
   * definition of whitespace. Refer to <A
   * href="http://www.w3.org/TR/1998/REC-xml-19980210#NT-S"> the definition of
   * <CODE>S</CODE></A> for details.
   *
   * @param ch
   *          Character to check as XML whitespace.
   * @return =true if <var>ch</var> is XML whitespace; otherwise =false.
   */
  public static boolean isWhiteSpace(char ch) {
    return (ch == 0x20) || (ch == 0x09) || (ch == 0xD) || (ch == 0xA);
  }

  /**
   * Tell if the string is whitespace.
   *
   * @param ch
   *          Character array to check as XML whitespace.
   * @param start
   *          Start index of characters in the array
   * @param length
   *          Number of characters in the array
   * @return True if the characters in the array are XML whitespace; otherwise,
   *         false.
   */
  public static boolean isWhiteSpace(char ch[], int start, int length) {

    int end = start + length;

    for (int s = start; s < end; s++) {
      if (!isWhiteSpace(ch[s]))
        return false;
    }

    return true;
  }

}
TOP

Related Classes of org.jbpm.xml.DomBuilder

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.