Package com.google.gxp.compiler.scala

Source Code of com.google.gxp.compiler.scala.ScalaUtil

/*
* Copyright (C) 2008 Google 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.google.gxp.compiler.scala;

import com.google.common.base.CharEscapers;
import com.google.common.base.Joiner;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.gxp.compiler.alerts.AlertSink;
import com.google.gxp.compiler.alerts.common.MissingTypeError;
import com.google.gxp.compiler.base.JavaAnnotation;
import com.google.gxp.compiler.base.NativeType;
import com.google.gxp.compiler.base.OutputLanguage;
import com.google.gxp.compiler.codegen.OutputLanguageUtil;
import com.google.gxp.compiler.codegen.IllegalTypeError;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Contains static functions for validating scala expressions,
* and a couple additional scala utility functions.
*/
public class ScalaUtil extends OutputLanguageUtil {

  private ScalaUtil() {
    super(RESERVED_WORDS, FORBIDDEN_OPS, OPS_FINDER,
          // TODO(harryh): is javaStringUnicodeEscaper() really the right thing here?
          CharEscapers.javaStringUnicodeEscaper(),
          CharEscapers.javascriptEscaper());
  }

  //
  // READ THIS BEFORE YOU CHANGE THE LIST BELOW!
  //
  // The list of disabled JavaScript operators was originally based on the list
  // of disabled Java Operators. If you want to enable something here, see
  // about getting it enabled for Java as well.
  //
  private static final Set<String> FORBIDDEN_OPS = ImmutableSet.of(
      // simple boolean
      // "!",
      // "!=",
      // "==",
      // "===",

      // boolean connectives
      //  "&&",
      //  "||",

      // boolean comparators
      // ">",
      // ">=",
      // "<",
      // "<=",

      // arithmetic
      // "*",
      // "+",
      // "-",
      // "/",
      // "%",

      // conditional operator (really ?:)
      // "?",

      // type inspection
      "in",
      "instanceof",
      "typeof",

      // object instantiation/deletion
      // "new",
      "delete",
      "void",

      // bitwise
      "^",
      "~",
      "&",
      "<<",
      ">>",
      ">>>",
      "|",

      // assignment -- I can't imagine any reason why it would ever be a good
      //               idea to re-enable these,
      "--",
      "-=",
      "/=",
      "*=",
      "&=",
      "%=",
      "++",
      "+=",
      "<<=",
      "=",
      ">>=",
      ">>>=",
      "|=",
      "^=");

  // the order is important! The '|' operator  is non-greedy in
  // regexes. Sorting in order of descending length works.
  //
  private static final Pattern OPS_FINDER = compileUnionPattern(
      "\\binstanceof\\b",
      "\\bdelete\\b",
      "\\btypeof\\b",
      "\\bvoid\\b",
      Pattern.quote(">>>="),
      Pattern.quote("<<="),
      Pattern.quote(">>="),
      Pattern.quote(">>>"),
      Pattern.quote("==="),
      Pattern.quote("--"),
      Pattern.quote("-="),
      Pattern.quote("!="),
      Pattern.quote("/="),
      Pattern.quote("^="),
      Pattern.quote("*="),
      Pattern.quote("&&"),
      Pattern.quote("&="),
      Pattern.quote("%="),
      Pattern.quote("++"),
      Pattern.quote("+="),
      Pattern.quote("<<"),
      Pattern.quote("<="),
      Pattern.quote("=="),
      Pattern.quote(">="),
      Pattern.quote(">>"),
      Pattern.quote("|="),
      Pattern.quote("||"),
      "\\bnew\\b",
      "\\bin\\b",
      Pattern.quote("-"),
      Pattern.quote("!"),
      Pattern.quote("/"),
      Pattern.quote("^"),
      Pattern.quote("~"),
      Pattern.quote("*"),
      Pattern.quote("&"),
      Pattern.quote("%"),
      Pattern.quote("+"),
      Pattern.quote("<"),
      Pattern.quote("="),
      Pattern.quote(">"),
      Pattern.quote("|"),
      Pattern.quote("?"))// just use ? to find ternary operator...

  private static final ImmutableSet<String> RESERVED_WORDS = ImmutableSet.of();
      /* TODO(harryh): fill this out "abstract",
      "as",
      "assert",
      "boolean",
      "break",
      "byte",
      "case",
      "catch",
      "char",
      "class",
      "continue",
      "const",
      "debugger",
      "default",
      "do",
      "double",
      "else",
      "enum",
      "export",
      "extends",
      "false",
      "final",
      "finally",
      "float",
      "for",
      "function",
      "goto",
      "if",
      "implements",
      "import",
      "in",
      "instanceof",
      "int",
      "interface",
      "is",
      "long",
      "namespace",
      "native",
      "new",
      "null",
      "package",
      "private",
      "protected",
      "public",
      "return",
      "short",
      "static",
      "super",
      "switch",
      "synchronized",
      "this",
      "throw",
      "throws",
      "transient",
      "true",
      "try",
      "typeof",
      "use",
      "var",
      "void",
      "volitile",
      "while",
      "with");*/

