Package org.eclipse.wst.xml.core.internal.provisional.format

Source Code of org.eclipse.wst.xml.core.internal.provisional.format.ElementNodeFormatter

/*******************************************************************************
* Copyright (c) 2001, 2007 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
*     Jesper Steen M�ller - xml:space='preserve' support
*    
*******************************************************************************/
package org.eclipse.wst.xml.core.internal.provisional.format;

import org.eclipse.jface.text.BadLocationException;
import org.eclipse.wst.sse.core.internal.format.IStructuredFormatContraints;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocument;
import org.eclipse.wst.sse.core.internal.provisional.text.IStructuredDocumentRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegion;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.sse.core.utils.StringUtils;
import org.eclipse.wst.xml.core.internal.Logger;
import org.eclipse.wst.xml.core.internal.contentmodel.CMAttributeDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMElementDeclaration;
import org.eclipse.wst.xml.core.internal.contentmodel.CMNamedNodeMap;
import org.eclipse.wst.xml.core.internal.document.AttrImpl;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMDocument;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.provisional.document.ISourceGenerator;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.eclipse.wst.xml.core.internal.ssemodelquery.ModelQueryAdapter;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;


public class ElementNodeFormatter extends DocumentNodeFormatter {
  static private final char DOUBLE_QUOTE = '"';//$NON-NLS-1$
  static private final String DOUBLE_QUOTES = "\"\"";//$NON-NLS-1$
  static private final char EQUAL_CHAR = '='; // equal sign$NON-NLS-1$
  static private final String PRESERVE = "preserve";//$NON-NLS-1$
  static private final String PRESERVE_QUOTED = "\"preserve\"";//$NON-NLS-1$
  static private final char SINGLE_QUOTE = '\'';//$NON-NLS-1$
  static private final String XML_SPACE = "xml:space";//$NON-NLS-1$
  static private final char SPACE_CHAR = ' '; //$NON-NLS-1$
  static private final String XSL_NAMESPACE = "http://www.w3.org/1999/XSL/Transform"; //$NON-NLS-1$
  static private final String XSL_ATTRIBUTE = "attribute"; //$NON-NLS-1$
  static private final String XSL_TEXT = "text"; //$NON-NLS-1$

  protected void formatEndTag(IDOMNode node, IStructuredFormatContraints formatContraints) {
    if (!isEndTagMissing(node)) {
      // end tag exists

      IStructuredDocument structuredDocument = node.getModel().getStructuredDocument();
      String lineDelimiter = structuredDocument.getLineDelimiter();
      String nodeIndentation = getNodeIndent(node);
      IDOMNode lastChild = (IDOMNode) node.getLastChild();
      if (lastChild != null && lastChild.getNodeType() != Node.TEXT_NODE) {
        if (isEndTagMissing(lastChild)) {
          // find deepest child
          IDOMNode deepestChild = (IDOMNode) lastChild.getLastChild();
          while (deepestChild != null && deepestChild.getLastChild() != null && isEndTagMissing(deepestChild)) {
            lastChild = deepestChild;
            deepestChild = (IDOMNode) deepestChild.getLastChild();
          }

          if (deepestChild != null) {
            if (deepestChild.getNodeType() == Node.TEXT_NODE) {
              // Special indentation handling if lastChild's end
              // tag is missing and deepestChild is a text node.
              String nodeText = deepestChild.getNodeValue();

              if (!nodeText.endsWith(lineDelimiter + nodeIndentation)) {
                nodeText = StringUtils.appendIfNotEndWith(nodeText, lineDelimiter);
                nodeText = StringUtils.appendIfNotEndWith(nodeText, nodeIndentation);
              }

              replaceNodeValue(deepestChild, nodeText);
            }
            else
              insertAfterNode(lastChild, lineDelimiter + nodeIndentation);
          }
        }
        else
          // indent end tag
          insertAfterNode(lastChild, lineDelimiter + nodeIndentation);
      }
      else if (lastChild == null && firstStructuredDocumentRegionContainsLineDelimiters(node)) {
        // BUG174243 do not indent end tag if node has empty content
        // (otherwise new text node would be introduced)
        ModelQueryAdapter adapter = (ModelQueryAdapter) ((IDOMDocument) node.getOwnerDocument()).getAdapterFor(ModelQueryAdapter.class);
        CMElementDeclaration elementDeclaration = (CMElementDeclaration) adapter.getModelQuery().getCMNode(node);
        if ((elementDeclaration == null) || (elementDeclaration.getContentType() != CMElementDeclaration.EMPTY)) {
          // indent end tag
          replace(structuredDocument, node.getFirstStructuredDocumentRegion().getEndOffset(), 0, lineDelimiter + nodeIndentation);
        }
      }

      // format end tag name
      IStructuredDocumentRegion endTagStructuredDocumentRegion = node.getLastStructuredDocumentRegion();
      if (endTagStructuredDocumentRegion.getRegions().size() >= 3) {
        ITextRegion endTagNameRegion = endTagStructuredDocumentRegion.getRegions().get(1);
        removeRegionSpaces(node, endTagStructuredDocumentRegion, endTagNameRegion);
      }
    }
  }

