Package lombok.ast

Source Code of lombok.ast.Template$ReplacementOrder

package lombok.ast;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import lombok.ast.grammar.Source;

public class Template<T extends Node> {
  private static <N extends Node> N process(Source s, String name, Class<N> type) {
    if (!s.getProblems().isEmpty()) {
      throw new AstException(null, "Can't parse snippet: " + s.getProblems().get(0).getMessage());
    }
    if (s.getNodes().isEmpty()) return null;
    if (s.getNodes().size() > 1) throw new AstException(null, "Can't parse snippet: more than one " + name + " in snippet");
    Node n = s.getNodes().get(0);
    if (type.isInstance(n)) return type.cast(n);
    throw new AstException(null, "Can't parse snippet: Not a " + name);
  }
 
  /**
   * Parses one construct that is legal as a type member, and returns it.
   *
   * Legal type members: <ul>
   * <li>Constructor Declaration</li>
   * <li>Method Declaration</li>
   * <li>Static or Instance Initializer</li>
   * <li>Any type declaration</li>
   * <li>The empty declaration (lone semi-colon)</li>
   * <li>A variable declaration</li>
   * </ul>
   *
   * Note that neither annotation method declarations nor enum constants will be parsed properly by this method.
   */
  public static TypeMember parseMember(String source) throws AstException {
    Source s = new Source(source, "memberSnippet");
    s.parseMember();
    return process(s, "type member", TypeMember.class);
  }
 
  public static MethodDeclaration parseMethod(String source) throws AstException {
    Source s = new Source(source, "methodSnippet");
    s.parseMember();
    return process(s, "method", MethodDeclaration.class);
  }
 
  public static ConstructorDeclaration parseConstructor(String source) throws AstException {
    Source s = new Source(source, "constructorSnippet");
    s.parseMember();
    return process(s, "constructor", ConstructorDeclaration.class);
  }
 
  public static VariableDeclaration parseField(String source) throws AstException {
    Source s = new Source(source, "fieldSnippet");
    s.parseMember();
    return process(s, "field", VariableDeclaration.class);
  }
 
  public static VariableDefinition parseVariableDefinition(String source) throws AstException {
    Source s = new Source(source, "vardefSnippet");
    s.parseMember();
    return process(s, "vardef", VariableDefinition.class);
  }
 
  public static Statement parseStatement(String source) throws AstException {
    Source s = new Source(source, "statementSnippet");
    s.parseStatement();
    return process(s, "statement", Statement.class);
  }
 
  public static Expression parseExpression(String source) throws AstException {
    Source s = new Source(source, "expressionSnippet");
    s.parseExpression();
    return process(s, "expression", Expression.class);
  }
 
  /**
   * NB: Do not simply pass the result of {@code parseX} to this method; parsing is extremely slow. Instead, parse a template once,
   * and then pass this one result every time. The template will never modify the original.
   */
  @SuppressWarnings("unchecked")
  public static <N extends Node> Template<N> of(N source) throws AstException {
    return new Template<N>((N) source.copy());
  }
 
  private final T node;
  private int location;
  private Node responsible;
  private List<ReplacementOrder> replacements = new ArrayList<ReplacementOrder>();
  private int replacementsPointer = 0;
 
  private static class ReplacementOrder {
    String identifierToReplace, statementToReplace, expressionToReplace, typeReferenceToReplace;
    List<? extends Node> replacement;
    Position position;
   
    static ReplacementOrder forIdentifier(String identifier, String newValue, Position position) {
      ReplacementOrder order = new ReplacementOrder();
      order.identifierToReplace = identifier;
      order.replacement = Collections.singletonList(newValue == null ? null : Identifier.of(newValue));
      order.position = position;
      return order;
    }
   
    static ReplacementOrder forStatement(String label, List<? extends Node> replacements, Position position) {
      ReplacementOrder order = new ReplacementOrder();
      order.statementToReplace = label;
      order.replacement = replacements == null ? Collections.<Node>emptyList() : replacements;
      order.position = position;
      return order;
    }
   
