ASTPrinter.java

/*
 * Portions of this software was developed by employees of the National Institute
 * of Standards and Technology (NIST), an agency of the Federal Government and is
 * being made available as a public service. Pursuant to title 17 United States
 * Code Section 105, works of NIST employees are not subject to copyright
 * protection in the United States. This software may be subject to foreign
 * copyright. Permission in the United States and in foreign countries, to the
 * extent that NIST may hold copyright, to use, copy, modify, create derivative
 * works, and distribute this software and its documentation without fee is hereby
 * granted on a non-exclusive basis, provided that this notice and disclaimer
 * of warranty appears in all copies.
 *
 * THE SOFTWARE IS PROVIDED 'AS IS' WITHOUT ANY WARRANTY OF ANY KIND, EITHER
 * EXPRESSED, IMPLIED, OR STATUTORY, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY
 * THAT THE SOFTWARE WILL CONFORM TO SPECIFICATIONS, ANY IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND FREEDOM FROM
 * INFRINGEMENT, AND ANY WARRANTY THAT THE DOCUMENTATION WILL CONFORM TO THE
 * SOFTWARE, OR ANY WARRANTY THAT THE SOFTWARE WILL BE ERROR FREE.  IN NO EVENT
 * SHALL NIST BE LIABLE FOR ANY DAMAGES, INCLUDING, BUT NOT LIMITED TO, DIRECT,
 * INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES, ARISING OUT OF, RESULTING FROM,
 * OR IN ANY WAY CONNECTED WITH THIS SOFTWARE, WHETHER OR NOT BASED UPON WARRANTY,
 * CONTRACT, TORT, OR OTHERWISE, WHETHER OR NOT INJURY WAS SUSTAINED BY PERSONS OR
 * PROPERTY OR OTHERWISE, AND WHETHER OR NOT LOSS WAS SUSTAINED FROM, OR AROSE OUT
 * OF THE RESULTS OF, OR USE OF, THE SOFTWARE OR SERVICES PROVIDED HEREUNDER.
 */

package gov.nist.secauto.metaschema.core.metapath;

import gov.nist.secauto.metaschema.core.metapath.ASTPrinter.State;

import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;