  protected void formatNode(IDOMNode node, IStructuredFormatContraints formatContraints) {
    if (node != null) {
      // format indentation before node
      formatIndentationBeforeNode(node, formatContraints);

      // format start tag
      IDOMNode newNode = node;
      int startTagStartOffset = node.getStartOffset();
      IDOMModel structuredModel = node.getModel();

      boolean currentlyInXmlSpacePreserve = formatContraints.getInPreserveSpaceElement();
      formatStartTag(node, formatContraints);
      // save new node
      newNode = (IDOMNode) structuredModel.getIndexedRegion(startTagStartOffset);

      IStructuredDocumentRegion flatNode = newNode.getFirstStructuredDocumentRegion();
      if (flatNode != null) {
        ITextRegionList regions = flatNode.getRegions();
        ITextRegion lastRegion = regions.get(regions.size() - 1);
        // format children and end tag if not empty start tag
        if (lastRegion.getType() != DOMRegionContext.XML_EMPTY_TAG_CLOSE) {
          // format children
          formatChildren(newNode, formatContraints);

          // save new node
          newNode = (IDOMNode) structuredModel.getIndexedRegion(startTagStartOffset);

          // format end tag
          formatEndTag(newNode, formatContraints);
        }
      }

      formatContraints.setInPreserveSpaceElement(currentlyInXmlSpacePreserve);
      // only indent if not at last node
      if (newNode != null && newNode.getNextSibling() != null)
        // format indentation after node
        formatIndentationAfterNode(newNode, formatContraints);
    }
  }

