| ## Copyright 2014 Google LLC |
| ## |
| ## Licensed under the Apache License, Version 2.0 (the "License"); |
| ## you may not use this file except in compliance with the License. |
| ## You may obtain a copy of the License at |
| ## |
| ## http://www.apache.org/licenses/LICENSE-2.0 |
| ## |
| ## Unless required by applicable law or agreed to in writing, software |
| ## distributed under the License is distributed on an "AS IS" BASIS, |
| ## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ## See the License for the specific language governing permissions and |
| ## limitations under the License. |
| |
| ## Template for each generated AutoAnnotation_Foo_bar class. |
| ## This template uses the Apache Velocity Template Language (VTL). |
| ## The variables ($pkg, $props, and so on) are defined by the fields of AutoAnnotationTemplateVars. |
| ## |
| ## Comments, like this one, begin with ##. The comment text extends up to and including the newline |
| ## character at the end of the line. So comments also serve to join a line to the next one. |
| ## Velocity deletes a newline after a directive (#if, #foreach, #end etc) so ## is not needed there. |
| ## That does mean that we sometimes need an extra blank line after such a directive. |
| ## |
| ## Post-processing will remove unwanted spaces and blank lines, but will not join two lines. |
| ## It will also replace classes spelled as (e.g.) `java.util.Arrays`, with the backquotes, to |
| ## use just Arrays if that class can be imported unambiguously, or java.util.Arrays if not. |
| |
| #macro (cloneArray $a) |
| #if ($gwtCompatible) |
| `java.util.Arrays`.copyOf($a, ${a}.length) |
| #else |
| ${a}.clone() |
| #end |
| #end |
| |
| #if (!$pkg.empty) |
| package $pkg; |
| #end |
| |
| ## The following line will be replaced by the required imports during post-processing. |
| `import` |
| |
| #if (!$generated.empty) |
| @${generated}("com.google.auto.value.processor.AutoAnnotationProcessor") |
| #else |
| // Generated by com.google.auto.value.processor.AutoAnnotationProcessor |
| #end |
| final class $className implements $annotationName, `java.io.Serializable` { |
| private static final long serialVersionUID = ${serialVersionUID}L; |
| |
| ## Fields |
| |
| #foreach ($m in $members) |
| #if ($params.containsKey($m.toString())) |
| |
| private final $m.type $m; |
| |
| #else |
| |
| private static final $m.type $m = $m.defaultValue; |
| |
| #end |
| #end |
| |
| ## Constructor |
| |
| $className( |
| #foreach ($p in $params.keySet()) |
| |
| $params[$p].type $members[$p] #if ($foreach.hasNext) , #end |
| #end ) { |
| #foreach ($p in $params.keySet()) |
| #if (!$members[$p].kind.primitive) |
| |
| if ($p == null) { |
| throw new NullPointerException("Null $p"); |
| } |
| |
| #end |
| |
| #if ($members[$p].kind == "ARRAY") |
| #if ($params[$p].kind == "ARRAY") |
| |
| this.$p = #cloneArray(${p}); |
| |
| #elseif ($members[$p].typeMirror.componentType.kind.primitive) |
| |
| this.$p = ${members[$p].typeMirror.componentType}ArrayFromCollection($p); |
| |
| #elseif ($members[$p].arrayOfClassWithBounds) |
| |
| @SuppressWarnings({"unchecked", "rawtypes"}) |
| ${members[$p].componentType}[] ${p}$ = ${p}.toArray(new Class[0]); |
| this.$p = ${p}$; |
| |
| #else |
| |
| this.$p = ${p}.toArray(new ${members[$p].componentType}[0]); |
| |
| #end |
| #else |
| |
| this.$p = $p; |
| |
| #end |
| #end |
| |
| } |
| |
| ## annotationType method (defined by the Annotation interface) |
| |
| @`java.lang.Override` |
| public Class<? extends $annotationName> annotationType() { |
| return ${annotationName}.class; |
| } |
| |
| ## Member getters |
| |
| #foreach ($m in $members) |
| |
| @`java.lang.Override` |
| public ${m.type} ${m}() { |
| |
| #if ($m.kind == "ARRAY") |
| |
| return #cloneArray(${m}); |
| |
| #else |
| |
| return ${m}; |
| |
| #end |
| |
| } |
| |
| #end |
| |
| ## toString |
| |
| #macro (appendMemberString $m) |
| #if ($m.typeMirror.toString() == "java.lang.String") |
| #set ($appendQuotedStringMethod = "true") |
| |
| appendQuoted(sb, $m) ## |
| #elseif ($m.typeMirror.toString() == "char") |
| #set ($appendQuotedCharMethod = "true") |
| |
| appendQuoted(sb, $m) ## |
| #elseif ($m.typeMirror.toString() == "java.lang.String[]") |
| #set ($appendQuotedStringArrayMethod = "true") |
| |
| appendQuoted(sb, $m) ## |
| #elseif ($m.typeMirror.toString() == "char[]") |
| #set ($appendQuotedCharArrayMethod = "true") |
| |
| appendQuoted(sb, $m) ## |
| #elseif ($m.kind == "ARRAY") |
| |
| sb.append(`java.util.Arrays`.toString($m)) ## |
| #else |
| |
| sb.append($m) ## |
| #end |
| #end |
| |
| @`java.lang.Override` |
| public String toString() { |
| StringBuilder sb = new StringBuilder("@$annotationFullName("); |
| |
| #foreach ($p in $params.keySet()) |
| |
| #if ($params.size() > 1 || $params.keySet().iterator().next() != "value") |
| |
| sb.append("$p="); |
| #end |
| |
| #appendMemberString($members[$p]); |
| |
| #if ($foreach.hasNext) |
| |
| sb.append(", "); |
| #end |
| |
| #end |
| |
| return sb.append(')').toString(); |
| } |
| |
| ## equals |
| |
| ## An expression that compares `this.something` against `that.something()`. |
| ## It can appear in a chain of && conditions, so if that would cause precedence |
| ## problems the expression needs to be parenthesized. |
| #macro (memberEqualsThatExpression $m) |
| #if ($m.kind == "FLOAT") |
| Float.floatToIntBits($m) == Float.floatToIntBits(that.${m}()) ## |
| #elseif ($m.kind == "DOUBLE") |
| Double.doubleToLongBits($m) == Double.doubleToLongBits(that.${m}()) ## |
| #elseif ($m.kind.primitive) |
| ($m == that.${m}()) ## parens not strictly needed but avoid confusion when comparing booleans |
| #elseif ($m.kind == "ARRAY") |
| #if ($params.containsKey($m.toString())) |
| `java.util.Arrays`.equals($m, |
| (that instanceof $className) |
| ? (($className) that).$m |
| : that.${m}()) ## |
| #else ## default value, so if |that| is also a $className then it has the same constant value |
| (that instanceof $className || `java.util.Arrays`.equals($m, that.${m}())) |
| #end |
| #else |
| ${m}.equals(that.${m}()) ## |
| #end |
| #end |
| |
| @`java.lang.Override` |
| public boolean equals($equalsParameterType o) { |
| if (o == this) { |
| return true; |
| } |
| if (o instanceof $annotationName) { |
| |
| #if ($members.isEmpty()) |
| |
| return true; |
| |
| #else |
| |
| $annotationName that = ($annotationName) o; |
| return ## |
| #foreach ($m in $members) |
| #memberEqualsThatExpression ($m)## |
| #if ($foreach.hasNext) |
| |
| && ## |
| #end |
| #end |
| ; |
| #end |
| |
| } |
| return false; |
| } |
| |
| ## hashCode |
| |
| ## An expression that returns the hashCode of `this.something`. |
| ## It appears on the right-hand side of an ^ operator, so if that would cause precedence |
| ## problems the expression needs to be parenthesized. |
| #macro (memberHashCodeExpression $m) |
| #if ($m.kind == "LONG") |
| (int) (($m >>> 32) ^ $m) ## |
| #elseif ($m.kind == "FLOAT") |
| Float.floatToIntBits($m) ## |
| #elseif ($m.kind == "DOUBLE") |
| (int) ((Double.doubleToLongBits($m) >>> 32) ^ Double.doubleToLongBits($m)) ## |
| #elseif ($m.kind == "BOOLEAN") |
| ($m ? 1231 : 1237) ## |
| #elseif ($m.kind.primitive) |
| $m ## |
| #elseif ($m.kind == "ARRAY") |
| `java.util.Arrays`.hashCode($m) ## |
| #else |
| ${m}.hashCode() ## |
| #end |
| #end |
| |
| ## The hashCode is the sum of two parts, an invariable part and a variable part. The invariable part |
| ## comes from defaulted members (ones that don't appear in the @AutoAnnotation method parameters) |
| ## whose values have hash codes that never change. (That doesn't include Class constants, for |
| ## example.) We precompute the invariable part, as an optimization but also in order to avoid |
| ## falling afoul of constant-overflow checks in the compiler. |
| |
| @`java.lang.Override` |
| public int hashCode() { |
| return |
| ## If the invariable part is 0, we avoid outputting `return 0 + ...` just because it generates |
| ## unnecessary byte code. But if there are no members then we must say `return 0;` here. |
| ## We must write $members.isEmpty() because $members is a Map and Velocity interprets |
| ## $members.empty as meaning $members["empty"] in that case. |
| #if ($invariableHashSum != 0 || $members.isEmpty()) |
| |
| $invariableHashSum |
| // $invariableHashSum is the contribution from default members $invariableHashes |
| #end |
| #foreach ($m in $members) |
| #if (!$invariableHashes.contains($m.toString())) |
| |
| + ($m.nameHash ^ #memberHashCodeExpression($m)) |
| // $m.nameHash is 127 * "${m}".hashCode() |
| #end |
| #end |
| |
| ; |
| |
| } |
| |
| ## support functions |
| |
| #foreach ($w in $wrapperTypesUsedInCollections) |
| #set ($prim = $w.getField("TYPE").get("")) |
| |
| private static ${prim}[] ${prim}ArrayFromCollection(`java.util.Collection`<${w.simpleName}> c) { |
| ${prim}[] a = new ${prim}[c.size()]; |
| int i = 0; |
| for (${prim} x : c) { |
| a[i++] = x; |
| } |
| return a; |
| } |
| #end |
| |
| #if ($appendQuotedStringArrayMethod) |
| #set ($appendQuotedStringMethod = "true") |
| |
| private static void appendQuoted(StringBuilder sb, String[] strings) { |
| sb.append('['); |
| String sep = ""; |
| for (String s : strings) { |
| sb.append(sep); |
| sep = ", "; |
| appendQuoted(sb, s); |
| } |
| sb.append(']'); |
| } |
| #end |
| |
| #if ($appendQuotedCharArrayMethod) |
| #set ($appendQuotedCharMethod = "true") |
| |
| private static void appendQuoted(StringBuilder sb, char[] chars) { |
| sb.append('['); |
| String sep = ""; |
| for (char c : chars) { |
| sb.append(sep); |
| sep = ", "; |
| appendQuoted(sb, c); |
| } |
| sb.append(']'); |
| } |
| #end |
| |
| #if ($appendQuotedStringMethod) |
| #set ($appendEscapedMethod = "true") |
| |
| private static void appendQuoted(StringBuilder sb, String s) { |
| sb.append('"'); |
| for (int i = 0; i < s.length(); i++) { |
| appendEscaped(sb, s.charAt(i)); |
| } |
| sb.append('"'); |
| } |
| #end |
| |
| #if ($appendQuotedCharMethod) |
| #set ($appendEscapedMethod = "true") |
| |
| private static void appendQuoted(StringBuilder sb, char c) { |
| sb.append('\''); |
| appendEscaped(sb, c); |
| sb.append('\''); |
| } |
| #end |
| |
| #if ($appendEscapedMethod) |
| private static void appendEscaped(StringBuilder sb, char c) { |
| switch (c) { |
| case '\\': |
| case '"': |
| case '\'': |
| sb.append('\\').append(c); |
| break; |
| case '\n': |
| sb.append("\\n"); |
| break; |
| case '\r': |
| sb.append("\\r"); |
| break; |
| case '\t': |
| sb.append("\\t"); |
| break; |
| default: |
| if (c < 0x20) { |
| sb.append('\\'); |
| appendWithZeroPadding(sb, Integer.toOctalString(c), 3); |
| } else if (c < 0x7f || Character.isLetter(c)) { |
| sb.append(c); |
| } else { |
| sb.append("\\u"); |
| appendWithZeroPadding(sb, Integer.toHexString(c), 4); |
| } |
| break; |
| } |
| } |
| |
| ## We use this rather than String.format because that doesn't exist on GWT. |
| |
| private static void appendWithZeroPadding(StringBuilder sb, String s, int width) { |
| for (int i = width - s.length(); i > 0; i--) { |
| sb.append('0'); |
| } |
| sb.append(s); |
| } |
| #end |
| } |