    static ReplacementOrder forExpression(String identifier, Node replacement, Position position) {
      ReplacementOrder order = new ReplacementOrder();
      order.expressionToReplace = identifier;
      order.replacement = Collections.singletonList(replacement);
      order.position = position;
      return order;
    }
   
    static ReplacementOrder forTypeReference(String identifier, Node replacement, Position position) {
      ReplacementOrder order = new ReplacementOrder();
      order.typeReferenceToReplace = identifier;
      order.replacement = Collections.singletonList(replacement);
      order.position = position;
      return order;
    }
  }
 
  private final AstVisitor visitor = new ForwardingAstVisitor() {
    @Override public boolean visitNode(Node node) {
      node.setPosition(new Position(location, location, responsible));
      return false;
    }
   
    @Override public void endVisit(Node node) {
      node.setPosition(new Position(node.getPosition().getStart(), location, responsible));
    }
   
    private ReplacementOrder currentOrder() {
      return (replacementsPointer < replacements.size()) ? replacements.get(replacementsPointer) : null;
    }
   
    @Override public boolean visitIdentifier(Identifier node) {
      ReplacementOrder order = currentOrder();
      if (order != null && order.identifierToReplace != null) {
        if (order.identifierToReplace.equals(node.astValue())) {
          Node replacement = order.replacement.get(0);
          int startLoc = order.position == null ? location : order.position.getStart();
          int endLoc = order.position == null ? location : order.position.getEnd();
          replacement.setPosition(new Position(startLoc, endLoc, responsible));
          location = endLoc;
          node.replace(replacement);
          replacementsPointer++;
          return true;
        }
      }
     
      return visitNode(node);
    }
   
    @Override public boolean visitLabelledStatement(LabelledStatement node) {
      ReplacementOrder order = currentOrder();
      if (order != null && order.statementToReplace.equals(node.astLabel().astValue())) {
        if (!(node.rawStatement() instanceof EmptyStatement)) {
          throw new IllegalStateException("Placeholder statements in templates should be of the form: \"labelName: ;\" - i.e. a labelled empty statement");
        }
       
        int startLoc, endLoc;
        if (order.position == null) {
          if (order.replacement.isEmpty() || order.replacement.get(0).getPosition().getStart() < 0) startLoc = location;
          else startLoc = order.replacement.get(0).getPosition().getStart();
          if (order.replacement.isEmpty() || order.replacement.get(order.replacement.size() - 1).getPosition().getEnd() < 0) endLoc = location;
          else endLoc = order.replacement.get(order.replacement.size() - 1).getPosition().getEnd();
        } else {
          startLoc = order.position.getStart();
          endLoc = order.position.getEnd();
        }
        switch (order.replacement.size()) {
        case 0:
          node.unparent();
          break;
        case 1:
          Node replacement = order.replacement.get(0);
          if (replacement.getPosition().isUnplaced()) Ast.setAllPositions(replacement, new Position(startLoc, endLoc, responsible));
          node.replace(replacement);
          break;
        default:
          // Replacing multiple statements only works in a Block.
          Block b = node.upToBlock();
          if (b == null) throw new IllegalStateException("Replacing one placeholder statement with multiple statements is legal only if the placeholder is in a block");
          b.rawContents().addAfter(node, order.replacement.toArray(new Node[0]));
          node.unparent();
          for (Node n : order.replacement) {
            if (n.getPosition().isUnplaced()) Ast.setAllPositions(n, new Position(startLoc, endLoc, responsible));
          }
        }
        replacementsPointer++;
        location = endLoc;
        return true;
      }
     
      return visitNode(node);
    }
   
    @Override public boolean visitVariableReference(VariableReference node) {
      ReplacementOrder order = currentOrder();
      if (order != null && order.expressionToReplace.equals(node.astIdentifier().astValue())) {
        Node replacement = order.replacement.get(0);
        int startLoc, endLoc;
        if (order.position == null) {
          if (order.replacement.isEmpty() || replacement.getPosition().getStart() < 0) startLoc = location;
          else startLoc = replacement.getPosition().getStart();
          if (order.replacement.isEmpty() || replacement.getPosition().getEnd() < 0) endLoc = location;
          else endLoc = replacement.getPosition().getEnd();
        } else {
          startLoc = order.position.getStart();
          endLoc = order.position.getEnd();
        }
        if (replacement.getPosition().isUnplaced()) Ast.setAllPositions(replacement, new Position(startLoc, endLoc, responsible));
        location = endLoc;
        node.replace(replacement);
        replacementsPointer++;
        return true;
      }
     
      return visitNode(node);
    }
   
    @Override public boolean visitTypeReference(TypeReference node) {
      ReplacementOrder order = currentOrder();
      if (order != null && node.astParts().size() == 1 && node.astParts().last().rawTypeArguments().isEmpty() &&
          node.astParts().last().astIdentifier().astValue().equals(order.typeReferenceToReplace)) {
       
        Node replacement = order.replacement.get(0);
        int startLoc, endLoc;
        if (order.position == null) {
          if (order.replacement.isEmpty() || replacement.getPosition().getStart() < 0) startLoc = location;
          else startLoc = replacement.getPosition().getStart();
          if (order.replacement.isEmpty() || replacement.getPosition().getEnd() < 0) endLoc = location;
          else endLoc = replacement.getPosition().getEnd();
        } else {
          startLoc = order.position.getStart();
          endLoc = order.position.getEnd();
        }
        if (replacement.getPosition().isUnplaced()) Ast.setAllPositions(replacement, new Position(startLoc, endLoc, responsible));
        location = endLoc;
        node.replace(replacement);
        replacementsPointer++;
        return true;
      }
     
      return visitNode(node);
    }
  };
 
