diff options
Diffstat (limited to 'value/src/main/java/com/google/auto/value/processor/builder.vm')
-rw-r--r-- | value/src/main/java/com/google/auto/value/processor/builder.vm | 285 |
1 files changed, 285 insertions, 0 deletions
diff --git a/value/src/main/java/com/google/auto/value/processor/builder.vm b/value/src/main/java/com/google/auto/value/processor/builder.vm new file mode 100644 index 00000000..630330ca --- /dev/null +++ b/value/src/main/java/com/google/auto/value/processor/builder.vm @@ -0,0 +1,285 @@ +## 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 AutoValue and AutoBuilder builders. +## This template uses the Apache Velocity Template Language (VTL). +## The variables ($isFinal, $props, and so on) are defined by the fields of AutoValueOrBuilderTemplateVars. +## +## 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. +## +#foreach ($a in $builderAnnotations) +$a +#end +#if (!$autoBuilder) static #end## +#if ($isFinal) final #end## +class ${builderName}${builderFormalTypes} ## +#if ($builderIsInterface) implements #else extends #end + ${builderTypeName}${builderActualTypes} { + +#foreach ($p in $props) + + #if ($p.kind.primitive) + + private $types.boxedClass($p.typeMirror).simpleName $p; + + #else + + #if ($builderPropertyBuilders[$p.name]) + ## If you have ImmutableList.Builder<String> stringsBuilder() then we define two fields: + ## private ImmutableList.Builder<String> stringsBuilder$; + ## private ImmutableList<String> strings; + + private ${builderPropertyBuilders[$p.name].builderType} ## + ${builderPropertyBuilders[$p.name].name}; + + #end + + private $p.type $p #if ($p.optional && !$p.nullable) = $p.optional.empty #end ; + + #end +#end + + ${builderName}() { + } + +#if ($toBuilderConstructor) + + private ${builderName}(${origClass}${actualTypes} source) { + + #foreach ($p in $props) + + this.$p = source.${p.getter}(); + + #end + + } + +#end + +#foreach ($p in $props) + + ## The following is either null or an instance of PropertyBuilderClassifier.PropertyBuilder + #set ($propertyBuilder = $builderPropertyBuilders[$p.name]) + + ## Setter and/or property builder + + #foreach ($setter in $builderSetters[$p.name]) + + @`java.lang.Override` + ${setter.access}${builderTypeName}${builderActualTypes} ## + ${setter.name}(${setter.nullableAnnotation}$setter.parameterType $p) { + + ## Omit null check for primitive, or @Nullable, or if we are going to be calling a copy method + ## such as Optional.of, which will have its own null check if appropriate. + #if (!$setter.primitiveParameter && !$p.nullable && ${setter.copy($p)} == $p) + + #if ($identifiers) + + if ($p == null) { + throw new NullPointerException("Null $p.name"); + } + #else + ## Just throw NullPointerException with no message if it's null. + ## The Object cast has no effect on the code but silences an ErrorProne warning. + + ((`java.lang.Object`) ${p}).getClass(); + #end + + #end + + #if ($propertyBuilder) + + if (${propertyBuilder.name} != null) { + throw new IllegalStateException(#if ($identifiers)"Cannot set $p after calling ${p.name}Builder()"#end); + } + + #end + + this.$p = ${setter.copy($p)}; + return this; + } + + #end + + #if ($propertyBuilder) + + @`java.lang.Override` + ${propertyBuilder.access}$propertyBuilder.builderType ${p.name}Builder($propertyBuilder.propertyBuilderMethodParameters) { + if (${propertyBuilder.name} == null) { + + ## This is the first time someone has asked for the builder. If the property it sets already + ## has a value (because it came from a toBuilder() call on the AutoValue class, or because + ## there is also a setter for this property) then we copy that value into the builder. + ## Otherwise the builder starts out empty. + ## If we have neither a setter nor a toBuilder() method, then the builder always starts + ## off empty. + + #if ($builderSetters[$p.name].empty && $toBuilderMethods.empty) + + ${propertyBuilder.name} = ${propertyBuilder.initializer}; + + #else + + if ($p == null) { + ${propertyBuilder.name} = ${propertyBuilder.initializer}; + } else { + + #if (${propertyBuilder.builtToBuilder}) + + ${propertyBuilder.name} = ${p}.${propertyBuilder.builtToBuilder}(); + + #else + + ${propertyBuilder.name} = ${propertyBuilder.initializer}; + ${propertyBuilder.name}.${propertyBuilder.copyAll}($p); + + #end + + $p = null; + } + + #end + + } #if (!$propertyBuilder.propertyBuilderMethodParameters.empty) else { + ## This check only happens if the property-builder method has a parameter. + ## We don't know if the existing builder was created with the same parameter, + ## so we throw to avoid possibly giving you a builder that is different from + ## the one you asked for. + + throw new IllegalStateException("Property builder for $p.name is already defined"); + } + #end + + return $propertyBuilder.name; + } + + #end + + ## Getter + + #if ($builderGetters[$p.name]) + + @`java.lang.Override` + ${p.nullableAnnotation}${builderGetters[$p.name].access}$builderGetters[$p.name].type ${p.getter}() { + #if ($builderGetters[$p.name].optional) + + if ($p == null) { + return $builderGetters[$p.name].optional.empty; + } else { + return ${builderGetters[$p.name].optional.rawType}.of($p); + } + + #else + #if ($builderRequiredProperties.contains($p)) + + if ($p == null) { + throw new IllegalStateException(#if ($identifiers)"Property \"$p.name\" has not been set"#end); + } + + #end + + #if ($propertyBuilder) + + if (${propertyBuilder.name} != null) { + return ${propertyBuilder.name}.build(); + } + if ($p == null) { + ${propertyBuilder.beforeInitDefault} + $p = ${propertyBuilder.initDefault}; + } + + #end + + return $p; + + #end + + } + + #end +#end + +## build() method + + @`java.lang.Override` + ${buildMethod.get().access}${builtType} ${buildMethod.get().name}() ${buildMethod.get().throws} { + +#foreach ($p in $props) + #set ($propertyBuilder = $builderPropertyBuilders[$p.name]) + #if ($propertyBuilder) + + if (${propertyBuilder.name} != null) { + this.$p = ${propertyBuilder.name}.build(); + } else if (this.$p == null) { + ${propertyBuilder.beforeInitDefault} + this.$p = ${propertyBuilder.initDefault}; + } + + #end +#end + +#if (!$builderRequiredProperties.empty) + if (#foreach ($p in $builderRequiredProperties)## + this.$p == null## + #if ($foreach.hasNext) + + || #end + #end) { + + #if ($identifiers) ## build a friendly message showing all missing properties + #if ($builderRequiredProperties.size() == 1) + + `java.lang.String` missing = " $builderRequiredProperties.iterator().next()"; + + #else + + `java.lang.StringBuilder` missing = new `java.lang.StringBuilder`(); + + #foreach ($p in $builderRequiredProperties) + + if (this.$p == null) { + missing.append(" $p.name"); + } + + #end + #end + + throw new IllegalStateException("Missing required properties:" + missing); + + #else ## just throw an exception if anything is missing + + throw new IllegalStateException(); + + #end + + } + +#end + + #if ($builtType != "void") return #end ${build}( +#foreach ($p in $props) + + this.$p #if ($foreach.hasNext) , #end +#end ); + } +} |