blob: 5cdad62948e79d24baf00b46df032f577debec8e [file] [log] [blame]
## 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
}