Skip to content
Commits on Source (2)
......@@ -3,12 +3,12 @@
<parent>
<groupId>net.java.sezpoz.demo</groupId>
<artifactId>pom</artifactId>
<version>1.10</version>
<version>1.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api</artifactId>
<packaging>jar</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz Demo API</name>
<dependencies>
<dependency>
......
......@@ -3,12 +3,12 @@
<parent>
<groupId>net.java.sezpoz.demo</groupId>
<artifactId>pom</artifactId>
<version>1.10</version>
<version>1.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>app</artifactId>
<packaging>jar</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz Demo Application</name>
<dependencies>
<dependency>
......
......@@ -3,12 +3,12 @@
<parent>
<groupId>net.java.sezpoz.demo</groupId>
<artifactId>pom</artifactId>
<version>1.10</version>
<version>1.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>plugin1</artifactId>
<packaging>jar</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz Demo Plugin #1</name>
<dependencies>
<dependency>
......
......@@ -3,12 +3,12 @@
<parent>
<groupId>net.java.sezpoz.demo</groupId>
<artifactId>pom</artifactId>
<version>1.10</version>
<version>1.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>plugin2</artifactId>
<packaging>jar</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz Demo Plugin #2</name>
<dependencies>
<dependency>
......
......@@ -3,13 +3,13 @@
<parent>
<groupId>net.java.sezpoz</groupId>
<artifactId>pom</artifactId>
<version>1.10</version>
<version>1.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>net.java.sezpoz.demo</groupId>
<artifactId>pom</artifactId>
<packaging>pom</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz Demo</name>
<build>
<plugins>
......
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<prerequisites>
<maven>3.0</maven>
</prerequisites>
<parent>
<groupId>org.sonatype.oss</groupId>
<artifactId>oss-parent</artifactId>
......@@ -9,7 +12,7 @@
<groupId>net.java.sezpoz</groupId>
<artifactId>pom</artifactId>
<packaging>pom</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz</name>
<url>https://github.com/jglick/sezpoz</url>
<description>SezPoz is a lightweight and simple-to-learn library that lets you perform modular service lookups.</description>
......@@ -24,7 +27,7 @@
<connection>scm:git:git@github.com:jglick/sezpoz.git</connection>
<developerConnection>scm:git:git@github.com:jglick/sezpoz.git</developerConnection>
<url>git@github.com:jglick/sezpoz.git</url>
<tag>pom-1.10</tag>
<tag>pom-1.12</tag>
</scm>
<licenses>
<license>
......@@ -37,7 +40,7 @@
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<version>3.5.1</version>
<configuration>
<source>1.6</source>
<target>1.6</target>
......@@ -45,40 +48,44 @@
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<version>2.6</version>
</plugin>
<plugin>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.7</version>
<version>2.10.3</version>
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>2.5</version>
<version>2.7</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.8</version>
<version>2.19.1</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>2.4.1</version>
<version>3.0.0</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.5</version>
<version>2.8.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.3.1</version>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.0-beta-3</version>
<version>3.5.1</version>
</plugin>
<plugin>
<artifactId>maven-release-plugin</artifactId>
<version>2.5.3</version>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2</version>
<version>1.5.0</version>
</plugin>
</plugins>
</pluginManagement>
......@@ -92,7 +99,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>versions-maven-plugin</artifactId>
<version>1.2</version>
<version>2.2</version>
</plugin>
</plugins>
</reporting>
......
......@@ -3,26 +3,20 @@
<parent>
<groupId>net.java.sezpoz</groupId>
<artifactId>pom</artifactId>
<version>1.10</version>
<version>1.12</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>sezpoz</artifactId>
<packaging>jar</packaging>
<version>1.10</version>
<version>1.12</version>
<name>SezPoz Library</name>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-annotations</artifactId>
<version>1.9</version>
<type>jar</type>
</dependency>
</dependencies>
<build>
<plugins>
......@@ -36,7 +30,7 @@
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>animal-sniffer-maven-plugin</artifactId>
<version>1.9</version>
<version>1.15</version>
<executions>
<execution>
<phase>package</phase>
......@@ -63,6 +57,17 @@
<forkMode>pertest</forkMode>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<mainClass>net.java.sezpoz.impl.Inspector</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
</plugins>
</build>
<reporting>
......
......@@ -45,6 +45,7 @@ import java.util.NoSuchElementException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.java.sezpoz.impl.Indexer;
import net.java.sezpoz.impl.SerAnnotatedElement;
/**
......@@ -139,7 +140,7 @@ public final class Index<A extends Annotation,I> implements Iterable<IndexItem<A
}
if (ois == null) {
if (resources == null) {
resources = loader.getResources("META-INF/annotations/" + annotation.getName());
resources = loader.getResources(Indexer.METAINF_ANNOTATIONS + annotation.getName());
}
if (!resources.hasMoreElements()) {
// Exhausted all streams.
......
......@@ -38,6 +38,7 @@ import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
......@@ -54,7 +55,6 @@ import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedOptions;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.AnnotationValue;
......@@ -70,19 +70,18 @@ import javax.tools.Diagnostic.Kind;
import javax.tools.FileObject;
import javax.tools.StandardLocation;
import net.java.sezpoz.Indexable;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
/**
* Processor for indexable annotations.
*/
@IgnoreJRERequirement
@SupportedSourceVersion(SourceVersion.RELEASE_6)
@SupportedAnnotationTypes("*")
@SupportedOptions("sezpoz.quiet")
public class Indexer6 extends AbstractProcessor {
public class Indexer extends AbstractProcessor {
public static final String METAINF_ANNOTATIONS = "META-INF/annotations/";
/** public for ServiceLoader */
public Indexer6() {}
public Indexer() {}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
......@@ -102,15 +101,20 @@ public class Indexer6 extends AbstractProcessor {
}
}
// map from indexable annotation names, to actual uses
Map<String,List<SerAnnotatedElement>> output = new HashMap<String,List<SerAnnotatedElement>>();
Map<String,Map<String,SerAnnotatedElement>> output = new TreeMap<String,Map<String,SerAnnotatedElement>>();
Map<String,Collection<Element>> originatingElementsByAnn = new HashMap<String,Collection<Element>>();
scan(annotations, originatingElementsByAnn, roundEnv, output);
write(output, originatingElementsByAnn);
return false;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latest();
}
private void scan(Set<? extends TypeElement> annotations, Map<String,Collection<Element>> originatingElementsByAnn,
RoundEnvironment roundEnv, Map<String,List<SerAnnotatedElement>> output) {
RoundEnvironment roundEnv, Map<String,Map<String,SerAnnotatedElement>> output) {
for (TypeElement ann : annotations) {
AnnotationMirror indexable = null;
for (AnnotationMirror _indexable : processingEnv.getElementUtils().getAllAnnotationMirrors(ann)) {
......@@ -146,9 +150,9 @@ public class Indexer6 extends AbstractProcessor {
continue;
}
originatingElements.add(elt);
List<SerAnnotatedElement> existingOutput = output.get(annName);
Map<String,SerAnnotatedElement> existingOutput = output.get(annName);
if (existingOutput == null) {
existingOutput = new ArrayList<SerAnnotatedElement>();
existingOutput = new TreeMap<String,SerAnnotatedElement>();
output.put(annName, existingOutput);
}
SerAnnotatedElement ser = makeSerAnnotatedElement(elt, ann);
......@@ -157,18 +161,18 @@ public class Indexer6 extends AbstractProcessor {
(ser.memberName != null ? "." + ser.memberName : "") +
" indexed under " + annName.replace('$', '.'));
}
existingOutput.add(ser);
existingOutput.put(ser.key(), ser);
}
}
}
private void write(Map<String,List<SerAnnotatedElement>> output, Map<String,Collection<Element>> originatingElementsByAnn) {
for (Map.Entry<String,List<SerAnnotatedElement>> outputEntry : output.entrySet()) {
private void write(Map<String,Map<String,SerAnnotatedElement>> output, Map<String,Collection<Element>> originatingElementsByAnn) {
for (Map.Entry<String,Map<String,SerAnnotatedElement>> outputEntry : output.entrySet()) {
String annName = outputEntry.getKey();
try {
List<SerAnnotatedElement> elements = outputEntry.getValue();
Map<String,SerAnnotatedElement> elements = outputEntry.getValue();
try {
FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", "META-INF/annotations/" + annName);
FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", METAINF_ANNOTATIONS + annName);
// Read existing annotations, for incremental compilation.
InputStream is = in.openInputStream();
try {
......@@ -183,7 +187,9 @@ public class Indexer6 extends AbstractProcessor {
if (el == null) {
break;
}
elements.add(el);
if (!elements.containsKey(el.key())) {
elements.put(el.key(), el);
}
}
} finally {
is.close();
......@@ -192,12 +198,12 @@ public class Indexer6 extends AbstractProcessor {
// OK, created for the first time
}
FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT,
"", "META-INF/annotations/" + annName,
"", METAINF_ANNOTATIONS + annName,
originatingElementsByAnn.get(annName).toArray(new Element[0]));
OutputStream os = out.openOutputStream();
try {
ObjectOutputStream oos = new ObjectOutputStream(os);
for (SerAnnotatedElement el : elements) {
for (SerAnnotatedElement el : elements.values()) {
oos.writeObject(el);
}
oos.writeObject(null);
......@@ -205,6 +211,19 @@ public class Indexer6 extends AbstractProcessor {
} finally {
os.close();
}
out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT,
"", METAINF_ANNOTATIONS + annName + ".txt",
originatingElementsByAnn.get(annName).toArray(new Element[0]));
Writer w = out.openWriter();
try {
w.write("# informational; use java -jar sezpoz.jar to see authoritative contents\n");
for (SerAnnotatedElement el : elements.values()) {
w.write(el.toString());
w.write('\n');
}
} finally {
w.close();
}
} catch (IOException x) {
processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
}
......
package net.java.sezpoz.impl;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/** CLI utility for inspecting binary SezPoz metadata. */
public class Inspector {
private static final byte[] ZIP_MAGIC = {0x50, 0x4b, 0x03, 0x04};
private static final byte[] SER_MAGIC = {(byte) 0xac, (byte) 0xed, 0x00, 0x05};
public static void main(String[] args) throws Exception {
if (args.length == 0) {
System.err.println("Usage: java -jar sezpoz.jar [ something.jar | some.serialized.Annotation ]+");
}
for (String arg : args) {
System.out.println("--- " + arg);
byte[] magic = new byte[4];
InputStream is = new FileInputStream(arg);
try {
is.read(magic, 0, 4);
} finally {
is.close();
}
if (Arrays.equals(magic, ZIP_MAGIC)) {
JarFile jf = new JarFile(arg, false);
try {
Enumeration<JarEntry> entries = jf.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String name = entry.getName();
if (name.startsWith(Indexer.METAINF_ANNOTATIONS)) {
String annotation = name.substring(Indexer.METAINF_ANNOTATIONS.length());
if (annotation.isEmpty() || annotation.endsWith(".txt")) {
continue;
}
System.out.println("# " + annotation);
is = jf.getInputStream(entry);
try {
is.read(magic, 0, 4);
} finally {
is.close();
}
if ((Arrays.equals(magic, SER_MAGIC))) {
is = jf.getInputStream(entry);
try {
dump(is);
} finally {
is.close();
}
} else {
System.err.println("does not look like a Java serialized file");
}
}
}
} finally {
jf.close();
}
} else if (Arrays.equals(magic, SER_MAGIC)) {
is = new FileInputStream(arg);
try {
dump(is);
} finally {
is.close();
}
} else {
System.err.println("does not look like either a JAR file or a Java serialized file");
}
}
}
private static void dump(InputStream is) throws Exception {
ObjectInput oi = new ObjectInputStream(is);
while (true) {
SerAnnotatedElement el = (SerAnnotatedElement) oi.readObject();
if (el == null) {
break;
}
System.out.println(el);
}
}
}
......@@ -44,7 +44,7 @@ public final class SerAnnConst implements Serializable {
/** fully qualified name of annotation type */
public final String name;
/** values of annotation attrs, as in {@link #SerAnnotatedElement} */
/** values of annotation attrs, as in {@link SerAnnotatedElement} */
public final TreeMap<String,Object> values;
SerAnnConst(String name, TreeMap<String,Object> values) {
......
......@@ -40,7 +40,7 @@ import java.util.TreeMap;
* One META-INF/annotations/* file is a sequence of serialized SerAnnotatedElement
* instances, terminated by a null.
*/
public final class SerAnnotatedElement implements Serializable {
public final class SerAnnotatedElement implements Serializable, Comparable<SerAnnotatedElement> {
private static final long serialVersionUID = 1L;
......@@ -76,6 +76,13 @@ public final class SerAnnotatedElement implements Serializable {
}
public String toString() {
return values.isEmpty() ? key() : key() + values;
}
/**
* Key based on the annotated element, ignoring annotation values.
*/
public String key() {
StringBuffer b = new StringBuffer(className);
if (memberName != null) {
b.append('#');
......@@ -84,8 +91,11 @@ public final class SerAnnotatedElement implements Serializable {
b.append("()");
}
}
b.append(values);
return b.toString();
}
@Override public int compareTo(SerAnnotatedElement o) {
return key().compareTo(o.key());
}
}
......@@ -36,14 +36,233 @@ import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.TreeSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
/**
* Test for JDK 6 (JSR 199) version of indexer.
public class IndexerTest {
protected File dir, src, clz;
@Before public void setUp() throws Exception {
dir = TestUtils.getWorkDir(this);
TestUtils.clearDir(dir);
src = new File(dir, "src");
clz = new File(dir, "clz");
}
@Test public void basicUsage() throws Exception {
File src1 = new File(dir, "src1");
TestUtils.makeSource(src1, "api.MenuItem",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable(type=javax.swing.Action.class)",
"public @interface MenuItem {",
"String menuName();",
"String itemName();",
"String iconPath() default \"\";",
"}");
File clz1 = new File(dir, "clz1");
TestUtils.runApt(src1, null, clz1, null, null);
File src2 = new File(dir, "src2");
TestUtils.makeSource(src2, "impl.PrintAction",
"@api.MenuItem(menuName=\"File\", itemName=\"Print\", iconPath=\".../print.png\")",
"@Deprecated", // make sure this is ignored!
"public class PrintAction extends javax.swing.AbstractAction {",
"public void actionPerformed(java.awt.event.ActionEvent e) {}",
"}");
File clz2 = new File(dir, "clz2");
TestUtils.runApt(src2, null, clz2, new File[] {clz1}, null);
assertEquals(Collections.singletonMap("api.MenuItem", Collections.singletonList(
"impl.PrintAction{iconPath=.../print.png, itemName=Print, menuName=File}"
)), TestUtils.findMetadata(clz2));
}
@Test public void strangeAnnStringVals() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"String x();",
"}");
TestUtils.makeSource(src, "y.C",
"@x.A(x=\"foo\\\\\\\"\\n\")",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Collections.singletonList(
"y.C{x=foo\\\"\n}"
)), TestUtils.findMetadata(clz));
}
@Test public void compositeVals() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"Other other();",
"Other[] others();",
"int i();",
"boolean b();",
"char c();",
"}");
TestUtils.makeSource(src, "x.Other",
"@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)",
"public @interface Other {",
"int v();",
"}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"@A(other=@Other(v=1),others=@Other(v=2),i=3,b=true,c='x')",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Collections.singletonList(
"y.C{b=true, c=x, i=3, other=@x.Other{v=1}, others=[@x.Other{v=2}]}"
)), TestUtils.findMetadata(clz));
}
@Test public void defaultValsNotWritten() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"int i() default 0;",
"}");
TestUtils.makeSource(src, "x.B",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface B {",
"A[] as() default {};",
"}");
TestUtils.makeSource(src, "y.C1",
"@x.A(i=33)",
"public class C1 {}");
TestUtils.makeSource(src, "y.C2",
"@x.A()",
"public class C2 {}");
TestUtils.makeSource(src, "y.C3",
"@x.A",
"public class C3 {}");
TestUtils.makeSource(src, "y.C4",
"@x.B",
"public class C4 {}");
TestUtils.makeSource(src, "y.C5",
"import x.*;",
"@B(as={@A, @A(), @A(i=20)})",
"public class C5 {}");
TestUtils.runApt(src, null, clz, null, null);
Map<String,List<String>> expected = new HashMap<String,List<String>>();
expected.put("x.A", Arrays.asList(
"y.C1{i=33}",
"y.C2",
"y.C3"
));
expected.put("x.B", Arrays.asList(
"y.C4",
"y.C5{as=[@x.A{}, @x.A{}, @x.A{i=20}]}"
));
assertEquals(expected, TestUtils.findMetadata(clz));
}
@Test public void serializeExoticConstants() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"Class clazz();",
"E[] enoom();",
"}");
TestUtils.makeSource(src, "x.E",
"public enum E {ONE, TWO}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"@A(clazz=String.class,enoom={E.ONE,E.TWO})",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Collections.singletonList(
"y.C{clazz=java.lang.String, enoom=[ONE, TWO]}"
)), TestUtils.findMetadata(clz));
}
@Test public void methodsAndFields() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"boolean b();",
"}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"public class C {",
"@A(b=true)",
"public static final Object F = null;",
"@A(b=false)",
"public static Object m() {return null;}",
"}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Arrays.asList(
"y.C#F{b=true}",
"y.C#m(){b=false}"
)), TestUtils.findMetadata(clz));
}
@Test public void nestedClasses() throws Exception {
TestUtils.makeSource(src, "x.Outer",
"import java.lang.annotation.*;",
"public class Outer {",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@net.java.sezpoz.Indexable",
"@Retention(RetentionPolicy.SOURCE)",
"public @interface A {",
"Class<?> type();",
"}",
"}");
TestUtils.makeSource(src, "y.Auter",
"public class Auter {",
"public static class T {}",
"@x.Outer.A(type=T.class)",
"public static class C {}",
"}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.Outer$A", Collections.singletonList(
"y.Auter$C{type=y.Auter$T}"
)), TestUtils.findMetadata(clz));
}
/* Uncompilable anyway: "annotation value must be a class literal"
@Test public void exoticClassConstants() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"Class[] clazz();",
"}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"@A(clazz={Integer.class, Integer.TYPE})",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null, useJsr199());
assertEquals(Collections.singletonMap("x.A", Collections.singleton(
"y.C{clazz=[java.lang.Integer, int]}"
)), TestUtils.findMetadata(clz));
}
*/
public class Indexer6Test extends IndexerTestBase {
@Test public void incrementalCompilation() throws Exception {
File src1 = new File(dir, "src1");
......@@ -63,17 +282,25 @@ public class Indexer6Test extends IndexerTestBase {
"public class Impl1 {}");
File clz2 = new File(dir, "clz2");
TestUtils.runApt(src2, null, clz2, new File[] {clz1}, null);
assertEquals(Collections.singletonMap("Thing", Collections.singleton(
assertEquals(Collections.singletonMap("Thing", Collections.singletonList(
"Impl1{name=one}"
)), TestUtils.findMetadata(clz2));
TestUtils.makeSource(src2, "Impl2",
"@Thing(name=\"two\")",
"public class Impl2 {}");
TestUtils.runApt(src2, "Impl2", clz2, new File[] {clz1}, null);
assertEquals(Collections.singletonMap("Thing", new TreeSet<String>(Arrays.asList(
assertEquals(Collections.singletonMap("Thing", Arrays.asList(
"Impl1{name=one}",
"Impl2{name=two}"
))), TestUtils.findMetadata(clz2));
)), TestUtils.findMetadata(clz2));
TestUtils.makeSource(src2, "Impl2",
"@Thing(name=\"Two\")",
"public class Impl2 {}");
TestUtils.runApt(src2, null, clz2, new File[] {clz1}, null);
assertEquals(Collections.singletonMap("Thing", Arrays.asList(
"Impl1{name=one}",
"Impl2{name=Two}"
)), TestUtils.findMetadata(clz2));
}
// XXX the following should be moved to IndexerTestBase when Indexer5 implements these things:
......
/*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License ("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file. This particular file is subject to the "Classpath"
* exception as provided in the GPL Version 2 section of the License file
* that accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* The Original Software is SezPoz. The Initial Developer of the Original
* Software is Sun Microsystems, Inc. Copyright 2006-2011 Oracle
* Corporation. All Rights Reserved.
*
* If you wish your version of this file to be governed by only the CDDL
* or only the GPL Version 2, indicate your decision by adding
* "[Contributor] elects to include this software in this distribution
* under the [CDDL or GPL Version 2] license." If you do not indicate a
* single choice of license, a recipient has the option to distribute
* your version of this file under either the CDDL, the GPL Version 2 or
* to extend the choice of license to its licensees as provided above.
* However, if you add GPL Version 2 code and therefore, elected the GPL
* Version 2 license, then the option applies only if the new code is
* made subject to such option by the copyright holder.
*/
package net.java.sezpoz.impl;
import java.io.File;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
/**
* Tries to run indexer and confirms that it serializes annotations correctly.
*/
public abstract class IndexerTestBase {
protected File dir, src, clz;
@Before public void setUp() throws Exception {
dir = TestUtils.getWorkDir(this);
TestUtils.clearDir(dir);
src = new File(dir, "src");
clz = new File(dir, "clz");
}
@Test public void basicUsage() throws Exception {
File src1 = new File(dir, "src1");
TestUtils.makeSource(src1, "api.MenuItem",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable(type=javax.swing.Action.class)",
"public @interface MenuItem {",
"String menuName();",
"String itemName();",
"String iconPath() default \"\";",
"}");
File clz1 = new File(dir, "clz1");
TestUtils.runApt(src1, null, clz1, null, null);
File src2 = new File(dir, "src2");
TestUtils.makeSource(src2, "impl.PrintAction",
"@api.MenuItem(menuName=\"File\", itemName=\"Print\", iconPath=\".../print.png\")",
"@Deprecated", // make sure this is ignored!
"public class PrintAction extends javax.swing.AbstractAction {",
"public void actionPerformed(java.awt.event.ActionEvent e) {}",
"}");
File clz2 = new File(dir, "clz2");
TestUtils.runApt(src2, null, clz2, new File[] {clz1}, null);
assertEquals(Collections.singletonMap("api.MenuItem", Collections.singleton(
"impl.PrintAction{iconPath=.../print.png, itemName=Print, menuName=File}"
)), TestUtils.findMetadata(clz2));
}
@Test public void strangeAnnStringVals() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"String x();",
"}");
TestUtils.makeSource(src, "y.C",
"@x.A(x=\"foo\\\\\\\"\\n\")",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Collections.singleton(
"y.C{x=foo\\\"\n}"
)), TestUtils.findMetadata(clz));
}
@Test public void compositeVals() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"Other other();",
"Other[] others();",
"int i();",
"boolean b();",
"char c();",
"}");
TestUtils.makeSource(src, "x.Other",
"@java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.RUNTIME)",
"public @interface Other {",
"int v();",
"}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"@A(other=@Other(v=1),others=@Other(v=2),i=3,b=true,c='x')",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Collections.singleton(
"y.C{b=true, c=x, i=3, other=@x.Other{v=1}, others=[@x.Other{v=2}]}"
)), TestUtils.findMetadata(clz));
}
@Test public void defaultValsNotWritten() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"int i() default 0;",
"}");
TestUtils.makeSource(src, "x.B",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface B {",
"A[] as() default {};",
"}");
TestUtils.makeSource(src, "y.C1",
"@x.A(i=33)",
"public class C1 {}");
TestUtils.makeSource(src, "y.C2",
"@x.A()",
"public class C2 {}");
TestUtils.makeSource(src, "y.C3",
"@x.A",
"public class C3 {}");
TestUtils.makeSource(src, "y.C4",
"@x.B",
"public class C4 {}");
TestUtils.makeSource(src, "y.C5",
"import x.*;",
"@B(as={@A, @A(), @A(i=20)})",
"public class C5 {}");
TestUtils.runApt(src, null, clz, null, null);
Map<String,Set<String>> expected = new HashMap<String,Set<String>>();
expected.put("x.A", new TreeSet<String>(Arrays.asList(new String[] {
"y.C1{i=33}",
"y.C2{}",
"y.C3{}",
})));
expected.put("x.B", new TreeSet<String>(Arrays.asList(new String[] {
"y.C4{}",
"y.C5{as=[@x.A{}, @x.A{}, @x.A{i=20}]}"
})));
assertEquals(expected, TestUtils.findMetadata(clz));
}
@Test public void serializeExoticConstants() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"Class clazz();",
"E[] enoom();",
"}");
TestUtils.makeSource(src, "x.E",
"public enum E {ONE, TWO}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"@A(clazz=String.class,enoom={E.ONE,E.TWO})",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", Collections.singleton(
"y.C{clazz=java.lang.String, enoom=[ONE, TWO]}"
)), TestUtils.findMetadata(clz));
}
@Test public void methodsAndFields() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"boolean b();",
"}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"public class C {",
"@A(b=true)",
"public static final Object F = null;",
"@A(b=false)",
"public static Object m() {return null;}",
"}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.A", new TreeSet<String>(Arrays.asList(new String[] {
"y.C#F{b=true}",
"y.C#m(){b=false}",
}))), TestUtils.findMetadata(clz));
}
@Test public void nestedClasses() throws Exception {
TestUtils.makeSource(src, "x.Outer",
"import java.lang.annotation.*;",
"public class Outer {",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@net.java.sezpoz.Indexable",
"@Retention(RetentionPolicy.SOURCE)",
"public @interface A {",
"Class<?> type();",
"}",
"}");
TestUtils.makeSource(src, "y.Auter",
"public class Auter {",
"public static class T {}",
"@x.Outer.A(type=T.class)",
"public static class C {}",
"}");
TestUtils.runApt(src, null, clz, null, null);
assertEquals(Collections.singletonMap("x.Outer$A", Collections.singleton(
"y.Auter$C{type=y.Auter$T}"
)), TestUtils.findMetadata(clz));
}
/* Uncompilable anyway: "annotation value must be a class literal"
@Test public void exoticClassConstants() throws Exception {
TestUtils.makeSource(src, "x.A",
"import java.lang.annotation.*;",
"@Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})",
"@Retention(RetentionPolicy.SOURCE)",
"@net.java.sezpoz.Indexable",
"public @interface A {",
"Class[] clazz();",
"}");
TestUtils.makeSource(src, "y.C",
"import x.*;",
"@A(clazz={Integer.class, Integer.TYPE})",
"public class C {}");
TestUtils.runApt(src, null, clz, null, null, useJsr199());
assertEquals(Collections.singletonMap("x.A", Collections.singleton(
"y.C{clazz=[java.lang.Integer, int]}"
)), TestUtils.findMetadata(clz));
}
*/
}
......@@ -184,20 +184,23 @@ public class TestUtils {
/**
* Find contents of META-INF/annotations/* in a dest dir.
* @return map from simple file names to set of SerAnnotatedElement.toString()s
* @return map from simple file names to list of {@link SerAnnotatedElement#toString}s
*/
public static Map<String,SortedSet<String>> findMetadata(File dest) throws Exception {
public static Map<String,List<String>> findMetadata(File dest) throws Exception {
File dir = new File(new File(dest, "META-INF"), "annotations");
if (!dir.isDirectory()) {
return Collections.emptyMap();
}
Map<String,SortedSet<String>> metadata = new HashMap<String,SortedSet<String>>();
Map<String,List<String>> metadata = new HashMap<String,List<String>>();
for (String kid : dir.list()) {
if (kid.endsWith(".txt")) {
continue;
}
File f = new File(dir, kid);
InputStream is = new FileInputStream(f);
try {
ObjectInputStream ois = new ObjectInputStream(is);
SortedSet<String> entries = new TreeSet<String>();
List<String> entries = new ArrayList<String>();
while (true) {
SerAnnotatedElement el = (SerAnnotatedElement) ois.readObject();
if (el == null) {
......