Package com.puppetlabs.xtext.dommodel

Source Code of com.puppetlabs.xtext.dommodel.DomModelUtils

/**
* Copyright (c) 2013 Puppet Labs, Inc. and other contributors, as listed below.
* 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:
*   Puppet Labs
*/
package com.puppetlabs.xtext.dommodel;

import java.io.IOException;
import java.util.Iterator;

import com.puppetlabs.xtext.dommodel.IDomNode.NodeType;
import com.puppetlabs.xtext.dommodel.formatter.css.IStyle;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleSet;
import com.puppetlabs.xtext.dommodel.formatter.css.StyleSetWithTracking;
import com.puppetlabs.xtext.dommodel.formatter.css.debug.EffectiveStyleAppender;
import com.puppetlabs.xtext.dommodel.formatter.css.debug.FormattingTracer;
import com.puppetlabs.xtext.textflow.CharSequences;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.grammaranalysis.impl.GrammarElementTitleSwitch;
import org.eclipse.xtext.nodemodel.INode;
import org.eclipse.xtext.nodemodel.SyntaxErrorMessage;

import com.google.common.base.Joiner;
import com.google.common.collect.ArrayListMultimap;
import com.google.inject.Inject;

/**
* Utilities for a IDomModel
*
*/
public class DomModelUtils {

  // IMPORTANT: To use this, call binder.requestStaticInjection(DomModelUtils.class)
  @Inject
  private static FormattingTracer tracer;

  private static void appendEffectiveStyle(Appendable result, IDomNode node, String prefix) throws IOException {
    if(tracer == null) {
      result.append(" effective: [-off-]");
      return;
    }
    StyleSet effective = tracer.getEffectiveStyle(node);
    if(effective == null) {
      result.append(" effective: [null]");
      return;
    }
    if(prefix.length() != 0) {
      result.append("\n");
      result.append(prefix);
    }

    result.append(" effective: [");
    EffectiveStyleAppender styleAppender = new EffectiveStyleAppender(result);
    ArrayListMultimap<String, IStyle<?>> ruleToStyle = ArrayListMultimap.create();
    if(effective instanceof StyleSetWithTracking) {
      StyleSetWithTracking trackingSet = (StyleSetWithTracking) effective;
      for(IStyle<?> style : effective.getStyles()) {
        String n = trackingSet.getStyleSource(style).getRuleName();
        if(n == null || n.length() < 1)
          throw new RuntimeException("Rule without name");
        ruleToStyle.put(n, style);
      }
    }
    else {
      for(IStyle<?> style : effective.getStyles()) {
        ruleToStyle.put("(unknown source)", style);
      }
    }
    boolean firstSource = true;
    for(String source : ruleToStyle.keySet()) {
      if(!firstSource)
        result.append(", ");
      firstSource = false;
      result.append("From: ");
      result.append(source);
      result.append(" {");
      boolean firstStyle = true;
      for(IStyle<?> style : ruleToStyle.get(source)) {
        if(!firstStyle)
          result.append(", ");
        firstStyle = false;
        style.visit(node, styleAppender);
      }
      result.append("}");
    }
    result.append("]");

  }

  private static void appendTypeAndClassifiers(Appendable result, IDomNode node) throws IOException {
    result.append(", ");
    NodeType nodeType = node.getNodeType();
    if(nodeType == null)
      result.append("(unknown node type)");
    else
      result.append(nodeType.toString());

    if(node != null && node.getStyleClassifiers().size() > 0) {
      result.append(" [");
      Joiner.on(", ").appendTo(result, node.getStyleClassifiers());
      result.append("] ");
    }

  }

  /**
   * Creates a string representation of the given node. Useful for debugging.
   *
   * @return a debug string for the given node.
   */
  public static String compactDump(IDomNode node, boolean showHidden) {
    StringBuilder result = new StringBuilder();
    try {
      compactDump(node, showHidden, "", result);
    }
    catch(IOException e) {
      return e.getMessage();
    }
    return result.toString();
  }

