Package org.eclipse.wst.xml.core.internal.contentmodel.util

Source Code of org.eclipse.wst.xml.core.internal.contentmodel.util.DOMContentBuilderImpl$ExternalCMDocumentSupport

/*******************************************************************************
* Copyright (c) 2002, 2010 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     IBM Corporation - initial API and implementation
*     Jens Lukowski/Innoopract - initial renaming/restructuring
*    
*******************************************************************************/
package org.eclipse.wst.xml.core.internal.contentmodel.util;

import java.util.Hashtable;
import java.util.List;
import java.util.Stack;
import java.util.Vector;

import org.eclipse.wst.xml.core.internal.contentmodel.CMAnyElement;
import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMContent;
import org.eclipse.wst.xml.core.internal.contentmodel.CMDataType;
import org.eclipse.wst.xml.core.internal.contentmodel.CMDocument;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMGroup;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNode;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNodeList;
import org.eclipse.wst.xml.core.internal.contentmodel.ContentModelManager;
import org.eclipse.wst.xml.core.internal.contentmodel.internal.util.CMDataTypeValueHelper;
import org.w3c.dom.Attr;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;


/**
* todo... common up this code with 'ContentBuilder'
*/
public class DOMContentBuilderImpl extends CMVisitor implements DOMContentBuilder {
  protected int buildPolicy = BUILD_ALL_CONTENT;
  protected Hashtable propertyTable = new Hashtable();

  protected boolean alwaysVisit = false;
  protected List resultList;
  protected Document document;
  protected Node currentParent;
  protected Node topParent;
  protected Vector visitedCMElementDeclarationList = new Vector();
  protected boolean attachNodesToParent = true;
  protected NamespaceTable namespaceTable;

  protected List namespaceInfoList;
  protected Element rootElement; // this is used only teporarily via
                  // createDefaultRootContent
  protected ExternalCMDocumentSupport externalCMDocumentSupport;

  public boolean supressCreationOfDoctypeAndXMLDeclaration;

  protected CMDataTypeValueHelper valueHelper = new CMDataTypeValueHelper();

  protected int numOfRepeatableElements = 1;
  protected Stack cmGroupStack = new Stack();
  protected int depthLimit = -1;

  protected int domLevel;
  private int originalBuildPolicy;
 
  public interface ExternalCMDocumentSupport {
    public CMDocument getCMDocument(Element element, String uri);
  }

  public void setExternalCMDocumentSupport(ExternalCMDocumentSupport externalCMDocumentSupport) {
    this.externalCMDocumentSupport = externalCMDocumentSupport;
  }

  public DOMContentBuilderImpl(Document document) {
    this.document = document;
    namespaceTable = new NamespaceTable(document);
  }

  public void setBuildPolicy(int buildPolicy) {
    this.buildPolicy = buildPolicy;
  }

  public int getBuildPolicy() {
    return buildPolicy;
  }

  protected boolean buildAllContent(int policy) {
    return (policy & BUILD_ALL_CONTENT) == BUILD_ALL_CONTENT;
  }

  protected boolean buildOptionalElements(int policy) {
    return (policy & BUILD_OPTIONAL_ELEMENTS) == BUILD_OPTIONAL_ELEMENTS;
  }

  protected boolean buildOptionalAttributes(int policy) {
    return (policy & BUILD_OPTIONAL_ATTRIBUTES) == BUILD_OPTIONAL_ATTRIBUTES;
  }

  protected boolean buildFirstChoice(int policy) {
    return (policy & BUILD_FIRST_CHOICE) == BUILD_FIRST_CHOICE;
  }

  protected boolean buildTextNodes(int policy) {
    return (policy & BUILD_TEXT_NODES) == BUILD_TEXT_NODES;
  }

  protected boolean buildFirstSubstitution(int policy) {
    return (policy & BUILD_FIRST_SUBSTITUTION) == BUILD_FIRST_SUBSTITUTION;
  }

  public List getResult() {
    return resultList;
  }

  public void setProperty(String propertyName, Object value) {
    propertyTable.put(propertyName, value);
  }

  public Object getProperty(String propertyName) {
    return propertyTable.get(propertyName);
  }

  public void build(Node parent, CMNode child) {
    resultList = new Vector();
    topParent = parent;
    currentParent = parent;
    if (parent instanceof Element) {
      namespaceTable.addElementLineage((Element) parent);
    }
    attachNodesToParent = false;
    alwaysVisit = true;
    visitCMNode(child);
  }

