Package com.puppetlabs.xtext.serializer.acceptor

Source Code of com.puppetlabs.xtext.serializer.acceptor.DomModelSequenceAdapter

/**
* 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.serializer.acceptor;

import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.ACTION;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.COMMENT;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.DATATYPE;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.ENUM;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.KEYWORD;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.RULECALL;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.TERMINAL;
import static com.puppetlabs.xtext.dommodel.IDomNode.NodeType.WHITESPACE;

import java.util.List;

import com.puppetlabs.xtext.dommodel.DomModelUtils;
import com.puppetlabs.xtext.dommodel.IDomNode;
import com.puppetlabs.xtext.dommodel.IDomNode.NodeClassifier;
import com.puppetlabs.xtext.dommodel.IDomNode.NodeType;
import com.puppetlabs.xtext.dommodel.formatter.comments.ICommentConfiguration;
import com.puppetlabs.xtext.dommodel.impl.BaseDomNode;
import com.puppetlabs.xtext.dommodel.impl.CompositeDomNode;
import com.puppetlabs.xtext.dommodel.impl.LeafDomNode;
import org.eclipse.xtext.formatting.ILineSeparatorInformation;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.AbstractRule;
import org.eclipse.xtext.Action;
import org.eclipse.xtext.GrammarUtil;
import org.eclipse.xtext.Keyword;
import org.eclipse.xtext.RuleCall;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.ILeafNode;
import org.eclipse.xtext.parsetree.reconstr.IHiddenTokenHelper;
import org.eclipse.xtext.serializer.acceptor.ISequenceAcceptor;
import org.eclipse.xtext.serializer.diagnostic.ISerializationDiagnostic;

import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.inject.Inject;

/**
* TODO: (list of things to discuss)
* - Calls that pass a data object and an index
* - Initialization (the Serializer calls init depending on implementation class).
* Suggest adding init(EObject context, EObject semantic) to ISequenceAcceptor.
*
*/
public class DomModelSequenceAdapter implements ISequenceAcceptor {

  protected ISerializationDiagnostic.Acceptor errorAcceptor;

  private CompositeDomNode current;

  private List<CompositeDomNode> stack;

  protected IHiddenTokenHelper hiddenTokenHelper;

  Function<AbstractRule, String> ruleToName = new Function<AbstractRule, String>() {

    @Override
    public String apply(AbstractRule from) {
      return from.getName();
    }
  };

  /**
   * Required to encode certain facts about comments in a generic way
   */
  ILineSeparatorInformation lineSeparatorInformation;

  ICommentConfiguration<?> commentConfiguration;

