All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.less.jsonast.AstPrinter Maven / Gradle / Ivy

The newest version!
/**
 * Copyright (c) 2018 SQUARESPACE, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.squarespace.less.jsonast;

import static com.squarespace.less.jsonast.AstEmitter.ALPHA;
import static com.squarespace.less.jsonast.AstEmitter.ANONYMOUS;
import static com.squarespace.less.jsonast.AstEmitter.ARGUMENT;
import static com.squarespace.less.jsonast.AstEmitter.ASSIGNMENT;
import static com.squarespace.less.jsonast.AstEmitter.ATTR_ELEMENT;
import static com.squarespace.less.jsonast.AstEmitter.BLOCK_DIRECTIVE;
import static com.squarespace.less.jsonast.AstEmitter.COLOR;
import static com.squarespace.less.jsonast.AstEmitter.COMMENT;
import static com.squarespace.less.jsonast.AstEmitter.CONDITION;
import static com.squarespace.less.jsonast.AstEmitter.DEFINITION;
import static com.squarespace.less.jsonast.AstEmitter.DIMENSION;
import static com.squarespace.less.jsonast.AstEmitter.DIRECTIVE;
import static com.squarespace.less.jsonast.AstEmitter.EXPRESSION;
import static com.squarespace.less.jsonast.AstEmitter.EXPRESSION_LIST;
import static com.squarespace.less.jsonast.AstEmitter.FALSE;
import static com.squarespace.less.jsonast.AstEmitter.FEATURE;
import static com.squarespace.less.jsonast.AstEmitter.FEATURES;
import static com.squarespace.less.jsonast.AstEmitter.FUNCTION_CALL;
import static com.squarespace.less.jsonast.AstEmitter.GUARD;
import static com.squarespace.less.jsonast.AstEmitter.IMPORT;
import static com.squarespace.less.jsonast.AstEmitter.KEYWORD;
import static com.squarespace.less.jsonast.AstEmitter.MEDIA;
import static com.squarespace.less.jsonast.AstEmitter.MIXIN;
import static com.squarespace.less.jsonast.AstEmitter.MIXIN_ARGS;
import static com.squarespace.less.jsonast.AstEmitter.MIXIN_CALL;
import static com.squarespace.less.jsonast.AstEmitter.MIXIN_PARAMS;
import static com.squarespace.less.jsonast.AstEmitter.OPERATION;
import static com.squarespace.less.jsonast.AstEmitter.PARAMETER;
import static com.squarespace.less.jsonast.AstEmitter.PAREN;
import static com.squarespace.less.jsonast.AstEmitter.PROPERTY;
import static com.squarespace.less.jsonast.AstEmitter.QUOTED;
import static com.squarespace.less.jsonast.AstEmitter.RATIO;
import static com.squarespace.less.jsonast.AstEmitter.RULE;
import static com.squarespace.less.jsonast.AstEmitter.RULESET;
import static com.squarespace.less.jsonast.AstEmitter.SELECTOR;
import static com.squarespace.less.jsonast.AstEmitter.SELECTORS;
import static com.squarespace.less.jsonast.AstEmitter.SHORTHAND;
import static com.squarespace.less.jsonast.AstEmitter.STYLESHEET;
import static com.squarespace.less.jsonast.AstEmitter.TEXT_ELEMENT;
import static com.squarespace.less.jsonast.AstEmitter.TRUE;
import static com.squarespace.less.jsonast.AstEmitter.UNICODE_RANGE;
import static com.squarespace.less.jsonast.AstEmitter.URL;
import static com.squarespace.less.jsonast.AstEmitter.VALUE_ELEMENT;
import static com.squarespace.less.jsonast.AstEmitter.VARIABLE;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.eclipsesource.json.JsonArray;
import com.eclipsesource.json.JsonObject;
import com.eclipsesource.json.JsonValue;
import com.squarespace.less.core.LessUtils;

/**
 * Renders a JSON AST into a text representation for easier comparison
 * during testing.
 */
public class AstPrinter {

  private static final String INDENT = "  ";
  private final JsonArray root;
  private final List strings;

  public AstPrinter(JsonObject obj) {
    this.root = obj.get("root").asArray();
    this.strings = copyStrings(obj.get("strings").asArray());
  }

  public String print() {
    return print(root, 0);
  }

  private String print(JsonArray obj, int depth) {
    StringBuilder buf = new StringBuilder();
    int type = obj.get(0).asInt();
    if (type == -1) {
      return "NULL";
    }
    int[] rec = TYPES[type];
    String name = NAMES.get(rec[0]);
    buf.append('[').append(name);
    for (int i = 1; i < rec.length; i++) {
      JsonValue val = obj.get(i);
      buf.append(", ");
      switch (rec[i]) {
        case LIST:
          buf.append(printList(val.asArray(), depth + 1));
          break;

        case VALUE:
          buf.append(print(val.asArray(), depth));
          break;

        case STR:
        {
          int idx = val.asInt();
          String str = this.strings.get(idx);
          if (str == null) {
            buf.append("null");
          } else {
            buf.append('\'').append(LessUtils.escapeJava(str)).append('\'');
          }
          break;
        }

        case BOOL:
          buf.append(val.asInt() == 0 ? false : true);
          break;

        case NUM:
        {
          // JSON lib we use for strict parsing doesn't let us see the number type.
          // Hack.
          String s = val.toString();
          if (s.indexOf('.') != -1) {
            buf.append(val.asDouble());
          } else {
            buf.append(val.asLong());
          }
          break;
        }

        default:
          System.err.println("unexpected:" + rec[i]);
          break;
      }
    }
    buf.append(']');
    return buf.toString();
  }

