--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding2.java.api.generator;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.mdsal.binding2.java.api.generator.renderers.BuilderRenderer;
+import org.opendaylight.mdsal.binding2.model.api.CodeGenerator;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedTransferObject;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding2.model.api.Type;
+import org.opendaylight.mdsal.binding2.model.api.UnitName;
+import org.opendaylight.mdsal.binding2.spec.Augmentable;
+import org.opendaylight.mdsal.binding2.spec.Augmentation;
+import org.opendaylight.yangtools.concepts.Identifier;
+
+/**
+ * Transformer of the data from the virtual form to JAVA programming language.
+ * The result source code represent java class. For generation of the source
+ * code is used the template written in Twirl (Scala based) language.
+ */
+@Beta
+public final class BuilderGenerator implements CodeGenerator {
+
+ @Override
+ public String generate(Type type) {
+ if ((type instanceof GeneratedType) && !(type instanceof GeneratedTransferObject)) {
+ final GeneratedType genType = (GeneratedType) type;
+ return new BuilderRenderer(genType).generateTemplate();
+ } else {
+ return "";
+ }
+ }
+
+ @Override
+ public boolean isAcceptable(Type type) {
+ if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
+ for (Type t : ((GeneratedType) type).getImplements()) {
+ // "rpc" and "grouping" elements do not implement Augmentable
+ if (t.getFullyQualifiedName().equals(Augmentable.class.getName())) {
+ return true;
+ } else if (t.getFullyQualifiedName().equals(Augmentation.class.getName())) {
+ return true;
+ }
+
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public Identifier getUnitName(Type type) {
+ return new UnitName(type.getName());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.mdsal.binding2.java.api.generator.renderers;
+
+import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.DOT;
+import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getPropertyList;
+import static org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.toFirstLower;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.common.collect.Collections2;
+import com.google.common.collect.ImmutableSortedSet;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import org.opendaylight.mdsal.binding2.generator.util.ReferencedTypeImpl;
+import org.opendaylight.mdsal.binding2.generator.util.Types;
+import org.opendaylight.mdsal.binding2.generator.util.generated.type.builder.GeneratedTOBuilderImpl;
+import org.opendaylight.mdsal.binding2.java.api.generator.util.AlphabeticallyTypeMemberComparator;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedProperty;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedTransferObject;
+import org.opendaylight.mdsal.binding2.model.api.GeneratedType;
+import org.opendaylight.mdsal.binding2.model.api.MethodSignature;
+import org.opendaylight.mdsal.binding2.model.api.ParameterizedType;
+import org.opendaylight.mdsal.binding2.model.api.Type;
+import org.opendaylight.mdsal.binding2.spec.Augmentable;
+import org.opendaylight.mdsal.binding2.spec.TreeNode;
+import org.opendaylight.mdsal.binding2.txt.builderConstructorHelperTemplate;
+import org.opendaylight.mdsal.binding2.txt.builderTemplate;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.concepts.Identifiable;
+
+public class BuilderRenderer extends BaseRenderer {
+
+ /**
+ * Set of class attributes (fields) which are derived from the getter methods names
+ */
+ private final Set<GeneratedProperty> properties;
+
+ /**
+ * Set of name from properties
+ */
+ private final Map<GeneratedProperty, String> importedNamesForProperties = new HashMap<>();
+
+ /**
+ * list of all imported names for template
+ */
+ private final Map<String, String> importedNames = new HashMap<>();
+
+ /**
+ * Generated property is set if among methods is found one with the name GET_AUGMENTATION_METHOD_NAME
+ */
+ private GeneratedProperty augmentField;
+
+ public BuilderRenderer(final GeneratedType type) {
+ super(type);
+ this.properties = propertiesFromMethods(createMethods());
+ putToImportMap(Builder.class.getSimpleName(), Builder.class.getPackage().getName());
+ }
+
+ /**
+ * Creates set of generated property instances from getter <code>methods</code>.
+ *
+ * @param methods set of method signature instances which should be transformed to list of properties
+ * @return set of generated property instances which represents the getter <code>methods</code>
+ */
+ private Set<GeneratedProperty> propertiesFromMethods(final Collection<MethodSignature> methods) {
+ if (methods == null || methods.isEmpty()) {
+ return Collections.emptySet();
+ }
+ final Set<GeneratedProperty> result = new LinkedHashSet<>();
+ for (MethodSignature method : methods) {
+ final GeneratedProperty createdField = propertyFromGetter(method);
+ if (createdField != null) {
+ result.add(createdField);
+ importedNamesForProperties.put(createdField, importedName(createdField.getReturnType()));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Creates generated property instance from the getter <code>method</code> name and return type.
+ *
+ * @param method method signature from which is the method name and return type obtained
+ * @return generated property instance for the getter <code>method</code>
+ * @throws IllegalArgumentException
+ * <li>if the <code>method</code> equals <code>null</code></li>
+ * <li>if the name of the <code>method</code> equals <code>null</code></li>
+ * <li>if the name of the <code>method</code> is empty</li>
+ * <li>if the return type of the <code>method</code> equals <code>null</code></li>
+ */
+ private GeneratedProperty propertyFromGetter(final MethodSignature method) {
+ Preconditions.checkArgument(method != null, "Method cannot be NULL");
+ Preconditions.checkArgument(!Strings.isNullOrEmpty(method.getName()), "Method name cannot be NULL or empty");
+ Preconditions.checkArgument(method.getReturnType() != null, "Method return type reference cannot be NULL");
+ final String prefix = Types.BOOLEAN.equals(method.getReturnType()) ? "is" : "get";
+ if (method.getName().startsWith(prefix)) {
+ final String fieldName = toFirstLower(method.getName().substring(prefix.length()));
+ final GeneratedTOBuilderImpl tmpGenTO = new GeneratedTOBuilderImpl("foo", "foo");
+ tmpGenTO.addProperty(fieldName)
+ .setReturnType(method.getReturnType());
+ return tmpGenTO.toInstance().getProperties().get(0);
+ }
+ return null;
+ }
+
+ /**
+ * Returns set of method signature instances which contains all the methods of the <code>genType</code>
+ * and all the methods of the implemented interfaces.
+ *
+ * @returns set of method signature instances
+ */
+ private Set<MethodSignature> createMethods() {
+ final Set<MethodSignature> methods = new LinkedHashSet<>();
+ methods.addAll(getType().getMethodDefinitions());
+ collectImplementedMethods(methods, getType().getImplements());
+ final Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(
+ new AlphabeticallyTypeMemberComparator<MethodSignature>())
+ .addAll(methods)
+ .build();
+ return sortedMethods;
+ }
+
+ /**
+ * Adds to the <code>methods</code> set all the methods of the <code>implementedIfcs</code>
+ * and recursively their implemented interfaces.
+ *
+ * @param methods set of method signatures
+ * @param implementedIfcs list of implemented interfaces
+ */
+ private void collectImplementedMethods(final Set<MethodSignature> methods, List<Type> implementedIfcs) {
+ if (implementedIfcs != null && !implementedIfcs.isEmpty()) {
+ for (Type implementedIfc : implementedIfcs) {
+ if ((implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject))) {
+ final GeneratedType ifc = (GeneratedType) implementedIfc;
+ methods.addAll(ifc.getMethodDefinitions());
+ collectImplementedMethods(methods, ifc.getImplements());
+ } else if (Augmentable.class.getName().equals(implementedIfc.getFullyQualifiedName())) {
+ for (Method method : Augmentable.class.getMethods()) {
+ if ("getAugmentation".equals(method.getName())) {
+ final String fullyQualifiedName = method.getReturnType().getName();
+ final String aPackage = getPackage(fullyQualifiedName);
+ final String name = getName(fullyQualifiedName);
+ final GeneratedTOBuilderImpl generatedTOBuilder = new GeneratedTOBuilderImpl(aPackage, name);
+ final ReferencedTypeImpl referencedType = new ReferencedTypeImpl(aPackage, name);
+ final ReferencedTypeImpl generic = new ReferencedTypeImpl(getType().getPackageName(),
+ getType().getName());
+ final ParameterizedType parametrizedReturnType = Types.parameterizedTypeFor(referencedType, generic);
+ generatedTOBuilder.addMethod(method.getName()).setReturnType(parametrizedReturnType);
+ augmentField = propertyFromGetter(generatedTOBuilder.toInstance().getMethodDefinitions().get(0));
+ importedNames.put("map", importedName(Map.class));
+ importedNames.put("hashMap", importedName(HashMap.class));
+ importedNames.put("class", importedName(Class.class));
+// To do This is for third party, is it needed ?
+// importedNames.put("augmentationHolder", importedName(AugmentationHolder.class));
+ importedNames.put("collections", importedName(Collections.class));
+ importedNames.put("augmentFieldReturnType", importedName(augmentField.getReturnType()));
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Returns the name of the package from <code>fullyQualifiedName</code>.
+ *
+ * @param fullyQualifiedName string with fully qualified type name (package + type)
+ * @return string with the package name
+ */
+ private String getPackage(final String fullyQualifiedName) {
+ final int lastDotIndex = fullyQualifiedName.lastIndexOf(DOT);
+ return (lastDotIndex == -1) ? "" : fullyQualifiedName.substring(0, lastDotIndex);
+ }
+
+ /**
+ * Returns the name of tye type from <code>fullyQualifiedName</code>
+ *
+ * @param fullyQualifiedName string with fully qualified type name (package + type)
+ * @return string with the name of the type
+ */
+ private String getName(final String fullyQualifiedName) {
+ final int lastDotIndex = fullyQualifiedName.lastIndexOf(DOT);
+ return (lastDotIndex == -1) ? fullyQualifiedName : fullyQualifiedName.substring(lastDotIndex + 1);
+ }
+
+ public static Set<Type> getAllIfcs(final Type type) {
+ final Set<Type> baseIfcs = new HashSet<>();
+ if (type instanceof GeneratedType && !(type instanceof GeneratedTransferObject)) {
+ for (Type impl : ((GeneratedType)type).getImplements()) {
+ if (impl instanceof GeneratedType && !(((GeneratedType)impl).getMethodDefinitions().isEmpty())) {
+ baseIfcs.add(impl);
+ }
+ baseIfcs.addAll(getAllIfcs(impl));
+ }
+ }
+ return baseIfcs;
+ }
+
+ /**
+ * Method is used to find out if given type implements any interface from uses.
+ */
+ public static boolean hasImplementsFromUses(GeneratedType type) {
+ for (Type impl : getAllIfcs(type)) {
+ if ((impl instanceof GeneratedType) && !(((GeneratedType)impl).getMethodDefinitions().isEmpty())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static Set<String> toListOfNames(final Set<Type> types) {
+ final Set<String> names = new HashSet<>();
+ for (Type currentType : types) {
+ names.add(currentType.getFullyQualifiedName());
+ }
+ return names;
+ }
+
+ @Override
+ protected String body() {
+ importedNames.put("genType", importedName(getType()));
+ importedNames.put("arrays", importedName(Arrays.class));
+ importedNames.put("objects", importedName(Objects.class));
+ importedNames.put("object", importedName(Object.class));
+ importedNames.put("string", importedName(String.class));
+ importedNames.put("stringBuilder", importedName(StringBuilder.class));
+ importedNames.put("treeNode", importedName(TreeNode.class));
+ // list for generate copy constructor
+ final String copyConstructorHelper = generateListForCopyConstructor();
+ List<String> getterMethods = new ArrayList<>(Collections2.transform(properties, this::getterMethod));
+
+ return builderTemplate.render(getType(), properties, importedNames, importedNamesForProperties, augmentField,
+ copyConstructorHelper, getterMethods)
+ .body();
+ }
+
+ private String generateListForCopyConstructor() {
+ final List allProps = new ArrayList<>(properties);
+ final boolean isList = implementsIfc(getType(), Types.parameterizedTypeFor(Types.typeForClass(Identifiable.class),
+ getType()));
+ final Type keyType = getKey(getType());
+ if (isList && keyType != null) {
+ final List<GeneratedProperty> keyProps = ((GeneratedTransferObject) keyType).getProperties();
+ final Comparator<GeneratedProperty> function = (GeneratedProperty p1, GeneratedProperty p2) -> {
+ String name2 = p1.getName();
+ String name3 = p2.getName();
+ return name2.compareTo(name3);
+ };
+ Collections.sort(keyProps, function);
+ for (GeneratedProperty keyProp : keyProps) {
+ removeProperty(allProps, keyProp.getName());
+ }
+ removeProperty(allProps, "key");
+ importedNames.put("keyTypeConstructor", importedName(keyType));
+ return builderConstructorHelperTemplate.render(allProps, keyProps, importedNames, getPropertyList(allProps))
+ .body();
+ }
+ return builderConstructorHelperTemplate.render(allProps, null, importedNames, null).body();
+ }
+
+ private Type getKey(final GeneratedType genType) {
+ for (MethodSignature methodSignature : genType.getMethodDefinitions()) {
+ if ("getKey".equals(methodSignature.getName())) {
+ return methodSignature.getReturnType();
+ }
+ }
+ return null;
+ }
+
+ private boolean implementsIfc(final GeneratedType type, final Type impl) {
+ return type.getImplements().contains(impl);
+ }
+
+ private void removeProperty(final Collection<GeneratedProperty> properties, final String name) {
+ for (final GeneratedProperty property : properties) {
+ if (name.equals(property.getName())) {
+ properties.remove(property);
+ break;
+ }
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+@*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *@
+
+@import org.opendaylight.mdsal.binding2.model.api.GeneratedProperty
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.fieldName
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getterMethodName
+
+@(allProps: List[GeneratedProperty], keyProps: List[GeneratedProperty], importedNames: Map[String, String],
+keyPropsList: String)
+@if(keyProps != null) {
+if (base.getKey() == null) {
+ this._key = new @{importedNames.get("keyTypeConstructor")}(
+ @{keyPropsList}
+ );
+ @for(field <- keyProps) {
+ this.@{fieldName(field)} = base.
+ @{getterMethodName(field)}();
+ }
+} @{"else"} {
+ this._key = base.getKey();
+ @for(field <- keyProps) {
+ this.@{fieldName(field)} = _key.@{getterMethodName(field)}();
+ }
+}
+}
+@for(field <- allProps) {
+ this.@{fieldName(field)} = base.@{getterMethodName(field)}();
+}
--- /dev/null
+@*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ *@
+
+@import java.util.List
+@import org.opendaylight.mdsal.binding2.model.api.ConcreteType
+@import org.opendaylight.mdsal.binding2.model.api.GeneratedType
+@import org.opendaylight.mdsal.binding2.model.api.GeneratedTransferObject
+@import org.opendaylight.mdsal.binding2.model.api.GeneratedProperty
+@import org.opendaylight.mdsal.binding2.model.api.Type
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.fieldName
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.formatDataForJavaDocBuilder
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getSimpleNameForBuilder
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getterMethodName
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.getRestrictions
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.propertyNameFromGetter
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.toFirstUpper
+@import org.opendaylight.mdsal.binding2.java.api.generator.util.TextTemplateUtil.wrapToDocumentation
+@import org.opendaylight.mdsal.binding2.java.api.generator.renderers.BuilderRenderer.getAllIfcs
+@import org.opendaylight.mdsal.binding2.java.api.generator.renderers.BuilderRenderer.hasImplementsFromUses
+@import org.opendaylight.mdsal.binding2.java.api.generator.renderers.BuilderRenderer.toListOfNames
+@import org.opendaylight.mdsal.binding2.java.api.generator.rangeGenerators.AbstractRangeGenerator
+@import org.opendaylight.mdsal.binding2.java.api.generator.rangeGenerators.LengthGenerator
+@import org.opendaylight.yangtools.concepts.Builder
+
+@(genType: GeneratedType, properties: Set[GeneratedProperty], importedNames: Map[String, String],
+ImportedNamesWithProperties: Map[GeneratedProperty, String], augmentField: GeneratedProperty, copyConstructorHelper: String,
+getterMethods: List[String])
+@if(genType != null) {
+@{wrapToDocumentation(formatDataForJavaDocBuilder(importedNames.get("genType")))}
+public class @{genType.getName}Builder implements @{getSimpleNameForBuilder} <@{importedNames.get("genType")}> {
+
+ @generateFields(false)
+
+ @generateAugmentField(false)
+
+ @generateConstructorsFromIfcs()
+
+ @generateCopyConstructor(false)
+
+ @generateMethodFieldsFrom()
+
+ @generateGetters(false)
+
+ @generateSetters()
+
+ public @{genType.getName} build() {
+ return new @{genType.getName}Impl(this);
+ }
+
+ private static final class @{genType.getName}Impl implements @{genType.getName} {
+
+ @implementedInterfaceGetter()
+
+ @generateFields(true)
+
+ @generateAugmentField(true)
+
+ @generateCopyConstructor(true)
+
+ @generateGetters(true)
+
+ @generateHashCode()
+
+ @generateEquals()
+
+ @generateToString()
+ }
+}
+}
+
+@**
+ * Template method which generates class attributes.
+ *
+ * @param isFinal value which specify whether field is|isn't final
+ * @param genType is genType
+ * @return string with class attributes and their types
+ *@
+@generateFields(isFinal: Boolean) = {
+ @if(ImportedNamesWithProperties != null) {
+ @for((key, value) <- ImportedNamesWithProperties) {
+ private @if(isFinal) { final}
+ @{value} @{fieldName(key)};
+ }
+ }
+}
+
+@**
+ * Template method which generates class attributes.
+ *
+ * @param boolean value which specify whether field is|isn't final
+ * @return string with class attributes and their types
+ *@
+@generateAugmentField(isPrivate: Boolean) = {
+ @if(augmentField != null) {
+ @if(isPrivate) {private }
+ @{importedNames.get("map")}<@{importedNames.get("class")}<? extends@{importedNames.get("augmentFieldReturnType")}>,
+ @{importedNames.get("augmentFieldReturnType")}>@{augmentField.getName} = @{importedNames.get("collections")}.emptyMap();
+ }
+}
+
+@implementedInterfaceGetter() = {
+ public @{importedNames.get("class")}<@{importedNames.get("genType")}> getImplementedInterface() {
+ return @{importedNames.get("genType")}.class;
+ }
+}
+
+@**
+ * Generate default constructor and constructor for every implemented interface from uses statements.
+ *@
+@generateConstructorsFromIfcs() = {
+ public @{genType.getName}Builder() {
+ }
+ @if(genType.isInstanceOf[GeneratedType] && !genType.isInstanceOf[GeneratedTransferObject]) {
+ @for(impl <- genType.asInstanceOf[GeneratedType].getImplements) {
+ @generateConstructorFromIfc(impl)
+ }
+ }
+}
+
+@generateMethodFieldsFrom() = {
+ @if(genType.isInstanceOf[GeneratedType] && !genType.isInstanceOf[GeneratedTransferObject]) {
+ @if(hasImplementsFromUses(genType.asInstanceOf[GeneratedType])) {
+ /**
+ *Set fields from given grouping argument. Valid argument is instance of one of following types:
+ * <ul>
+ @for(impl <- getAllIfcs(genType.asInstanceOf[GeneratedType])) {
+ * <li>@{impl.getFullyQualifiedName}</li>
+ }
+ * </ul>
+ *
+ * @@param arg grouping object
+ * @@throws IllegalArgumentException if given argument is none of valid types
+ */
+
+ public void fieldsFrom(@{importedNames.get("treeNode")} arg) {
+ boolean isValidArg = false;
+ @for(impl <- getAllIfcs(genType.asInstanceOf[GeneratedType])) {
+ @if(impl.isInstanceOf[GeneratedType] && !impl.asInstanceOf[GeneratedType].getMethodDefinitions.isEmpty) {
+ if (arg instanceof @{impl.asInstanceOf[GeneratedType].getFullyQualifiedName}) {
+ @if(!impl.isInstanceOf[GeneratedTransferObject]) {
+ @for(getter <- genType.asInstanceOf[GeneratedType].getMethodDefinitions) {
+ this._@{propertyNameFromGetter(getter)} = ((@{impl.asInstanceOf[GeneratedType].getFullyQualifiedName})arg).@{getter.getName}();
+ }
+ }
+ isValidArg = true;
+ }
+ }
+ }
+ if (!isValidArg) {
+ throw new IllegalArgumentException(
+ "expected one of: @{toListOfNames(getAllIfcs(genType.asInstanceOf[GeneratedType]))} \n" +
+ "but was: " + arg
+ );
+ }
+ }
+ }
+ }
+}
+
+@**
+ * Generate constructor with argument of given type.
+ *@
+@generateConstructorFromIfc(impl: Type) = {
+ @if(impl.isInstanceOf[GeneratedType]) {
+ @if(!impl.asInstanceOf[GeneratedType].getMethodDefinitions.isEmpty) {
+ public @{genType.getName}Builder(
+ @{impl.getFullyQualifiedName} arg) {
+ @{printConstructorPropertySetter(impl)}
+ }
+ }
+ @for(implTypeImplement <- impl.asInstanceOf[GeneratedType].getImplements) {
+ @generateConstructorFromIfc(implTypeImplement)
+ }
+ }
+}
+
+@printConstructorPropertySetter(implementedIfc: Type) = {
+ @if(implementedIfc.isInstanceOf[GeneratedType] && !implementedIfc.isInstanceOf[GeneratedTransferObject]) {
+ @for(getter <- implementedIfc.asInstanceOf[GeneratedType].getMethodDefinitions) {
+ this._@{propertyNameFromGetter(getter)} = arg.@{getter.getName}();
+ }
+ @for(impl <- implementedIfc.asInstanceOf[GeneratedType].getImplements) {
+ @{printConstructorPropertySetter(impl)}
+ }
+ }
+}
+
+@generateCopyConstructor(impl: Boolean) = {
+ @if(impl) {private} else {public}
+ @{genType.getName}
+ @if(impl) {Impl} else {Builder}
+ (@{genType.getName}
+ @if(impl) {Builder} base) {
+ @{copyConstructorHelper}
+ @if(augmentField != null) {
+ @if(impl) {
+ switch (base.@{augmentField.getName}.size()) {
+ case 0:
+ this.@{augmentField.getName} = @{importedNames.get("collections")}.emptyMap();
+ break;
+ case 1:
+ final @{importedNames.get("map")}.Entry<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>, @{importedNames.get("augmentFieldReturnType")}> e = base.@{augmentField.getName}.entrySet().iterator().next();
+ this.@{augmentField.getName} = @{importedNames.get("collections")}.<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>, @{importedNames.get("augmentFieldReturnType")}>singletonMap(e.getKey(), e.getValue());
+ break;
+ default :
+ this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>(base.@{augmentField.getName});
+ }
+ } else {
+ if (base instanceof @{genType.getName}Impl) {
+ @{genType.getName}Impl impl = (@{genType.getName}Impl) base;
+ if (!impl.@{augmentField.getName}.isEmpty()) {
+ this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>(impl.@{augmentField.getName});
+ }
+ } @{"else"} if (base instanceof @{importedNames.get("augmentationHolder")}) {
+ @@SuppressWarnings("unchecked")
+ @{importedNames.get("augmentationHolder")}<@{importedNames.get("genType")}> casted =(@{importedNames.get("augmentationHolder")}<@{importedNames.get("genType")}>) base;
+ if (!casted.augmentations().isEmpty()) {
+ this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>(casted.augmentations());
+ }
+ }
+ }
+ }
+ }
+}
+
+
+@generateSetters() = {
+ @for(field <- properties) {
+ @if(!field.getReturnType.isInstanceOf[GeneratedType] && getRestrictions(field.getReturnType) != null) {
+ @if(getRestrictions(field.getReturnType).getRangeConstraints != null && !getRestrictions(field.getReturnType).getRangeConstraints.isEmpty) {
+ @{AbstractRangeGenerator.forType(field.getReturnType).generateRangeChecker(toFirstUpper(field.getName),
+ getRestrictions(field.getReturnType).getRangeConstraints)}
+ }
+ @if(getRestrictions(field.getReturnType).getLengthConstraints != null && !getRestrictions(field.getReturnType).getLengthConstraints.isEmpty) {
+ @{LengthGenerator.generateLengthChecker(fieldName(field), field.getReturnType,
+ getRestrictions(field.getReturnType).getLengthConstraints)}
+ }
+ }
+ public @{genType.getName}Builder set@{toFirstUpper(field.getName)}(final @{importedNames.get("augmentFieldReturnType")} value) {
+ @if(!field.getReturnType.isInstanceOf[GeneratedType] && getRestrictions(field.getReturnType) != null) {
+ if (value != null) {
+ @if(getRestrictions(field.getReturnType).getRangeConstraints != null && !getRestrictions(field.getReturnType).getRangeConstraints.isEmpty) {
+ @if(field.getReturnType.isInstanceOf[ConcreteType]) {
+ @{AbstractRangeGenerator.forType(field.getReturnType).generateRangeCheckerCall(toFirstUpper(field.getName), "value")}
+ } else {
+ @{AbstractRangeGenerator.forType(field.getReturnType).generateRangeCheckerCall(toFirstUpper(field.getName), "value.getValue()")}
+ }
+ }
+ @if(getRestrictions(field.getReturnType).getLengthConstraints != null && !getRestrictions(field.getReturnType).getLengthConstraints.isEmpty) {
+ @if(field.getReturnType.isInstanceOf[ConcreteType]) {
+ @{LengthGenerator.generateLengthCheckerCall(fieldName(field), "value")}
+ } else {
+ @{LengthGenerator.generateLengthCheckerCall(fieldName(field), "value.getValue()")}
+ }
+ }
+ }
+ }
+ this.@{fieldName(field)} = value;
+ return this;
+ }
+ }
+ @if(augmentField != null) {
+ public @{genType.getName}Builder add@{toFirstUpper(augmentField.getName)}(@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}> augmentationType, @{importedNames.get("augmentFieldReturnType")} augmentation) {
+ if (augmentation == null) {
+ return remove@{toFirstUpper(augmentField.getName)}(augmentationType);
+ }
+
+ if (!(this.@{augmentField.getName} instanceof @{importedNames.get("hashMap")})) {
+ this.@{augmentField.getName} = new @{importedNames.get("hashMap")}<>();
+ }
+
+ this.@{augmentField.getName}.put(augmentationType, augmentation);
+ return this;
+ }
+
+ public @{genType.getName}Builder remove@{toFirstUpper(augmentField.getName)}
+ (@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}> augmentationType) {
+ if (this.@{augmentField.getName} instanceof @{importedNames.get("hashMap")}) {
+ this.@{augmentField.getName}.remove(augmentationType);
+ }
+ return this;
+ }
+ }
+}
+
+@generateGetters(addOverride: Boolean) = {
+ @if(!getterMethods.isEmpty) {
+ @for(property <- getterMethods) {
+ @if(addOverride) {@@Override}
+ @{property}
+ }
+ }
+ @if(augmentField != null) {
+ @@SuppressWarnings("unchecked")
+ @if(addOverride) {@@Override}
+ public <E extends @{importedNames.get("augmentFieldReturnType")}> E get@{toFirstUpper(augmentField.getName)}
+ (@{importedNames.get("class")}<E> augmentationType) {
+ if (augmentationType == null) {
+ throw new IllegalArgumentException("Augmentation Type reference cannot be NULL!");
+ }
+ return (E) @{augmentField.getName}.get(augmentationType);
+ }
+ }
+}
+
+@generateHashCode() = {
+ @if(!properties.isEmpty || augmentField != null) {
+ private int hash = 0;
+ private volatile boolean hashValid = false;
+
+ @@Override
+ public int hashCode() {
+ if (hashValid) {
+ return hash;
+ }
+
+ final int prime = 31;
+ int result = 1;
+ @for(property <- properties) {
+ @if(property.getReturnType.getName.contains("[")) {
+ result = prime * result + @{importedNames.get("arrays")}.hashCode(@{fieldName(property)});
+ } else {
+ result = prime * result + @{importedNames.get("objects")}.hashCode(@{fieldName(property)});
+ }
+ }
+ @if(augmentField != null) {
+ result = prime * result + @{importedNames.get("objects")}.hashCode(@{augmentField.getName});
+ }
+
+ hash = result;
+ hashValid = true;
+ return result;
+ }
+ }
+}
+
+@generateToString() = {
+ @if(properties != null) {
+ @@Override
+ public @{importedNames.get("string")} toString() {
+ @{importedNames.get("stringBuilder")} builder = new @{importedNames.get("stringBuilder")} ("@{genType.getName} [");
+ boolean first = true;
+ @for(property <- properties) {
+ if (@{fieldName(property)} != null) {
+ if (first) {
+ first = false;
+ } else {
+ builder.append(", ");
+ }
+ builder.append("@{fieldName(property)}=");
+ @if(property.getReturnType.getName.contains("[")) {
+ builder.append(@{importedNames.get("arrays")}.toString(@{fieldName(property)}));
+ } else {
+ builder.append(@{fieldName(property)});
+ }
+ }
+ }
+ @if(augmentField != null) {
+ if (first) {
+ first = false;
+ } @{"else"} {
+ builder.append(", ");
+ }
+ builder.append("@{augmentField.getName}=");
+ builder.append(@{augmentField.getName}.values());
+ }
+ return builder.append(']').toString();
+ }
+ }
+}
+
+@generateEquals() = {
+ @if(!properties.isEmpty || augmentField != null) {
+ @@Override
+ public boolean equals(@{importedNames.get("object")} obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!(obj instanceof @{importedNames.get("treeNode")})) {
+ return false;
+ }
+ if (!@{importedNames.get("genType")}.class.equals(((@{importedNames.get("treeNode")})obj).getImplementedInterface)) {
+ return false;
+ }
+ @{importedNames.get("genType")} other = (@{importedNames.get("genType")})obj;
+ @for(property <- properties) {
+ @if(property.getReturnType.getName.contains("[")) {
+ if (!@{importedNames.get("arrays")}.equals(@{fieldName(property)}, other.@{getterMethodName(property)}()))
+ } else {
+ if (!@{importedNames.get("objects")}.equals(@{fieldName(property)}, other.@{getterMethodName(property)}()))
+ }
+ {
+ return false;
+ }
+ }
+ @if(augmentField != null) {
+ if (getClass() == obj.getClass()) {
+ // Simple case: we are comparing against self
+ @{genType.getName}Impl otherImpl = (@{genType.getName}Impl) obj;
+ if (!@{importedNames.get("objects")}.equals(@{augmentField.getName}, otherImpl.@{augmentField.getName})) {
+ return false;
+ }
+ } @{"else"} {
+ // Hard case: compare our augments with presence there...
+ for (@{importedNames.get("map")}.Entry<@{importedNames.get("class")}<? extends @{importedNames.get("augmentFieldReturnType")}>, @{importedNames.get("augmentFieldReturnType")}> e : @{augmentField.getName}.entrySet()) {
+ if (!e.getValue().equals(other.getAugmentation(e.getKey()))) {
+ return false;
+ }
+ }
+ // .. and give the other one the chance to do the same
+ if (!obj.equals(this)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ }
+}
\ No newline at end of file