  public void createDefaultRootContent(CMDocument cmDocument, CMElementDeclaration rootCMElementDeclaration, List namespaceInfoList) throws Exception {
    this.namespaceInfoList = namespaceInfoList;
    createDefaultRootContent(cmDocument, rootCMElementDeclaration);
  }

  public void createDefaultRootContent(CMDocument cmDocument, CMElementDeclaration rootCMElementDeclaration) throws Exception {
    String grammarFileName = cmDocument.getNodeName();
    if (!supressCreationOfDoctypeAndXMLDeclaration) {
      // TODO cs... investigate to see if this code path is ever used,
      // doesn't seem to be
      // for now I'm setting the encoding to UTF-8 just incase this code
      // path is used somewhere
      //
      String piValue = "version=\"1.0\""; //$NON-NLS-1$
      String encoding = "UTF-8"; //$NON-NLS-1$
      piValue += " encoding=\"" + encoding + "\""; //$NON-NLS-1$ //$NON-NLS-2$     
      ProcessingInstruction pi = document.createProcessingInstruction("xml", piValue); //$NON-NLS-1$
      document.appendChild(pi);

      // if we have a 'dtd' then add a DOCTYPE tag
      //
      if (grammarFileName != null && grammarFileName.endsWith("dtd")) //$NON-NLS-1$
      {
        DOMImplementation domImpl = document.getImplementation();
        DocumentType documentType = domImpl.createDocumentType(rootCMElementDeclaration.getElementName(), grammarFileName, grammarFileName);
        document.appendChild(documentType);
      }
    }

    // if we have a schema add an xsi:schemaLocation attribute
    //
    if (grammarFileName != null && grammarFileName.endsWith("xsd") && namespaceInfoList != null) //$NON-NLS-1$
    {
      DOMNamespaceInfoManager manager = new DOMNamespaceInfoManager();
      String name = rootCMElementDeclaration.getNodeName();
      if (namespaceInfoList.size() > 0) {
        NamespaceInfo info = (NamespaceInfo) namespaceInfoList.get(0);
        if (info.prefix != null && info.prefix.length() > 0) {
          name = info.prefix + ":" + name; //$NON-NLS-1$
        }
      }
      rootElement = createElement(rootCMElementDeclaration, name, document);
      manager.addNamespaceInfo(rootElement, namespaceInfoList, true);
    }
    createDefaultContent(document, rootCMElementDeclaration);
  }

  public void createDefaultContent(Node parent, CMElementDeclaration ed) {
    currentParent = parent;
    alwaysVisit = true;
    originalBuildPolicy = buildPolicy;
    visitCMElementDeclaration(ed);
  }

  public String computeName(CMNode cmNode, Node parent) {
    String prefix = null;
    return DOMNamespaceHelper.computeName(cmNode, parent, prefix, namespaceTable);
  }

  // overide the following 'create' methods to control how nodes are created
  //
  protected Element createElement(CMElementDeclaration ed, String name, Node parent) {
    return document.createElement(name);
  }

  protected Attr createAttribute(CMAttributeDeclaration ad, String name, Node parent) {
    return document.createAttribute(name);
  }

  protected Text createTextNode(CMDataType dataType, String value, Node parent) {
    return document.createTextNode(value);
  }

  protected void handlePushParent(Element parent, CMElementDeclaration ed) {
    domLevel++;
  }

  protected void handlePopParent(Element element, CMElementDeclaration ed) {
      domLevel--;
  }

  // The range must be between 1 and 99.
  public void setNumOfRepeatableElements(int i) {
    numOfRepeatableElements = i;
  }

  protected int getNumOfRepeatableElements() {
    return numOfRepeatableElements;
  }

