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