Yang code generator cleanup
[controller.git] / opendaylight / config / yang-jmx-generator-plugin / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / plugin / ftl / RuntimeRegistratorFtlTemplate.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 static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.lang.String.format;
13
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.collect.Lists;
16 import java.io.Closeable;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashMap;
21 import java.util.HashSet;
22 import java.util.LinkedHashMap;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Set;
27 import java.util.concurrent.atomic.AtomicInteger;
28 import org.opendaylight.controller.config.api.runtime.HierarchicalRuntimeBeanRegistration;
29 import org.opendaylight.controller.config.api.runtime.RootRuntimeBeanRegistrator;
30 import org.opendaylight.controller.config.yangjmxgenerator.RuntimeBeanEntry;
31 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation;
32 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Annotation.Parameter;
33 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.Field;
34 import org.opendaylight.controller.config.yangjmxgenerator.plugin.ftl.model.MethodDefinition;
35 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
36
37 public class RuntimeRegistratorFtlTemplate extends GeneralClassTemplate {
38
39     private RuntimeRegistratorFtlTemplate(RuntimeBeanEntry runtimeBeanEntry,
40             String name, List<Field> fields, List<MethodDefinition> methods) {
41         // TODO header
42         super(null, runtimeBeanEntry.getPackageName(), name, Collections
43                 .<String> emptyList(), Collections.singletonList(Closeable.class
44                 .getCanonicalName()), fields, methods);
45     }
46
47     public static RuntimeBeanEntry findRoot(
48             Collection<RuntimeBeanEntry> runtimeBeanEntries) {
49         RuntimeBeanEntry result = null;
50         for (RuntimeBeanEntry rb : runtimeBeanEntries) {
51             if (rb.isRoot()) {
52                 if (result != null) {
53                     throw new IllegalArgumentException(
54                             "More than one root runtime bean found");
55                 }
56                 result = rb;
57             }
58         }
59         if (result != null) {
60             return result;
61         }
62         throw new IllegalArgumentException("No root runtime bean found");
63     }
64
65     private static String constructConstructorBody(
66             List<Field> constructorParameters) {
67         StringBuilder constructorBody = new StringBuilder();
68         for (Field field : constructorParameters) {
69             constructorBody.append("this.");
70             constructorBody.append(field.getName());
71             constructorBody.append("=");
72             constructorBody.append(field.getName());
73             constructorBody.append(";\n");
74         }
75         return constructorBody.toString();
76     }
77
78     // TODO Move to factory
79     /**
80      * Get registrator and n registration ftls where n is equal to total number
81      * of runtime beans in hierarchy.
82      */
83     public static Map<String, FtlTemplate> create(RuntimeBeanEntry rootRB) {
84         checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root");
85         String registratorName = getJavaNameOfRuntimeRegistrator(rootRB);
86         List<MethodDefinition> methods = new ArrayList<>();
87         Field rootRuntimeBeanRegistratorField = new Field(
88                 Lists.newArrayList("final"),
89                 RootRuntimeBeanRegistrator.class.getName(),
90                 "rootRuntimeBeanRegistrator");
91         List<Field> constructorParameters = Lists
92                 .newArrayList(rootRuntimeBeanRegistratorField);
93         String constructorBody = constructConstructorBody(constructorParameters);
94         MethodDefinition constructor = MethodDefinition.createConstructor(
95                 registratorName, constructorParameters, constructorBody);
96         methods.add(constructor);
97
98         LinkedHashMap<String, RuntimeRegistratorFtlTemplate> RuntimeRegistratorFtlTemplates = createRegistrationHierarchy(
99                 rootRB, Collections.<String> emptySet());
100         RuntimeRegistratorFtlTemplate rootFtlFile = RuntimeRegistratorFtlTemplates
101                 .values().iterator().next();
102
103         {// add register(rootruntimemxbean)
104             String fullyQualifiedNameOfMXBean = FullyQualifiedNameHelper
105                     .getFullyQualifiedName(rootRB.getPackageName(), rootRB.getJavaNameOfRuntimeMXBean());
106             String childRegistratorFQN = rootFtlFile.getFullyQualifiedName();
107             Field rbParameter = new Field(fullyQualifiedNameOfMXBean, "rb");
108             StringBuilder registerBody = new StringBuilder();
109             registerBody.append(format("%s %s = this.%s.registerRoot(%s);\n",
110                     HierarchicalRuntimeBeanRegistration.class
111                             .getCanonicalName(), hierachicalRegistration
112                             .getName(), rootRuntimeBeanRegistratorField
113                             .getName(), rbParameter.getName()));
114             registerBody.append(format("return new %s(%s);\n",
115                     rootFtlFile.getFullyQualifiedName(),
116                     hierachicalRegistration.getName()));
117
118             MethodDefinition registerMethod = new MethodDefinition(
119                     childRegistratorFQN, "register",
120                     Collections.singletonList(rbParameter), registerBody.toString());
121             methods.add(registerMethod);
122         }
123
124         MethodDefinition closeRegistrator = createCloseMethodToCloseField(rootRuntimeBeanRegistratorField);
125         methods.add(closeRegistrator);
126
127         // TODO add header
128         GeneralClassTemplate registrator = new GeneralClassTemplate(null,
129                 rootRB.getPackageName(), registratorName,
130                 Collections.<String> emptyList(), Collections.singletonList(Closeable.class
131                 .getCanonicalName()), constructorParameters, methods);
132
133         checkState(!RuntimeRegistratorFtlTemplates.containsKey(registrator
134                 .getTypeDeclaration().getName()), "Name conflict: "
135                 + registrator.getTypeDeclaration().getName());
136         Map<String, FtlTemplate> result = new HashMap<>();
137         result.putAll(RuntimeRegistratorFtlTemplates);
138         result.put(registrator.getTypeDeclaration().getName(), registrator);
139         return result;
140     }
141
142     private static Field hierachicalRegistration = new Field(
143             Lists.newArrayList("final"),
144             HierarchicalRuntimeBeanRegistration.class.getCanonicalName(),
145             "registration");
146
147     // TODO move to factory + RuntimeBeanEntry
148     /**
149      * Create ftls representing registrations. First registration is represents
150      * parent.
151      *
152      * @return map containing java class name as key, instance representing the
153      *         java file as value.
154      */
155     private static LinkedHashMap<String, RuntimeRegistratorFtlTemplate> createRegistrationHierarchy(
156             RuntimeBeanEntry parent, Set<String> occupiedKeys) {
157         LinkedHashMap<String, RuntimeRegistratorFtlTemplate> unorderedResult = new LinkedHashMap<>();
158         List<MethodDefinition> methods = new ArrayList<>();
159
160         // hierarchy of ON is created as follows:
161         // root RB: <domain>, type=RuntimeBean
162         // 1st RB in hierarchy: <domain>, type=RuntimeBean, <java name of leaf
163         // list>: key or counter
164         // n-th RB in hierarchy has same ON as n-1, with added <java name of
165         // leaf list>: key or counter
166         if (occupiedKeys.contains(parent.getJavaNamePrefix())) {
167             throw new IllegalArgumentException(
168                     "Name conflict in runtime bean hierarchy - java name found more than "
169                             + "once. Consider using java-name extension. Conflicting name: "
170                             + parent.getJavaNamePrefix());
171         }
172         Set<String> currentOccupiedKeys = new HashSet<>(occupiedKeys);
173         currentOccupiedKeys.add(parent.getJavaNamePrefix());
174
175         Field registratorsMapField = new Field(Collections.singletonList("final"),
176                 TypeHelper.getGenericType(Map.class, String.class,
177                         AtomicInteger.class), "unkeyedMap", "new "
178                         + TypeHelper.getGenericType(HashMap.class,
179                                 String.class, AtomicInteger.class) + "()");
180
181         // create register methods for children
182         for (RuntimeBeanEntry child : parent.getChildren()) {
183             checkArgument(parent.getPackageName()
184                     .equals(child.getPackageName()), "Invalid package name");
185
186             // call itself recursively to generate child
187             // registrators/registrations
188             LinkedHashMap<String, RuntimeRegistratorFtlTemplate> childRegistratorMap = createRegistrationHierarchy(
189                     child, currentOccupiedKeys);
190             for (Entry<String, RuntimeRegistratorFtlTemplate> entry : childRegistratorMap
191                     .entrySet()) {
192                 if (unorderedResult.containsKey(entry.getKey())) {
193                     throw new IllegalStateException(
194                             "Conflicting name found while generating runtime registration:"
195                                     + entry.getKey());
196                 }
197                 unorderedResult.put(entry.getKey(), entry.getValue());
198             }
199
200             if (!childRegistratorMap.isEmpty()) {
201                 // first entry is the direct descendant according to the create
202                 // contract
203                 RuntimeRegistratorFtlTemplate childRegistrator = childRegistratorMap
204                         .values().iterator().next();
205                 StringBuilder body = new StringBuilder();
206                 String key, value;
207                 key = child.getJavaNamePrefix();
208                 body.append(format(
209                         "String key = \"%s\"; //TODO: check for conflicts\n",
210                         key));
211
212                 if (child.getKeyJavaName().isPresent()) {
213                     value = "bean.get" + child.getKeyJavaName().get() + "()";
214                     value = "String.valueOf(" + value + ")";
215                 } else {
216                     body.append("java.util.concurrent.atomic.AtomicInteger counter = unkeyedMap.get(key);\n"
217                             + "if (counter==null){\n"
218                             + "counter = new java.util.concurrent.atomic.AtomicInteger();\n"
219                             + "unkeyedMap.put(key, counter);\n" + "}\n");
220                     value = "String.valueOf(counter.incrementAndGet())";
221                 }
222                 body.append(format("String value = %s;\n", value));
223                 body.append(format("%s r = %s.register(key, value, bean);\n",
224                         HierarchicalRuntimeBeanRegistration.class
225                                 .getCanonicalName(), hierachicalRegistration
226                                 .getName()));
227                 body.append(format("return new %s(r);",
228                         childRegistrator.getFullyQualifiedName()));
229
230                 Field param = new Field(Lists.newArrayList("final"),
231                         child.getJavaNameOfRuntimeMXBean(), "bean");
232                 MethodDefinition register = new MethodDefinition(
233                         Collections.singletonList("synchronized"),
234                         childRegistrator.getFullyQualifiedName(), "register",
235                         Collections.singletonList(param), Collections.<String> emptyList(),
236                         Collections.<Annotation> emptyList(), body.toString());
237                 methods.add(register);
238
239             }
240         }
241
242         // create parent registration
243         String createdName = getJavaNameOfRuntimeRegistration(parent.getJavaNamePrefix());
244
245         List<Field> constructorParameters = Collections.singletonList(hierachicalRegistration);
246         String constructorBody = constructConstructorBody(constructorParameters);
247
248         MethodDefinition constructor = MethodDefinition.createConstructor(
249                 createdName, constructorParameters, constructorBody);
250
251         MethodDefinition closeRegistrator = createCloseMethodToCloseField(hierachicalRegistration);
252         methods.add(closeRegistrator);
253         methods.add(constructor);
254         List<Field> privateFields = Lists.newArrayList(registratorsMapField);
255         privateFields.addAll(constructorParameters);
256
257         RuntimeRegistratorFtlTemplate created = new RuntimeRegistratorFtlTemplate(
258                 parent, createdName, privateFields, methods);
259
260         LinkedHashMap<String, RuntimeRegistratorFtlTemplate> result = new LinkedHashMap<>();
261         result.put(created.getTypeDeclaration().getName(), created);
262         checkState(!unorderedResult.containsKey(created.getTypeDeclaration()
263                 .getName()), "Naming conflict: "
264                 + created.getTypeDeclaration().getName());
265         result.putAll(unorderedResult);
266         return result;
267     }
268
269     private static MethodDefinition createCloseMethodToCloseField(Field field) {
270         String body = field.getName() + ".close();";
271         // TODO Thrown exception breaks build
272         // return new MethodDefinition(Collections.<String> emptyList(), "void",
273         // "close", Collections.<Field> emptyList(),
274         // Arrays.asList(IOException.class.getCanonicalName()),
275         // Collections.<Annotation> emptyList(), body);
276         List<Annotation> annotations = Lists.newArrayList(new Annotation(
277                 "Override", Collections.<Parameter> emptyList()));
278         return new MethodDefinition(Collections.<String> emptyList(), "void",
279                 "close", Collections.<Field> emptyList(),
280                 Collections.<String> emptyList(), annotations, body);
281     }
282
283     @VisibleForTesting
284     public static String getJavaNameOfRuntimeRegistration(String javaNamePrefix) {
285         return javaNamePrefix + "RuntimeRegistration";
286     }
287
288     public static String getJavaNameOfRuntimeRegistrator(RuntimeBeanEntry rootRB) {
289         checkArgument(rootRB.isRoot(), "RuntimeBeanEntry must be root");
290         return rootRB.getJavaNamePrefix() + "RuntimeRegistrator";
291     }
292 }