  private static void compactDump(IDomNode node, boolean showHidden, String prefix, Appendable result)
      throws IOException {
    if(node == null) {
      result.append("null");
      return;
    }

    if(!showHidden && isHidden(node))
      return;

    if(prefix.length() != 0) {
      result.append("\n");
      result.append(prefix);
    }
    // Text
    result.append("'");
    if(node.getText() != null)
      result.append(encodedString(node.getText()));
    result.append("' ");

    // Semantic
    result.append("s: ");
    result.append(semanticTitle(node));

    // Has INode or not (most have)
    if(node.getNode() == null)
      result.append(" NoNode");

    // Style classifiers

    // Grammar
    result.append(" g: ");
    // if(node == null)
    // result.append("-");

    if(!node.isLeaf()) {
      if(node.getGrammarElement() != null)
        result.append(new GrammarElementTitleSwitch().showAssignments().doSwitch(node.getGrammarElement()));
      else
        result.append("(unknown)");
      appendTypeAndClassifiers(result, node);

      String newPrefix = prefix + "  ";
      result.append(" {");
      Iterator<? extends IDomNode> children = node.getChildren().iterator();
      while(children.hasNext()) {
        IDomNode child = children.next();
        compactDump(child, showHidden, newPrefix, result);
      }
      result.append("\n");
      result.append(prefix);
      result.append("}");

      // appendTypeAndClassifiers(result, node);
      appendEffectiveStyle(result, node, "");

      if(containsError(node) && node.getNode() != null) {
        SyntaxErrorMessage error = node.getNode().getSyntaxErrorMessage();
        if(error != null)
          result.append(" SyntaxError: [" + error.getIssueCode() + "] " + error.getMessage());
      }
    }
    else if(node.isLeaf()) {
      // it is a leaf
      if(isHidden(node))
        result.append("hidden ");
      if(node.getGrammarElement() != null)
        result.append(new GrammarElementTitleSwitch().showAssignments().doSwitch(node.getGrammarElement()));
      else
        result.append("(unknown)");
      // result.append(" => '");
      // result.append(encodedString(node.getText()));
      // result.append("'");

      appendTypeAndClassifiers(result, node);
      appendEffectiveStyle(result, node, prefix + "    ");
      if(containsError(node) && node.getNode() != null) {
        SyntaxErrorMessage error = node.getNode().getSyntaxErrorMessage();
        if(error != null)
          result.append(" SyntaxError: [" + error.getIssueCode() + "] " + error.getMessage());
      }
    }
    else {
      result.append("neither leaf nor composite!! ");
      result.append(node.getClass().getName());
    }
  }

  public static boolean containsComment(IDomNode node) {
    return node.getStyleClassifiers().contains(IDomNode.NodeClassifier.CONTAINS_COMMENT);
  }

  public static boolean containsError(IDomNode node) {
    return node.getStyleClassifiers().contains(IDomNode.NodeClassifier.CONTAINS_ERROR);
  }

  public static boolean containsHidden(IDomNode node) {
    return node.getStyleClassifiers().contains(IDomNode.NodeClassifier.CONTAINS_HIDDEN);
  }

  public static boolean containsWhitespace(IDomNode node) {
    return node.getStyleClassifiers().contains(IDomNode.NodeClassifier.CONTAINS_WHITESPACE);
  }

  public static String encodedString(String input) {
    input = input.replace("\n", "\\n");
    input = input.replace("\t", "\\t");
    input = input.replace("\r", "\\r");
    return input;

  }

  public static IDomNode findNodeForSemanticObject(IDomNode domModel, EObject o) {

    Iterator<IDomNode> itor = domModel.treeIterator();
    while(itor.hasNext()) {
      IDomNode n = itor.next();
      if(n.getSemanticObject() == o)
        return n;
    }
    return null;
  }

  public static IDomNode firstLeaf(IDomNode node) {
    if(node == null || node.isLeaf())
      return node;
    int sz = node.getChildren().size();
    if(sz == 0)
      return nextLeaf(node);
    return firstLeaf(node.getChildren().get(0));
  }

  /**
   * Returns the first node representing a textual non whitespace token. (i.e. a leaf node
   * that has a textual representation).
   *
   * @param node
   * @return
   */
  public static IDomNode firstTokenWithText(IDomNode node) {
    IDomNode n = firstLeaf(node);
    while(n != null && !isLeafWithText(n))
      n = nextLeaf(n);
    return n;
  }

  /**
   * This method converts a node to text.
   *
   * Leading and trailing text from hidden tokens (whitespace/comments) is removed. Text from hidden tokens that is
   * surrounded by text from non-hidden tokens is summarized to a single whitespace.
   *
   * The preferred use case of this method is to convert the {@link ICompositeNode} that has been created for a data
   * type rule to text.
   *
   * This is also the recommended way to convert a node to text if you want to invoke
   * {@link org.eclipse.xtext.conversion.IValueConverterService#toValue(String, String, INode)}
   *
   */
  public static String getTokenText(IDomNode node) {
    if(node.isLeaf())
      return node.getText();

    StringBuilder builder = new StringBuilder(Math.max(node.getLength(), 1));
    boolean hiddenSeen = false;
    for(IDomNode leaf : node.getChildren()) {
      if(!isHidden(leaf)) {
        if(hiddenSeen && builder.length() > 0)
          builder.append(' ');
        builder.append(leaf.getText());
        hiddenSeen = false;
      }
      else {
        hiddenSeen = true;
      }
    }
    return builder.toString();
  }

  /**
   * Returns true if node holds only comment tokens - hidden or not.
   *
   * @return true if node holds only comment tokens
   */

  public static boolean isComment(IDomNode node) {
    return node.getNodeType().equals(IDomNode.NodeType.COMMENT);
  }

