Package org.eclipse.wst.html.core.internal.format

Source Code of org.eclipse.wst.html.core.internal.format.HTMLElementFormatter

/*******************************************************************************
* Copyright (c) 2004, 2008 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
*******************************************************************************/
package org.eclipse.wst.html.core.internal.format;

import java.util.Iterator;

import org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatter;
import org.eclipse.wst.css.core.internal.formatter.CSSSourceFormatterFactory;
import org.eclipse.wst.css.core.internal.provisional.adapters.IStyleDeclarationAdapter;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSModel;
import org.eclipse.wst.css.core.internal.provisional.document.ICSSNode;
import org.eclipse.wst.html.core.internal.provisional.HTMLFormatContraints;
import org.eclipse.wst.sse.core.internal.provisional.INodeAdapter;
import org.eclipse.wst.sse.core.internal.provisional.INodeNotifier;
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.ITextRegionContainer;
import org.eclipse.wst.sse.core.internal.provisional.text.ITextRegionList;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMElement;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMNode;
import org.eclipse.wst.xml.core.internal.provisional.format.StructuredFormatPreferencesXML;
import org.eclipse.wst.xml.core.internal.regions.DOMRegionContext;
import org.w3c.dom.Attr;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;

// nakamori_TODO: check and remove CSS formatting

public class HTMLElementFormatter extends HTMLFormatter {

  /**
   */
  protected HTMLElementFormatter() {
    super();
  }

  /**
   */
  private void compressTailingSpaces(IStructuredDocumentRegion flatNode, ITextRegion region) {
    int offset = region.getTextEnd();
    int count = region.getEnd() - offset;
    if (count == 1) {
      String source = flatNode.getFullText(region);
      int start = region.getStart();
      if (source != null && source.charAt(offset - start) == ' ') {
        // nothing to do
        return;
      }
    }
    replaceSource(flatNode, offset, count, " ");//$NON-NLS-1$
  }

  /**
   */
  private void formatEndTag(IDOMElement element, HTMLFormatContraints contraints) {
    Node lastChild = element.getLastChild();

    if (lastChild != null && lastChild instanceof IDOMElement && lastChild.getNodeName().equals("jsp:scriptlet")) { //$NON-NLS-1$
      insertBreakAfter((IDOMElement) lastChild, contraints);
      return;
    }


    IStructuredDocumentRegion endStructuredDocumentRegion = element.getEndStructuredDocumentRegion();
    if (endStructuredDocumentRegion == null)
      return;

    if (element.isJSPTag() || element.isCommentTag()) {
      String endTag = endStructuredDocumentRegion.getText();
      if (endTag != null && endTag.length() > 0) {
        setWidth(contraints, endTag);
      }
      return;
    }

    ITextRegion prevRegion = null;
    ITextRegionList regions = endStructuredDocumentRegion.getRegions();
    Iterator e = regions.iterator();
    while (e.hasNext()) {
      ITextRegion region = (ITextRegion) e.next();
      if (region == null)
        continue;
      String regionType = region.getType();
      if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTag(regionType)) {
        if (prevRegion != null && prevRegion.getType() == DOMRegionContext.XML_END_TAG_OPEN) {
          removeTailingSpaces(endStructuredDocumentRegion, prevRegion);
        }
      }
      else if (regionType == DOMRegionContext.XML_TAG_CLOSE) {
        if (prevRegion != null && (prevRegion.getType() == DOMRegionContext.XML_TAG_NAME || isNestedRootTag(prevRegion.getType()))) {
          removeTailingSpaces(endStructuredDocumentRegion, prevRegion);
        }
      }
      prevRegion = region;
    }
    if (prevRegion != null && (prevRegion.getType() == DOMRegionContext.XML_TAG_NAME || isNestedRootTag(prevRegion.getType()))) {
      removeTailingSpaces(endStructuredDocumentRegion, prevRegion);
    }