  private static final Set<String> TYPE_ARGUMENT_QUALIFIERS
    = ImmutableSet.of("extends", "super");

  private static final Pattern IDENTIFIER_REGEX
    = Pattern.compile("^[A-Za-z_][A-Za-z0-9_]*$");

  private static final Pattern TYPE_TOKEN_REGEX
    = Pattern.compile("^([A-Za-z_][A-Za-z0-9_]*|[\\?\\[\\]<>{},\\.])(.*)",
                      Pattern.DOTALL);

  private static final Map<String, String> PRIMITIVE_TO_BOXED_MAP =
      ImmutableMap.<String, String>builder()
        .put("boolean", "Boolean")
        .put("byte", "Number")
        .put("char", "Character")
        .put("double", "Number")
        .put("float", "Number")
        .put("int", "Number")
        .put("long", "Number")
        .put("short", "Number")
        .build();

  private static final Set<String> PRIMITIVE_TYPES =
      ImmutableSet.copyOf(PRIMITIVE_TO_BOXED_MAP.keySet());

  public static final boolean isPrimitiveType(String s) {
    return PRIMITIVE_TYPES.contains(s);
  }

  private static boolean isIdentifier(String s) {
    return s != null
        && !RESERVED_WORDS.contains(s)
        && IDENTIFIER_REGEX.matcher(s).matches();
  }

  /**
   * Validate the given NativeType and adds alerts to the sink if
   * necessary.
   *
   * @return a String representing the validated type
   */
  public static String validateType(AlertSink alertSink, NativeType type) {
    String ret = type.getNativeType(OutputLanguage.SCALA);
    if (ret == null) {
      alertSink.add(new MissingTypeError(type, OutputLanguage.SCALA));
      return ret;
    }

    ret = ret.replace('{', '[').replace('}', ']').trim();

    // tokenize the type
    Queue<String> tokens = new LinkedList<String>();
    String s = type.getNativeType(OutputLanguage.SCALA).trim();
    while (s.length() != 0) {
      Matcher m = TYPE_TOKEN_REGEX.matcher(s);
      if (m.find()) {
        tokens.add(m.group(1));
        s = m.group(2).trim();
      } else {
        alertSink.add(new IllegalTypeError(type, OutputLanguage.SCALA));
        return ret;
      }
    }

    if (!(parseType(tokens) && tokens.isEmpty())) {
      alertSink.add(new IllegalTypeError(type, OutputLanguage.SCALA));
    }

    return ret;
  }

  /**
   * Validate the given NativeType and adds alerts to the sink if
   * necessary. Allows for conjunctive types (ex: Foo & Bar).
   *
   * This is taking a bit of a shortcut, as if java ever allowed something like
   * "extends List<? extends Foo & Bar> & Baz" an alert would be incorrectly
   * be generated.  Java doesn't currently allow this though so we're fine.
   *
   * @return a String representing the validated type
   */
  public static String validateConjunctiveType(AlertSink alertSink, NativeType type) {
    List<String> subTypes = Lists.newArrayList();
    for (String subType : type.getNativeType(OutputLanguage.SCALA).split("&")) {
      subTypes.add(validateType(alertSink, new NativeType(type, subType)));
    }

    return Joiner.on(" & ").join(subTypes);
  }

  /**
   * Validate that the given {@link JavaAnnotation} contains a well formed
   * Scala annotation.
   *
   * @return the well formed annotation.
   */
  public static String validateAnnotation(AlertSink alertSink, JavaAnnotation annotation) {
    // TODO(harryh): actually do some validation
    return annotation.getWith();
  }

  /**
   * Parses the following rule from the JLS:
   *   Type:
   *     Identifier [TypeArguments]{ . Identifier [TypeArguments]} {[]}
   *     BasicType {[]}
   * (actually, this rule deviates from the JLS, which seems to have a
   * bug in that it doesn't allow arrays of BasicTypes)
   *
   * MODIFIED so that {}s can sub for []s
   */
  private static boolean parseType(Queue<String> tokens) {
    if (isPrimitiveType(tokens.peek())) {
      tokens.poll();
    } else {
      while (true) {
        if (!isIdentifier(tokens.poll())) {
          return false;
        }
        if ("[".equals(tokens.peek())) {
          if (!parseTypeArguments(tokens, "[", "]")) {
            return false;
          }
        }
        if ("{".equals(tokens.peek())) {
          if (!parseTypeArguments(tokens, "{", "}")) {
            return false;
          }
        }
        if (".".equals(tokens.peek())) {
          tokens.poll();
        } else {
          break;
        }
      }
    }
    while ("[".equals(tokens.peek())) {
      tokens.poll();
      if (!"]".equals(tokens.poll())) {
        return false;
      }
    }
    return true;
  }

