done firefox evaluate idea/140.1953
authorVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Mon, 26 Jan 2015 17:40:33 +0000 (18:40 +0100)
committerVladimir Krivosheev <vladimir.krivosheev@jetbrains.com>
Mon, 26 Jan 2015 17:44:55 +0000 (18:44 +0100)
platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/FieldLoader.java
platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/FieldProcessor.java
platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/TypeWriter.java [new file with mode: 0644]

index ad4f12e289043b8d14bb410a890e0a6035ea0588..b9e9c8ddeebec5c051b2e24f2ecc71c125bc7772 100644 (file)
@@ -5,12 +5,13 @@ import org.jetbrains.annotations.NotNull;
 class FieldLoader {
   final String name;
   final String jsonName;
-
   final ValueReader valueReader;
+  final boolean skipRead;
 
-  FieldLoader(@NotNull String name, @NotNull String jsonName, @NotNull ValueReader valueReader) {
+  FieldLoader(@NotNull String name, @NotNull String jsonName, @NotNull ValueReader valueReader, boolean skipRead) {
     this.name = name;
     this.jsonName = jsonName;
     this.valueReader = valueReader;
+    this.skipRead = skipRead;
   }
 }
index ae603d16c35583a64827a480f0fed9b03708bd3b..34522fd63d774e30254458bd47f78fa1f93fa23d 100644 (file)
@@ -1,5 +1,6 @@
 package org.jetbrains.protocolReader;
 
+import gnu.trove.THashSet;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.jsonProtocol.JsonField;
 import org.jetbrains.jsonProtocol.JsonOptionalField;
@@ -8,10 +9,7 @@ import org.jetbrains.jsonProtocol.JsonSubtypeCasting;
 import java.lang.reflect.Method;
 import java.lang.reflect.ParameterizedType;
 import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.LinkedHashMap;
-import java.util.List;
+import java.util.*;
 
 final class FieldProcessor<T> {
   final List<FieldLoader> fieldLoaders = new ArrayList<>();
@@ -27,6 +25,15 @@ final class FieldProcessor<T> {
     // todo sort by source location
     Arrays.sort(methods, (o1, o2) -> o1.getName().compareTo(o2.getName()));
 
+    Set<String> skippedNames = new THashSet<>();
+    for (Method method : methods) {
+      JsonField annotation = method.getAnnotation(JsonField.class);
+      if (annotation != null && !annotation.primitiveValue().isEmpty()) {
+        skippedNames.add(annotation.primitiveValue());
+        skippedNames.add(annotation.primitiveValue() + "Type");
+      }
+    }
+
     Package classPackage = typeClass.getPackage();
     for (Method method : methods) {
       Class<?> methodClass = method.getDeclaringClass();
@@ -48,7 +55,7 @@ final class FieldProcessor<T> {
         MethodHandler methodHandler;
         JsonSubtypeCasting jsonSubtypeCaseAnnotation = method.getAnnotation(JsonSubtypeCasting.class);
         if (jsonSubtypeCaseAnnotation == null) {
-          methodHandler = processFieldGetterMethod(method);
+          methodHandler = createMethodHandler(method, skippedNames.contains(method.getName()));
         }
         else {
           methodHandler = processManualSubtypeMethod(method, jsonSubtypeCaseAnnotation);
@@ -63,7 +70,7 @@ final class FieldProcessor<T> {
   }
 
   @NotNull
-  private MethodHandler processFieldGetterMethod(@NotNull Method method) {
+  private MethodHandler createMethodHandler(@NotNull Method method, boolean skipRead) {
     String jsonName = method.getName();
     JsonField fieldAnnotation = method.getAnnotation(JsonField.class);
     if (fieldAnnotation != null && !fieldAnnotation.name().isEmpty()) {
@@ -85,7 +92,7 @@ final class FieldProcessor<T> {
 
     ValueReader fieldTypeParser = reader.getFieldTypeParser(genericReturnType, false, method);
     if (fieldTypeParser != InterfaceReader.VOID_PARSER) {
-      fieldLoaders.add(new FieldLoader(method.getName(), jsonName, fieldTypeParser));
+      fieldLoaders.add(new FieldLoader(method.getName(), jsonName, fieldTypeParser, skipRead));
     }
 
     final String effectiveFieldName = fieldTypeParser == InterfaceReader.VOID_PARSER ? null : method.getName();
diff --git a/platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/TypeWriter.java b/platform/script-debugger/protocol/protocol-reader/src/org/jetbrains/protocolReader/TypeWriter.java
new file mode 100644 (file)
index 0000000..e47017c
--- /dev/null
@@ -0,0 +1,255 @@
+package org.jetbrains.protocolReader;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.jsonProtocol.JsonObjectBased;
+
+import java.lang.reflect.Method;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+class TypeWriter<T> {
+  public static final char FIELD_PREFIX = '_';
+
+  final Class<T> typeClass;
+
+  private final List<VolatileFieldBinding> volatileFields;
+
+  private final LinkedHashMap<Method, MethodHandler> methodHandlerMap;
+
+  /** Loaders that should read values and save them in field array on parse time. */
+  private final List<FieldLoader> fieldLoaders;
+
+  /** Subtype aspects of the type or null */
+  final ExistingSubtypeAspect subtypeAspect;
+
+  private final boolean hasLazyFields;
+
+  TypeWriter(Class<T> typeClass, TypeRef<?> jsonSuperClass,
+             List<VolatileFieldBinding> volatileFields,
+             LinkedHashMap<Method, MethodHandler> methodHandlerMap,
+             List<FieldLoader> fieldLoaders,
+             boolean hasLazyFields) {
+    this.typeClass = typeClass;
+    this.volatileFields = volatileFields;
+    this.methodHandlerMap = methodHandlerMap;
+    this.fieldLoaders = fieldLoaders;
+    this.hasLazyFields = hasLazyFields;
+    subtypeAspect = jsonSuperClass == null ? null : new ExistingSubtypeAspect(jsonSuperClass);
+  }
+
+  public void writeInstantiateCode(@NotNull ClassScope scope, @NotNull TextOutput out) {
+    writeInstantiateCode(scope, false, out);
+  }
+
+  public void writeInstantiateCode(@NotNull ClassScope scope, boolean deferredReading, @NotNull TextOutput out) {
+    String className = scope.getTypeImplReference(this);
+    if (deferredReading || subtypeAspect == null) {
+      out.append("new ").append(className);
+    }
+    else {
+      subtypeAspect.writeInstantiateCode(className, out);
+    }
+  }
+
+  public void write(@NotNull FileScope fileScope) {
+    TextOutput out = fileScope.getOutput();
+    String valueImplClassName = fileScope.getTypeImplShortName(this);
+    out.append("private static final class ").append(valueImplClassName);
+
+    out.append(" implements ").append(typeClass.getCanonicalName()).openBlock();
+
+    if (hasLazyFields || JsonObjectBased.class.isAssignableFrom(typeClass)) {
+      out.append("private ").append(Util.JSON_READER_CLASS_NAME).space().append(Util.PENDING_INPUT_READER_NAME).semi().newLine();
+    }
+
+    ClassScope classScope = fileScope.newClassScope();
+    for (VolatileFieldBinding field : volatileFields) {
+      field.writeFieldDeclaration(classScope, out);
+      out.newLine();
+    }
+
+    for (FieldLoader loader : fieldLoaders) {
+      out.append("private").space();
+      loader.valueReader.appendFinishedValueTypeName(out);
+      out.space().append(FIELD_PREFIX).append(loader.name);
+      if (loader.valueReader instanceof PrimitiveValueReader) {
+        String defaultValue = ((PrimitiveValueReader)loader.valueReader).defaultValue;
+        if (defaultValue != null) {
+          out.append(" = ").append(defaultValue);
+        }
+      }
+      out.semi();
+      out.newLine();
+    }
+
+    if (subtypeAspect != null) {
+      subtypeAspect.writeSuperFieldJava(out);
+    }
+
+    writeConstructorMethod(valueImplClassName, classScope, out);
+    out.newLine();
+
+    if (subtypeAspect != null) {
+      subtypeAspect.writeParseMethod(valueImplClassName, classScope, out);
+    }
+
+    for (Map.Entry<Method, MethodHandler> entry : methodHandlerMap.entrySet()) {
+      out.newLine();
+      entry.getValue().writeMethodImplementationJava(classScope, entry.getKey(), out);
+      out.newLine();
+    }
+
+    writeBaseMethods(out);
+    if (subtypeAspect != null) {
+      subtypeAspect.writeGetSuperMethodJava(out);
+    }
+    out.indentOut().append('}');
+  }
+
+  /**
+   * Generates Java implementation of standard methods of JSON type class (if needed):
+   * {@link org.jetbrains.jsonProtocol.JsonObjectBased#getDeferredReader()}
+   */
+  private void writeBaseMethods(TextOutput out) {
+    Class<?> typeClass = this.typeClass;
+    Method method;
+    try {
+      method = typeClass.getMethod("getDeferredReader");
+    }
+    catch (SecurityException e) {
+      throw new RuntimeException(e);
+    }
+    catch (NoSuchMethodException ignored) {
+      // Method not found, skip.
+      return;
+    }
+
+    out.newLine();
+    MethodHandler.writeMethodDeclarationJava(out, method);
+    out.openBlock();
+    out.append("return ").append(Util.PENDING_INPUT_READER_NAME).semi();
+    out.closeBlock();
+  }
+
+  private void writeConstructorMethod(@NotNull String valueImplClassName, @NotNull ClassScope classScope, @NotNull TextOutput out) {
+    out.newLine().append(valueImplClassName).append('(').append(Util.JSON_READER_PARAMETER_DEF).comma().append("String name");
+    if (subtypeAspect != null) {
+      subtypeAspect.writeSuperConstructorParamJava(out);
+    }
+    out.append(')').openBlock();
+
+    if (subtypeAspect != null) {
+      subtypeAspect.writeSuperConstructorInitialization(out);
+    }
+
+    if (JsonObjectBased.class.isAssignableFrom(typeClass) || hasLazyFields) {
+      out.append(Util.PENDING_INPUT_READER_NAME).append(" = ").append(Util.READER_NAME).append(".subReader()").semi().newLine();
+    }
+
+    if (fieldLoaders.isEmpty()) {
+      out.append(Util.READER_NAME).append(".skipValue()").semi();
+    }
+    else {
+      out.append("if (name == null)").openBlock();
+      {
+        out.append("if (reader.beginObject().hasNext())").openBlock();
+        {
+          out.append("name = reader.nextName()").semi();
+        }
+        out.closeBlock();
+        out.newLine().append("else").openBlock();
+        {
+          out.append("return").semi();
+        }
+        out.closeBlock();
+      }
+      out.closeBlock();
+      out.newLine();
+
+      writeReadFields(out, classScope);
+
+      // we don't read all data if we have lazy fields, so, we should not check end of stream
+      //if (!hasLazyFields) {
+        out.newLine().newLine().append(Util.READER_NAME).append(".endObject()").semi();
+      //}
+    }
+    out.closeBlock();
+  }
+
+  private void writeReadFields(TextOutput out, ClassScope classScope) {
+    boolean stopIfAllFieldsWereRead = hasLazyFields;
+    boolean hasOnlyOneFieldLoader = fieldLoaders.size() == 1;
+    boolean isTracedStop = stopIfAllFieldsWereRead && !hasOnlyOneFieldLoader;
+    if (isTracedStop) {
+      out.newLine().append("int i = 0").semi();
+    }
+
+    out.newLine().append("do").openBlock();
+    boolean isFirst = true;
+    String operator = "if";
+    for (FieldLoader fieldLoader : fieldLoaders) {
+      if (fieldLoader.skipRead) {
+        continue;
+      }
+
+      if (!isFirst) {
+        out.newLine();
+      }
+
+      out.append(operator).append(" (name");
+      out.append(".equals(\"").append(fieldLoader.jsonName).append("\"))").openBlock();
+      {
+        String primitiveValueName = fieldLoader.valueReader instanceof ObjectValueReader ? ((ObjectValueReader)fieldLoader.valueReader).primitiveValueName : null;
+        if (primitiveValueName != null) {
+          out.append("if (reader.peek() == com.google.gson.stream.JsonToken.BEGIN_OBJECT)").openBlock();
+        }
+        assignField(out, fieldLoader.name);
+
+        fieldLoader.valueReader.writeReadCode(classScope, false, out);
+        out.semi();
+
+        if (primitiveValueName != null) {
+          out.closeBlock();
+          out.newLine().append("else").openBlock();
+
+          assignField(out, primitiveValueName + "Type");
+          out.append("reader.peek()").semi().newLine();
+
+          assignField(out, primitiveValueName);
+          out.append("reader.nextString(true)").semi();
+
+          out.closeBlock();
+        }
+
+        if (stopIfAllFieldsWereRead && !isTracedStop) {
+          out.newLine().append(Util.READER_NAME).append(".skipValues()").semi().newLine().append("break").semi();
+        }
+      }
+      out.closeBlock();
+
+      if (isFirst) {
+        isFirst = false;
+        operator = "else if";
+      }
+    }
+
+    out.newLine().append("else").openBlock().append("reader.skipValue();");
+    if (isTracedStop) {
+      out.newLine().append("continue").semi();
+    }
+    out.closeBlock();
+    if (isTracedStop) {
+      out.newLine().newLine().append("if (i == ").append(fieldLoaders.size() - 1).append(")").openBlock();
+      out.append(Util.READER_NAME).append(".skipValues()").semi().newLine().append("break").semi().closeBlock();
+      out.newLine().append("else").openBlock().append("i++").semi().closeBlock();
+    }
+    out.closeBlock();
+    out.newLine().append("while ((name = reader.nextNameOrNull()) != null)").semi();
+  }
+
+  @NotNull
+  private static TextOutput assignField(TextOutput out, String fieldName) {
+    return out.append(FIELD_PREFIX).append(fieldName).append(" = ");
+  }
+}
\ No newline at end of file