  /**
   * This method formats the start tag name, and formats the attributes if
   * available.
   */
  protected void formatStartTag(IDOMNode node, IStructuredFormatContraints formatContraints) {
    StructuredFormatPreferencesXML preferences = (StructuredFormatPreferencesXML) getFormatPreferences();
    String singleIndent = preferences.getIndent();
    String lineIndent = formatContraints.getCurrentIndent();
    String attrIndent = lineIndent + singleIndent;
    boolean splitMultiAttrs = preferences.getSplitMultiAttrs();
    boolean alignEndBracket = preferences.isAlignEndBracket();
    boolean sawXmlSpace = false;

    IStructuredDocumentRegion flatNode = node.getFirstStructuredDocumentRegion();
    NamedNodeMap attributes = node.getAttributes();

    // Note: attributes should not be null even if the node has no
    // attributes. However, attributes.getLength() will be 0. But, check
    // for null just in case.
    if (attributes != null) {
      // compute current available line width
      int currentAvailableLineWidth = 0;
      try {
        // 1 is for "<"
        int nodeNameOffset = node.getStartOffset() + 1 + node.getNodeName().length();
        int lineOffset = node.getStructuredDocument().getLineInformationOfOffset(nodeNameOffset).getOffset();
        String text = node.getStructuredDocument().get(lineOffset, nodeNameOffset - lineOffset);
        int usedWidth = getIndentationLength(text);
        currentAvailableLineWidth = preferences.getLineWidth() - usedWidth;
      }
      catch (BadLocationException e) {
        // log for now, unless we find reason not to
        Logger.log(Logger.INFO, e.getMessage());
      }

      StringBuffer stringBuffer = new StringBuffer();
      String lineDelimiter = node.getModel().getStructuredDocument().getLineDelimiter();
      int attrLength = attributes.getLength();
      int lastUndefinedRegionOffset = 0;
      boolean startTagSpansOver1Line = false;

      for (int i = 0; i < attrLength; i++) {
        AttrImpl attr = (AttrImpl) attributes.item(i);
        ITextRegion nameRegion = attr.getNameRegion();
        ITextRegion equalRegion = attr.getEqualRegion();
        ITextRegion valueRegion = attr.getValueRegion();

        // append undefined regions
        String undefinedRegion = getUndefinedRegions(node, lastUndefinedRegionOffset, attr.getStartOffset() - lastUndefinedRegionOffset);
        stringBuffer.append(undefinedRegion);
        lastUndefinedRegionOffset = attr.getStartOffset();

        // check for xml:space attribute
        if (flatNode.getText(nameRegion).compareTo(XML_SPACE) == 0) {
          if (valueRegion == null) {
            // [111674] If nothing has been written yet, treat as
            // preserve, but only as hint
            formatContraints.setInPreserveSpaceElement(true);
            // Note we don't set 'sawXmlSpace', so that default or
            // fixed DTD/XSD values may override.
          }
          else {
            ISourceGenerator generator = node.getModel().getGenerator();
            String newAttrValue = generator.generateAttrValue(attr);

            // There is a problem in
            // StructuredDocumentRegionUtil.getAttrValue(ITextRegion)
            // when the region is instanceof ContextRegion.
            // Workaround for now.
            if (flatNode.getText(valueRegion).length() == 1) {
              char firstChar = flatNode.getText(valueRegion).charAt(0);
              if ((firstChar == DOUBLE_QUOTE) || (firstChar == SINGLE_QUOTE))
                newAttrValue = DOUBLE_QUOTES;
            }

            if (newAttrValue.compareTo(PRESERVE_QUOTED) == 0)
              formatContraints.setInPreserveSpaceElement(true);
            else
              formatContraints.setInPreserveSpaceElement(false);
            sawXmlSpace = true;
          }
        }

        if (splitMultiAttrs && attrLength > 1) {
          stringBuffer.append(lineDelimiter + attrIndent);
          stringBuffer.append(flatNode.getText(nameRegion));
          startTagSpansOver1Line = true;
          if (valueRegion != null) {
            // append undefined regions
            undefinedRegion = getUndefinedRegions(node, lastUndefinedRegionOffset, flatNode.getStartOffset(equalRegion) - lastUndefinedRegionOffset);
            stringBuffer.append(undefinedRegion);
            lastUndefinedRegionOffset = flatNode.getStartOffset(equalRegion);

            stringBuffer.append(EQUAL_CHAR);

            // append undefined regions
            undefinedRegion = getUndefinedRegions(node, lastUndefinedRegionOffset, flatNode.getStartOffset(valueRegion) - lastUndefinedRegionOffset);
            stringBuffer.append(undefinedRegion);
            lastUndefinedRegionOffset = flatNode.getStartOffset(valueRegion);

            // Note: trim() should not be needed for
            // valueRegion.getText(). Just a workaround for a
            // problem found in valueRegion for now.
            stringBuffer.append(flatNode.getText(valueRegion).trim());
          }
        }
        else {
          if (valueRegion != null) {
            int textLength = 1 + flatNode.getText(nameRegion).length() + 1 + flatNode.getText(valueRegion).length();
            if (i == attrLength - 1) {
              if (flatNode != null) {
                ITextRegionList regions = flatNode.getRegions();
                ITextRegion lastRegion = regions.get(regions.size() - 1);
                if (lastRegion.getType() != DOMRegionContext.XML_EMPTY_TAG_CLOSE)
                  // 3 is for " />"
                  textLength += 3;
                else
                  // 1 is for ">"
                  textLength++;
              }
            }

            if (currentAvailableLineWidth >= textLength) {
              stringBuffer.append(SPACE_CHAR);
              currentAvailableLineWidth--;
            }
            else {
              stringBuffer.append(lineDelimiter + attrIndent);
              startTagSpansOver1Line = true;
              currentAvailableLineWidth = preferences.getLineWidth() - attrIndent.length();
            }

            stringBuffer.append(flatNode.getText(nameRegion));

            // append undefined regions
            undefinedRegion = getUndefinedRegions(node, lastUndefinedRegionOffset, flatNode.getStartOffset(equalRegion) - lastUndefinedRegionOffset);
            stringBuffer.append(undefinedRegion);
            lastUndefinedRegionOffset = flatNode.getStartOffset(equalRegion);

            stringBuffer.append(EQUAL_CHAR);

            // append undefined regions
            undefinedRegion = getUndefinedRegions(node, lastUndefinedRegionOffset, flatNode.getStartOffset(valueRegion) - lastUndefinedRegionOffset);
            stringBuffer.append(undefinedRegion);
            lastUndefinedRegionOffset = flatNode.getStartOffset(valueRegion);

            // Note: trim() should not be needed for
            // valueRegion.getText(). Just a workaround for a
            // problem found in valueRegion for now.
            stringBuffer.append(flatNode.getText(valueRegion).trim());

            currentAvailableLineWidth -= flatNode.getText(nameRegion).length();
            currentAvailableLineWidth--;
            currentAvailableLineWidth -= flatNode.getText(valueRegion).trim().length();
          }
          else {
            if (currentAvailableLineWidth >= 1 + flatNode.getText(nameRegion).length()) {
              stringBuffer.append(SPACE_CHAR);
              currentAvailableLineWidth--;
            }
            else {
              stringBuffer.append(lineDelimiter + attrIndent);
              startTagSpansOver1Line = true;
              currentAvailableLineWidth = preferences.getLineWidth() - attrIndent.length();
            }

            stringBuffer.append(flatNode.getText(nameRegion));

            currentAvailableLineWidth -= flatNode.getText(nameRegion).length();
          }
        }
      }

      // append undefined regions
      String undefinedRegion = getUndefinedRegions(node, lastUndefinedRegionOffset, node.getEndOffset() - lastUndefinedRegionOffset);
      stringBuffer.append(undefinedRegion);

      IDOMModel structuredModel = node.getModel();
      IStructuredDocument structuredDocument = structuredModel.getStructuredDocument();
      // 1 is for "<"
      int offset = node.getStartOffset() + 1 + node.getNodeName().length();
      // 1 is for "<"
      int length = node.getFirstStructuredDocumentRegion().getTextLength() - 1 - node.getNodeName().length();

      if (flatNode != null) {
        ITextRegionList regions = flatNode.getRegions();
        ITextRegion firstRegion = regions.get(0);
        ITextRegion lastRegion = regions.get(regions.size() - 1);

        if (firstRegion.getType() == DOMRegionContext.XML_END_TAG_OPEN)
          // skip formatting for end tags in this format: </tagName>
          return;
        else {
          if (lastRegion.getType() == DOMRegionContext.XML_TAG_CLOSE || lastRegion.getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE)
            length = length - lastRegion.getLength();

          if (lastRegion.getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE) {
            // leave space before XML_EMPTY_TAG_CLOSE: <tagName />
            // unless already going to move end bracket
            if (!startTagSpansOver1Line || !alignEndBracket)
              stringBuffer.append(SPACE_CHAR);
          }
        }
      }

      if (startTagSpansOver1Line && alignEndBracket) {
        stringBuffer.append(lineDelimiter).append(lineIndent);
      }

      replace(structuredDocument, offset, length, stringBuffer.toString());

      // BUG108074 & BUG84688 - preserve whitespace in xsl:text &
      // xsl:attribute
      String nodeNamespaceURI = node.getNamespaceURI();
      if (XSL_NAMESPACE.equals(nodeNamespaceURI)) {
        String nodeName = ((Element) node).getLocalName();
        if (XSL_ATTRIBUTE.equals(nodeName) || XSL_TEXT.equals(nodeName)) {
          sawXmlSpace = true;
          formatContraints.setInPreserveSpaceElement(true);
        }
      }

      // If we didn't see a xml:space attribute above, we'll look for
      // one in the DTD.
      // We do not check for a conflict between a DTD's 'fixed' value
      // and the attribute value found in the instance document, we
      // leave that to the validator.
      if (!sawXmlSpace) {
        ModelQueryAdapter adapter = (ModelQueryAdapter) ((IDOMDocument) node.getOwnerDocument()).getAdapterFor(ModelQueryAdapter.class);
        CMElementDeclaration elementDeclaration = (CMElementDeclaration) adapter.getModelQuery().getCMNode(node);
        if (elementDeclaration != null) {
          int contentType = elementDeclaration.getContentType();
          if (preferences.isPreservePCDATAContent() && contentType == CMElementDeclaration.PCDATA) {
            formatContraints.setInPreserveSpaceElement(true);
          }
          else {
            CMNamedNodeMap cmAttributes = elementDeclaration.getAttributes();
            // Check implied values from the DTD way.
            CMAttributeDeclaration attributeDeclaration = (CMAttributeDeclaration) cmAttributes.getNamedItem(XML_SPACE);
            if (attributeDeclaration != null) {
              // CMAttributeDeclaration found, check it out.
              String defaultValue = attributeDeclaration.getAttrType().getImpliedValue();

              // xml:space="preserve" means preserve space,
              // everything else means back to default.
              if (PRESERVE.compareTo(defaultValue) == 0)
                formatContraints.setInPreserveSpaceElement(true);
              else
                formatContraints.setInPreserveSpaceElement(false);
            }
          }
        }
      }
    }
  }

  protected String getUndefinedRegions(IDOMNode node, int startOffset, int length) {
    String result = NodeFormatter.EMPTY_STRING;

    IStructuredDocumentRegion flatNode = node.getFirstStructuredDocumentRegion();
    ITextRegionList regions = flatNode.getRegions();
    for (int i = 0; i < regions.size(); i++) {
      ITextRegion region = regions.get(i);
      String regionType = region.getType();
      int regionStartOffset = flatNode.getStartOffset(region);

      if (regionType.compareTo(DOMRegionContext.UNDEFINED) == 0 && regionStartOffset >= startOffset && regionStartOffset < startOffset + length)
        result = result + flatNode.getFullText(region);
    }

    if (result.length() > 0)
      return SPACE_CHAR + result.trim();
    else
      return result;
  }
}
TOP

Related Classes of org.eclipse.wst.xml.core.internal.provisional.format.ElementNodeFormatter

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.