  /**
   * A node that represents text that is hidden from the grammar.
   *
   * @return true if node holds only hidden tokens
   */
  public static boolean isHidden(IDomNode node) {
    return node.getStyleClassifiers().contains(IDomNode.NodeClassifier.HIDDEN);
  }

  /**
   * Returns true for a node that is non whitspace and has a textual representation.
   *
   * @param node
   * @return
   */
  public static boolean isLeafWithText(IDomNode node) {
    if(!node.isLeaf())
      return false;
    if(node.getNodeType() == NodeType.WHITESPACE || node.getNodeType() == NodeType.ACTION)
      return false;
    if(node.getText().length() == 0)
      return false;
    return true;
  }

  /**
   * Returns true for a node that has textual representation (even if text has zero length).
   *
   * @param node
   * @return
   */
  public static boolean isToken(IDomNode node) {
    if(!node.isLeaf())
      return false;
    if(node.getNodeType() == NodeType.ACTION)
      return false;
    return true;
  }

  /**
   * Returns true if node holds only whitespace tokens - hidden or not.
   *
   * @return true if node holds only whitespace tokens
   */
  public static boolean isWhitespace(IDomNode node) {
    return node != null && node.getNodeType() == IDomNode.NodeType.WHITESPACE;
  }

  public static IDomNode lastLeaf(IDomNode node) {
    if(node == null || node.isLeaf())
      return node;
    int sz = node.getChildren().size();
    if(sz == 0)
      return previousLeaf(node);
    return lastLeaf(node.getChildren().get(sz - 1));
  }

  /**
   * Returns the last node representing a textual non whitespace token. (i.e. a leaf node
   * that has a textual representation).
   *
   * @param node
   * @return
   */
  public static IDomNode lastTokenWithText(IDomNode node) {
    IDomNode n = lastLeaf(node);
    while(n != null && !isLeafWithText(n))
      n = previousLeaf(n);
    return n;
  }

  public static IDomNode nextLeaf(IDomNode node) {
    IDomNode n = node.getNextSibling();
    if(n != null)
      return firstLeaf(n);
    Iterator<IDomNode> parents = node.parents();
    while(parents.hasNext()) {
      IDomNode parent = parents.next();
      IDomNode p = parent.getNextSibling();
      if(p != null)
        return firstLeaf(p);
    }
    // ran out of parents
    return null;
  }

  public static IDomNode nextToken(IDomNode node) {
    IDomNode n = nextLeaf(node);
    while(n != null && !isLeafWithText(n))
      n = nextLeaf(n);
    return n;
  }

  public static IDomNode nextWhitespace(IDomNode node) {
    for(IDomNode n = nextLeaf(node); n != null; n = nextLeaf(n)) {
      if(isWhitespace(n))
        return n;
    }
    return null;
  }

  public static IDomNode nodeForGrammarElement(IDomNode node, EObject grammarElement) {
    Iterator<IDomNode> itor = node.treeIterator();
    if(itor.hasNext())
      for(IDomNode n = itor.next(); itor.hasNext(); n = itor.next()) {
        if(grammarElement == n.getGrammarElement())
          return n;
      }
    return null;
  }

  /**
   * The position on the line for the IDomNode searches backwards in the total text from the start position
   * of the text in the given node.
   *
   * @param node
   * @param lineDelimiter
   * @return
   */
  public static int posOnLine(IDomNode node, String lineDelimiter) {
    final INode n = node.getNode();
    if(n == null)
      return -1;
    int offsetOfNode = node.getNode().getTotalOffset();
    return offsetOfNode -
        Math.max(0, 1 + CharSequences.lastIndexOf(n.getRootNode().getText(), lineDelimiter, offsetOfNode - 1));
  }

  public static IDomNode previousLeaf(IDomNode node) {
    IDomNode n = node.getPreviousSibling();
    if(n != null)
      return lastLeaf(n);
    Iterator<IDomNode> parents = node.parents();
    while(parents.hasNext()) {
      IDomNode parent = parents.next();
      IDomNode p = parent.getPreviousSibling();
      if(p != null)
        return lastLeaf(p);
    }
    // ran out of parents
    return null;
  }

  public static IDomNode previousToken(IDomNode node) {
    IDomNode n = previousLeaf(node);
    while(n != null && !isLeafWithText(n))
      n = previousLeaf(n);
    return n;
  }

  public static IDomNode previousWhitespace(IDomNode node) {
    for(IDomNode n = previousLeaf(node); n != null; n = previousLeaf(n)) {
      if(isWhitespace(n))
        return n;
    }
    return null;
  }

  private static String semanticTitle(IDomNode node) {

    EObject o = node.getSemanticObject();
    if(o == null)
      return "-";
    return o.eClass().getName();
  }
}
TOP

Related Classes of com.puppetlabs.xtext.dommodel.DomModelUtils

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.