# HG changeset patch # User Jaroslav Tulach # Date 1359914289 -3600 # Node ID f095ea52f417880de159d97aa7e0d8923002fa7b # Parent e6fdcaab8dc720c12e52ad76daa68839b50ac9c1 Supporting annotations with arrays diff -r e6fdcaab8dc7 -r f095ea52f417 emul/mini/pom.xml --- a/emul/mini/pom.xml Sun Feb 03 17:55:48 2013 +0100 +++ b/emul/mini/pom.xml Sun Feb 03 18:58:09 2013 +0100 @@ -22,6 +22,12 @@ 0.3-SNAPSHOT jar + + org.testng + testng + 6.5.2 + test + diff -r e6fdcaab8dc7 -r f095ea52f417 emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java --- a/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java Sun Feb 03 17:55:48 2013 +0100 +++ b/emul/mini/src/main/java/org/apidesign/bck2brwsr/emul/reflect/AnnotationImpl.java Sun Feb 03 18:58:09 2013 +0100 @@ -18,6 +18,8 @@ package org.apidesign.bck2brwsr.emul.reflect; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import org.apidesign.bck2brwsr.core.JavaScriptBody; /** @@ -29,7 +31,7 @@ return getClass(); } - @JavaScriptBody(args = { "a", "n", "values" }, body = "" + @JavaScriptBody(args = { "a", "n", "arr", "values" }, body = "" + "function f(v, p) {\n" + " var val = v;\n" + " var prop = p;\n" @@ -37,27 +39,42 @@ + " return val[prop];\n" + " };\n" + "}\n" - + "var props = Object.getOwnPropertyNames(values);\n" - + "for (var i = 0; i < props.length; i++) {\n" - + " var p = props[i];\n" - + " a[p] = new f(values, p);\n" + + "for (var i = 0; i < arr.length; i += 2) {\n" + + " var m = arr[i];\n" + + " var p = arr[i + 1];\n" + + " a[m] = new f(values, p);\n" + "}\n" + "a['$instOf_' + n] = true;\n" + "return a;" ) - private static T create(AnnotationImpl a, String n, Object values) { - return null; - } + private static native T create( + AnnotationImpl a, String n, String[] methodsAndProps, Object values + ); + public static T create(Class annoClass, Object values) { - return create(new AnnotationImpl(), annoClass.getName().replace('.', '_'), values); + return create(new AnnotationImpl(), + annoClass.getName().replace('.', '_'), + findProps(annoClass), values + ); } public static Annotation[] create(Object anno) { String[] names = findNames(anno); Annotation[] ret = new Annotation[names.length]; for (int i = 0; i < names.length; i++) { - String n = names[i].substring(1, names[i].length() - 1).replace('/', '_'); - ret[i] = create(new AnnotationImpl(), n, findData(anno, names[i])); + String annoNameSlash = names[i].substring(1, names[i].length() - 1); + Class annoClass; + try { + annoClass = Class.forName(annoNameSlash.replace('/', '.')); + } catch (ClassNotFoundException ex) { + throw new IllegalStateException("Can't find annotation class " + annoNameSlash); + } + ret[i] = create( + new AnnotationImpl(), + annoNameSlash.replace('/', '_'), + findProps(annoClass), + findData(anno, names[i]) + ); } return ret; } @@ -70,12 +87,19 @@ + "}" + "return arr;" ) - private static String[] findNames(Object anno) { - throw new UnsupportedOperationException(); - } + private static native String[] findNames(Object anno); @JavaScriptBody(args={ "anno", "p"}, body="return anno[p];") - private static Object findData(Object anno, String p) { - throw new UnsupportedOperationException(); + private static native Object findData(Object anno, String p); + + private static String[] findProps(Class annoClass) { + final Method[] marr = MethodImpl.findMethods(annoClass, Modifier.PUBLIC); + String[] arr = new String[marr.length * 2]; + int pos = 0; + for (Method m : marr) { + arr[pos++] = MethodImpl.toSignature(m); + arr[pos++] = m.getName(); + } + return arr; } } diff -r e6fdcaab8dc7 -r f095ea52f417 javap/src/main/java/org/apidesign/javap/AnnotationParser.java --- a/javap/src/main/java/org/apidesign/javap/AnnotationParser.java Sun Feb 03 17:55:48 2013 +0100 +++ b/javap/src/main/java/org/apidesign/javap/AnnotationParser.java Sun Feb 03 18:58:09 2013 +0100 @@ -35,9 +35,11 @@ */ public class AnnotationParser { private final boolean textual; + private final boolean iterateArray; - protected AnnotationParser(boolean textual) { + protected AnnotationParser(boolean textual, boolean iterateArray) { this.textual = textual; + this.iterateArray = iterateArray; } protected void visitAnnotationStart(String type) throws IOException { @@ -45,6 +47,13 @@ protected void visitAnnotationEnd(String type) throws IOException { } + + protected void visitValueStart(String attrName, char type) throws IOException { + } + + protected void visitValueEnd(String attrName, char type) throws IOException { + } + protected void visitAttr( String annoType, String attr, String attrType, String value @@ -89,9 +98,11 @@ } } - private void readValue(DataInputStream dis, ClassData cd, String typeName, String attrName) - throws IOException { + private void readValue( + DataInputStream dis, ClassData cd, String typeName, String attrName + ) throws IOException { char type = (char)dis.readByte(); + visitValueStart(attrName, type); if (type == '@') { readAnno(dis, cd); } else if ("CFJZsSIDB".indexOf(type) >= 0) { // NOI18N @@ -112,13 +123,20 @@ } else if (type == '[') { int cnt = dis.readUnsignedShort(); for (int i = 0; i < cnt; i++) { - readValue(dis, cd, typeName, attrName); + readValue(dis, cd, typeName, iterateArray ? attrName : null); } } else if (type == 'e') { int enumT = dis.readUnsignedShort(); + String attrType = cd.stringValue(enumT, textual); int enumN = dis.readUnsignedShort(); + String val = cd.stringValue(enumN, textual); + if (textual) { + val = '"' + val + '"'; + } + visitAttr(typeName, attrName, attrType, val); } else { throw new IOException("Unknown type " + type); } + visitValueEnd(attrName, type); } } diff -r e6fdcaab8dc7 -r f095ea52f417 vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java --- a/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Sun Feb 03 17:55:48 2013 +0100 +++ b/vm/src/main/java/org/apidesign/vm4brwsr/ByteCodeToJavaScript.java Sun Feb 03 18:58:09 2013 +0100 @@ -1605,7 +1605,7 @@ final String jvmType = "Lorg/apidesign/bck2brwsr/core/JavaScriptBody;"; class P extends AnnotationParser { public P() { - super(false); + super(false, true); } int cnt; @@ -1661,7 +1661,7 @@ final String[] values = new String[attrNames.length]; final boolean[] found = { false }; final String jvmType = "L" + className.replace('.', '/') + ";"; - AnnotationParser ap = new AnnotationParser(false) { + AnnotationParser ap = new AnnotationParser(false, true) { @Override protected void visitAttr(String type, String attr, String at, String value) { if (type.equals(jvmType)) { @@ -1699,34 +1699,54 @@ } private static void generateAnno(ClassData cd, final Appendable out, byte[] data) throws IOException { - AnnotationParser ap = new AnnotationParser(true) { - int anno; - int cnt; + AnnotationParser ap = new AnnotationParser(true, false) { + int[] cnt = new int[32]; + int depth; @Override protected void visitAnnotationStart(String type) throws IOException { - if (anno++ > 0) { + if (cnt[depth]++ > 0) { out.append(","); } out.append('"').append(type).append("\" : {\n"); - cnt = 0; + cnt[++depth] = 0; } @Override protected void visitAnnotationEnd(String type) throws IOException { out.append("\n}\n"); + depth--; + } + + @Override + protected void visitValueStart(String attrName, char type) throws IOException { + if (cnt[depth]++ > 0) { + out.append(",\n"); + } + cnt[++depth] = 0; + if (attrName != null) { + out.append(attrName).append(" : "); + } + if (type == '[') { + out.append("["); + } + } + + @Override + protected void visitValueEnd(String attrName, char type) throws IOException { + if (type == '[') { + out.append("]"); + } + depth--; } @Override protected void visitAttr(String type, String attr, String attrType, String value) throws IOException { - if (attr == null) { + if (attr == null && value == null) { return; } - if (cnt++ > 0) { - out.append(",\n"); - } - out.append(attr).append("__").append(attrType).append(" : ").append(value); + out.append(value); } }; ap.parse(data, cd); diff -r e6fdcaab8dc7 -r f095ea52f417 vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Sun Feb 03 17:55:48 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassTest.java Sun Feb 03 18:58:09 2013 +0100 @@ -87,6 +87,9 @@ @Test public void jsAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getMarker__I", Double.valueOf(10)); } + @Test public void jsArrayAnnotation() throws Exception { + assertExec("Check array annotation", Classes.class, "getMarkerNicknames__Ljava_lang_String_2", Classes.getMarkerNicknames()); + } @Test public void jsStringAnnotation() throws Exception { assertExec("Check class annotation", Classes.class, "getNamer__Ljava_lang_String_2Z", "my text", true); } diff -r e6fdcaab8dc7 -r f095ea52f417 vm/src/test/java/org/apidesign/vm4brwsr/Classes.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Sun Feb 03 17:55:48 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/Classes.java Sun Feb 03 18:58:09 2013 +0100 @@ -27,7 +27,7 @@ * * @author Jaroslav Tulach */ -@ClassesMarker(number = 10) +@ClassesMarker(number = 10, nicknames = { "Ten", "Deset" }) @ClassesNamer(name = "my text") public class Classes { public static String nameOfIO() { @@ -39,6 +39,7 @@ } private static final Class PRELOAD = Runnable.class; + private static final Class PRELOAD2 = ClassesMarker.E.class; public static boolean isInterface(String s) throws ClassNotFoundException { return Class.forName(s).isInterface(); @@ -57,7 +58,7 @@ return new IOException().getClass().getName().toString(); } - @ClassesMarker(number = 1) + @ClassesMarker(number = 1, nicknames = { "One", "Jedna" } ) public static String name() { return IOException.class.getName().toString(); } @@ -89,6 +90,17 @@ ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); return cm == null ? -1 : cm.number(); } + public static String getMarkerNicknames() { + ClassesMarker cm = Classes.class.getAnnotation(ClassesMarker.class); + if (cm == null) { + return null; + } + StringBuilder sb = new StringBuilder(); + for (String s : cm.nicknames()) { + sb.append(s).append("\n"); + } + return sb.toString().toString(); + } public static String getNamer(boolean direct) { if (direct) { ClassesNamer cm = Classes.class.getAnnotation(ClassesNamer.class); diff -r e6fdcaab8dc7 -r f095ea52f417 vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java --- a/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java Sun Feb 03 17:55:48 2013 +0100 +++ b/vm/src/test/java/org/apidesign/vm4brwsr/ClassesMarker.java Sun Feb 03 18:58:09 2013 +0100 @@ -27,4 +27,10 @@ @Retention(RetentionPolicy.RUNTIME) public @interface ClassesMarker { int number(); + String[] nicknames(); + E count() default E.ONE; + + enum E { + ONE, TWO; + } }