--- /dev/null Mon Sep 23 02:04:33 2013 +++ new/test/type-annotations/Helper.java Mon Sep 23 02:04:32 2013 @@ -0,0 +1,951 @@ +/* + * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/** + * @bug 8013497 + * @summary Utility class for constructing, compiling and execute code + * @author Charlie Wang + */ +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import javax.tools.Diagnostic; +import javax.tools.DiagnosticCollector; +import javax.tools.JavaCompiler; +import javax.tools.JavaCompiler.CompilationTask; +import javax.tools.JavaFileObject.Kind; +import javax.tools.JavaFileObject; +import javax.tools.SimpleJavaFileObject; +import javax.tools.StandardJavaFileManager; +import javax.tools.StandardLocation; +import javax.tools.ToolProvider; +import com.sun.source.util.JavacTask; +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedType; +import java.lang.reflect.AnnotatedArrayType; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; + +/** + * Helper provides below features for annotation test: + * 1. Code construction - SrcType + * SrcType contains all parts of source code needed to construct a class + * for annotation test. In each enum's getSrc(), it supports to replace [#ANNO] + * and [TYPE] AND [NAME], which is usually all the elements needed to construct + * a small snippet like package, class, method, field etc. + * 2. Code compile and load + * compilecode() and loadClass compiles test code and then load it to + * memory. All users need to do is to construct a class code, send it to Helper, + * then they would get a class object of the test object which is enough for + * annotation test. + */ +public class Helper { + + // Set it to true to get more debug information + static final boolean DEBUG = true; + + public enum Declaration implements DeclarationGenerator { + + PACKAGE_INFO("[#ANNO] \npackage #NAME; \n"), + PACKAGE("\npackage #NAME; \n"), + IMPORT("\nimport java.io.*;\n" + + "import java.util.*;\n" + + "import java.lang.*;\n" + + "import java.lang.reflect.*;\n" + + "import java.lang.annotation.*;\n") { + public String getSrc(String... annoStr) { + if (null == annoStr) { + return template; + } else if (0 == annoStr.length) { + return ""; + } else if (1 == annoStr.length) { + return annoStr[0]; + } else { + throw new RuntimeException("illegal input for declaration!"); + } + } + }, + CLASS("\n[#ANNO] \nclass #NAME"), + INTERFACE("\n[#ANNO] \ninterface #NAME"), + EXTENDS(" extends [#ANNO] #NAME"), + IMPLEMENTS(" implements [[#ANNO] #NAME, ]") { + public String getSrc(String... annoStr) { + return IMPLEMENTS.replaceMultipleAnnoStr(annoStr); + } + }, + GENERICS1("<[[#ANNO1] #TYPE1 [#INHERITANCE [#ANNO2] #TYPE2], ]>") { + // input should be like ["@realanno1 Type1 + //extends @realanno2 Type2", "@realanno3 Type3 + //super @realanno4 Type4"] + public String getSrc(String... annoStr) { + if (null == annoStr || 0 == annoStr.length) { + return ""; + } + + String temp = template; + for (String as : annoStr) { + temp = temp.replaceAll("\\[\\[#ANNO1\\] #TYPE1 " + + "\\[#INHERITANCE \\[#ANNO2\\] #TYPE2\\], \\]", + "[#ANNO1] #TYPE1 [#INHERITANCE [#ANNO2] #TYPE2], " + + "[[#TMP_ANNO1] #TMP_TYPE1 [#TMP_INHERITANCE " + + "[#TMP_ANNO2] #TMP_TYPE2], ]"); + + int j = 0; // index of ) + String ss = as.trim(); + + // put @anno to [#ANNO1] + String[] sa = GENERICS1.replaceAnnoStr(ss, temp, + "\\[#ANNO1\\]", " [#ANNO1]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE1 + if (0 == ss.length()) { + throw new RuntimeException("illegal generics1."); + } + j = ss.indexOf(" "); + String type = (j == -1 ? ss : ss.substring(0, j)); + temp = temp.replaceFirst("#TYPE1", type); + if (-1 != j) { + ss = ss.substring(j).trim(); + } + + // get #INHERITANCE + if (ss.startsWith("extends") || ss.startsWith("super")) { + temp = temp.replaceAll("\\[#INHERITANCE " + + "\\[#ANNO2\\] #TYPE2\\]", + "#INHERITANCE \\[#ANNO2\\] #TYPE2"); + + j = ss.indexOf(" "); + if (-1 == j) { + throw new RuntimeException("illegal generics1."); + } + String inheritance = ss.substring(0, j); + temp = temp.replaceFirst("#INHERITANCE", inheritance); + ss = ss.substring(j).trim(); + + // put @anno to [#ANNO2] + sa = GENERICS1.replaceAnnoStr(ss, temp, "\\[#ANNO2\\]", + " [#ANNO2]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE2 + if (0 == ss.length() || -1 < ss.indexOf(" ")) { + throw new RuntimeException("illegal generics1."); + } + type = ss; + temp = temp.replaceFirst("#TYPE2", type); + } else { + temp = temp.replaceAll("\\[#INHERITANCE \\[#ANNO2\\] " + + "#TYPE2\\]", ""); + } + temp = temp.replaceAll("TMP_", ""); + } + + return temp.replaceAll(", \\[\\[#ANNO1\\] #TYPE1 " + + "\\[#INHERITANCE \\[#ANNO2\\] #TYPE2\\], \\]", ""); + } + }, + GENERICS2("<[[#ANNO1] #TYPE1 " + + "extends [#ANNO2] #TYPE2 & [#ANNO3] #TYPE3, ]>") { + // input should be like ["@realanno1 Type1 + // @realanno2 Type2" "@realanno3 Type3] + public String getSrc(String... annoStr) { + if (null == annoStr || 0 == annoStr.length) { + return ""; + } + String temp = template; + for (String as : annoStr) { + temp = temp.replaceAll("\\[\\[#ANNO1\\] #TYPE1 " + + "extends \\[#ANNO2\\] #TYPE2 " + + "& \\[#ANNO3\\] #TYPE3, \\]", + "[#ANNO1\\] #TYPE1 extends \\[#ANNO2\\] #TYPE2 " + + "& \\[#ANNO3\\] #TYPE3, " + + "\\[\\[#TMP_ANNO1\\] #TTMP_YPE1 " + + "extends \\[#TMP_ANNO2\\] #TMP_TYPE2 " + + "& \\[#TMP_ANNO3\\] #TMP_TYPE3, \\]"); + int j = 0; + String ss = as.trim(); + // put @anno to [#ANNO1] + String[] sa = GENERICS2.replaceAnnoStr(ss, temp, + "\\[#ANNO1\\]", " [#ANNO1]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE1 + if (0 == ss.length()) { + throw new RuntimeException("illegal generics2."); + } + j = ss.indexOf(" "); + if (-1 == j) { + throw new RuntimeException("illegal generics2."); + } + String type = ss.substring(0, j).trim(); + ss = ss.substring(j).trim(); + temp = temp.replaceFirst("#TYPE1", type); + + // put @anno to [#ANNO2] + sa = GENERICS2.replaceAnnoStr(ss, temp, "\\[#ANNO2\\]", + " [#ANNO2]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE2 + if (0 == ss.length()) { + throw new RuntimeException("illegal generics2."); + } + j = ss.indexOf(" "); + if (-1 == j) { + throw new RuntimeException("illegal generics2."); + } + type = ss.substring(0, j).trim(); + ss = ss.substring(j).trim(); + temp = temp.replaceFirst("#TYPE2", type); + + // put @anno to [#ANNO3] + sa = GENERICS2.replaceAnnoStr(ss, temp, "\\[#ANNO3\\]", + " [#ANNO3]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE1 + if (0 == ss.length()) { + throw new RuntimeException("illegal generics2."); + } + j = ss.indexOf(" "); + if (-1 != j) { + throw new RuntimeException("illegal generics2."); + } + type = ss.trim(); + temp = temp.replaceFirst("#TYPE3", type); + + temp = temp.replaceAll("TMP_", ""); + } + + return temp.replaceAll(", \\[\\[#ANNO1\\] #TYPE1 " + + "extends \\[#ANNO2\\] #TYPE2 " + + "& \\[#ANNO3\\] #TYPE3, \\]", ""); + } + }, + CLASS_BODY_START(" {\n") { + public String getSrc(String... annoStr) { + if (0 == annoStr.length) { + return ""; + } else { + return template; + } + } + }, + FIELD_TYPE(" public [#ANNO] #NAME"), + FIELD_ARRAY(" [[#ANNO] [] ] ") { + // input should be like ["@A() @B()", "@C() @D()"...] + public String getSrc(String... annoStr) { + if (null == annoStr || 0 == annoStr.length) { + return ""; + } + + String temp = template; + for (String as : annoStr) { + temp = temp.replaceAll("\\[\\[#ANNO\\] \\[\\] \\]", + "[#ANNO] [] [[#TMP_ANNO] [] ]"); + // replace "[#ANNO]..." with "@realannotation [#ANNO]..." + String ss = as.trim(); + String[] sa = FIELD_ARRAY.replaceAnnoStr(ss, temp, + "\\[#ANNO\\]", " [#ANNO]"); + ss = sa[0]; + temp = sa[1].replaceAll("TMP_", ""); + } + return temp.replaceAll("\\[\\[#ANNO\\] \\[\\] \\]", ""); + } + }, + FIELD_NAME(" #NAME;\n"), + CONSTRUCTOR_HEADER("\n public [#ANNO] #NAME"), + METHOD_HEADER("\n public [#ANNO] #TYPE #NAME") { + // input should be like "@realanno Type method" + public String getSrc(String... annoStr) { + if (0 == annoStr.length) { + return ""; + } + + String temp = template; + for (String as : annoStr) { + int j = 0; // index of ")" + String ss = as.trim(); + + String[] sa = METHOD_HEADER.replaceAnnoStr(ss, temp, + "\\[#ANNO\\]", " [#ANNO]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE + if (j == ss.length()) { + throw new RuntimeException("illegal method head."); + } + j = ss.indexOf(" ") + 1; + if (0 == j) { + throw new RuntimeException("illegal method head."); + } + String type = ss.substring(0, j); + temp = temp.replaceFirst("#TYPE", type); + ss = ss.substring(j).trim(); + + // get #NAME + if (0 <= ss.indexOf(" ") || 0 == ss.length()) { + throw new RuntimeException("illegal method head."); + } + temp = temp.replaceFirst("#NAME", ss); + } + + return temp; + } + }, + METHOD_PARAMETER("([[#ANNO] #TYPE #NAME, ])") { + // input should be like "@realanno Type arg" + public String getSrc(String... annoStr) { + if (null == annoStr || 0 == annoStr.length) { + return ""; + } + + if (1 == annoStr.length && "".equals(annoStr[0])) { + return "()"; + } + + String temp = template; + for (String as : annoStr) { + temp = temp.replaceFirst("\\[\\[#ANNO\\] #TYPE #NAME, \\]", + "[#ANNO] #TYPE #NAME, " + + "[[#TMP_ANNO] #TMP_TYPE #TMP_NAME, ]"); + + int j = 0; + String ss = as.trim(); + + String[] sa = METHOD_PARAMETER.replaceAnnoStr(ss, temp, + "\\[#ANNO\\]", " [#ANNO]"); + ss = sa[0]; + temp = sa[1]; + + // get #TYPE + if (j == ss.length()) { + throw new RuntimeException("illegal method parameter."); + } + j = ss.indexOf(" ") + 1; + if (0 == j) { + throw new RuntimeException("illegal method parameter."); + } + String type = ss.substring(0, j); + temp = temp.replaceFirst("#TYPE", type); + ss = ss.substring(j).trim(); + + // get #NAME + if (0 <= ss.indexOf(" ") || 0 == ss.length()) { + throw new RuntimeException("illegal method parameter."); + } + temp = temp.replaceFirst("#NAME", ss); + + temp = temp.replaceAll("TMP_", ""); + } + + return temp.replaceAll(", \\[\\[#ANNO\\] #TYPE #NAME, \\]", ""); + } + }, + METHOD_EXCEPTION(" throws [[#ANNO] #NAME, ]") { + public String getSrc(String... annoStr) { + return METHOD_EXCEPTION.replaceMultipleAnnoStr(annoStr); + } + }, + METHOD_BODY(" {return null;}\n") { + public String getSrc(String... annoStr) { + if (null == annoStr || 0 == annoStr.length) { + return ""; + } else if (1 == annoStr.length && !"".equals(annoStr[0])) { + return annoStr[0]; + } else { + return template; + } + } + }, + CLASS_BODY_END("\n}\n") { + public String getSrc(String... annoStr) { + return CLASS_BODY_END.getDefSrc(annoStr); + } + }, + ANNOTATION_TYPE_1("\n@Target(ElementType.TYPE_USE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\n@Repeatable(TypeAnno1Container.class)" + + "\n@interface TypeAnno1 {" + + "\n String value();" + + "\n}") { + public String getSrc(String... annoStr) { + return ANNOTATION_TYPE_1.getDefSrc(annoStr); + } + + public String toString() { + return "@TypeAnno1(\"TypeAnno1\")"; + } + }, + ANNOTATION_TYPE_2("\n@Target(ElementType.TYPE_USE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\n@Repeatable(TypeAnno2Container.class)" + + "\n@interface TypeAnno2 {" + + "\n String value();" + + "\n}") { + public String getSrc(String... annoStr) { + return ANNOTATION_TYPE_2.getDefSrc(annoStr); + } + + public String toString() { + return "@TypeAnno2(\"TypeAnno2\")"; + } + }, + ANNOTATION_TYPE_3("\n@Target(ElementType.TYPE_USE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\n@Repeatable(TypeAnno3Container.class)" + + "\n@interface TypeAnno3 {" + + "\n}") { + public String getSrc(String... annoStr) { + return ANNOTATION_TYPE_3.getDefSrc(annoStr); + } + + public String toString() { + return "@TypeAnno3"; + } + }, + CONTAINING_ANNOTATION_TYPE_1("\n@Target(ElementType.TYPE_USE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\n@interface TypeAnno1Container {" + + "\n TypeAnno1[] value();" + + "\n}") { + public String getSrc(String... annoStr) { + return CONTAINING_ANNOTATION_TYPE_1.getDefSrc(annoStr); + } + }, + CONTAINING_ANNOTATION_TYPE_2("\n@Target(ElementType.TYPE_USE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\n@interface TypeAnno2Container {" + + "\n TypeAnno2[] value();" + + "\n}") { + public String getSrc(String... annoStr) { + return CONTAINING_ANNOTATION_TYPE_2.getDefSrc(annoStr); + } + }, + CONTAINING_ANNOTATION_TYPE_3("\n@Target(ElementType.TYPE_USE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\n@interface TypeAnno3Container {" + + "\n TypeAnno3[] value();" + + "\n}") { + public String getSrc(String... annoStr) { + return CONTAINING_ANNOTATION_TYPE_3.getDefSrc(annoStr); + } + }, + PACKAGE_ANNOTATION_1("\n@Target(ElementType.PACKAGE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\npublic @interface PkgAnno1 {" + + "\n String value();" + + "\n}") { + public String getSrc(String... annoStr) { + return PACKAGE_ANNOTATION_1.getDefSrc(annoStr); + } + + public String toString() { + return "@pkg.PkgAnno1(\"PkgAnno1\")"; + } + }, + PACKAGE_ANNOTATION_2("\n@Target(ElementType.PACKAGE)" + + "\n@Retention(RetentionPolicy.RUNTIME)" + + "\npublic @interface PkgAnno2 {" + + "\n String value();" + + "\n}") { + public String getSrc(String... annoStr) { + return PACKAGE_ANNOTATION_2.getDefSrc(annoStr); + } + + public String toString() { + return "@pkg.PkgAnno2(\"PkgAnno2\")"; + } + }; + String template = ""; + + private Declaration(String template) { + this.template = template; + } + + /** + * Gets the first annotation of the input string + * @param str a string headed by annotation(s) + * @return [first anno, the rest of the str] + */ + private static String[] getFirstAnno(String str) { + if (null == str) { + throw new RuntimeException("illegal input."); + } + + String[] ret = new String[2]; + str = str.trim(); + int i = str.indexOf("@"); + int j = str.indexOf(" "); + if (-1 == j) { + j = str.length(); + } + // annotation check - should be like @A or @A() or @A("...") + if (2 > j - i) { + throw new RuntimeException("illegal annotation."); + } + ret[0] = str.substring(i, j); + ret[1] = str.substring(j).trim(); + return ret; + } + + /** + * Change [#ANNO] #NAME into @annotation name + * @param annoStr @annotation + name to be extracted + * @return desired string with @annotation and name + */ + private String replaceMultipleAnnoStr(String... annoStr) { + if (0 == annoStr.length) { + return ""; + } + + String temp = template; + for (String as : annoStr) { + temp = temp.replaceAll("\\[\\[#ANNO\\] #NAME, \\]", + "[#ANNO] #NAME, [[#TMP_ANNO] #TMP_NAME, ]"); + // replace "[#ANNO]..." with "@realannotation [#ANNO]..." + int i = 0; // index of @ + int j = 0; // index of " " + for (; -1 != as.indexOf("@", j);) { + i = as.indexOf("@", j); + j = as.indexOf(" ", i); + // annotation check - should be like @A or @A() or @A("...") + if (2 > j - i) { + throw new RuntimeException("illegal annotation."); + } + String anno = as.substring(i, j); + temp = temp.replaceFirst("\\[#ANNO\\]", anno + " [#ANNO]"); + + } + String name = as.substring(j, as.length()).trim(); + temp = temp.replaceAll("#NAME", name).replaceAll( + "\\[#ANNO\\]", ""); + temp = temp.replaceAll("\\[\\[#TMP_ANNO\\] #TMP_NAME, \\]", + "[[#ANNO] #NAME, ]"); + } + return temp.replaceAll(", \\[\\[#ANNO\\] #NAME, \\]", ""); + } + + /** + * Extract annotations from input string and put them in + * declarations + * @param ss + * @param temp + * @param decl + * @param rep1 + * @param rep2 + * @return [the rest of the input string after stripping heading + * annotations, the rest of declaration after stripping [ANNO]] + */ + private static String[] replaceAnnoStr(String ss, String temp, + String rep1, String rep2) { + while (ss.startsWith("@")) { + String[] sa = getFirstAnno(ss); + String anno = sa[0]; + ss = sa[1].trim(); + temp = temp.replaceFirst(rep1, anno + rep2); + } + temp = temp.replaceAll(rep1, ""); + + return new String[] {ss, temp}; + } + + private String getDefSrc(String... annoStr) { + if (0 == annoStr.length) { + return ""; + } else { + return template; + } + } + + /** + * Get the template + * @return template + */ + public String getTemplate() { + return template; + } + + /** + * Get the desired source code declaration + * @param annoStr @annotation , type, name combinations + * @return desired source code declaration + */ + public String getSrc(String... annoStr) { + if (0 == annoStr.length || 1 == annoStr.length) { + return replaceMultipleAnnoStr(annoStr); + } else { + throw new RuntimeException( + "illegal input for declaration!"); + } + } + } + + // Compile a list of FileObjects + // Used when packages are needed and classes need to be loaded at runtime + static File destDir = new File(System.getProperty("user.dir")); + + /** + * Compile code + * @param diagnostics diagnostics of the compilation + * @param files files to compile + * @return true - successful compilation, false failed compilation + */ + public static boolean compileCode( + DiagnosticCollector diagnostics, + Iterable files) { + boolean ok = false; + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); + if (compiler == null) { + throw new RuntimeException("can't get javax.tools.JavaCompiler!"); + } + StandardJavaFileManager fm = compiler.getStandardFileManager( + null, null, null); + if (isPkgInfoPresent(files)) { + JavacTask task = (JavacTask) compiler.getTask(null, fm, diagnostics, + null, null, files); + try { + fm.setLocation(StandardLocation.CLASS_OUTPUT, + Arrays.asList(destDir)); + task.generate(); + } catch (IOException ioe) { + throw new RuntimeException( + "Compilation failed for package level tests", ioe); + } + ok = diagnostics.getDiagnostics().stream().anyMatch(d -> d.getKind() + == Diagnostic.Kind.ERROR); + } else { + CompilationTask task = compiler.getTask(null, null, diagnostics, + null, null, files); + ok = task.call(); + } + return ok; + } + + /** + * Check if package needs to be generated + * @param files + * @return true - yes, false - no + */ + static private boolean isPkgInfoPresent( + Iterable files) { + for (JavaFileObject jfo : files) { + String name = jfo.getName(); + if (name.contains("package-info") || name.contains("PkgAnno")) { + return true; + } + } + return false; + } + + static JavaFileObject getFile(String name, String code) throws Exception{ + return new JavaStringFileObject(name, code); + } + + static class JavaStringFileObject extends SimpleJavaFileObject { + + final String theCode; + public JavaStringFileObject(String fileName, String theCode) + throws URISyntaxException { + super(new URI("string:///" + fileName.replace('.', '/') + ".java"), + Kind.SOURCE); + this.theCode = theCode; + } + + @Override + public CharSequence getCharContent(boolean ignoreEncodingErrors) { + return theCode; + } + } + + private static URLClassLoader URLCL = null; + + private static URLClassLoader getClassLoader() throws Exception { + if (null != URLCL) + return URLCL; + + ClassLoader parentClassLoader = new Helper().getClass().getClassLoader(); + URLCL = getClassLoader(parentClassLoader, destDir); + return URLCL; + } + private static URLClassLoader getClassLoader(ClassLoader parentClassLoader, + File... destDirs) throws Exception { + List list = new ArrayList<>(); + for (File f : destDirs) { + list.add(new URL( + "file:" + f.toString().replace("\\", "/") + "/")); + } + return new URLClassLoader(list.toArray(new URL[list.size()]), + parentClassLoader); + } + + /** + * Load class to class loader + * @param className + * @return loaded class + * @throws Exception + */ + public static Class loadClass(String className) throws Exception { + try { + return Class.forName(className, true, getClassLoader()); + } catch (ClassNotFoundException | MalformedURLException e) { + throw new RuntimeException("Error loading class " + className, e); + } + } + + /** + * Generate class from test code + * @param testCode + * @param parentClassLoader + * @param clsName + * @throws Exception + */ + public static void genClass(String testCode, ClassLoader parentClassLoader, + String clsName) throws Exception { + Map lhm = new LinkedHashMap(); + lhm.put(clsName, testCode); + genClasses(parentClassLoader, lhm); + } + + /** + * Generate classes from test code + * @param parentClassLoader + * @param clsList class name - class code correspondences + * @throws Exception + */ + public static void genClasses(ClassLoader parentClassLoader, + Map clsList) throws Exception { + Iterable files = new ArrayList(); + for (String clsName : clsList.keySet()) { + String testCode = clsList.get(clsName); + ((List) files).add(getFile(clsName, testCode)); + + } + DiagnosticCollector diagnostics = + new DiagnosticCollector<>(); + + // Compile the list of JavaFileObjects + try { + Helper.compileCode(diagnostics, files); + } catch (Exception ex) { + throw new RuntimeException( + "Exception when compiling class "); + } + } + + /** + * Get annotations from annotation object + * @param annotation annotation object + * @return annotation string like @Anno("Anno") + * @throws Exception + */ + public static String getAnno(Annotation annotation) throws Exception { + if (null == annotation) { + return ""; + } + StringBuilder result = new StringBuilder(""); + String annoName = ""; + Object value = null; + try { + Method method = annotation.annotationType().getMethod("value", + new Class[]{}); + method.setAccessible(true); + value = method.invoke(annotation, new Object[]{}); + } catch (NoSuchMethodException e) { + //expected exception for annotations without value + } + + if (null == value) { + annoName = annotation.annotationType().getSimpleName(); + result.append("@" + annoName); + result.append(" "); + } else if (value instanceof String) { + annoName = annotation.annotationType().getSimpleName(); + result.append("@" + annoName); + result.append("(\"" + value + "\") "); + } else { + //for repeated anno + Annotation[] av = (Annotation[]) value; + for (int j = 0; j < av.length; j++) { + annoName = av[j].annotationType().getSimpleName(); + value = " "; + try { + Method method = av[j].annotationType().getMethod("value", + new Class[]{}); + method.setAccessible(true); + value = method.invoke(av[j], new Object[]{}); + value = "(\"" + value + "\") "; + } catch (NoSuchMethodException e) { + //expected exception for annotations without value + } + result.append("@" + annoName); + result.append(value); + } + } + return result.toString(); + } + + /** + * Get annotations from annotation object array + * @param annotations annotation objects + * @return annotation string like @Anno1("Anno1")@Anno2("Anno2") + * @throws Exception + */ + public static String getAnno(Annotation[] annotations) throws Exception { + StringBuilder result = new StringBuilder(""); + if (null != annotations) { + for (int i = 0; i < annotations.length; i++) { + String anno = getAnno(annotations[i]); + if (!"".equals(anno)) { + result.append(anno); + } + } + } + return result.toString(); + } + + /** + * Get AnnotatedType type and value, output as string + * @param at + * @return annotation string like @Anno1("Anno1")@Anno2("Anno2") + * @throws Exception + */ + public static String getAT(AnnotatedType at) throws Exception { + if (null == at) + System.out.println(23); + + Annotation[] annotations = at.getAnnotations(); + return getAnno(annotations); + } + + /** + * Get AnnotatedArrayType type and value, output as string + * @param at + * @return annotation string like @Anno1("Anno1")@Anno2("Anno2") + * @throws Exception + */ + public static String getArrAT(AnnotatedType at) throws Exception { + String ret = ""; + if (at instanceof AnnotatedArrayType) { + at = ((AnnotatedArrayType) at).getAnnotatedGenericComponentType(); + ret += getArrAT(at); + } + Annotation[] annotations = at.getAnnotations(); + return getAnno(annotations) + ";" + ret; + } + + /** + * Get multiple AnnotatedType type and value, output as string + * @param as + * @return list of annotations + * @throws Exception + */ + public static List getMultipleAT(AnnotatedType[] as) throws Exception { + List result = new ArrayList<>(); + for (int i = 0; i < as.length; i++) { + if (null != as[i]) { + result.add(getAT(as[i])); + } + } + + return result; + } + + /** + * Compare two string of annotations and check if they contain + * the same annotations + * @param anno1 + * @param anno2 + * @return true - yes, false - no + */ + public static boolean compareAnnoWithDiffSeq(String anno1, String anno2) { + String[] split1 = anno1.trim().split("\\s+"); + String[] split2 = anno2.trim().split("\\s+"); + Arrays.sort(split1); + Arrays.sort(split2); + return Arrays.equals(split1, split2); + } + + /** + * For debug use + * @param string + */ + public static void debugPrint(String string) { + if (DEBUG) { + System.out.println(string); + } + } + + /** + * For debug use + * @param string + */ + public static void debugPrintToFile(String string, String fileName) { + if (DEBUG) { + try { + String path = + System.getProperty("test.classes", ".") + + File.separator + fileName; + File dirFile = new File(path); + if (!(dirFile.exists()) && !(dirFile.isDirectory())) { + if (!dirFile.mkdirs()) { + throw new IOException(); + } + } + + FileOutputStream fos = + new FileOutputStream(path + File.separator + + "TypeAnnoClsBase.java"); + OutputStreamWriter osw = new OutputStreamWriter(fos); + BufferedWriter bw = new BufferedWriter(osw); + bw.write(string); + bw.close(); + } catch (IOException e) { + e.printStackTrace(); + System.out.println("Error output test source to file."); + } + } + } +}