+ final CharSequence generateInnerClass(final GeneratedType innerClass) {
+ if (!(innerClass instanceof GeneratedTransferObject)) {
+ return "";
+ }
+
+ final GeneratedTransferObject gto = (GeneratedTransferObject) innerClass;
+ final NestedJavaGeneratedType innerJavaType = javaType.getEnclosedType(innerClass.getIdentifier());
+ return gto.isUnionType() ? new UnionTemplate(innerJavaType, gto).generateAsInnerClass()
+ : new ClassTemplate(innerJavaType, gto).generateAsInnerClass();
+ }
+
+ /**
+ * Return imported name of java.util class, whose hashCode/equals methods we want to invoke on the property. Returns
+ * {@link Arrays} if the property is an array, {@link Objects} otherwise.
+ *
+ * @param property Generated property
+ * @return Imported class name
+ */
+ final String importedUtilClass(final GeneratedProperty property) {
+ return importedName(property.getReturnType().getName().indexOf('[') != -1 ? JU_ARRAYS : JU_OBJECTS);
+ }
+
+ final String generatedAnnotation() {
+ return "@" + importedName(GENERATED) + "(\"mdsal-binding-generator\")";
+ }
+
+ /**
+ * Run type analysis, which results in identification of the augmentable type, as well as all methods available
+ * to the type, expressed as properties.
+ */
+ static Map.Entry<Type, Set<BuilderGeneratedProperty>> analyzeTypeHierarchy(final GeneratedType type) {
+ final Set<MethodSignature> methods = new LinkedHashSet<>();
+ final Type augmentType = createMethods(type, methods);
+ final Set<MethodSignature> sortedMethods = ImmutableSortedSet.orderedBy(METHOD_COMPARATOR).addAll(methods)
+ .build();
+
+ return new AbstractMap.SimpleImmutableEntry<>(augmentType, propertiesFromMethods(sortedMethods));
+ }
+
+ static final Restrictions restrictionsForSetter(final Type actualType) {
+ return actualType instanceof GeneratedType ? null : getRestrictions(actualType);
+ }
+
+ static final Restrictions getRestrictions(final Type type) {
+ if (type instanceof ConcreteType) {
+ return ((ConcreteType) type).getRestrictions();
+ }
+ if (type instanceof GeneratedTransferObject) {
+ return ((GeneratedTransferObject) type).getRestrictions();
+ }
+ return null;
+ }
+
+ /**
+ * Generate a call to {@link Object#clone()} if target field represents an array. Returns an empty string otherwise.
+ *
+ * @param property Generated property
+ * @return The string used to clone the property, or an empty string
+ */
+ static final String cloneCall(final GeneratedProperty property) {
+ return property.getReturnType().getName().endsWith("[]") ? ".clone()" : "";
+ }
+
+ /**
+ * 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 static ParameterizedType createMethods(final GeneratedType type, final Set<MethodSignature> methods) {
+ methods.addAll(type.getMethodDefinitions());
+ return collectImplementedMethods(type, methods, type.getImplements());
+ }
+
+ /**
+ * 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 static ParameterizedType collectImplementedMethods(final GeneratedType type,
+ final Set<MethodSignature> methods, final List<Type> implementedIfcs) {
+ if (implementedIfcs == null || implementedIfcs.isEmpty()) {
+ return null;
+ }
+
+ ParameterizedType augmentType = null;
+ for (Type implementedIfc : implementedIfcs) {
+ if (implementedIfc instanceof GeneratedType && !(implementedIfc instanceof GeneratedTransferObject)) {
+ final GeneratedType ifc = (GeneratedType) implementedIfc;
+ addImplMethods(methods, ifc);
+
+ final ParameterizedType t = collectImplementedMethods(type, methods, ifc.getImplements());
+ if (t != null && augmentType == null) {
+ augmentType = t;
+ }
+ } else if (Augmentable.class.getName().equals(implementedIfc.getFullyQualifiedName())) {
+ augmentType = Types.parameterizedTypeFor(AUGMENTATION_RET_TYPE, Type.of(type.getIdentifier()));
+ }
+ }
+
+ return augmentType;
+ }
+
+ private static void addImplMethods(final Set<MethodSignature> methods, final GeneratedType implType) {
+ for (final MethodSignature implMethod : implType.getMethodDefinitions()) {
+ if (hasOverrideAnnotation(implMethod)) {
+ methods.add(implMethod);
+ } else {
+ final String implMethodName = implMethod.getName();
+ if (BindingMapping.isGetterMethodName(implMethodName)
+ && getterByName(methods, implMethodName).isEmpty()) {
+
+ methods.add(implMethod);
+ }
+ }
+ }
+ }
+
+ protected static Optional<MethodSignature> getterByName(final Iterable<MethodSignature> methods,
+ final String implMethodName) {
+ for (MethodSignature method : methods) {
+ final String methodName = method.getName();
+ if (BindingMapping.isGetterMethodName(methodName) && isSameProperty(method.getName(), implMethodName)) {
+ return Optional.of(method);
+ }
+ }
+ return Optional.empty();
+ }
+
+ protected static String propertyNameFromGetter(final MethodSignature getter) {
+ return propertyNameFromGetter(getter.getName());
+ }
+
+ protected static String propertyNameFromGetter(final String getterName) {
+ final String prefix;
+ if (BindingMapping.isGetterMethodName(getterName)) {
+ prefix = BindingMapping.GETTER_PREFIX;
+ } else if (BindingMapping.isNonnullMethodName(getterName)) {
+ prefix = BindingMapping.NONNULL_PREFIX;
+ } else if (BindingMapping.isRequireMethodName(getterName)) {
+ prefix = BindingMapping.REQUIRE_PREFIX;
+ } else {
+ throw new IllegalArgumentException(getterName + " is not a getter");
+ }
+ return StringExtensions.toFirstLower(getterName.substring(prefix.length()));