  private String printList(JsonArray list, int depth) {
    int size = list.size();
    if (size == 0) {
      return "[]";
    }
    StringBuilder buf = new StringBuilder();
    buf.append("[\n");
    String prefix = repeat(depth);
    for (int i = 0; i < size; i++) {
      JsonArray val = list.get(i).asArray();
      buf.append(prefix).append(this.print(val, depth));
      if (i < size - 1) {
        buf.append(",\n");
      }
    }
    buf.append('\n').append(repeat(depth - 1)).append(']');
    return buf.toString();
  }

  private static String repeat(int count) {
    String res = "";
    for (int i = 0; i < count; i++) {
      res += INDENT;
    }
    return res;
  }

  private static List copyStrings(JsonArray arr) {
    List result = new ArrayList<>();
    int size = arr.size();
    for (int i = 0; i < size; i++) {
      JsonValue value = arr.get(i);
      result.add(value.isNull() ? null : value.asString());
    }
    return result;
  }

  private static final int LIST = 1;
  private static final int VALUE = 2;
  private static final int STR = 3;
  private static final int BOOL = 4;
  private static final int NUM = 5;

  private static final int[][] TYPES = new int[][] {
    { ALPHA, VALUE },
    { ANONYMOUS, STR },
    { ARGUMENT, STR, VALUE },
    { ASSIGNMENT, STR, VALUE },
    { ATTR_ELEMENT, STR, LIST },
    { BLOCK_DIRECTIVE, STR, LIST },
    { COLOR, NUM, NUM, NUM, NUM, STR },
    { COMMENT, STR, NUM, BOOL },
    { CONDITION, STR, VALUE, VALUE, BOOL },
    { DEFINITION, STR, VALUE },
    { DIMENSION, NUM, STR },
    { DIRECTIVE, STR, VALUE },
    { EXPRESSION, LIST },
    { EXPRESSION_LIST, LIST },
    { FALSE },
    { FEATURE, VALUE, VALUE },
    { FEATURES, LIST },
    { FUNCTION_CALL, STR, LIST },
    { GUARD, LIST },
    { IMPORT, VALUE, NUM, VALUE },
    { KEYWORD, STR },
    { MEDIA, VALUE, LIST },
    { MIXIN, STR, VALUE, VALUE, LIST },
    { MIXIN_ARGS, NUM, LIST },
    { MIXIN_CALL, VALUE, VALUE, BOOL },
    { MIXIN_PARAMS, LIST },
    { OPERATION, STR, VALUE, VALUE },
    { PARAMETER, STR, VALUE, BOOL },
    { PAREN, VALUE },
    { PROPERTY, STR },
    { QUOTED, NUM, BOOL, LIST },
    { RATIO, STR },
    { RULE, VALUE, VALUE, BOOL },
    { RULESET, VALUE, LIST },
    { SELECTOR, LIST },
    { SELECTORS, LIST },
    { SHORTHAND, VALUE, VALUE },
    { STYLESHEET, NUM, LIST },
    { TEXT_ELEMENT, STR, STR },
    { TRUE },
    { UNICODE_RANGE, STR },
    { URL, VALUE },
    { VALUE_ELEMENT, STR, VALUE },
    { VARIABLE, STR, BOOL, BOOL },
  };

  private static final Map NAMES = new HashMap() { {
    put(ALPHA, "ALPHA");
    put(ANONYMOUS, "ANONYMOUS");
    put(ARGUMENT, "ARGUMENT");
    put(ASSIGNMENT, "ASSIGNMENT");
    put(ATTR_ELEMENT, "ATTR_ELEMENT");
    put(BLOCK_DIRECTIVE, "BLOCK_DIRECTIVE");
    put(COLOR, "COLOR");
    put(COMMENT, "COMMENT");
    put(CONDITION, "CONDITION");
    put(DEFINITION, "DEFINITION");
    put(DIMENSION, "DIMENSION");
    put(DIRECTIVE, "DIRECTIVE");
    put(EXPRESSION, "EXPRESSION");
    put(EXPRESSION_LIST, "EXPRESSION_LIST");
    put(FALSE, "FALSE");
    put(FEATURE, "FEATURE");
    put(FEATURES, "FEATURES");
    put(FUNCTION_CALL, "FUNCTION_CALL");
    put(GUARD, "GUARD");
    put(IMPORT, "IMPORT");
    put(KEYWORD, "KEYWORD");
    put(MEDIA, "MEDIA");
    put(MIXIN, "MIXIN");
    put(MIXIN_ARGS, "MIXIN_ARGS");
    put(MIXIN_CALL, "MIXIN_CALL");
    put(MIXIN_PARAMS, "MIXIN_PARAMS");
    put(OPERATION, "OPERATION");
    put(PARAMETER, "PARAMETER");
    put(PAREN, "PAREN");
    put(PROPERTY, "PROPERTY");
    put(QUOTED, "QUOTED");
    put(RATIO, "RATIO");
    put(RULE, "RULE");
    put(RULESET, "RULESET");
    put(SELECTOR, "SELECTOR");
    put(SELECTORS, "SELECTORS");
    put(SHORTHAND, "SHORTHAND");
    put(STYLESHEET, "STYLESHEET");
    put(TEXT_ELEMENT, "TEXT_ELEMENT");
    put(TRUE, "TRUE");
    put(UNICODE_RANGE, "UNICODE_RANGE");
    put(URL, "URL");
    put(VALUE_ELEMENT, "VALUE_ELEMENT");
    put(VARIABLE, "VARIABLE");
  } };
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy