2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.controller.config.yangjmxgenerator;
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Optional;
12 import com.google.common.collect.Lists;
13 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
14 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
15 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
16 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
17 import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
18 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
19 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
20 import org.opendaylight.yangtools.yang.common.QName;
21 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
22 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
24 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
26 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
27 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
28 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
29 import org.opendaylight.yangtools.yang.model.api.Module;
30 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
31 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
32 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.UsesNode;
35 import java.util.ArrayList;
36 import java.util.Collection;
37 import java.util.Collections;
38 import java.util.Comparator;
39 import java.util.Deque;
40 import java.util.HashMap;
41 import java.util.HashSet;
42 import java.util.LinkedList;
43 import java.util.List;
46 import java.util.TreeSet;
48 import static com.google.common.base.Preconditions.checkArgument;
49 import static com.google.common.base.Preconditions.checkState;
52 * Holds information about runtime bean to be generated. There are two kinds of
53 * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
54 * represents state that must be present at time of configuration module
55 * instantiation. Root RB must have depthLevel set to 0 and cannot have
56 * children. There might be other RBs defined in yang, but no other RB can have
57 * isRoot set to true. At least one RB must be root and all other RBs must be
58 * lined via children so that a tree with all beans can be created.
60 public class RuntimeBeanEntry {
61 private final String packageName;
62 private final String yangName, javaNamePrefix;
63 private final boolean isRoot;
64 private final Optional<String> keyYangName, keyJavaName;
65 private final Map<String, AttributeIfc> attributeMap;
66 private final List<RuntimeBeanEntry> children;
67 private final Set<Rpc> rpcs;
70 RuntimeBeanEntry(String packageName,
71 DataSchemaNode nodeForReporting, String yangName,
72 String javaNamePrefix, boolean isRoot,
73 Optional<String> keyYangName, List<AttributeIfc> attributes,
74 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
76 checkArgument(isRoot == false || keyYangName.isPresent() == false,
77 "Root RuntimeBeanEntry must not have key " + "set");
78 this.packageName = packageName;
80 this.yangName = yangName;
81 this.javaNamePrefix = javaNamePrefix;
82 this.children = Collections.unmodifiableList(children);
83 this.rpcs = Collections.unmodifiableSet(rpcs);
85 this.keyYangName = keyYangName;
86 Map<String, AttributeIfc> map = new HashMap<>();
88 for (AttributeIfc a : attributes) {
89 checkState(map.containsKey(a.getAttributeYangName()) == false,
90 "Attribute already defined: " + a.getAttributeYangName()
91 + " in " + nodeForReporting);
92 map.put(a.getAttributeYangName(), a);
95 if (keyYangName.isPresent()) {
96 AttributeIfc keyJavaName = map.get(keyYangName.get());
97 checkArgument(keyJavaName != null, "Key " + keyYangName.get()
98 + " not found in attribute " + "list " + attributes
99 + " in " + nodeForReporting);
100 this.keyJavaName = Optional
101 .of(keyJavaName.getUpperCaseCammelCase());
103 keyJavaName = Optional.absent();
105 attributeMap = Collections.unmodifiableMap(map);
109 * @return map containing all class names as key, extracted RuntimeBeans as
110 * values. If more than zero values is returned, exactly one
111 * RuntimeBeanEntry will have isRoot set to true, even if yang does
112 * not contain special configuration for it.
114 public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
115 String packageName, ChoiceCaseNode container,
116 String moduleYangName, TypeProviderWrapper typeProviderWrapper,
117 String javaNamePrefix, Module currentModule) {
119 Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
121 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
122 packageName, container, typeProviderWrapper, currentModule,
124 Map<String, RuntimeBeanEntry> result = new HashMap<>();
126 List<AttributeIfc> attributes;
128 if (attributesRpcsAndRuntimeBeans.isEmpty() == false) {
129 attributes = attributesRpcsAndRuntimeBeans.getAttributes();
130 rpcs = attributesRpcsAndRuntimeBeans.getRpcs();
132 // create artificial root if not defined in yang
133 attributes = Collections.emptyList();
134 rpcs = Collections.emptySet();
136 RuntimeBeanEntry rootRuntimeBeanEntry = createRoot(packageName,
137 container, moduleYangName, attributes, javaNamePrefix,
138 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(), rpcs);
140 Deque<RuntimeBeanEntry> stack = new LinkedList<>();
141 stack.add(rootRuntimeBeanEntry);
143 while (stack.isEmpty() == false) {
144 RuntimeBeanEntry first = stack.pollFirst();
145 if (result.containsKey(first.getJavaNameOfRuntimeMXBean())) {
146 throw new NameConflictException(
147 first.getJavaNameOfRuntimeMXBean(), null, null);
149 result.put(first.getJavaNameOfRuntimeMXBean(), first);
150 stack.addAll(first.getChildren());
155 private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
156 Module currentModule) {
157 // currently only looks for local identities (found in currentModule)
158 Map<QName, Set<RpcDefinition>> result = new HashMap<>();
159 for (IdentitySchemaNode identity : currentModule.getIdentities()) {
161 result.put(identity.getQName(), new HashSet<RpcDefinition>());
164 for (RpcDefinition rpc : currentModule.getRpcs()) {
165 ContainerSchemaNode input = rpc.getInput();
167 for (UsesNode uses : input.getUses()) {
169 if (uses.getGroupingPath().getPath().size() != 1)
172 // check grouping path
173 QName qname = uses.getGroupingPath().getPath().get(0);
175 .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
178 for (SchemaNode refinedNode : uses.getRefines().values()) {
180 for (UnknownSchemaNode unknownSchemaNode : refinedNode
181 .getUnknownSchemaNodes()) {
182 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
183 .equals(unknownSchemaNode.getNodeType())) {
184 String localIdentityName = unknownSchemaNode
186 QName identityQName = new QName(
187 currentModule.getNamespace(),
188 currentModule.getRevision(),
190 Set<RpcDefinition> rpcDefinitions = result
192 if (rpcDefinitions == null) {
193 throw new IllegalArgumentException(
194 "Identity referenced by rpc not found. Identity:"
195 + localIdentityName + " , rpc "
198 rpcDefinitions.add(rpc);
209 * Get direct descendants of this subtree, together with attributes defined
212 private static AttributesRpcsAndRuntimeBeans extractSubtree(
213 String packageName, DataNodeContainer subtree,
214 TypeProviderWrapper typeProviderWrapper, Module currentModule,
215 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
217 List<AttributeIfc> attributes = Lists.newArrayList();
218 // List<JavaAttribute> javaAttributes = new ArrayList<>();
219 // List<TOAttribute> toAttributes = new ArrayList<>();
220 List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
221 for (DataSchemaNode child : subtree.getChildNodes()) {
222 // child leaves can be java attributes, TO attributes, or child
224 if (child instanceof LeafSchemaNode) {
225 // just save the attribute
226 LeafSchemaNode leaf = (LeafSchemaNode) child;
227 attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
228 } else if (child instanceof ContainerSchemaNode) {
229 ContainerSchemaNode container = (ContainerSchemaNode) child;
230 // this can be either TO or hierarchical RB
231 TOAttribute toAttribute = TOAttribute.create(container,
232 typeProviderWrapper, packageName);
233 attributes.add(toAttribute);
234 } else if (child instanceof ListSchemaNode) {
235 if (isInnerStateBean(child)) {
236 ListSchemaNode listSchemaNode = (ListSchemaNode) child;
237 RuntimeBeanEntry hierarchicalChild = createHierarchical(
238 packageName, listSchemaNode, typeProviderWrapper,
239 currentModule, identitiesToRpcs);
240 runtimeBeanEntries.add(hierarchicalChild);
241 } else /* ordinary list attribute */{
242 ListAttribute listAttribute = ListAttribute.create(
243 (ListSchemaNode) child, typeProviderWrapper, packageName);
244 attributes.add(listAttribute);
247 } else if (child instanceof LeafListSchemaNode) {
248 ListAttribute listAttribute = ListAttribute.create(
249 (LeafListSchemaNode) child, typeProviderWrapper);
250 attributes.add(listAttribute);
252 throw new IllegalStateException("Unexpected running-data node "
256 Set<Rpc> rpcs = new HashSet<>();
257 SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
258 for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
259 .getUnknownSchemaNodes()) {
260 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
261 .equals(unknownSchemaNode.getNodeType())) {
262 String localIdentityName = unknownSchemaNode.getNodeParameter();
263 QName identityQName = new QName(currentModule.getNamespace(),
264 currentModule.getRevision(), localIdentityName);
265 Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
267 if (rpcDefinitions == null) {
268 throw new IllegalArgumentException("Cannot find identity "
269 + localIdentityName + " to be used as "
270 + "context reference when resolving "
271 + unknownSchemaNode);
273 // convert RpcDefinition to Rpc
274 for (RpcDefinition rpcDefinition : rpcDefinitions) {
275 String name = TypeProviderWrapper
276 .findJavaParameter(rpcDefinition);
277 AttributeIfc returnType;
278 if (rpcDefinition.getOutput() == null
279 || rpcDefinition.getOutput().getChildNodes().size() == 0) {
280 returnType = VoidAttribute.getInstance();
281 } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
282 DataSchemaNode returnDSN = rpcDefinition.getOutput()
283 .getChildNodes().iterator().next();
284 returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper, packageName);
287 throw new IllegalArgumentException(
288 "More than one child node in rpc output is not supported. "
289 + "Error occured in " + rpcDefinition);
291 List<JavaAttribute> parameters = new ArrayList<>();
292 for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
294 if (childNode.isAddedByUses() == false) { // skip
297 checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
298 + "Currently only leafs and empty output nodes are supported, got " + childNode);
299 JavaAttribute javaAttribute = new JavaAttribute(
300 (LeafSchemaNode) childNode,
301 typeProviderWrapper);
302 parameters.add(javaAttribute);
305 Rpc newRpc = new Rpc(returnType, name, rpcDefinition
306 .getQName().getLocalName(), parameters);
311 return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
315 private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper,
316 String packageName) {
317 if (child instanceof LeafSchemaNode) {
318 LeafSchemaNode leaf = (LeafSchemaNode) child;
319 return new JavaAttribute(leaf, typeProviderWrapper);
320 } else if (child instanceof ContainerSchemaNode) {
321 ContainerSchemaNode container = (ContainerSchemaNode) child;
322 TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper, packageName);
324 } else if (child instanceof ListSchemaNode) {
325 return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper, packageName);
326 } else if (child instanceof LeafListSchemaNode) {
327 return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper);
329 throw new IllegalStateException("Unknown output data node " + child + " for rpc");
333 private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
334 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
336 public int compare(DataSchemaNode o1, DataSchemaNode o2) {
337 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
340 dataSchemaNodes.addAll(childNodes);
341 return dataSchemaNodes;
344 private static boolean isInnerStateBean(DataSchemaNode child) {
345 for (UnknownSchemaNode unknownSchemaNode : child
346 .getUnknownSchemaNodes()) {
347 if (unknownSchemaNode.getNodeType().equals(
348 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
354 private static RuntimeBeanEntry createHierarchical(String packageName,
355 ListSchemaNode listSchemaNode,
356 TypeProviderWrapper typeProviderWrapper, Module currentModule,
357 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
359 // supported are numeric types, strings, enums
360 // get all attributes
361 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
362 packageName, listSchemaNode, typeProviderWrapper,
363 currentModule, identitiesToRpcs);
365 Optional<String> keyYangName;
366 if (listSchemaNode.getKeyDefinition().size() == 0) {
367 keyYangName = Optional.absent();
368 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
369 // key must be either null or one of supported key types
370 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
372 keyYangName = Optional.of(keyQName.getLocalName());
375 throw new IllegalArgumentException(
376 "More than one key is not supported in " + listSchemaNode);
379 String javaNamePrefix = TypeProviderWrapper
380 .findJavaNamePrefix(listSchemaNode);
382 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
383 listSchemaNode, listSchemaNode.getQName().getLocalName(),
384 javaNamePrefix, false, keyYangName,
385 attributesRpcsAndRuntimeBeans.getAttributes(),
386 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
387 attributesRpcsAndRuntimeBeans.getRpcs());
389 return rbFromAttributes;
392 private static RuntimeBeanEntry createRoot(String packageName,
393 DataSchemaNode nodeForReporting, String attributeYangName,
394 List<AttributeIfc> attributes, String javaNamePrefix,
395 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
396 return new RuntimeBeanEntry(packageName, nodeForReporting,
397 attributeYangName, javaNamePrefix, true,
398 Optional.<String> absent(), attributes, children, rpcs);
401 public boolean isRoot() {
405 public Optional<String> getKeyYangName() {
409 public Optional<String> getKeyJavaName() {
413 public Collection<AttributeIfc> getAttributes() {
414 return attributeMap.values();
417 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
421 public String getYangName() {
425 public String getPackageName() {
429 public String getJavaNamePrefix() {
430 return javaNamePrefix;
433 public List<RuntimeBeanEntry> getChildren() {
437 public Set<Rpc> getRpcs() {
441 private static class AttributesRpcsAndRuntimeBeans {
442 private final List<RuntimeBeanEntry> runtimeBeanEntries;
443 private final List<AttributeIfc> attributes;
444 private final Set<Rpc> rpcs;
446 public AttributesRpcsAndRuntimeBeans(
447 List<RuntimeBeanEntry> runtimeBeanEntries,
448 List<AttributeIfc> attributes, Set<Rpc> rpcs) {
449 this.runtimeBeanEntries = runtimeBeanEntries;
450 this.attributes = attributes;
454 private List<AttributeIfc> getAttributes() {
458 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
459 return runtimeBeanEntries;
462 public boolean isEmpty() {
463 return attributes.isEmpty() && rpcs.isEmpty();
466 private Set<Rpc> getRpcs() {
471 public static class Rpc {
472 private final String name;
473 private final List<JavaAttribute> parameters;
474 private final AttributeIfc returnType;
475 private final String yangName;
477 Rpc(AttributeIfc returnType, String name, String yangName,
478 List<JavaAttribute> parameters) {
479 this.returnType = returnType;
481 this.parameters = parameters;
482 this.yangName = yangName;
485 public String getYangName() {
489 public String getName() {
493 public List<JavaAttribute> getParameters() {
497 public AttributeIfc getReturnType() {
502 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
504 public String getJavaNameOfRuntimeMXBean() {
505 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
508 public String getFullyQualifiedName(String typeName) {
509 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
513 private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
514 return javaNamePrefix + MXBEAN_SUFFIX;
518 public String toString() {
519 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
520 + yangName + '\'' + ", packageName='" + packageName + '\''
521 + ", keyYangName=" + keyYangName + '}';