    // BUG123890 (end tag length was already prefactored into
    // formatStartTag so no need to do it here)
    // String newEndTag = endStructuredDocumentRegion.getText();
    // if (newEndTag != null && newEndTag.length() > 0) {
    // setWidth(contraints, newEndTag);
    // }
  }

  /**
   */
  protected void formatNode(IDOMNode node, HTMLFormatContraints contraints) {
    if (node == null)
      return;
    IDOMElement element = (IDOMElement) node;

    formatStartTag(element, contraints);

    formatChildNodes(element, contraints);

    formatEndTag(element, contraints);
  }

  /**
   */
  private void formatStartTag(IDOMElement element, HTMLFormatContraints contraints) {

    if (element.getNodeName().equals("jsp:scriptlet")) { //$NON-NLS-1$
      insertBreakBefore(element, contraints);
      return;
    }

    IStructuredDocumentRegion startStructuredDocumentRegion = element.getStartStructuredDocumentRegion();
    if (startStructuredDocumentRegion == null)
      return;

    // We should format attributes in JSPTag?
    // if (element.isJSPTag() || element.isCommentTag()) {
    if (element.isCommentTag()) {
      String startTag = startStructuredDocumentRegion.getText();
      if (startTag != null && startTag.length() > 0) {
        setWidth(contraints, startTag);
      }
      return;
    }

    // first process style attribute
    if (element.isGlobalTag()) {
      Attr attr = element.getAttributeNode("style");//$NON-NLS-1$
      if (attr != null)
        formatStyleAttr(attr);
    }
    boolean insertBreak = false;
    insertBreak = ((StructuredFormatPreferencesXML) getFormatPreferences()).getSplitMultiAttrs();
    boolean alignEndBracket = ((StructuredFormatPreferencesXML) getFormatPreferences()).isAlignEndBracket();
    boolean attributesSplitted = false;

    if (insertBreak) {
      NamedNodeMap attributes = element.getAttributes();
      if (attributes == null || attributes.getLength() < 2)
        insertBreak = false;
    }
    String breakSpaces = getBreakSpaces(element);
    String originalBreakSpaces = breakSpaces;
    String indent = getIndent();
    if (indent != null && indent.length() > 0) {
      breakSpaces += indent;
    }
    ITextRegion lastBreakRegion = null;

    ITextRegion prevRegion = null;
    ITextRegionList regions = startStructuredDocumentRegion.getRegions();
    Iterator e = regions.iterator();
    while (e.hasNext()) {
      ITextRegion region = (ITextRegion) e.next();
      if (region == null)
        continue;

      ITextRegion breakRegion = null;

      String regionType = region.getType();
      if (regionType == DOMRegionContext.XML_TAG_NAME || isNestedTag(regionType)) {
        if (prevRegion != null && prevRegion.getType() == DOMRegionContext.XML_TAG_OPEN) {
          removeTailingSpaces(startStructuredDocumentRegion, prevRegion);
        }
        breakRegion = region;
      }
      else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
        if (prevRegion != null && (prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS)) {
          // attribute name without value
          breakRegion = prevRegion;
        }
      }
      else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
        if (prevRegion != null && prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME) {
          removeTailingSpaces(startStructuredDocumentRegion, prevRegion);
        }
      }
      else if (regionType == DOMRegionContext.XML_TAG_ATTRIBUTE_VALUE) {
        if (prevRegion != null && prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS) {
          removeTailingSpaces(startStructuredDocumentRegion, prevRegion);
        }
        breakRegion = region;
      }
      else if (regionType == DOMRegionContext.XML_TAG_CLOSE || regionType == DOMRegionContext.XML_EMPTY_TAG_CLOSE) {
        if (prevRegion != null && (prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS)) {
          // attribute name without value
          breakRegion = prevRegion;
        }
      }

      if (breakRegion != null) {
        int end = breakRegion.getTextEnd();
        if (lastBreakRegion != null) {
          int offset = lastBreakRegion.getEnd();
          int count = end - offset;
          if (insertBreak || !isWidthAvailable(contraints, count + 1)) {
            replaceTailingSpaces(startStructuredDocumentRegion, lastBreakRegion, breakSpaces);
            setWidth(contraints, breakSpaces);
            attributesSplitted = true;
          }
          else {
            compressTailingSpaces(startStructuredDocumentRegion, lastBreakRegion);
            addWidth(contraints, 1);
          }
          addWidth(contraints, count);
        }
        else {
          addWidth(contraints, end);
        }
        lastBreakRegion = breakRegion;
      }

      prevRegion = region;
    }
    if (prevRegion != null && (prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_NAME || prevRegion.getType() == DOMRegionContext.XML_TAG_ATTRIBUTE_EQUALS)) {
      // attribute name without value
      int end = prevRegion.getTextEnd();
      if (lastBreakRegion != null) {
        int offset = lastBreakRegion.getEnd();
        int count = end - offset;
        if (insertBreak || !isWidthAvailable(contraints, count + 1)) {
          replaceTailingSpaces(startStructuredDocumentRegion, lastBreakRegion, breakSpaces);
          setWidth(contraints, breakSpaces);
          attributesSplitted = true;
        }
        else {
          compressTailingSpaces(startStructuredDocumentRegion, lastBreakRegion);
          addWidth(contraints, 1);
        }
        addWidth(contraints, count);
      }
      else {
        addWidth(contraints, end);
      }
      lastBreakRegion = prevRegion;
    }

    if (lastBreakRegion != null) {
      int offset = lastBreakRegion.getTextEnd();
      int count = startStructuredDocumentRegion.getLength() - offset;
      if (prevRegion != null && prevRegion.getType() == DOMRegionContext.XML_EMPTY_TAG_CLOSE) {
        compressTailingSpaces(startStructuredDocumentRegion, lastBreakRegion);
        count++;
      }
      else {
        removeTailingSpaces(startStructuredDocumentRegion, lastBreakRegion);
        // BUG123890 (pre-factor in end tag)
        count += element.getTagName().length() + 3;
      }
      addWidth(contraints, count);
    }
    else {
      addWidth(contraints, startStructuredDocumentRegion.getLength());
    }
    // BUG113584 - align last bracket
    if (alignEndBracket && attributesSplitted) {
      removeTailingSpaces(startStructuredDocumentRegion, lastBreakRegion);
      replaceTailingSpaces(startStructuredDocumentRegion, lastBreakRegion, originalBreakSpaces);
      contraints.setAvailableLineWidth(getLineWidth() - originalBreakSpaces.length() - 1);
    }
  }

  /**
   * ISSUE: this is a bit of hidden JSP knowledge that was implemented this
   * way for expedency. Should be evolved in future to depend on
   * "nestedContext".
   */
  private boolean isNestedTag(String regionType) {
    final String JSP_ROOT_TAG_NAME = "JSP_ROOT_TAG_NAME"; //$NON-NLS-1$
    final String JSP_DIRECTIVE_NAME = "JSP_DIRECTIVE_NAME"; //$NON-NLS-1$
    boolean result = regionType.equals(JSP_ROOT_TAG_NAME) || regionType.equals(JSP_DIRECTIVE_NAME);
    return result;
  }

  /**
   * ISSUE: this is a bit of hidden JSP knowledge that was implemented this
   * way for expedency. Should be evolved in future to depend on
   * "nestedContext".
   */
  private boolean isNestedRootTag(String regionType) {
    final String JSP_ROOT_TAG_NAME = "JSP_ROOT_TAG_NAME"; //$NON-NLS-1$
    boolean result = regionType.equals(JSP_ROOT_TAG_NAME);
    return result;
  }


  /**
   */
  private void formatStyleAttr(Attr attr) {
    if (attr == null)
      return;
    // if someone's made it a container somehow, CSS can't format it
    if (((IDOMNode) attr).getValueRegion() instanceof ITextRegionContainer)
      return;
    String value = getCSSValue(attr);
    if (value == null)
      return;
    String oldValue = ((IDOMNode) attr).getValueSource();
    if (oldValue != null && value.equals(oldValue))
      return;
    attr.setValue(value);
  }

  /**
   */
  private ICSSModel getCSSModel(Attr attr) {
    if (attr == null)
      return null;
    INodeNotifier notifier = (INodeNotifier) attr.getOwnerElement();
    if (notifier == null)
      return null;
    INodeAdapter adapter = notifier.getAdapterFor(IStyleDeclarationAdapter.class);
    if (adapter == null)
      return null;
    if (!(adapter instanceof IStyleDeclarationAdapter))
      return null;
    IStyleDeclarationAdapter styleAdapter = (IStyleDeclarationAdapter) adapter;
    return styleAdapter.getModel();
  }

  /**
   */
  private String getCSSValue(Attr attr) {
    ICSSModel model = getCSSModel(attr);
    if (model == null)
      return null;
    ICSSNode document = model.getDocument();
    if (document == null)
      return null;
    INodeNotifier notifier = (INodeNotifier) document;
    CSSSourceFormatter formatter = (CSSSourceFormatter) notifier.getAdapterFor(CSSSourceFormatter.class);
    // try another way to get formatter
    if (formatter == null)
      formatter = CSSSourceFormatterFactory.getInstance().getSourceFormatter(notifier);
    if (formatter == null)
      return null;
    StringBuffer buffer = formatter.format(document);
    if (buffer == null)
      return null;
    return buffer.toString();
  }

  /**
   */
  private void removeTailingSpaces(IStructuredDocumentRegion flatNode, ITextRegion region) {
    int offset = region.getTextEnd();
    int count = region.getEnd() - offset;
    if (count <= 0)
      return;
    replaceSource(flatNode, offset, count, null);
  }

  /**
   */
  private void replaceTailingSpaces(IStructuredDocumentRegion flatNode, ITextRegion region, String spaces) {
    int offset = region.getTextEnd();
    int count = region.getEnd() - offset;
    if (count == spaces.length()) {
      String source = flatNode.getFullText(region);
      if (source != null && source.endsWith(spaces)) {
        // nothing to do
        return;
      }
    }
    replaceSource(flatNode, offset, count, spaces);
  }
}
TOP

Related Classes of org.eclipse.wst.html.core.internal.format.HTMLElementFormatter

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.