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