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

©2013 OpenDaylight, A Linux Foundation Collaborative Project. All Rights Reserved.
OpenDaylight is a registered trademark of The OpenDaylight Project, Inc.
Linux Foundation and OpenDaylight are registered trademarks of the Linux Foundation.
Linux is a registered trademark of Linus Torvalds.