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 static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Preconditions.checkState;
13 import com.google.common.annotations.VisibleForTesting;
14 import com.google.common.base.Optional;
15 import com.google.common.collect.Lists;
17 import java.util.ArrayList;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Comparator;
21 import java.util.Deque;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.LinkedList;
25 import java.util.List;
28 import java.util.TreeSet;
30 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
31 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
32 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
33 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
34 import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
35 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
36 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
37 import org.opendaylight.yangtools.yang.common.QName;
38 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
40 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.Module;
46 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
47 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.UsesNode;
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(final String packageName,
71 final DataNodeContainer nodeForReporting, final String yangName,
72 final String javaNamePrefix, final boolean isRoot,
73 final Optional<String> keyYangName, final List<AttributeIfc> attributes,
74 final List<RuntimeBeanEntry> children, final 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 final String packageName, final DataNodeContainer container,
116 final String moduleYangName, final TypeProviderWrapper typeProviderWrapper,
117 final String javaNamePrefix, final 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 final 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) {
173 // check grouping path
174 QName qname = uses.getGroupingPath().getPath().get(0);
176 .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME)) {
180 for (SchemaNode refinedNode : uses.getRefines().values()) {
182 for (UnknownSchemaNode unknownSchemaNode : refinedNode
183 .getUnknownSchemaNodes()) {
184 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
185 .equals(unknownSchemaNode.getNodeType())) {
186 String localIdentityName = unknownSchemaNode
188 QName identityQName = QName.create(
189 currentModule.getNamespace(),
190 currentModule.getRevision(),
192 Set<RpcDefinition> rpcDefinitions = result
194 if (rpcDefinitions == null) {
195 throw new IllegalArgumentException(
196 "Identity referenced by rpc not found. Identity:"
197 + localIdentityName + " , rpc "
200 rpcDefinitions.add(rpc);
211 * Get direct descendants of this subtree, together with attributes defined
214 private static AttributesRpcsAndRuntimeBeans extractSubtree(
215 final String packageName, final DataNodeContainer subtree,
216 final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
217 final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
219 List<AttributeIfc> attributes = Lists.newArrayList();
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 = QName.create(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().isEmpty()) {
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(final DataSchemaNode child, final TypeProviderWrapper typeProviderWrapper,
316 final 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(final Collection<DataSchemaNode> childNodes) {
334 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
336 public int compare(final DataSchemaNode o1, final DataSchemaNode o2) {
337 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
340 dataSchemaNodes.addAll(childNodes);
341 return dataSchemaNodes;
344 private static boolean isInnerStateBean(final DataSchemaNode child) {
345 for (UnknownSchemaNode unknownSchemaNode : child
346 .getUnknownSchemaNodes()) {
347 if (unknownSchemaNode.getNodeType().equals(
348 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME)) {
355 private static RuntimeBeanEntry createHierarchical(final String packageName,
356 final ListSchemaNode listSchemaNode,
357 final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
358 final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
360 // supported are numeric types, strings, enums
361 // get all attributes
362 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
363 packageName, listSchemaNode, typeProviderWrapper,
364 currentModule, identitiesToRpcs);
366 Optional<String> keyYangName;
367 if (listSchemaNode.getKeyDefinition().isEmpty()) {
368 keyYangName = Optional.absent();
369 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
370 // key must be either null or one of supported key types
371 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
373 keyYangName = Optional.of(keyQName.getLocalName());
376 throw new IllegalArgumentException(
377 "More than one key is not supported in " + listSchemaNode);
380 String javaNamePrefix = TypeProviderWrapper
381 .findJavaNamePrefix(listSchemaNode);
383 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
384 listSchemaNode, listSchemaNode.getQName().getLocalName(),
385 javaNamePrefix, false, keyYangName,
386 attributesRpcsAndRuntimeBeans.getAttributes(),
387 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
388 attributesRpcsAndRuntimeBeans.getRpcs());
390 return rbFromAttributes;
393 private static RuntimeBeanEntry createRoot(final String packageName,
394 final DataNodeContainer nodeForReporting, final String attributeYangName,
395 final List<AttributeIfc> attributes, final String javaNamePrefix,
396 final List<RuntimeBeanEntry> children, final Set<Rpc> rpcs) {
397 return new RuntimeBeanEntry(packageName, nodeForReporting,
398 attributeYangName, javaNamePrefix, true,
399 Optional.<String> absent(), attributes, children, rpcs);
402 public boolean isRoot() {
406 public Optional<String> getKeyYangName() {
410 public Optional<String> getKeyJavaName() {
414 public Collection<AttributeIfc> getAttributes() {
415 return attributeMap.values();
418 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
422 public String getYangName() {
426 public String getPackageName() {
430 public String getJavaNamePrefix() {
431 return javaNamePrefix;
434 public List<RuntimeBeanEntry> getChildren() {
438 public Set<Rpc> getRpcs() {
442 private static class AttributesRpcsAndRuntimeBeans {
443 private final List<RuntimeBeanEntry> runtimeBeanEntries;
444 private final List<AttributeIfc> attributes;
445 private final Set<Rpc> rpcs;
447 public AttributesRpcsAndRuntimeBeans(
448 final List<RuntimeBeanEntry> runtimeBeanEntries,
449 final List<AttributeIfc> attributes, final Set<Rpc> rpcs) {
450 this.runtimeBeanEntries = runtimeBeanEntries;
451 this.attributes = attributes;
455 private List<AttributeIfc> getAttributes() {
459 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
460 return runtimeBeanEntries;
463 public boolean isEmpty() {
464 return attributes.isEmpty() && rpcs.isEmpty();
467 private Set<Rpc> getRpcs() {
472 public static class Rpc {
473 private final String name;
474 private final List<JavaAttribute> parameters;
475 private final AttributeIfc returnType;
476 private final String yangName;
478 Rpc(final AttributeIfc returnType, final String name, final String yangName,
479 final List<JavaAttribute> parameters) {
480 this.returnType = returnType;
482 this.parameters = parameters;
483 this.yangName = yangName;
486 public String getYangName() {
490 public String getName() {
494 public List<JavaAttribute> getParameters() {
498 public AttributeIfc getReturnType() {
503 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
505 public String getJavaNameOfRuntimeMXBean() {
506 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
509 public String getFullyQualifiedName(final String typeName) {
510 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
514 private static String getJavaNameOfRuntimeMXBean(final String javaNamePrefix) {
515 return javaNamePrefix + MXBEAN_SUFFIX;
519 public String toString() {
520 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
521 + yangName + '\'' + ", packageName='" + packageName + '\''
522 + ", keyYangName=" + keyYangName + '}';