  public void visitCMElementDeclaration(CMElementDeclaration ed) {
    int forcedMin = (buildOptionalElements(buildPolicy) || alwaysVisit) ? 1 : 0;
    int min = Math.max(ed.getMinOccur(), forcedMin);

    // Correct the min value if the element is contained in
    // a group.
    if (!cmGroupStack.isEmpty()) {
      CMGroup group = (CMGroup) cmGroupStack.peek();
      int gmin = group.getMinOccur();
      if (gmin == 0)
        if (buildOptionalElements(buildPolicy)) {
          /* do nothing: min = min */
        }
        else {
          min = min * gmin; // min = 0
        }
      else {
        min = min * gmin;
      }
    }

    int max = Math.min(ed.getMaxOccur(), getNumOfRepeatableElements());
    if (max < min)
      max = min;

    alwaysVisit = false;

    // Note - ed may not be abstract but has substitutionGroups
    // involved.
    if (buildFirstSubstitution(buildPolicy) || isAbstract(ed)) // leave
                                  // this
                                  // for
                                  // backward
                                  // compatibility
                                  // for now
    {
      // Note - To change so that if ed is optional, we do not
      // generate anything here.
      ed = getSubstitution(ed);

      // Note - the returned ed may be an abstract element in
      // which case the xml will be invalid.
    }

    if (min > 0 && !visitedCMElementDeclarationList.contains(ed)) {
      visitedCMElementDeclarationList.add(ed);
      for (int i = 1; i <= max; i++) {
        // create an Element for each
        Element element = null;
        if (rootElement != null) {
          element = rootElement;
          rootElement = null;
        }
        else {
          element = createElement(ed, computeName(ed, currentParent), currentParent);
        }

        // visit the children of the GrammarElement
        Node oldParent = currentParent;
        currentParent = element;
        handlePushParent(element, ed);

        namespaceTable.addElement(element);

        boolean oldAttachNodesToParent = attachNodesToParent;
        attachNodesToParent = true;

        // instead of calling super.visitCMElementDeclaration()
        // we duplicate the code with some minor modifications
        CMNamedNodeMap nodeMap = ed.getAttributes();
        int size = nodeMap.getLength();
        for (int j = 0; j < size; j++) {
          visitCMNode(nodeMap.item(j));
        }

        CMContent content = ed.getContent();
        if (content != null) {
          visitCMNode(content);
        }

        if (ed.getContentType() == CMElementDeclaration.PCDATA) {
          CMDataType dataType = ed.getDataType();
          if (dataType != null) {
            visitCMDataType(dataType);
          }
        }
        // end duplication
        attachNodesToParent = oldAttachNodesToParent;
        handlePopParent(element, ed);
        currentParent = oldParent;
        linkNode(element);
      }
      int size = visitedCMElementDeclarationList.size();
      visitedCMElementDeclarationList.remove(size - 1);
    }
  }


  public void visitCMDataType(CMDataType dataType) {
    Text text = null;
    String value = null;

    // For backward compatibility:
    // Previous code uses a property value but new one uses
    // buildPolicy.
    if (getProperty(PROPERTY_BUILD_BLANK_TEXT_NODES) != null && getProperty(PROPERTY_BUILD_BLANK_TEXT_NODES).equals("true")) //$NON-NLS-1$
      buildPolicy = buildPolicy ^ BUILD_TEXT_NODES;

    if (buildTextNodes(buildPolicy)) {
      value = valueHelper.getValue(dataType);
      if (value == null) {
        if (currentParent != null && currentParent.getNodeType() == Node.ELEMENT_NODE) {
          value = currentParent.getNodeName();
        }
        else {
          value = "pcdata"; //$NON-NLS-1$
        }
      }
    }
    else {
      value = ""; //$NON-NLS-1$
    }
    text = createTextNode(dataType, value, currentParent);
    linkNode(text);
  }

  public void visitCMNode(CMNode node) {
    if (depthLimit != -1) {
      if (domLevel > depthLimit) {
        buildPolicy = buildPolicy &= ~BUILD_OPTIONAL_ELEMENTS;
      } else {
        buildPolicy = originalBuildPolicy;
      }
    }
    super.visitCMNode(node);
  }