@SuppressWarnings("PMD.CouplingBetweenObjects")
final class ASTPrinter
    extends AbstractExpressionVisitor<String, State> {

  private static final ASTPrinter SINGLETON = new ASTPrinter();

  /**
   * Get the singleton instance.
   *
   * @return the instance
   */
  public static ASTPrinter instance() {
    return SINGLETON;
  }

  private ASTPrinter() {
    // disable construction
  }

  @Override
  protected String visitChildren(IExpression expr, State context) {
    context.push();
    String result = super.visitChildren(expr, context);
    context.pop();
    return result;
  }

  @Override
  protected String aggregateResult(String result, String nextResult, State context) {
    StringBuilder buffer = new StringBuilder();
    if (result != null) {
      buffer.append(result);
      // buffer.append(" ar "+System.lineSeparator());
    }

    buffer.append(context.getIndentation())
        .append(nextResult);
    return buffer.toString();
  }

  @Override
  protected String defaultResult() {
    return "";
  }

  /**
   * Append the {@code childResult} to the record produced for the current node.
   *
   * @param expr
   *          the current node
   * @param childResult
   *          the output generated for the curren't node's children
   * @param context
   *          the output context state
   * @return the string representation of the node tree for the current node and
   *         its children
   */
  @SuppressWarnings("static-method")
  protected String appendNode(@NonNull IExpression expr, @Nullable String childResult, @NonNull State context) {
    StringBuilder buffer = new StringBuilder();
    buffer.append(context.getIndentation())
        .append(expr.toASTString());
    if (childResult != null) {
      buffer.append(System.lineSeparator())
          .append(childResult);
    }
    return buffer.toString();
  }

  /**
   * Visit a node and produce a string representation of its the node tree.
   *
   * @param expression
   *          the node to build the node tree for
   * @return the string representation of the node tree for the provided
   *         expression node and its children
   */
  public String visit(@NonNull IExpression expression) {
    return visit(expression, new State());
  }

  @Override
  public String visitAddition(Addition expr, State context) {
    return appendNode(expr, super.visitAddition(expr, context), context);
  }

  @Override
  public String visitAnd(And expr, State context) {
    return appendNode(expr, super.visitAnd(expr, context), context);
  }

  @Override
  public String visitStep(Step expr, State context) {
    return appendNode(expr, super.visitStep(expr, context), context);
  }

  @Override
  public String visitValueComparison(ValueComparison expr, State context) {
    return appendNode(expr, super.visitValueComparison(expr, context), context);
  }

  @Override
  public String visitGeneralComparison(GeneralComparison expr, State context) {
    return appendNode(expr, super.visitGeneralComparison(expr, context), context);
  }

  @Override
  public String visitContextItem(ContextItem expr, State context) {
    return appendNode(expr, super.visitContextItem(expr, context), context);
  }

  @Override
  public String visitDecimalLiteral(DecimalLiteral expr, State context) {
    return appendNode(expr, super.visitDecimalLiteral(expr, context), context);
  }

  @Override
  public String visitDivision(Division expr, State context) {
    return appendNode(expr, super.visitDivision(expr, context), context);
  }

  @Override
  public String visitExcept(@NonNull Except expr, State context) {
    return appendNode(expr, super.visitExcept(expr, context), context);
  }

  @Override
  public String visitFlag(Flag expr, State context) {
    return appendNode(expr, super.visitFlag(expr, context), context);
  }

  @Override
  public String visitFunctionCall(FunctionCall expr, State context) {
    return appendNode(expr, super.visitFunctionCall(expr, context), context);
  }

  @Override
  public String visitIntegerDivision(IntegerDivision expr, State context) {
    return appendNode(expr, super.visitIntegerDivision(expr, context), context);
  }

  @Override
  public String visitIntegerLiteral(IntegerLiteral expr, State context) {
    return appendNode(expr, super.visitIntegerLiteral(expr, context), context);
  }

  @Override
  public String visitIntersect(Intersect expr, State context) {
    return appendNode(expr, super.visitIntersect(expr, context), context);
  }

  @Override
  public String visitMetapath(Metapath expr, State context) {
    return appendNode(expr, super.visitMetapath(expr, context), context);
  }

  @Override
  public String visitModulo(Modulo expr, State context) {
    return appendNode(expr, super.visitModulo(expr, context), context);
  }

  @Override
  public String visitModelInstance(ModelInstance expr, State context) {
    return appendNode(expr, super.visitModelInstance(expr, context), context);
  }

  @Override
  public String visitMultiplication(Multiplication expr, State context) {
    return appendNode(expr, super.visitMultiplication(expr, context), context);
  }

  @Override
  public String visitName(Name expr, State context) {
    return appendNode(expr, super.visitName(expr, context), context);
  }

  @Override
  public String visitNegate(Negate expr, State context) {
    return appendNode(expr, super.visitNegate(expr, context), context);
  }

  @Override
  public String visitOr(Or expr, State context) {
    return appendNode(expr, super.visitOr(expr, context), context);
  }

  @Override
  public String visitAxis(Axis expr, State context) {
    return appendNode(expr, super.visitAxis(expr, context), context);
  }

  @Override
  public String visitPredicate(Predicate expr, State context) {
    return appendNode(expr, super.visitPredicate(expr, context), context);
  }

  @Override
  public String visitRelativeDoubleSlashPath(RelativeDoubleSlashPath expr, State context) {
    return appendNode(expr, super.visitRelativeDoubleSlashPath(expr, context), context);
  }

  @Override
  public String visitRelativeSlashPath(RelativeSlashPath expr, State context) {
    return appendNode(expr, super.visitRelativeSlashPath(expr, context), context);
  }

  @Override
  public String visitRootDoubleSlashPath(RootDoubleSlashPath expr, State context) {
    return appendNode(expr, super.visitRootDoubleSlashPath(expr, context), context);
  }

  @Override
  public String visitRootSlashOnlyPath(RootSlashOnlyPath expr, State context) {
    return appendNode(expr, super.visitRootSlashOnlyPath(expr, context), context);
  }

  @Override
  public String visitRootSlashPath(RootSlashPath expr, State context) {
    return appendNode(expr, super.visitRootSlashPath(expr, context), context);
  }

  @Override
  public String visitStringConcat(StringConcat expr, State context) {
    return appendNode(expr, super.visitStringConcat(expr, context), context);
  }

  @Override
  public String visitStringLiteral(StringLiteral expr, State context) {
    return appendNode(expr, super.visitStringLiteral(expr, context), context);
  }

  @Override
  public String visitSubtraction(Subtraction expr, State context) {
    return appendNode(expr, super.visitSubtraction(expr, context), context);
  }

  @Override
  public String visitUnion(Union expr, State context) {
    return appendNode(expr, super.visitUnion(expr, context), context);
  }

  @Override
  public String visitWildcard(Wildcard expr, State context) {
    return appendNode(expr, super.visitWildcard(expr, context), context);
  }

  @Override
  public String visitLet(Let expr, State context) {
    return appendNode(expr, super.visitLet(expr, context), context);
  }

  @Override
  public String visitVariableReference(VariableReference expr, State context) {
    return appendNode(expr, super.visitVariableReference(expr, context), context);
  }

  static class State {
    private int indentation; // 0;
    private int lastIndentation; // 0;
    private String indentationPadding = "";

    public String getIndentation() {
      if (indentation != lastIndentation) {
        StringBuilder buffer = new StringBuilder();
        for (int i = 0; i < indentation; i++) {
          buffer.append("  ");
        }
        lastIndentation = indentation;
        indentationPadding = buffer.toString();
      }
      return indentationPadding;
    }

    public State push() {
      indentation++;
      return this;
    }

    public State pop() {
      indentation--;
      return this;
    }
  }
}