  @Inject
  public DomModelSequenceAdapter(IHiddenTokenHelper hiddenTokenHelper, ICommentConfiguration<?> commentConfiguration,
      ILineSeparatorInformation lineSeparatorInformation, ISerializationDiagnostic.Acceptor errorAcceptor) {
    current = new CompositeDomNode();
    stack = Lists.newArrayList();
    this.errorAcceptor = errorAcceptor;
    this.hiddenTokenHelper = hiddenTokenHelper;
    this.lineSeparatorInformation = lineSeparatorInformation;
    this.commentConfiguration = commentConfiguration;
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedCrossRefDatatype(org.eclipse.xtext.RuleCall,
   *      java.lang.String, org.eclipse.emf.ecore.EObject, int, org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public void acceptAssignedCrossRefDatatype(RuleCall datatypeRC, String token, EObject value, int index,
      ICompositeNode node) {
    addCompositeNodeToCurrent(GrammarUtil.containingCrossReference(datatypeRC), token, node, DATATYPE, //
      NodeClassifier.CROSSREF, NodeClassifier.ASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedCrossRefEnum(org.eclipse.xtext.RuleCall, java.lang.String,
   *      org.eclipse.emf.ecore.EObject, int, org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public void acceptAssignedCrossRefEnum(RuleCall enumRC, String token, EObject value, int index, ICompositeNode node) {
    addCompositeNodeToCurrent(GrammarUtil.containingCrossReference(enumRC), token, node, ENUM, //
      NodeClassifier.CROSSREF, NodeClassifier.ASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedCrossRefKeyword(org.eclipse.xtext.Keyword, java.lang.String,
   *      org.eclipse.emf.ecore.EObject, int, org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptAssignedCrossRefKeyword(Keyword kw, String token, EObject value, int index, ILeafNode node) {
    addLeafNodeToCurrent(GrammarUtil.containingCrossReference(kw), token, node, KEYWORD, //
      NodeClassifier.CROSSREF, NodeClassifier.ASSIGNED);

  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedCrossRefTerminal(org.eclipse.xtext.RuleCall,
   *      java.lang.String, org.eclipse.emf.ecore.EObject, int, org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptAssignedCrossRefTerminal(RuleCall terminalRC, String token, EObject value, int index,
      ILeafNode node) {
    addLeafNodeToCurrent(GrammarUtil.containingCrossReference(terminalRC), token, node, TERMINAL, //
      NodeClassifier.CROSSREF, NodeClassifier.ASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedDatatype(org.eclipse.xtext.RuleCall, java.lang.String,
   *      java.lang.Object, int, org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public void acceptAssignedDatatype(RuleCall datatypeRC, String token, Object value, int index, ICompositeNode node) {
    addCompositeNodeToCurrent(datatypeRC, token, node, DATATYPE, //
      NodeClassifier.ASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedEnum(org.eclipse.xtext.RuleCall, java.lang.String,
   *      java.lang.Object, int, org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public void acceptAssignedEnum(RuleCall enumRC, String token, Object value, int index, ICompositeNode node) {
    addCompositeNodeToCurrent(enumRC, token, node, ENUM, //
      NodeClassifier.ASSIGNED);
  }

  @Override
  public void acceptAssignedKeyword(Keyword keyword, String token, Object value, int index, ILeafNode node) {
    addLeafNodeToCurrent(keyword, token, node, KEYWORD, //
      NodeClassifier.ASSIGNED);

  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#acceptAssignedTerminal(org.eclipse.xtext.RuleCall, java.lang.String,
   *      java.lang.Object, int, org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptAssignedTerminal(RuleCall terminalRC, String token, Object value, int index, ILeafNode node) {
    addLeafNodeToCurrent(terminalRC, token, node, TERMINAL, //
      NodeClassifier.ASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISequenceAcceptor#acceptComment(org.eclipse.xtext.AbstractRule, java.lang.String,
   *      org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptComment(AbstractRule rule, String token, ILeafNode node) {
    // It is of value to know if a comment extends over multiple lines or if it ends with a line separator
    //
    final String lineSep = lineSeparatorInformation.getLineSeparator();
    List<Object> commentClassification = Lists.newArrayList();
    // generic, textual classification
    if(token.endsWith(lineSep))
      commentClassification.add(NodeClassifier.LINESEPARATOR_TERMINATED);
    final int lineSepIx = token.indexOf(lineSep);
    if(lineSepIx != -1 && lineSepIx != token.lastIndexOf(lineSep))
      commentClassification.add(NodeClassifier.MULTIPLE_LINES);

    // semantic comment classification
    commentClassification.add(commentConfiguration.classify(rule));

    addLeafNodeToCurrent(rule, token, node, COMMENT, commentClassification.toArray());
  }

  /**
   * An unassigned rule call is not meaningful to keep in the DOM tree. It is basically information
   * about the rule path taken to something concrete like a terminal.
   * This implementation simply does nothing.
   *
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#acceptUnassignedAction(org.eclipse.xtext.Action)
   */
  @Override
  public void acceptUnassignedAction(Action action) {
    BaseDomNode n = addLeafNodeToCurrent(action, "", null, ACTION, //
      NodeClassifier.INSTANTIATION);
    n.setSemanticElement(action.getType().getClassifier());
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#acceptUnassignedDatatype(org.eclipse.xtext.RuleCall, java.lang.String,
   *      org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public void acceptUnassignedDatatype(RuleCall datatypeRC, String token, ICompositeNode node) {
    addCompositeNodeToCurrent(datatypeRC, token, node, DATATYPE, //
      NodeClassifier.UNASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#acceptUnassignedEnum(org.eclipse.xtext.RuleCall, java.lang.String,
   *      org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public void acceptUnassignedEnum(RuleCall enumRC, String token, ICompositeNode node) {
    addCompositeNodeToCurrent(enumRC, token, node, ENUM, //
      NodeClassifier.UNASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#acceptUnassignedKeyword(org.eclipse.xtext.Keyword, java.lang.String,
   *      org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptUnassignedKeyword(Keyword keyword, String token, ILeafNode node) {
    addLeafNodeToCurrent(keyword, token, node, KEYWORD, //
      NodeClassifier.ASSIGNED);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#acceptUnassignedTerminal(org.eclipse.xtext.RuleCall, java.lang.String,
   *      org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptUnassignedTerminal(RuleCall terminalRC, String token, ILeafNode node) {
    addLeafNodeToCurrent(terminalRC, token, node, TERMINAL, //
      NodeClassifier.UNASSIGNED);
  }

  /**
   * TODO: When the ILeafNode is null, it is not possible to know if this is HIDDEN or not.
   * Currently, HIDDEN is set if node is null, else this is controlled by node.isHidden().
   *
   * @see org.eclipse.xtext.serializer.acceptor.ISequenceAcceptor#acceptWhitespace(org.eclipse.xtext.AbstractRule, java.lang.String,
   *      org.eclipse.xtext.nodemodel.ILeafNode)
   */
  @Override
  public void acceptWhitespace(AbstractRule rule, String token, ILeafNode node) {
    BaseDomNode n = addLeafNodeToCurrent(rule, token, node, WHITESPACE, //
      NodeClassifier.HIDDEN);

    // TODO: Not a very robust way of checking if the whitespace is implied
    // Will probably need to change when also creating a dom model from the node model
    //
    n.setClassifiers(token == IDomNode.IMPLIED_EMPTY_WHITESPACE, NodeClassifier.IMPLIED);
  }

  protected BaseDomNode addCompositeNodeToCurrent(EObject rule, String token, ICompositeNode node, NodeType nodeType,
      Object... classifiers) {
    BaseDomNode result = new LeafDomNode();
    result.setText(token);
    result.setNode(node);
    result.setGrammarElement(rule);
    result.setNodeType(nodeType);
    result.setClassifiers(true, classifiers);
    addNodeToCurrent(result);
    return result;
  }

  protected BaseDomNode addLeafNodeToCurrent(EObject rule, String token, ILeafNode node, NodeType nodeType,
      Object... classifiers) {
    BaseDomNode result = new LeafDomNode();
    result.setText(token);
    result.setNode(node);
    result.setGrammarElement(rule);
    result.setNodeType(nodeType);
    result.setClassifiers(true, classifiers);
    if(node != null && node.isHidden()) {
      result.setClassifiers(true, NodeClassifier.HIDDEN);
    }
    addNodeToCurrent(result);
    return result;
  }

  protected void addNodeToCurrent(BaseDomNode node) {
    current.addChild(node);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#enterAssignedAction(org.eclipse.xtext.Action,
   *      org.eclipse.emf.ecore.EObject, org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public boolean enterAssignedAction(Action action, EObject semanticChild, ICompositeNode node) {
    push();
    current.setGrammarElement(action);
    current.setSemanticElement(semanticChild);
    current.setNode(node);
    current.setNodeType(ACTION);
    current.setClassifiers(true, NodeClassifier.ASSIGNED);
    return true;
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#enterAssignedParserRuleCall(org.eclipse.xtext.RuleCall,
   *      org.eclipse.emf.ecore.EObject, org.eclipse.xtext.nodemodel.ICompositeNode)
   */
  @Override
  public boolean enterAssignedParserRuleCall(RuleCall rc, EObject semanticChild, ICompositeNode node) {
    push();
    current.setGrammarElement(rc);
    current.setSemanticElement(semanticChild);
    current.setNode(node);
    current.setNodeType(RULECALL);
    current.setClassifiers(true, NodeClassifier.ASSIGNED);
    return true;
  }

  /**
   * This information is not terribly useful in the DOM as it says that the grammar has performed
   * a rule call and is now in a particular rule. Eventually the rule will result is something.
   * This implementation simply does nothing.
   *
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#enterUnassignedParserRuleCall(org.eclipse.xtext.RuleCall)
   */
  @Override
  public void enterUnassignedParserRuleCall(RuleCall rc) {
    // push();
    // current.setGrammarElement(rc);
    // current.setSemanticElement(null);
    // current.setNode(null);
    // current.setNodeType(RULECALL);
    // current.setClassifiers(true, NodeClassifier.UNASSIGNED);

    // // For debug printout of entered rule with hidden
    // AbstractRule r = rc.getRule();
    // if(r instanceof ParserRule) {
    // ParserRule pr = ((ParserRule) r);
    // if(pr.isDefinesHiddenTokens()) {
    // StringBuilder builder = new StringBuilder();
    // builder.append("HIDDEN(");
    // Joiner joiner = Joiner.on(", ");
    // if(pr.getHiddenTokens().size() != 0)
    // joiner.appendTo(builder, Iterables.transform(pr.getHiddenTokens(), ruleToName));
    // builder.append(")");
    // System.out.println(builder.toString());
    // }
    // }
    // push();
    // current.setGrammarElement(rc);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#finish()
   */
  @Override
  public void finish() {
    if(stack.size() == 0) {
      // TODO: Discuss this issue
      // ensure there is a whitespace node at the very end (to enable defining a
      // formatting rule for trailing WS)
      IDomNode lastLeaf = DomModelUtils.lastLeaf(current);
      if(lastLeaf != null) {
        if(!DomModelUtils.isWhitespace(lastLeaf))
          acceptWhitespace(hiddenTokenHelper.getWhitespaceRuleFor(null, ""), //
            IDomNode.IMPLIED_EMPTY_WHITESPACE, null);
      }
      current.doLayout();
    }
  }

  protected CompositeDomNode getCurrent() {
    return current;
  }

  /**
   * Returns the constructed Dom model. The returned IDomNode is always a composite node
   *
   * @return the constructed dom model root
   */
  public IDomNode getDomModel() {
    return current;
  }

  /**
   * @param context
   * @param semanticObject
   */
  public void init(EObject context, EObject semanticObject) {
    current.setGrammarElement(context);
    current.setSemanticElement(semanticObject);
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#leaveAssignedAction(org.eclipse.xtext.Action,
   *      org.eclipse.emf.ecore.EObject)
   */
  @Override
  public void leaveAssignedAction(Action action, EObject semanticChild) {
    pop();
  }

  /**
   * @see org.eclipse.xtext.serializer.acceptor.ISemanticSequenceAcceptor#leaveAssignedParserRuleCall(org.eclipse.xtext.RuleCall,
   *      org.eclipse.emf.ecore.EObject)
   */
  @Override
  public void leaveAssignedParserRuleCall(RuleCall rc, EObject semanticChild) {
    pop();
  }

  /**
   * Since the information about entry to a RuleCall is not recorded, neither is leaving it.
   * This implementation does nothing.
   * TODO: Method name is misspelled in the interface - report issue
   *
   * @see org.eclipse.xtext.serializer.acceptor.ISyntacticSequenceAcceptor#leaveUnssignedParserRuleCall(org.eclipse.xtext.RuleCall)
   */
  @Override
  public void leaveUnssignedParserRuleCall(RuleCall rc) {
    // Skip these
    // pop();
  }

  /**
   * Current is added as a child to the top of the stack, and the top of the stack is made current.
   * The stack is popped.
   *
   * @throws IndexOutOfBoundsException
   *             - if the calls to push/pop are not balanced and the stack is empty.
   */
  protected void pop() {
    try {
      CompositeDomNode top = stack.remove(stack.size() - 1);
      current = top;
    }
    catch(ArrayIndexOutOfBoundsException e) {
      throw e;
    }
  }

  /**
   * Adds current to the stack, and creates a new empty CompositeDomNode as the current node.
   */
  protected void push() {
    stack.add(current);
    CompositeDomNode newCurrent = new CompositeDomNode();
    current.addChild(newCurrent);
    current = newCurrent;
  }

}
TOP

Related Classes of com.puppetlabs.xtext.serializer.acceptor.DomModelSequenceAdapter

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.