Skip to content

Commit d67532c

Browse files
committed
An initial import of @autovalue
1 parent fa7058a commit d67532c

File tree

10 files changed

+1973
-0
lines changed

10 files changed

+1973
-0
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<module>compile-testing</module>
3636
<module>factory</module>
3737
<module>service</module>
38+
<module>value</module>
3839
</modules>
3940

4041
<properties>

value/pom.xml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!-- Copyright (C) 2012 Google, Inc. Copyright (C) 2012 Square, Inc. Licensed
3+
under the Apache License, Version 2.0 (the "License"); you may not use this
4+
file except in compliance with the License. You may obtain a copy of the
5+
License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by
6+
applicable law or agreed to in writing, software distributed under the License
7+
is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
8+
KIND, either express or implied. See the License for the specific language
9+
governing permissions and limitations under the License. -->
10+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
11+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
12+
<modelVersion>4.0.0</modelVersion>
13+
14+
<parent>
15+
<groupId>com.google.auto</groupId>
16+
<artifactId>auto-parent</artifactId>
17+
<version>1.0-SNAPSHOT</version>
18+
</parent>
19+
20+
<groupId>com.google.auto.value</groupId>
21+
<artifactId>auto-value</artifactId>
22+
<name>Auto-Value</name>
23+
<description>
24+
The Auto-Value code generator.
25+
</description>
26+
27+
<dependencies>
28+
<dependency>
29+
<groupId>junit</groupId>
30+
<artifactId>junit</artifactId>
31+
<version>4.10</version>
32+
<scope>test</scope>
33+
</dependency>
34+
<dependency>
35+
<groupId>com.google.auto.service</groupId>
36+
<artifactId>auto-service</artifactId>
37+
<version>1.0-SNAPSHOT</version>
38+
</dependency>
39+
<dependency>
40+
<groupId>org.ow2.asm</groupId>
41+
<artifactId>asm</artifactId>
42+
<version>4.1</version>
43+
</dependency>
44+
<dependency>
45+
<groupId>com.google.code.findbugs</groupId>
46+
<artifactId>jsr305</artifactId>
47+
<version>1.3.9</version>
48+
<scope>provided</scope>
49+
</dependency>
50+
<dependency>
51+
<groupId>com.google.testing.compile</groupId>
52+
<artifactId>compile-testing</artifactId>
53+
<version>1.0-SNAPSHOT</version>
54+
<scope>test</scope>
55+
</dependency>
56+
<dependency>
57+
<groupId>org.truth0</groupId>
58+
<artifactId>truth</artifactId>
59+
<version>0.11</version>
60+
<scope>test</scope>
61+
</dependency>
62+
<dependency>
63+
<groupId>com.google.guava</groupId>
64+
<artifactId>guava-testlib</artifactId>
65+
<version>14.0.1</version>
66+
<scope>test</scope>
67+
</dependency>
68+
</dependencies>
69+
70+
<build>
71+
<plugins>
72+
<plugin>
73+
<groupId>org.apache.maven.plugins</groupId>
74+
<artifactId>maven-compiler-plugin</artifactId>
75+
<configuration>
76+
<compilerArgument>-proc:none</compilerArgument>
77+
</configuration>
78+
</plugin>
79+
</plugins>
80+
</build>
81+
</project>
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
/*
2+
* Copyright (C) 2013 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.auto.value;
17+
18+
import java.util.ArrayDeque;
19+
import java.util.ArrayList;
20+
import java.util.Deque;
21+
import java.util.HashMap;
22+
import java.util.List;
23+
import java.util.Map;
24+
25+
/**
26+
* An ultrasimplified Java parser for {@link EclipseHack} that examines classes to extract just
27+
* the abstract methods. The parsing is very superficial. It assumes that the source text is
28+
* syntactically correct, which it must be in the context of an annotation processor because the
29+
* compiler doesn't invoke the processor if there are syntax errors.
30+
*
31+
* <p>We recognize the text {@code ... class Foo ... { ... } } as a class called Foo, whose
32+
* definition extends to the matching right brace. Within a class definition, we recognize the text
33+
* {@code abstract ... bar ( ) } as an abstract method called bar.
34+
*
35+
* <p>We construct a {@code Map<String, List<String>>} that represents the abstract methods found in
36+
* each class, in the order they were found. If com.example.Foo contains a nested class Bar, then
37+
* there will be an entry for "com.example.Foo.Bar" in this Map.
38+
*
39+
* @author emcmanus@google.com (Éamonn McManus)
40+
*/
41+
final class AbstractMethodExtractor {
42+
AbstractMethodExtractor() {}
43+
44+
// Here are the details of the matching. We track the current brace depth, and we artificially
45+
// consider that the whole file is at brace depth 1 inside a pseudo-class whose name is the
46+
// name of the package. When we see a class definition, we push the fully-qualified name of the
47+
// class on a stack so that we can associate abstract methods we find with the possibly-nested
48+
// class they belong to. A class definition must occur at brace depth one more than the class
49+
// containing it, which is equivalent to saying that the brace depth must be the same as the
50+
// class stack depth. This check excludes local class definitions within methods and
51+
// initializers. If we meet these constraints and we see the word "class" followed by an
52+
// identifier Foo, then we consider that we are entering the definition of class Foo. We determine
53+
// the fully-qualified name of Foo, which is container.Foo, where container is the current top of
54+
// the class stack (initially, the package name). We push this new fully-qualified name on the
55+
// class stack. We have not yet seen the left brace with the class definition so at this point the
56+
// class stack depth is one more than the brace depth. When we subsequently see a right brace that
57+
// takes us back to this situation then we know we have completed the definition of Foo and we can
58+
// pop it from the class stack.
59+
//
60+
// We check that the token after "class" is indeed an identifier to avoid confusion
61+
// with Foo.class. Even though the tokenizer does not distinguish between identifiers and
62+
// keywords, it is enough to exclude the single word "instanceof" because that is the only word
63+
// that can legally appear after Foo.class (though in a legal program the resultant expression
64+
// will always be true).
65+
//
66+
// Again, we are at the top level of a class when the brace depth is equal to the class stack
67+
// depth. If we then see the word "abstract" then that is the start either of an abstract class
68+
// definition or of an abstract method. We record that we have seen "abstract" and we cancel
69+
// that indication as soon as we see a left brace, to exclude the abstract class case, and also
70+
// the case of interfaces or @interfaces redundantly declared abstract. Now, when
71+
// we see an identifier that is preceded by an uncanceled "abstract" and followed by a left paren
72+
// then we have found an abstract method of the class on the top of the class stack. We record it
73+
// in the list of abstract methods of the class on the top of the class stack. We don't bother
74+
// checking that the method has no parameters, because an @AutoValue class will cause a compiler
75+
// error if there are abstract methods with parameters, since the @AutoValue processor doesn't
76+
// know how to implement them in the concrete subclass it generates.
77+
Map<String, List<String>> abstractMethods(JavaTokenizer tokenizer, String packageName) {
78+
Map<String, List<String>> abstractMethods = new HashMap<String, List<String>>();
79+
Deque<String> classStack = new ArrayDeque<String>();
80+
classStack.addLast(packageName);
81+
int braceDepth = 1;
82+
boolean sawAbstract = false;
83+
String className = null;
84+
for (String previousToken = "", token = tokenizer.nextToken();
85+
token != null;
86+
previousToken = token, token = tokenizer.nextToken()) {
87+
boolean topLevel = (braceDepth == classStack.size());
88+
if (className != null) {
89+
// get last term in fully-qualified class name (e.g. "class some.package.Bar { ...")
90+
if (token.equals(".")) {
91+
className = tokenizer.nextToken();
92+
continue;
93+
} else {
94+
if (Character.isJavaIdentifierStart(className.charAt(0))
95+
&& !className.equals("instanceof")) {
96+
String container = classStack.getLast();
97+
classStack.addLast(container + "." + className);
98+
}
99+
className = null;
100+
}
101+
}
102+
if (token.equals("{")) {
103+
braceDepth++;
104+
sawAbstract = false;
105+
} else if (token.equals("}")) {
106+
braceDepth--;
107+
if (topLevel) {
108+
classStack.removeLast();
109+
}
110+
} else if (topLevel) {
111+
if (token.equals("class")) {
112+
className = tokenizer.nextToken();
113+
} else if (token.equals("abstract")) {
114+
sawAbstract = true;
115+
} else if (token.equals("(")) {
116+
if (sawAbstract && Character.isJavaIdentifierStart(previousToken.charAt(0))) {
117+
List<String> methods = abstractMethods.get(classStack.getLast());
118+
if (methods == null) {
119+
methods = new ArrayList<String>();
120+
abstractMethods.put(classStack.getLast(), methods);
121+
}
122+
methods.add(previousToken);
123+
}
124+
sawAbstract = false;
125+
}
126+
}
127+
}
128+
return abstractMethods;
129+
}
130+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Copyright (C) 2013 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.auto.value;
17+
18+
import java.io.IOException;
19+
import java.io.InputStream;
20+
import java.lang.reflect.Modifier;
21+
import java.util.ArrayList;
22+
import java.util.List;
23+
24+
import org.objectweb.asm.ClassReader;
25+
import org.objectweb.asm.ClassVisitor;
26+
import org.objectweb.asm.MethodVisitor;
27+
import org.objectweb.asm.Opcodes;
28+
29+
/**
30+
* A class file parser that lists the no-arg abstract methods in a class.
31+
*
32+
* @author Éamonn McManus
33+
*/
34+
class AbstractMethodLister {
35+
private final InputStream inputStream;
36+
37+
AbstractMethodLister(InputStream inputStream) {
38+
this.inputStream = inputStream;
39+
}
40+
41+
List<String> abstractNoArgMethods() {
42+
try {
43+
return abstractNoArgMethodsX();
44+
} catch (IOException e) {
45+
throw new RuntimeException(e);
46+
}
47+
}
48+
49+
private List<String> abstractNoArgMethodsX() throws IOException {
50+
ClassReader classReader = new ClassReader(inputStream);
51+
RecordingClassVisitor classVisitor = new RecordingClassVisitor();
52+
classReader.accept(classVisitor, 0);
53+
return classVisitor.abstractNoArgMethods;
54+
}
55+
56+
private static class RecordingClassVisitor extends ClassVisitor {
57+
private final List<String> abstractNoArgMethods = new ArrayList<String>();
58+
59+
RecordingClassVisitor() {
60+
super(Opcodes.ASM4);
61+
}
62+
63+
@Override
64+
public MethodVisitor visitMethod(
65+
int access, String name, String desc, String signature, String[] exceptions) {
66+
// The class-file method descriptor desc is a string that will contain "()" only if the
67+
// method has no arguments, and will end with "V" (actually "()V") only if the method
68+
// is void.
69+
if (Modifier.isAbstract(access) && desc.contains("()") && !desc.endsWith("V")) {
70+
abstractNoArgMethods.add(name);
71+
}
72+
return super.visitMethod(access, name, desc, signature, exceptions);
73+
}
74+
}
75+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (C) 2012 Google, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.auto.value;
17+
18+
import java.lang.annotation.ElementType;
19+
import java.lang.annotation.Retention;
20+
import java.lang.annotation.RetentionPolicy;
21+
import java.lang.annotation.Target;
22+
23+
/**
24+
* Annotation indicating that the annotated type is a value type with an automatically-generated
25+
* implementation.
26+
*
27+
* <p>Here is a simple example of a value type using {@code @AutoValue}:
28+
*
29+
* <pre>
30+
* {@code @AutoValue}
31+
* {@code public abstract class Contact {
32+
* public abstract String name();
33+
* public abstract List<String> phoneNumbers();
34+
* public abstract int sortOrder();
35+
*
36+
* public static Contact create(String name, List<String> phoneNumbers, int sortOrder) {
37+
* return new AutoValue_Contact(name, phoneNumbers, sortOrder);
38+
* }
39+
* }}</pre>
40+
*
41+
* <p>The generated subclass is called {@code AutoValue_Contact} but users of {@code Contact} do not
42+
* need to know that. Only the implementation of the {@code create} method does.
43+
*
44+
* <p>As an alternative to instantiating the generated subclass in the {@code create} method, you
45+
* can define a {@code Factory} interface like this:</p>
46+
*
47+
* <pre>
48+
* {@code @AutoValue}
49+
* {@code public abstract class Contact {
50+
* public abstract String name();
51+
* public abstract List<String> phoneNumbers();
52+
* public abstract int sortOrder();
53+
*
54+
* public static Contact create(String name, List<String> phoneNumbers, int sortOrder) {
55+
* return AutoValues.using(Factory.class).create(name, phoneNumbers, sortOrder);
56+
* }
57+
*
58+
* interface Factory {
59+
* Contact create(String name, List<String> phoneNumbers, int sortOrder);
60+
* }
61+
* }}</pre>
62+
*
63+
* @author Éamonn McManus
64+
*/
65+
/*
66+
* TODO(gak): We should obviously try to figure out whether this whole factory mechanism can be
67+
* replaced with @AutoFactory
68+
*/
69+
@Retention(RetentionPolicy.SOURCE)
70+
@Target(ElementType.TYPE)
71+
public @interface AutoValue {
72+
}

0 commit comments

Comments
 (0)