BUG-2976 Generate hash/equals for config generated DTOs
[controller.git] / opendaylight / config / yang-jmx-generator-plugin / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / plugin / ftl / TemplateFactory.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl;
9
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.Lists;
12 import com.google.common.collect.Maps;
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.List;
18 import java.util.Map;
19 import java.util.Map.Entry;
20 import javax.management.openmbean.SimpleType;
21 import org.opendaylight.controller.config.api.DependencyResolver;
22 import org.opendaylight.controller.config.api.IdentityAttributeRef;
23 import org.opendaylight.controller.config.api.RuntimeBeanRegistratorAwareModule;
24 import org.opendaylight.controller.config.api.annotations.AbstractServiceInterface;
25 import org.opendaylight.controller.config.api.runtime.RuntimeBean;
26 import org.opendaylight.controller.config.spi.AbstractModule;
27 import org.opendaylight.controller.config.yangjmxgenerator.AbstractEntry;
28 import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry;
29 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
30 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry.Rpc;
31 import org.opendaylight.controller.config.yangjmxgenerator.ServiceInterfaceEntry;
32 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
33 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
34 import org.opendaylight.controller.config.yangjmxgenerator.attribute.Dependency;
35 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
36 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
37 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
38 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
39 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TypedAttribute;
40 import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
41 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
42 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
43 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Constructor;
44 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
45 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Header;
46 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.IdentityRefModuleField;
47 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDeclaration;
48 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
49 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.ModuleField;
50 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
51 import org.opendaylight.yangtools.sal.binding.model.api.ParameterizedType;
52 import org.opendaylight.yangtools.sal.binding.model.api.Type;
53 import org.opendaylight.yangtools.yang.binding.BindingMapping;
54
55 public class TemplateFactory {
56
57     /**
58      * Get map of file name as key, FtlFile instance representing runtime mx
59      * bean as value that should be persisted from this instance.
60      */
61     public static Map<String, FtlTemplate> getTOAndMXInterfaceFtlFiles(
62             final RuntimeBeanEntry entry) {
63         Map<String, FtlTemplate> result = new HashMap<>();
64         { // create GeneralInterfaceFtlFile for runtime MXBean. Attributes will
65           // be transformed to getter methods
66             String mxBeanTypeName = entry.getJavaNameOfRuntimeMXBean();
67             List<String> extendedInterfaces = Arrays.asList(RuntimeBean.class
68                     .getCanonicalName());
69             List<MethodDeclaration> methods = new ArrayList<>();
70
71             // convert attributes to getters
72             for (AttributeIfc attributeIfc : entry.getAttributes()) {
73                 String returnType;
74                 returnType = getReturnType(attributeIfc);
75                 String getterName = "get"
76                         + attributeIfc.getUpperCaseCammelCase();
77                 MethodDeclaration getter = new MethodDeclaration(returnType,
78                         getterName, Collections.<Field> emptyList());
79                 methods.add(getter);
80             }
81
82             // add rpc methods
83             for (Rpc rpc : entry.getRpcs()) {
84                 // convert JavaAttribute parameters into fields
85                 List<Field> fields = new ArrayList<>();
86                 for (JavaAttribute ja : rpc.getParameters()) {
87                     Field field = new Field(Collections.<String> emptyList(),
88                             ja.getType().getFullyQualifiedName(),
89                             ja.getLowerCaseCammelCase(), ja.getNullableDefaultWrappedForCode());
90                     fields.add(field);
91                 }
92                 MethodDeclaration operation = new MethodDeclaration(
93                         getReturnType(rpc.getReturnType()), rpc.getName(), fields);
94                 methods.add(operation);
95             }
96
97             // FIXME header
98             GeneralInterfaceTemplate runtimeMxBeanIfc = new GeneralInterfaceTemplate(
99                     null, entry.getPackageName(), mxBeanTypeName,
100                     extendedInterfaces, methods);
101
102             result.put(runtimeMxBeanIfc.getTypeDeclaration().getName()
103                     + ".java", runtimeMxBeanIfc);
104         }
105
106         result.putAll(TemplateFactory.tOsFromRbe(entry));
107
108         return result;
109     }
110
111     // FIXME: put into Type.toString
112     static String serializeType(final Type type, final boolean addWildcards) {
113         if (type instanceof ParameterizedType){
114             ParameterizedType parameterizedType = (ParameterizedType) type;
115             StringBuilder sb = new StringBuilder();
116             sb.append(parameterizedType.getRawType().getFullyQualifiedName());
117             sb.append(addWildcards ? "<? extends " : "<");
118             boolean first = true;
119             for(Type parameter: parameterizedType.getActualTypeArguments()) {
120                 if (first) {
121                     first = false;
122                 } else {
123                     sb.append(",");
124                 }
125                 sb.append(serializeType(parameter));
126             }
127             sb.append(">");
128             return sb.toString();
129         } else {
130             return type.getFullyQualifiedName();
131         }
132     }
133
134     static String serializeType(final Type type) {
135         return serializeType(type, false);
136     }
137
138     private static String getReturnType(final AttributeIfc attributeIfc) {
139         String returnType;
140         if (attributeIfc instanceof TypedAttribute) {
141             Type type = ((TypedAttribute) attributeIfc).getType();
142             returnType = serializeType(type);
143         } else if (attributeIfc == VoidAttribute.getInstance()) {
144             return "void";
145         } else {
146             throw new UnsupportedOperationException(
147                     "Attribute not supported: "
148                             + attributeIfc.getClass());
149         }
150         return returnType;
151     }
152
153     public static GeneralInterfaceTemplate serviceInterfaceFromSie(
154             final ServiceInterfaceEntry sie) {
155
156         List<String> extendedInterfaces = Lists
157                 .newArrayList(AbstractServiceInterface.class.getCanonicalName());
158         if (sie.getBase().isPresent()) {
159             extendedInterfaces.add(sie.getBase().get().getFullyQualifiedName());
160         }
161
162         // FIXME header
163         GeneralInterfaceTemplate sieTemplate = new GeneralInterfaceTemplate(
164                 getHeaderFromEntry(sie), sie.getPackageName(),
165                 sie.getTypeName(), extendedInterfaces,
166                 Lists.<MethodDeclaration> newArrayList());
167         sieTemplate.setJavadoc(sie.getNullableDescription());
168
169         if (sie.getNullableDescription() != null) {
170             sieTemplate.getAnnotations().add(
171                     Annotation.createDescriptionAnnotation(sie
172                             .getNullableDescription()));
173         }
174         sieTemplate.getAnnotations().addAll(Annotation.createSieAnnotations(sie));
175
176         return sieTemplate;
177     }
178
179     public static AbstractFactoryTemplate abstractFactoryTemplateFromMbe(
180             final ModuleMXBeanEntry mbe) {
181         AbstractFactoryAttributesProcessor attrProcessor = new AbstractFactoryAttributesProcessor();
182         attrProcessor.processAttributes(mbe.getAttributes(),
183                 mbe.getPackageName());
184
185
186
187         return new AbstractFactoryTemplate(getHeaderFromEntry(mbe),
188                 mbe.getPackageName(), mbe.getAbstractFactoryName(),
189                 attrProcessor.getFields()
190         );
191     }
192
193     public static AbstractModuleTemplate abstractModuleTemplateFromMbe(
194             final ModuleMXBeanEntry mbe) {
195         AbstractModuleAttributesProcessor attrProcessor = new AbstractModuleAttributesProcessor(mbe.getAttributes());
196
197         List<ModuleField> moduleFields = attrProcessor.getModuleFields();
198         List<String> implementedIfcs = Lists.newArrayList(
199                 mbe.getFullyQualifiedName(mbe.getMXBeanInterfaceName()));
200
201         for (String implementedService : mbe.getProvidedServices().keySet()) {
202             implementedIfcs.add(implementedService);
203         }
204
205         boolean generateRuntime = false;
206         String registratorFullyQualifiedName = null;
207         if (mbe.getRuntimeBeans() != null
208                 && mbe.getRuntimeBeans().isEmpty() == false) {
209             generateRuntime = true;
210             RuntimeBeanEntry rootEntry = RuntimeRegistratorFtlTemplate
211                     .findRoot(mbe.getRuntimeBeans());
212             registratorFullyQualifiedName = rootEntry
213                     .getPackageName()
214                     .concat(".")
215                     .concat(RuntimeRegistratorFtlTemplate.getJavaNameOfRuntimeRegistrator(rootEntry));
216             implementedIfcs.add(RuntimeBeanRegistratorAwareModule.class
217                     .getCanonicalName());
218         }
219
220         List<String> extendedClasses = Collections.singletonList(AbstractModule.class.getCanonicalName() + "<" + mbe.getAbstractModuleName() + ">");
221
222         AbstractModuleTemplate abstractModuleTemplate = new AbstractModuleTemplate(
223                 getHeaderFromEntry(mbe), mbe.getPackageName(),
224                 mbe.getAbstractModuleName(), extendedClasses, implementedIfcs, moduleFields,
225                 attrProcessor.getMethods(), generateRuntime,
226                 registratorFullyQualifiedName);
227
228         if (mbe.getNullableDescription() != null) {
229             abstractModuleTemplate.getAnnotations().add(
230                     Annotation.createDescriptionAnnotation(mbe
231                             .getNullableDescription()));
232         }
233         return abstractModuleTemplate;
234     }
235
236     public static StubFactoryTemplate stubFactoryTemplateFromMbe(
237             final ModuleMXBeanEntry mbe) {
238         return new StubFactoryTemplate(getHeaderFromEntry(mbe),
239                 mbe.getPackageName(), mbe.getStubFactoryName(),
240                 mbe.getFullyQualifiedName(mbe.getAbstractFactoryName())
241         );
242     }
243
244     public static GeneralInterfaceTemplate mXBeanInterfaceTemplateFromMbe(
245             final ModuleMXBeanEntry mbe) {
246         MXBeanInterfaceAttributesProcessor attrProcessor = new MXBeanInterfaceAttributesProcessor();
247         attrProcessor.processAttributes(mbe.getAttributes());
248         GeneralInterfaceTemplate ifcTemplate = new GeneralInterfaceTemplate(
249                 getHeaderFromEntry(mbe), mbe.getPackageName(),
250                 mbe.getMXBeanInterfaceName(), Lists.<String> newArrayList(),
251                 attrProcessor.getMethods());
252         ifcTemplate.setJavadoc(mbe.getNullableDescription());
253         return ifcTemplate;
254     }
255
256     public static Map<String, GeneralClassTemplate> tOsFromMbe(
257             final ModuleMXBeanEntry mbe) {
258         Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
259         TOAttributesProcessor processor = new TOAttributesProcessor();
260         processor.processAttributes(mbe.getAttributes());
261         for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
262                 .getTOs()) {
263             List<Constructor> constructors = Lists.newArrayList();
264             constructors.add(new Constructor(to.getName(), "super();"));
265
266             Header header = getHeaderFromEntry(mbe);
267             retVal.put(
268                     to.getType(),
269                     new GeneralClassTemplate(header, mbe.getPackageName(), to
270                             .getName(), Collections.<String> emptyList(),
271                             Collections.<String> emptyList(), to.getFields(),
272                             to.getMethods(), false, false, constructors));
273         }
274         return retVal;
275     }
276
277     public static Map<String, GeneralClassTemplate> tOsFromRbe(
278             final RuntimeBeanEntry rbe) {
279         Map<String, GeneralClassTemplate> retVal = Maps.newHashMap();
280         TOAttributesProcessor processor = new TOAttributesProcessor();
281         Map<String, AttributeIfc> yangPropertiesToTypesMap = Maps.newHashMap(rbe.getYangPropertiesToTypesMap());
282
283         // Add TOs from output parameters
284         for (Rpc rpc : rbe.getRpcs()) {
285             AttributeIfc returnType = rpc.getReturnType();
286
287             if (returnType == VoidAttribute.getInstance()) {
288                 continue;
289             }
290             if (returnType instanceof JavaAttribute) {
291                 continue;
292             }
293             if (returnType instanceof ListAttribute && returnType.getOpenType() instanceof SimpleType) {
294                 continue;
295             }
296
297             Preconditions.checkState(yangPropertiesToTypesMap.containsKey(returnType.getAttributeYangName()) == false,
298                     "Duplicate TO %s for %s", returnType.getAttributeYangName(), rbe);
299             yangPropertiesToTypesMap.put(returnType.getAttributeYangName(), returnType);
300         }
301
302         processor.processAttributes(yangPropertiesToTypesMap);
303         for (org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.TemplateFactory.TOAttributesProcessor.TOInternal to : processor
304                 .getTOs()) {
305             List<Constructor> constructors = Lists.newArrayList();
306             constructors.add(new Constructor(to.getName(), "super();"));
307
308             // TODO header
309             retVal.put(
310                     to.getType(),
311                     new GeneralClassTemplate(null, rbe.getPackageName(), to
312                             .getName(), Collections.<String> emptyList(),
313                             Collections.<String> emptyList(), to.getFields(),
314                             to.getMethods(), false, false, constructors));
315         }
316         return retVal;
317     }
318
319     private static Header getHeaderFromEntry(final AbstractEntry mbe) {
320         return new Header(mbe.getYangModuleName(), mbe.getYangModuleLocalname());
321     }
322
323     // TODO refactor attribute processors
324
325     private static class TOAttributesProcessor {
326
327         private final List<TOInternal> tos = Lists.newArrayList();
328
329         void processAttributes(final Map<String, AttributeIfc> attributes) {
330             for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
331                 AttributeIfc attributeIfc = attrEntry.getValue();
332                 if (attributeIfc instanceof TOAttribute) {
333                     createTOInternal((TOAttribute) attributeIfc);
334                 }
335                 if (attributeIfc instanceof ListAttribute) {
336                     AttributeIfc innerAttr = ((ListAttribute) attributeIfc)
337                             .getInnerAttribute();
338                     if (innerAttr instanceof TOAttribute) {
339                         createTOInternal((TOAttribute) innerAttr);
340                     }
341                 }
342             }
343         }
344
345         private void createTOInternal(final TOAttribute toAttribute) {
346
347             Map<String, AttributeIfc> attrs = toAttribute.getCapitalizedPropertiesToTypesMap();
348             // recursive processing of TO's attributes
349             processAttributes(attrs);
350
351             tos.add(new TOInternal(toAttribute.getType(), attrs));
352         }
353
354         List<TOInternal> getTOs() {
355             return tos;
356         }
357
358         private static class TOInternal {
359             private final String fullyQualifiedName, name;
360             private List<Field> fields;
361             private List<MethodDefinition> methods;
362
363             public TOInternal(final Type type, final Map<String, AttributeIfc> attrs) {
364                 this(type.getFullyQualifiedName(), type.getName(), attrs, type.getPackageName());
365             }
366
367             public TOInternal(final String fullyQualifiedName, final String name,
368                     final Map<String, AttributeIfc> attrs, final String packageName) {
369                 this.fullyQualifiedName = fullyQualifiedName;
370                 this.name = name;
371                 processAttrs(attrs, packageName);
372             }
373
374             private final static String dependencyResolverVarName = "dependencyResolver";
375             private final static String dependencyResolverInjectMethodName = "injectDependencyResolver";
376
377             private void processAttrs(final Map<String, AttributeIfc> attrs, final String packageName) {
378                 fields = Lists.newArrayList();
379                 methods = Lists.newArrayList();
380
381                 // FIXME conflict if "dependencyResolver" field from yang
382                 Field depRes = new Field(DependencyResolver.class.getName(), dependencyResolverVarName);
383                 fields.add(depRes);
384                 methods.add(new MethodDefinition("void", dependencyResolverInjectMethodName, Lists.newArrayList(depRes),
385                         "this." + dependencyResolverVarName + " = " + dependencyResolverVarName + ";"));
386
387                 for (Entry<String, AttributeIfc> attrEntry : attrs.entrySet()) {
388                     String innerName = attrEntry.getKey();
389                     String varName = BindingMapping.getPropertyName(attrEntry.getKey());
390
391                     String fullyQualifiedName, nullableDefault = null;
392                     if (attrEntry.getValue() instanceof TypedAttribute) {
393                         Type type = ((TypedAttribute) attrEntry.getValue()).getType();
394                         if(attrEntry.getValue() instanceof JavaAttribute) {
395                             nullableDefault = ((JavaAttribute)attrEntry.getValue()).getNullableDefaultWrappedForCode();
396                             if(((JavaAttribute)attrEntry.getValue()).isIdentityRef()) {
397
398                                 String fieldType = serializeType(type, true);
399                                 String innerType = getInnerTypeFromIdentity(type);
400                                 methods.add(new MethodDefinition(fieldType, "resolve" + attrEntry.getKey(), Collections.<Field>emptyList(),
401                                         "return " + varName + ".resolveIdentity(" + dependencyResolverVarName + "," +  innerType + ".class);"));
402                                 type = identityRefType;
403                             }
404                         }
405                         fullyQualifiedName = serializeType(type);
406                     } else {
407                         fullyQualifiedName = FullyQualifiedNameHelper
408                                 .getFullyQualifiedName(packageName, attrEntry.getValue().getUpperCaseCammelCase());
409                     }
410                     fields.add(new Field(fullyQualifiedName, varName, nullableDefault, needsDepResolver(attrEntry.getValue())));
411
412                     String getterName = "get" + innerName;
413                     MethodDefinition getter = new MethodDefinition(
414                             fullyQualifiedName, getterName,
415                             Collections.<Field> emptyList(), "return "
416                                     + varName + ";");
417
418                     String setterName = "set" + innerName;
419                     MethodDefinition setter = new MethodDefinition("void",
420                             setterName, Lists.newArrayList(new Field(
421                                     fullyQualifiedName, varName)), "this."
422                                     + varName + " = " + varName + ";");
423                     methods.add(getter);
424                     methods.add(setter);
425                 }
426
427                 // Add hashCode
428                 final MethodDefinition hashCode = getHash(attrs);
429                 methods.add(hashCode);
430
431                 // Add equals
432                 final MethodDefinition equals = getEquals(attrs);
433                 methods.add(equals);
434             }
435
436             private MethodDefinition getEquals(final Map<String, AttributeIfc> attrs) {
437                 final StringBuilder equalsBodyBuilder = new StringBuilder(
438                         "        if (this == o) return true;\n" +
439                         "        if (o == null || getClass() != o.getClass()) return false;\n");
440                 equalsBodyBuilder.append(String.format(
441                         "        final %s that = (%s) o;\n", name, name));
442                 for (AttributeIfc s : attrs.values()) {
443                     equalsBodyBuilder.append(String.format(
444                             "        if(java.util.Objects.equals(%1$s, that.%1$s) == false) {\n" +
445                             "            return false;\n" +
446                             "        }\n\n", s.getLowerCaseCammelCase()));
447                 }
448                 equalsBodyBuilder.append(
449                         "       return true;\n");
450                 return new MethodDefinition("boolean", "equals", Collections.singletonList(new Field("Object", "o")),
451                         Collections.singletonList(new Annotation("Override", Collections.<Parameter>emptyList())), equalsBodyBuilder.toString());
452             }
453
454             private MethodDefinition getHash(final Map<String, AttributeIfc> attrs) {
455                 final StringBuilder hashBodyBuilder = new StringBuilder(
456                         "        return java.util.Objects.hash(");
457                 for (AttributeIfc s : attrs.values()) {
458                     hashBodyBuilder.append(s.getLowerCaseCammelCase());
459                     hashBodyBuilder.append(", ");
460                 }
461                 hashBodyBuilder.replace(hashBodyBuilder.length() - 2, hashBodyBuilder.length(), ");\n");
462                 return new MethodDefinition("int", "hashCode", Collections.<Field>emptyList(),
463                         Collections.singletonList(new Annotation("Override", Collections.<Parameter>emptyList())), hashBodyBuilder.toString());
464             }
465
466             String getType() {
467                 return fullyQualifiedName;
468             }
469
470             String getName() {
471                 return name;
472             }
473
474             List<Field> getFields() {
475                 return fields;
476             }
477
478             List<MethodDefinition> getMethods() {
479                 return methods;
480             }
481         }
482     }
483
484
485     private static class MXBeanInterfaceAttributesProcessor {
486         private final List<MethodDeclaration> methods = Lists.newArrayList();
487
488         void processAttributes(final Map<String, AttributeIfc> attributes) {
489             for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
490                 String returnType;
491                 AttributeIfc attributeIfc = attrEntry.getValue();
492
493                 if (attributeIfc instanceof TypedAttribute) {
494                     TypedAttribute typedAttribute = (TypedAttribute) attributeIfc;
495                     returnType = serializeType(typedAttribute.getType());
496
497                     if (attributeIfc instanceof JavaAttribute && ((JavaAttribute)attrEntry.getValue()).isIdentityRef()) {
498                         returnType = serializeType(identityRefType);
499                     }
500
501                 } else {
502                     throw new UnsupportedOperationException(
503                             "Attribute not supported: "
504                                     + attributeIfc.getClass());
505                 }
506
507                 String getterName = "get"
508                         + attributeIfc.getUpperCaseCammelCase();
509                 MethodDeclaration getter = new MethodDeclaration(returnType,
510                         getterName, Collections.<Field> emptyList());
511
512                 String varName = BindingMapping.getPropertyName(attrEntry.getKey());
513                 String setterName = "set"
514                         + attributeIfc.getUpperCaseCammelCase();
515                 MethodDeclaration setter = new MethodDeclaration("void",
516                         setterName, Lists.newArrayList(new Field(returnType,
517                                 varName)));
518
519                 methods.add(getter);
520                 methods.add(setter);
521
522                 if (attributeIfc.getNullableDescription() != null) {
523                     setter.setJavadoc(attrEntry.getValue()
524                             .getNullableDescription());
525                 }
526             }
527         }
528
529         List<MethodDeclaration> getMethods() {
530             return methods;
531         }
532     }
533
534     private static final Type identityRefType = new Type() {
535         public final Class<IdentityAttributeRef> IDENTITY_ATTRIBUTE_REF_CLASS = IdentityAttributeRef.class;
536
537         @Override
538         public String getPackageName() {
539             return IDENTITY_ATTRIBUTE_REF_CLASS.getPackage().getName();
540         }
541
542         @Override
543         public String getName() {
544             return IDENTITY_ATTRIBUTE_REF_CLASS.getSimpleName();
545         }
546
547         @Override
548         public String getFullyQualifiedName() {
549             return IDENTITY_ATTRIBUTE_REF_CLASS.getName();
550         }
551     };
552
553     private static class AbstractFactoryAttributesProcessor {
554
555         private final List<Field> fields = Lists.newArrayList();
556
557         void processAttributes(final Map<String, AttributeIfc> attributes,
558                 final String packageName) {
559             for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
560                 String type;
561                 String nullableDefaultWrapped = null;
562                 AttributeIfc attributeIfc = attrEntry.getValue();
563
564                 if (attributeIfc instanceof TypedAttribute) {
565                     TypedAttribute typedAttribute = (TypedAttribute) attributeIfc;
566                     type = serializeType(typedAttribute.getType());
567                 } else {
568                     throw new UnsupportedOperationException(
569                             "Attribute not supported: "
570                                     + attributeIfc.getClass());
571                 }
572
573                 fields.add(new Field(type, attributeIfc
574                         .getUpperCaseCammelCase(), nullableDefaultWrapped));
575             }
576         }
577
578         List<Field> getFields() {
579             return fields;
580         }
581     }
582
583     private static class AbstractModuleAttributesProcessor {
584         private static class Holder {
585             private final List<ModuleField> moduleFields;
586             private final List<MethodDefinition> methods;
587
588             private Holder(final List<ModuleField> moduleFields, final List<MethodDefinition> methods) {
589                 this.moduleFields = Collections.unmodifiableList(moduleFields);
590                 this.methods = Collections.unmodifiableList(methods);
591             }
592         }
593
594         private final Holder holder;
595
596
597         private AbstractModuleAttributesProcessor(final Map<String, AttributeIfc> attributes) {
598             this.holder = processAttributes(attributes);
599         }
600
601         private static Holder processAttributes(final Map<String, AttributeIfc> attributes) {
602             List<ModuleField> moduleFields = new ArrayList<>();
603             List<MethodDefinition> methods = new ArrayList<>();
604             for (Entry<String, AttributeIfc> attrEntry : attributes.entrySet()) {
605                 String type, nullableDefaultWrapped = null;
606                 AttributeIfc attributeIfc = attrEntry.getValue();
607                 boolean isIdentity = false;
608                 boolean needsDepResolver = needsDepResolver(attrEntry.getValue());
609
610                 if (attributeIfc instanceof TypedAttribute) {
611                     TypedAttribute typedAttribute = (TypedAttribute) attributeIfc;
612                     type = serializeType(typedAttribute.getType());
613                     if (attributeIfc instanceof JavaAttribute) {
614                         nullableDefaultWrapped = ((JavaAttribute) attributeIfc).getNullableDefaultWrappedForCode();
615                         if(((JavaAttribute)attrEntry.getValue()).isIdentityRef()) {
616                             isIdentity = true;
617                             type = serializeType(typedAttribute.getType(), true);
618                         }
619                     }
620                 } else {
621                     throw new UnsupportedOperationException(
622                             "Attribute not supported: "
623                                     + attributeIfc.getClass());
624                 }
625
626                 boolean isDependency = false;
627                 boolean isListOfDependencies = false;
628                 Dependency dependency = null;
629                 Annotation overrideAnnotation = new Annotation("Override",
630                         Collections.<Parameter> emptyList());
631                 List<Annotation> annotations = Lists
632                         .newArrayList(overrideAnnotation);
633
634                 if (attributeIfc instanceof AbstractDependencyAttribute) {
635                     isDependency = true;
636                     dependency = ((AbstractDependencyAttribute) attributeIfc)
637                             .getDependency();
638                     annotations.add(Annotation
639                             .createRequireIfcAnnotation(dependency.getSie()));
640                     if (attributeIfc instanceof ListDependenciesAttribute) {
641                         isListOfDependencies = true;
642                     }
643                 }
644
645                 String varName = BindingMapping.getPropertyName(attrEntry.getKey());
646
647                 ModuleField field;
648                 if (isIdentity) {
649                     String identityBaseClass = getInnerTypeFromIdentity(((TypedAttribute) attributeIfc).getType());
650                     IdentityRefModuleField identityField = new IdentityRefModuleField(type, varName,
651                             attributeIfc.getUpperCaseCammelCase(), identityBaseClass);
652
653                     String getterName = "get"
654                             + attributeIfc.getUpperCaseCammelCase() + "Identity";
655                     MethodDefinition additionalGetter = new MethodDefinition(type, getterName, Collections.<Field> emptyList(),
656                             Collections.<Annotation> emptyList(), "return " + identityField.getIdentityClassName()
657                                     + ";");
658                     methods.add(additionalGetter);
659
660                     String setterName = "set"
661                             + attributeIfc.getUpperCaseCammelCase();
662
663                     String setterBody = "this." + identityField.getIdentityClassName() + " = " + identityField.getIdentityClassName() + ";";
664                     MethodDefinition additionalSetter = new MethodDefinition("void",
665                             setterName,
666                             Lists.newArrayList(new Field(type, identityField.getIdentityClassName())),
667                             Collections.<Annotation> emptyList(), setterBody);
668                     additionalSetter.setJavadoc(attributeIfc.getNullableDescription());
669
670                     methods.add(additionalSetter);
671
672                     type = serializeType(identityRefType);
673                     field = identityField;
674                 } else {
675                     field = new ModuleField(type, varName, attributeIfc.getUpperCaseCammelCase(),
676                             nullableDefaultWrapped, isDependency, dependency, isListOfDependencies, needsDepResolver);
677                 }
678                 moduleFields.add(field);
679
680
681                 String getterName = "get"
682                         + attributeIfc.getUpperCaseCammelCase();
683                 MethodDefinition getter = new MethodDefinition(type,
684                         getterName, Collections.<Field> emptyList(),
685                         Lists.newArrayList(overrideAnnotation), "return "
686                         + varName + ";");
687
688                 methods.add(getter);
689
690                 String setterName = "set"
691                         + attributeIfc.getUpperCaseCammelCase();
692
693                 if (attributeIfc.getNullableDescription() != null) {
694                     annotations.add(Annotation
695                             .createDescriptionAnnotation(attributeIfc.getNullableDescription()));
696                 }
697
698                 String setterBody = "this." + varName + " = " + varName + ";";
699                 if (isListOfDependencies) {
700                     String nullCheck = String.format("if (%s == null) throw new IllegalArgumentException(\"Null not supported\");%n",
701                             varName);
702                     setterBody = nullCheck + setterBody;
703                 }
704                 MethodDefinition setter = new MethodDefinition("void",
705                         setterName,
706                         Lists.newArrayList(new Field(type, varName)),
707                         annotations, setterBody);
708                 setter.setJavadoc(attributeIfc.getNullableDescription());
709
710                 methods.add(setter);
711             }
712             return new Holder(moduleFields, methods);
713         }
714
715         List<ModuleField> getModuleFields() {
716             return holder.moduleFields;
717         }
718
719         List<MethodDefinition> getMethods() {
720             return holder.methods;
721         }
722
723     }
724
725
726     private static boolean needsDepResolver(final AttributeIfc value) {
727         if(value instanceof TOAttribute) {
728             return true;
729         }
730         if(value instanceof ListAttribute) {
731             AttributeIfc innerAttribute = ((ListAttribute) value).getInnerAttribute();
732             return needsDepResolver(innerAttribute);
733         }
734
735         return false;
736     }
737
738     private static String getInnerTypeFromIdentity(final Type type) {
739         Preconditions.checkArgument(type instanceof ParameterizedType);
740         Type[] args = ((ParameterizedType) type).getActualTypeArguments();
741         Preconditions.checkArgument(args.length ==1);
742         return serializeType(args[0]);
743     }
744 }