  public void visitCMGroup(CMGroup e) {
    cmGroupStack.push(e);

    int forcedMin = (buildOptionalElements(buildPolicy) || alwaysVisit) ? 1 : 0;
    int min = Math.max(e.getMinOccur(), forcedMin);

    int max = 0;
    if (e.getMaxOccur() == -1) // unbounded
      max = getNumOfRepeatableElements();
    else
      max = Math.min(e.getMaxOccur(), getNumOfRepeatableElements());

    if (max < min)
      max = min;

    alwaysVisit = false;

    for (int i = 1; i <= max; i++) {
      if (e.getOperator() == CMGroup.CHOICE && buildFirstChoice(buildPolicy)) {
        CMNode hintNode = null;

        // todo... the CMGroup should specify the hint... but it seems
        // as though
        // the Yamato guys are making the CMElement specify the hint.
        // I do it that way for now until... we should fix this post
        // GA
        //   
        int listSize = visitedCMElementDeclarationList.size();
        if (listSize > 0) {
          CMElementDeclaration ed = (CMElementDeclaration) visitedCMElementDeclarationList.get(listSize - 1);
          Object contentHint = ed.getProperty("contentHint"); //$NON-NLS-1$
          if (contentHint instanceof CMNode) {
            hintNode = (CMNode) contentHint;
          }
        }

        // see if this hint corresponds to a valid choice
        //
        CMNode cmNode = null;

        if (hintNode != null) {
          CMNodeList nodeList = e.getChildNodes();
          int nodeListLength = nodeList.getLength();
          for (int j = 0; j < nodeListLength; j++) {
            if (hintNode == nodeList.item(j)) {
              cmNode = hintNode;
            }
          }
        }

        // if no cmNode has been determined from the hint, just use
        // the first choice
        //
        if (cmNode == null) {
          CMNodeList nodeList = e.getChildNodes();
          if (nodeList.getLength() > 0) {
            cmNode = nodeList.item(0);
          }
        }

        if (cmNode != null) {
          // Bug 330260
          // Problem - Add child element also adds optional grand-child elements
          // This assumes 'e' is a model group choice, case 1. However 'e' could be a model group definition, case 2, where the
          // first child is a model group. In the first case (choice), the first child is an
          // element. Upon visiting the element (visitCMElementDeclaration), the minOccurs of the
          // choice is ALSO considered. If its minOccurs is 0, then the first element is not added as a child.
          // However, in the second case (model group definition), the first child is a choice, but the multiplicity is [1,1],
          // meaning, it is required. So the first element is then added as child, even though
          // the model group definition reference is optional. (minOccurs is not checked in this method, visitCMGroup)
          // Visit the node only if it is not a GROUP (model group). If it is an element, then visit it.
          if (!(cmNode.getNodeType() == CMNode.GROUP && min > 0)) {
            visitCMNode(cmNode);
          }
        }
      }
      else if (e.getOperator() == CMGroup.ALL // ALL
            || e.getOperator() == CMGroup.SEQUENCE) // SEQUENCE
      {
        // visit all of the content
        super.visitCMGroup(e);
      }
    }

    cmGroupStack.pop();
  }

  static int count = 0;

  public void visitCMAttributeDeclaration(CMAttributeDeclaration ad) {
    if (alwaysVisit || buildOptionalAttributes(buildPolicy) || ad.getUsage() == CMAttributeDeclaration.REQUIRED) {
      alwaysVisit = false;
      String name = computeName(ad, currentParent);
      String value = valueHelper.getValue(ad, namespaceTable);
      Attr attr = createAttribute(ad, name, currentParent);
      attr.setValue(value != null ? value : ""); //$NON-NLS-1$
      linkNode(attr);
    }
  }

  protected boolean isAbstract(CMNode ed) {
    boolean result = false;
    if (ed != null) {
      Object value = ed.getProperty("Abstract"); //$NON-NLS-1$
      result = (value == Boolean.TRUE);
    }
    return result;
  }

  protected CMElementDeclaration getSubstitution(CMElementDeclaration ed) {
    CMElementDeclaration result = ed;
    CMNodeList l = (CMNodeList) ed.getProperty("SubstitutionGroup"); //$NON-NLS-1$
    if (l != null) {
      for (int i = 0; i < l.getLength(); i++) {
        CMNode candidate = l.item(i);
        if (!isAbstract(candidate) && (candidate instanceof CMElementDeclaration)) {
          result = (CMElementDeclaration) candidate;
          break;
        }
      }
    }
    return result;
  }

  protected CMElementDeclaration getParentCMElementDeclaration() {
    CMElementDeclaration ed = null;
    int listSize = visitedCMElementDeclarationList.size();
    if (listSize > 0) {
      ed = (CMElementDeclaration) visitedCMElementDeclarationList.get(listSize - 1);
    }
    return ed;
  }

