Supporting annotations with arrays reflection
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Sun, 03 Feb 2013 18:58:09 +0100
branchreflection
changeset 652f095ea52f417
parent 651 e6fdcaab8dc7
child 653 bcdfc29fd004
Supporting annotations with arrays
emul/mini/pom.xml
emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java
javap/src/main/java/org/apidesign/javap/AnnotationParser.java
vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java
vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java
vm/src/test/java/org/apidesign/vm4brwsr/Classes.java
vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java
     1.1 --- a/emul/mini/pom.xml	Sun Feb 03 17:55:48 2013 +0100
     1.2 +++ b/emul/mini/pom.xml	Sun Feb 03 18:58:09 2013 +0100
     1.3 @@ -22,6 +22,12 @@
     1.4        <version>0.3-SNAPSHOT</version>
     1.5        <type>jar</type>
     1.6      </dependency>
     1.7 +    <dependency>
     1.8 +      <groupId>org.testng</groupId>
     1.9 +      <artifactId>testng</artifactId>
    1.10 +      <version>6.5.2</version>
    1.11 +      <scope>test</scope>
    1.12 +    </dependency>
    1.13    </dependencies>
    1.14    <build>
    1.15        <plugins>
     2.1 --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java	Sun Feb 03 17:55:48 2013 +0100
     2.2 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java	Sun Feb 03 18:58:09 2013 +0100
     2.3 @@ -18,6 +18,8 @@
     2.4  package org.apidesign.bck2brwsr.emul.reflect;
     2.5  
     2.6  import java.lang.annotation.Annotation;
     2.7 +import java.lang.reflect.Method;
     2.8 +import java.lang.reflect.Modifier;
     2.9  import org.apidesign.bck2brwsr.core.JavaScriptBody;
    2.10  
    2.11  /**
    2.12 @@ -29,7 +31,7 @@
    2.13          return getClass();
    2.14      }
    2.15  
    2.16 -    @JavaScriptBody(args = { "a", "n", "values" }, body = ""
    2.17 +    @JavaScriptBody(args = { "a", "n", "arr", "values" }, body = ""
    2.18          + "function f(v, p) {\n"
    2.19          + "  var val = v;\n"
    2.20          + "  var prop = p;\n"
    2.21 @@ -37,27 +39,42 @@
    2.22          + "    return val[prop];\n"
    2.23          + "  };\n"
    2.24          + "}\n"
    2.25 -        + "var props = Object.getOwnPropertyNames(values);\n"
    2.26 -        + "for (var i = 0; i < props.length; i++) {\n"
    2.27 -        + "  var p = props[i];\n"
    2.28 -        + "  a[p] = new f(values, p);\n"
    2.29 +        + "for (var i = 0; i < arr.length; i += 2) {\n"
    2.30 +        + "  var m = arr[i];\n"
    2.31 +        + "  var p = arr[i + 1];\n"
    2.32 +        + "  a[m] = new f(values, p);\n"
    2.33          + "}\n"
    2.34          + "a['$instOf_' + n] = true;\n"
    2.35          + "return a;"
    2.36      )
    2.37 -    private static <T extends Annotation> T create(AnnotationImpl a, String n, Object values) {
    2.38 -        return null;
    2.39 -    }
    2.40 +    private static native <T extends Annotation> T create(
    2.41 +        AnnotationImpl a, String n, String[] methodsAndProps, Object values
    2.42 +    );
    2.43 +    
    2.44      public static <T extends Annotation> T create(Class<T> annoClass, Object values) {
    2.45 -        return create(new AnnotationImpl(), annoClass.getName().replace('.', '_'), values);
    2.46 +        return create(new AnnotationImpl(), 
    2.47 +            annoClass.getName().replace('.', '_'), 
    2.48 +            findProps(annoClass), values
    2.49 +        );
    2.50      }
    2.51  
    2.52      public static Annotation[] create(Object anno) {
    2.53          String[] names = findNames(anno);
    2.54          Annotation[] ret = new Annotation[names.length];
    2.55          for (int i = 0; i < names.length; i++) {
    2.56 -            String n = names[i].substring(1, names[i].length() - 1).replace('/', '_');
    2.57 -            ret[i] = create(new AnnotationImpl(), n, findData(anno, names[i]));
    2.58 +            String annoNameSlash = names[i].substring(1, names[i].length() - 1);
    2.59 +            Class<?> annoClass;
    2.60 +            try {
    2.61 +                annoClass = Class.forName(annoNameSlash.replace('/', '.'));
    2.62 +            } catch (ClassNotFoundException ex) {
    2.63 +                throw new IllegalStateException("Can't find annotation class " + annoNameSlash);
    2.64 +            }
    2.65 +            ret[i] = create(
    2.66 +                new AnnotationImpl(), 
    2.67 +                annoNameSlash.replace('/', '_'),
    2.68 +                findProps(annoClass),
    2.69 +                findData(anno, names[i])
    2.70 +            );
    2.71          }
    2.72          return ret;
    2.73      }
    2.74 @@ -70,12 +87,19 @@
    2.75          + "}"
    2.76          + "return arr;"
    2.77      )
    2.78 -    private static String[] findNames(Object anno) {
    2.79 -        throw new UnsupportedOperationException();
    2.80 -    }
    2.81 +    private static native String[] findNames(Object anno);
    2.82  
    2.83      @JavaScriptBody(args={ "anno", "p"}, body="return anno[p];")
    2.84 -    private static Object findData(Object anno, String p) {
    2.85 -        throw new UnsupportedOperationException();
    2.86 +    private static native Object findData(Object anno, String p);
    2.87 +
    2.88 +    private static String[] findProps(Class<?> annoClass) {
    2.89 +        final Method[] marr = MethodImpl.findMethods(annoClass, Modifier.PUBLIC);
    2.90 +        String[] arr = new String[marr.length * 2];
    2.91 +        int pos = 0;
    2.92 +        for (Method m : marr) {
    2.93 +            arr[pos++] = MethodImpl.toSignature(m);
    2.94 +            arr[pos++] = m.getName();
    2.95 +        }
    2.96 +        return arr;
    2.97      }
    2.98  }
     3.1 --- a/javap/src/main/java/org/apidesign/javap/AnnotationParser.java	Sun Feb 03 17:55:48 2013 +0100
     3.2 +++ b/javap/src/main/java/org/apidesign/javap/AnnotationParser.java	Sun Feb 03 18:58:09 2013 +0100
     3.3 @@ -35,9 +35,11 @@
     3.4   */
     3.5  public class AnnotationParser {
     3.6      private final boolean textual;
     3.7 +    private final boolean iterateArray;
     3.8      
     3.9 -    protected AnnotationParser(boolean textual) {
    3.10 +    protected AnnotationParser(boolean textual, boolean iterateArray) {
    3.11          this.textual = textual;
    3.12 +        this.iterateArray = iterateArray;
    3.13      }
    3.14  
    3.15      protected void visitAnnotationStart(String type) throws IOException {
    3.16 @@ -45,6 +47,13 @@
    3.17  
    3.18      protected void visitAnnotationEnd(String type) throws IOException {
    3.19      }
    3.20 +
    3.21 +    protected void visitValueStart(String attrName, char type) throws IOException {
    3.22 +    }
    3.23 +
    3.24 +    protected void visitValueEnd(String attrName, char type) throws IOException {
    3.25 +    }
    3.26 +
    3.27      
    3.28      protected void visitAttr(
    3.29          String annoType, String attr, String attrType, String value
    3.30 @@ -89,9 +98,11 @@
    3.31          }
    3.32      }
    3.33  
    3.34 -    private void readValue(DataInputStream dis, ClassData cd, String typeName, String attrName) 
    3.35 -    throws IOException {
    3.36 +    private void readValue(
    3.37 +        DataInputStream dis, ClassData cd, String typeName, String attrName
    3.38 +    ) throws IOException {
    3.39          char type = (char)dis.readByte();
    3.40 +        visitValueStart(attrName, type);
    3.41          if (type == '@') {
    3.42              readAnno(dis, cd);
    3.43          } else if ("CFJZsSIDB".indexOf(type) >= 0) { // NOI18N
    3.44 @@ -112,13 +123,20 @@
    3.45          } else if (type == '[') {
    3.46              int cnt = dis.readUnsignedShort();
    3.47              for (int i = 0; i < cnt; i++) {
    3.48 -                readValue(dis, cd, typeName, attrName);
    3.49 +                readValue(dis, cd, typeName, iterateArray ? attrName : null);
    3.50              }
    3.51          } else if (type == 'e') {
    3.52              int enumT = dis.readUnsignedShort();
    3.53 +            String attrType = cd.stringValue(enumT, textual);
    3.54              int enumN = dis.readUnsignedShort();
    3.55 +            String val = cd.stringValue(enumN, textual);
    3.56 +            if (textual) {
    3.57 +                val = '"' + val + '"';
    3.58 +            }
    3.59 +            visitAttr(typeName, attrName, attrType, val);
    3.60          } else {
    3.61              throw new IOException("Unknown type " + type);
    3.62          }
    3.63 +        visitValueEnd(attrName, type);
    3.64      }
    3.65  }
     4.1 --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Sun Feb 03 17:55:48 2013 +0100
     4.2 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java	Sun Feb 03 18:58:09 2013 +0100
     4.3 @@ -1605,7 +1605,7 @@
     4.4          final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;";
     4.5          class P extends AnnotationParser {
     4.6              public P() {
     4.7 -                super(false);
     4.8 +                super(false, true);
     4.9              }
    4.10              
    4.11              int cnt;
    4.12 @@ -1661,7 +1661,7 @@
    4.13          final String[] values = new String[attrNames.length];
    4.14          final boolean[] found = { false };
    4.15          final String jvmType = "L" + className.replace('.', '/') + ";";
    4.16 -        AnnotationParser ap = new AnnotationParser(false) {
    4.17 +        AnnotationParser ap = new AnnotationParser(false, true) {
    4.18              @Override
    4.19              protected void visitAttr(String type, String attr, String at, String value) {
    4.20                  if (type.equals(jvmType)) {
    4.21 @@ -1699,34 +1699,54 @@
    4.22      }
    4.23  
    4.24      private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException {
    4.25 -        AnnotationParser ap = new AnnotationParser(true) {
    4.26 -            int anno;
    4.27 -            int cnt;
    4.28 +        AnnotationParser ap = new AnnotationParser(true, false) {
    4.29 +            int[] cnt = new int[32];
    4.30 +            int depth;
    4.31              
    4.32              @Override
    4.33              protected void visitAnnotationStart(String type) throws IOException {
    4.34 -                if (anno++ > 0) {
    4.35 +                if (cnt[depth]++ > 0) {
    4.36                      out.append(",");
    4.37                  }
    4.38                  out.append('"').append(type).append("\" : {\n");
    4.39 -                cnt = 0;
    4.40 +                cnt[++depth] = 0;
    4.41              }
    4.42  
    4.43              @Override
    4.44              protected void visitAnnotationEnd(String type) throws IOException {
    4.45                  out.append("\n}\n");
    4.46 +                depth--;
    4.47 +            }
    4.48 +
    4.49 +            @Override
    4.50 +            protected void visitValueStart(String attrName, char type) throws IOException {
    4.51 +                if (cnt[depth]++ > 0) {
    4.52 +                    out.append(",\n");
    4.53 +                }
    4.54 +                cnt[++depth] = 0;
    4.55 +                if (attrName != null) {
    4.56 +                    out.append(attrName).append(" : ");
    4.57 +                }
    4.58 +                if (type == '[') {
    4.59 +                    out.append("[");
    4.60 +                }
    4.61 +            }
    4.62 +
    4.63 +            @Override
    4.64 +            protected void visitValueEnd(String attrName, char type) throws IOException {
    4.65 +                if (type == '[') {
    4.66 +                    out.append("]");
    4.67 +                }
    4.68 +                depth--;
    4.69              }
    4.70              
    4.71              @Override
    4.72              protected void visitAttr(String type, String attr, String attrType, String value) 
    4.73              throws IOException {
    4.74 -                if (attr == null) {
    4.75 +                if (attr == null && value == null) {
    4.76                      return;
    4.77                  }
    4.78 -                if (cnt++ > 0) {
    4.79 -                    out.append(",\n");
    4.80 -                }
    4.81 -                out.append(attr).append("__").append(attrType).append(" : ").append(value);
    4.82 +                out.append(value);
    4.83              }
    4.84          };
    4.85          ap.parse(data, cd);
     5.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Sun Feb 03 17:55:48 2013 +0100
     5.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java	Sun Feb 03 18:58:09 2013 +0100
     5.3 @@ -87,6 +87,9 @@
     5.4      @Test public void jsAnnotation() throws Exception {
     5.5          assertExec("Check class annotation", Classes.class, "getMarker__I", Double.valueOf(10));
     5.6      }
     5.7 +    @Test public void jsArrayAnnotation() throws Exception {
     5.8 +        assertExec("Check array annotation", Classes.class, "getMarkerNicknames__Ljava_lang_String_2", Classes.getMarkerNicknames());
     5.9 +    }
    5.10      @Test public void jsStringAnnotation() throws Exception {
    5.11          assertExec("Check class annotation", Classes.class, "getNamer__Ljava_lang_String_2Z", "my text", true);
    5.12      }
     6.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Sun Feb 03 17:55:48 2013 +0100
     6.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java	Sun Feb 03 18:58:09 2013 +0100
     6.3 @@ -27,7 +27,7 @@
     6.4   *
     6.5   * @author Jaroslav Tulach <jtulach@netbeans.org>
     6.6   */
     6.7 -@ClassesMarker(number = 10)
     6.8 +@ClassesMarker(number = 10, nicknames = { "Ten", "Deset" })
     6.9  @ClassesNamer(name = "my text")
    6.10  public class Classes {
    6.11      public static String nameOfIO() {
    6.12 @@ -39,6 +39,7 @@
    6.13      }
    6.14      
    6.15      private static final Class<?> PRELOAD = Runnable.class;
    6.16 +    private static final Class<?> PRELOAD2 = ClassesMarker.E.class;
    6.17      
    6.18      public static boolean isInterface(String s) throws ClassNotFoundException {
    6.19          return Class.forName(s).isInterface();
    6.20 @@ -57,7 +58,7 @@
    6.21          return new IOException().getClass().getName().toString();
    6.22      }
    6.23      
    6.24 -    @ClassesMarker(number = 1)
    6.25 +    @ClassesMarker(number = 1, nicknames = { "One", "Jedna" } )
    6.26      public static String name() {
    6.27          return IOException.class.getName().toString();
    6.28      }
    6.29 @@ -89,6 +90,17 @@
    6.30          ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class);
    6.31          return cm == null ? -1 : cm.number();
    6.32      }
    6.33 +    public static String getMarkerNicknames() {
    6.34 +        ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class);
    6.35 +        if (cm == null) {
    6.36 +            return null;
    6.37 +        }
    6.38 +        StringBuilder sb = new StringBuilder();
    6.39 +        for (String s : cm.nicknames()) {
    6.40 +            sb.append(s).append("\n");
    6.41 +        }
    6.42 +        return sb.toString().toString();
    6.43 +    }
    6.44      public static String getNamer(boolean direct) {
    6.45          if (direct) {
    6.46              ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class);
     7.1 --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java	Sun Feb 03 17:55:48 2013 +0100
     7.2 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java	Sun Feb 03 18:58:09 2013 +0100
     7.3 @@ -27,4 +27,10 @@
     7.4  @Retention(RetentionPolicy.RUNTIME)
     7.5  public @interface ClassesMarker {
     7.6      int number();
     7.7 +    String[] nicknames();
     7.8 +    E count() default E.ONE;
     7.9 +    
    7.10 +    enum E {
    7.11 +        ONE, TWO;
    7.12 +    }
    7.13  }