  /**
   * Parses the following rule from the JLS:
   *   TypeArguments:
   *     < TypeArgument {, TypeArgument} >
   *
   * MODIFIED so that {}s can sub for []s
   */
  private static boolean parseTypeArguments(Queue<String> tokens,
                                            String start, String end) {
    if (!tokens.poll().equals(start)) {
      return false;
    }
    while (true) {
      if (!parseTypeArgument(tokens)) {
        return false;
      }
      if (",".equals(tokens.peek())) {
        tokens.poll();
      } else {
        break;
      }
    }
    return (end.equals(tokens.poll()));
  }

  /**
   * Parses the following rule from the JLS:
   *   TypeArgument:
   *     Type
   *     ? [( extends | super ) Type]
   */
  private static boolean parseTypeArgument(Queue<String> tokens) {
    if ("?".equals(tokens.peek())) {
      tokens.poll();
      if (TYPE_ARGUMENT_QUALIFIERS.contains(tokens.peek())) {
        tokens.poll();
        return parseType(tokens);
      }
    } else {
      return parseType(tokens);
    }
    return true;
  }

  //////////////////////////////////////////////////////////////////////
  // Functions for moving back and forth between reference and
  // primitive types
  //////////////////////////////////////////////////////////////////////

  /**
   * @return the most general reference type that corresponds to the specified
   * a Java type, or the specified Java type if it is already a reference type
   * (ie: a class/interface).
   */
  public static String toReferenceType(String type) {
    String result = PRIMITIVE_TO_BOXED_MAP.get(type);
    return (result == null) ? type : result;
  }

  public static String unbox(String expr, String type) {
    if (PRIMITIVE_TO_BOXED_MAP.containsKey(type)) {
      return "(" + expr + ")." + type + "Value()";
    } else {
      return expr;
    }
  }

  //////////////////////////////////////////////////////////////////////
  // Primitive Parsing
  //////////////////////////////////////////////////////////////////////

  // strcitly check for either "true" or "false"
  private static final Predicate<String> ISVALID_BOOLEAN
    = new Predicate<String>() {
      public boolean apply(String s) {
        s = s.trim();
        return (s.equals("true") || s.equals("false"));
      }
    };

  private static final Predicate<String> ISVALID_BYTE
    = new Predicate<String>() {
      public boolean apply(String s) {
        Byte.valueOf(s.trim());
        return true;
      }
    };


  // as long as s is a single character return it unmodified
  private static final Predicate<String> ISVALID_CHAR
    = new Predicate<String>() {
      public boolean apply(String s) {
        return (s.length() == 1);
      }
    };


  private static final Predicate<String> ISVALID_DOUBLE
    = new Predicate<String>() {
      public boolean apply(String s) {
        Double.valueOf(s.trim());
        return true;
      }
    };


  private static final Predicate<String> ISVALID_FLOAT
    = new Predicate<String>() {
      public boolean apply(String s) {
        Float.valueOf(s.trim());
        return true;
      }
    };


  private static final Predicate<String> ISVALID_INT
    = new Predicate<String>() {
      public boolean apply(String s) {
        Integer.valueOf(s.trim());
        return true;
      }
    };


  private static final Predicate<String> ISVALID_LONG
    = new Predicate<String>() {
      public boolean apply(String s) {
        Long.valueOf(s.trim());
        return true;
      }
    };


  private static final Predicate<String> ISVALID_SHORT
    = new Predicate<String>() {
      public boolean apply(String s) {
        Short.valueOf(s.trim());
        return true;
      }
    };

  private static final Map<String, Predicate<String>> PRIMITIVE_TO_VALIDATOR
    = ImmutableMap.<String, Predicate<String>>builder()
      .put("boolean", ISVALID_BOOLEAN)
      .put("byte",    ISVALID_BYTE)
      .put("char",    ISVALID_CHAR)
      .put("double",  ISVALID_DOUBLE)
      .put("float",   ISVALID_FLOAT)
      .put("int",     ISVALID_INT)
      .put("long",    ISVALID_LONG)
      .put("short",   ISVALID_SHORT)
      .build();

  /**
   * @return true if the primitive is a valid literal of the specified
   * type, false otherwise.
   */
  public static final boolean isValidPrimitive(String primitive, String type) {
    try {
      return PRIMITIVE_TO_VALIDATOR.get(type).apply(primitive);
    } catch (NumberFormatException e) {
      return false;
    }
  }

  /**
   * Static Singleton Instance
   *
   * Must be declared last in the source file.
   */
  public static final ScalaUtil INSTANCE = new ScalaUtil();
}
TOP

Related Classes of com.google.gxp.compiler.scala.ScalaUtil

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.