  public void visitCMAnyElement(CMAnyElement anyElement) {
    // ingnore buildPolicy for ANY elements... only create elements if
    // absolutely needed
    //
    int forcedMin = alwaysVisit ? 1 : 0;
    int min = Math.max(anyElement.getMinOccur(), forcedMin);
    alwaysVisit = false;

    String uri = anyElement.getNamespaceURI();
    String targetNSProperty = "http://org.eclipse.wst/cm/properties/targetNamespaceURI"; //$NON-NLS-1$
    CMDocument parentCMDocument = (CMDocument) anyElement.getProperty("CMDocument"); //$NON-NLS-1$
    CMElementDeclaration ed = null;

    // System.out.println("parentCMDocument = " + parentCMDocument);
    // //$NON-NLS-1$
    if (parentCMDocument != null) {
      if (uri == null || uri.startsWith("##") || uri.equals(parentCMDocument.getProperty(targetNSProperty))) //$NON-NLS-1$
      {
        ed = getSuitableElement(getParentCMElementDeclaration(), parentCMDocument);
      }
    }


    if (ed == null && externalCMDocumentSupport != null && uri != null && !uri.startsWith("##") && currentParent instanceof Element) //$NON-NLS-1$
    {
      CMDocument externalCMDocument = externalCMDocumentSupport.getCMDocument((Element) currentParent, uri);
      if (externalCMDocument != null) {
        ed = getSuitableElement(null, externalCMDocument);
      }
    }

    for (int i = 1; i <= min; i++) {
      if (ed != null) {
        visitCMElementDeclaration(ed);
      }
      else {
        Element element = document.createElement("ANY-ELEMENT"); //$NON-NLS-1$
        linkNode(element);
      }
    }
  }

  protected CMElementDeclaration getSuitableElement(CMNamedNodeMap nameNodeMap) {
    CMElementDeclaration result = null;
    int size = nameNodeMap.getLength();
    for (int i = 0; i < size; i++) {
      CMElementDeclaration candidate = (CMElementDeclaration) nameNodeMap.item(i);
      if (!visitedCMElementDeclarationList.contains(candidate)) {
        result = candidate;
        break;
      }
    }
    return result;
  }

  protected CMElementDeclaration getSuitableElement(CMElementDeclaration ed, CMDocument cmDocument) {
    CMElementDeclaration result = null;

    if (ed != null) {
      result = getSuitableElement(ed.getLocalElements());
    }

    if (result == null && cmDocument != null) {
      result = getSuitableElement(cmDocument.getElements());
    }

    return result;
  }


  public void linkNode(Node node) {
    if (attachNodesToParent && currentParent != null) {
      if (node.getNodeType() == Node.ATTRIBUTE_NODE) {
        ((Element) currentParent).setAttributeNode((Attr) node);
      }
      else {
        currentParent.appendChild(node);
      }
    }
    else if (resultList != null) {
      resultList.add(node);
    }
  }

  public static void testPopulateDocumentFromGrammarFile(Document document, String grammarFileName, String rootElementName, boolean hack) {
    try {
      CMDocument cmDocument = ContentModelManager.getInstance().createCMDocument(grammarFileName, null);
      CMNamedNodeMap elementMap = cmDocument.getElements();
      CMElementDeclaration element = (CMElementDeclaration) elementMap.getNamedItem(rootElementName);

      DOMContentBuilderImpl contentBuilder = new DOMContentBuilderImpl(document);
      contentBuilder.supressCreationOfDoctypeAndXMLDeclaration = hack;
      contentBuilder.createDefaultRootContent(cmDocument, element);

      System.out.println();
      System.out.println("-----------------------------"); //$NON-NLS-1$
      DOMWriter writer = new DOMWriter();
      if (hack) {
        writer.print(document, grammarFileName);
      }
      else {
        writer.print(document);
      }
      System.out.println("-----------------------------"); //$NON-NLS-1$
    }
    catch (Exception e) {
      System.out.println("Error: " + e); //$NON-NLS-1$
      e.printStackTrace();
    }
  }
 
  public void setOptionalElementDepthLimit(int depth) {
    depthLimit = depth;
  }
 

  // test
  //
  /*
   * public static void main(String arg[]) { if (arg.length >= 2) { try {
   * CMDocumentFactoryRegistry.getInstance().registerCMDocumentBuilderWithClassName("org.eclipse.wst.xml.core.internal.contentmodel.mofimpl.CMDocumentBuilderImpl");
   *
   * String grammarFileName = arg[0]; String rootElementName = arg[1];
   *
   * Document document =
   * (Document)Class.forName("org.apache.xerces.dom.DocumentImpl").newInstance();
   * testPopulateDocumentFromGrammarFile(document, grammarFileName,
   * rootElementName, true); } catch (Exception e) {
   * System.out.println("DOMContentBuilderImpl error"); e.printStackTrace(); } }
   * else { System.out.println("Usage : java
   * org.eclipse.wst.xml.util.DOMContentBuildingCMVisitor grammarFileName
   * rootElementName"); } }
   */
TOP

Related Classes of org.eclipse.wst.xml.core.internal.contentmodel.util.DOMContentBuilderImpl$ExternalCMDocumentSupport

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.