  private Template(T node) {
    this.node = node;
  }
 
  public Template<T> setStartPosition(int location) {
    this.location = location;
    return this;
  }
 
  public Template<T> setResponsibleNode(Node responsible) {
    this.responsible = responsible;
    return this;
  }
 
  public Template<T> replaceIdentifier(String placeholder, String replacement, Position p) {
    this.replacements.add(ReplacementOrder.forIdentifier(placeholder, replacement, p));
    return this;
  }
 
  public Template<T> replaceIdentifier(String placeholder, String replacement) {
    return replaceIdentifier(placeholder, replacement, null);
  }
 
  public Template<T> replaceStatement(String placeholder, Node replacement, Position p) {
    this.replacements.add(ReplacementOrder.forStatement(placeholder, replacement == null ? Collections.<Statement>emptyList() : Collections.singletonList(replacement), p));
    return this;
  }
 
  public Template<T> replaceStatement(String placeholder, Node replacement) {
    return replaceStatement(placeholder, replacement, null);
  }
 
  public Template<T> replaceStatement(String placeholder, List<? extends Node> replacement, Position p) {
    this.replacements.add(ReplacementOrder.forStatement(placeholder, replacement, p));
    return this;
  }
 
  public Template<T> replaceStatement(String placeholder, List<? extends Node> replacement) {
    return replaceStatement(placeholder, replacement, null);
  }
 
  public Template<T> replaceExpression(String placeholder, Node replacement, Position p) {
    this.replacements.add(ReplacementOrder.forExpression(placeholder, replacement, p));
    return this;
  }
 
  public Template<T> replaceExpression(String placeholder, Node replacement) {
    return replaceExpression(placeholder, replacement, null);
  }
 
  public Template<T> replaceTypeReference(String placeholder, Node replacement, Position p) {
    this.replacements.add(ReplacementOrder.forTypeReference(placeholder, replacement, p));
    return this;
  }
 
  public Template<T> replaceTypeReference(String placeholder, Node replacement) {
    return replaceTypeReference(placeholder, replacement, null);
  }
 
  public T finish() {
    node.accept(visitor);
    return node;
  }
}
TOP

Related Classes of lombok.ast.Template$ReplacementOrder

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.