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 java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Deque;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.LinkedList;
20 import java.util.List;
24 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
25 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
26 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
27 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
28 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
29 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
30 import org.opendaylight.yangtools.yang.common.QName;
31 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
32 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
33 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
34 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
35 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.Module;
39 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
40 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.UsesNode;
44 import com.google.common.annotations.VisibleForTesting;
45 import com.google.common.base.Optional;
46 import com.google.common.collect.Lists;
49 * Holds information about runtime bean to be generated. There are two kinds of
50 * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
51 * represents state that must be present at time of configuration module
52 * instantiation. Root RB must have depthLevel set to 0 and cannot have
53 * children. There might be other RBs defined in yang, but no other RB can have
54 * isRoot set to true. At least one RB must be root and all other RBs must be
55 * lined via children so that a tree with all beans can be created.
57 public class RuntimeBeanEntry {
58 private final String packageName;
59 private final String yangName, javaNamePrefix;
60 private final boolean isRoot;
61 private final Optional<String> keyYangName, keyJavaName;
62 private final Map<String, AttributeIfc> attributeMap;
63 private final List<RuntimeBeanEntry> children;
64 private final Set<Rpc> rpcs;
67 public RuntimeBeanEntry(String packageName,
68 DataSchemaNode nodeForReporting, String yangName,
69 String javaNamePrefix, boolean isRoot,
70 Optional<String> keyYangName, List<AttributeIfc> attributes,
71 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
73 checkArgument(isRoot == false || keyYangName.isPresent() == false,
74 "Root RuntimeBeanEntry must not have key " + "set");
75 this.packageName = packageName;
77 this.yangName = yangName;
78 this.javaNamePrefix = javaNamePrefix;
79 this.children = Collections.unmodifiableList(children);
80 this.rpcs = Collections.unmodifiableSet(rpcs);
82 this.keyYangName = keyYangName;
83 Map<String, AttributeIfc> map = new HashMap<>();
85 for (AttributeIfc a : attributes) {
86 checkState(map.containsKey(a.getAttributeYangName()) == false,
87 "Attribute already defined: " + a.getAttributeYangName()
88 + " in " + nodeForReporting);
89 map.put(a.getAttributeYangName(), a);
92 if (keyYangName.isPresent()) {
93 AttributeIfc keyJavaName = map.get(keyYangName.get());
94 checkArgument(keyJavaName != null, "Key " + keyYangName.get()
95 + " not found in attribute " + "list " + attributes
96 + " in " + nodeForReporting);
97 this.keyJavaName = Optional
98 .of(keyJavaName.getUpperCaseCammelCase());
100 keyJavaName = Optional.absent();
102 attributeMap = Collections.unmodifiableMap(map);
106 * @return map containing all class names as key, extracted RuntimeBeans as
107 * values. If more than zero values is returned, exactly one
108 * RuntimeBeanEntry will have isRoot set to true, even if yang does
109 * not contain special configuration for it.
111 public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
112 String packageName, ChoiceCaseNode container,
113 String moduleYangName, TypeProviderWrapper typeProviderWrapper,
114 String javaNamePrefix, Module currentModule) {
116 Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
118 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
119 packageName, container, typeProviderWrapper, currentModule,
121 Map<String, RuntimeBeanEntry> result = new HashMap<>();
123 List<AttributeIfc> attributes;
125 if (attributesRpcsAndRuntimeBeans.isEmpty() == false) {
126 attributes = attributesRpcsAndRuntimeBeans.getAttributes();
127 rpcs = attributesRpcsAndRuntimeBeans.getRpcs();
129 // create artificial root if not defined in yang
130 attributes = Collections.emptyList();
131 rpcs = Collections.emptySet();
133 RuntimeBeanEntry rootRuntimeBeanEntry = createRoot(packageName,
134 container, moduleYangName, attributes, javaNamePrefix,
135 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(), rpcs);
137 Deque<RuntimeBeanEntry> stack = new LinkedList<>();
138 stack.add(rootRuntimeBeanEntry);
140 while (stack.isEmpty() == false) {
141 RuntimeBeanEntry first = stack.pollFirst();
142 if (result.containsKey(first.getJavaNameOfRuntimeMXBean())) {
143 throw new NameConflictException(
144 first.getJavaNameOfRuntimeMXBean(), null, null);
146 result.put(first.getJavaNameOfRuntimeMXBean(), first);
147 stack.addAll(first.getChildren());
152 private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
153 Module currentModule) {
154 // currently only looks for local identities (found in currentModule)
155 Map<QName, Set<RpcDefinition>> result = new HashMap<>();
156 for (IdentitySchemaNode identity : currentModule.getIdentities()) {
158 result.put(identity.getQName(), new HashSet<RpcDefinition>());
161 for (RpcDefinition rpc : currentModule.getRpcs()) {
162 ContainerSchemaNode input = rpc.getInput();
163 for (UsesNode uses : input.getUses()) {
165 if (uses.getGroupingPath().getPath().size() != 1)
168 // check grouping path
169 QName qname = uses.getGroupingPath().getPath().get(0);
171 .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
174 for (SchemaNode refinedNode : uses.getRefines().values()) {
176 for (UnknownSchemaNode unknownSchemaNode : refinedNode
177 .getUnknownSchemaNodes()) {
178 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
179 .equals(unknownSchemaNode.getNodeType())) {
180 String localIdentityName = unknownSchemaNode
182 QName identityQName = new QName(
183 currentModule.getNamespace(),
184 currentModule.getRevision(),
186 Set<RpcDefinition> rpcDefinitions = result
188 if (rpcDefinitions == null) {
189 throw new IllegalArgumentException(
190 "Identity referenced by rpc not found. Identity:"
191 + localIdentityName + " , rpc "
194 rpcDefinitions.add(rpc);
205 * Get direct descendants of this subtree, together with attributes defined
208 private static AttributesRpcsAndRuntimeBeans extractSubtree(
209 String packageName, DataNodeContainer subtree,
210 TypeProviderWrapper typeProviderWrapper, Module currentModule,
211 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
213 List<AttributeIfc> attributes = Lists.newArrayList();
214 // List<JavaAttribute> javaAttributes = new ArrayList<>();
215 // List<TOAttribute> toAttributes = new ArrayList<>();
216 List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
217 for (DataSchemaNode child : subtree.getChildNodes()) {
218 // child leaves can be java attributes, TO attributes, or child
220 if (child instanceof LeafSchemaNode) {
221 // just save the attribute
222 LeafSchemaNode leaf = (LeafSchemaNode) child;
223 attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
224 } else if (child instanceof ContainerSchemaNode) {
225 ContainerSchemaNode container = (ContainerSchemaNode) child;
226 // this can be either TO or hierarchical RB
227 TOAttribute toAttribute = TOAttribute.create(container,
228 typeProviderWrapper);
229 attributes.add(toAttribute);
230 } else if (child instanceof ListSchemaNode) {
231 if (isInnerStateBean(child)) {
232 ListSchemaNode listSchemaNode = (ListSchemaNode) child;
233 RuntimeBeanEntry hierarchicalChild = createHierarchical(
234 packageName, listSchemaNode, typeProviderWrapper,
235 currentModule, identitiesToRpcs);
236 runtimeBeanEntries.add(hierarchicalChild);
237 } else /* ordinary list attribute */{
238 ListAttribute listAttribute = ListAttribute.create(
239 (ListSchemaNode) child, typeProviderWrapper);
240 attributes.add(listAttribute);
244 throw new IllegalStateException("Unknown running-data node "
245 + child + " , " + "" + "expected leaf or container");
248 Set<Rpc> rpcs = new HashSet<>();
249 SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
250 for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
251 .getUnknownSchemaNodes()) {
252 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
253 .equals(unknownSchemaNode.getNodeType())) {
254 String localIdentityName = unknownSchemaNode.getNodeParameter();
255 QName identityQName = new QName(currentModule.getNamespace(),
256 currentModule.getRevision(), localIdentityName);
257 Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
259 if (rpcDefinitions == null) {
260 throw new IllegalArgumentException("Cannot find identity "
261 + localIdentityName + " to be used as "
262 + "context reference when resolving "
263 + unknownSchemaNode);
265 // convert RpcDefinition to Rpc
266 for (RpcDefinition rpcDefinition : rpcDefinitions) {
267 String name = ModuleMXBeanEntry
268 .findJavaParameter(rpcDefinition);
270 if (rpcDefinition.getOutput() == null
271 || rpcDefinition.getOutput().getChildNodes().size() == 0) {
273 } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
274 DataSchemaNode returnDSN = rpcDefinition.getOutput()
275 .getChildNodes().iterator().next();
277 returnDSN instanceof LeafSchemaNode,
278 "Unexpected type of rpc return type. "
279 + "Currently only leafs and empty output nodes are supported, got "
281 LeafSchemaNode returnLeaf = (LeafSchemaNode) returnDSN;
282 // We currently expect leaf defined in output element in yang to be named result
283 // FIXME: value of result is fully qualified name - should be extended to accept TOs
284 String localName = returnLeaf.getQName().getLocalName();
286 localName.equals("result"),
287 "Unexpected name of leaf in output element, expected leaf named result, was %s at %s",
288 localName, currentModule.getName());
290 returnType = typeProviderWrapper.getType(returnLeaf)
291 .getFullyQualifiedName();
293 throw new IllegalArgumentException(
294 "More than one child node in rpc output is not supported. "
295 + "Error occured in " + rpcDefinition);
297 List<JavaAttribute> parameters = new ArrayList<>();
298 for (DataSchemaNode childNode : rpcDefinition.getInput()
300 if (childNode.isAddedByUses() == false) { // skip
303 checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
304 + "Currently only leafs and empty output nodes are supported, got " + childNode);
305 JavaAttribute javaAttribute = new JavaAttribute(
306 (LeafSchemaNode) childNode,
307 typeProviderWrapper);
308 parameters.add(javaAttribute);
311 Rpc newRpc = new Rpc(returnType, name, rpcDefinition
312 .getQName().getLocalName(), parameters);
317 return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
321 private static boolean isInnerStateBean(DataSchemaNode child) {
322 for (UnknownSchemaNode unknownSchemaNode : child
323 .getUnknownSchemaNodes()) {
324 if (unknownSchemaNode.getNodeType().equals(
325 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
331 private static RuntimeBeanEntry createHierarchical(String packageName,
332 ListSchemaNode listSchemaNode,
333 TypeProviderWrapper typeProviderWrapper, Module currentModule,
334 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
336 // supported are numeric types, strings, enums
337 // get all attributes
338 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
339 packageName, listSchemaNode, typeProviderWrapper,
340 currentModule, identitiesToRpcs);
342 Optional<String> keyYangName;
343 if (listSchemaNode.getKeyDefinition().size() == 0) {
344 keyYangName = Optional.absent();
345 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
346 // key must be either null or one of supported key types
347 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
349 keyYangName = Optional.of(keyQName.getLocalName());
352 throw new IllegalArgumentException(
353 "More than one key is not supported in " + listSchemaNode);
356 String javaNamePrefix = ModuleMXBeanEntry
357 .findJavaNamePrefix(listSchemaNode);
359 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
360 listSchemaNode, listSchemaNode.getQName().getLocalName(),
361 javaNamePrefix, false, keyYangName,
362 attributesRpcsAndRuntimeBeans.getAttributes(),
363 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
364 attributesRpcsAndRuntimeBeans.getRpcs());
366 return rbFromAttributes;
369 private static RuntimeBeanEntry createRoot(String packageName,
370 DataSchemaNode nodeForReporting, String attributeYangName,
371 List<AttributeIfc> attributes, String javaNamePrefix,
372 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
373 return new RuntimeBeanEntry(packageName, nodeForReporting,
374 attributeYangName, javaNamePrefix, true,
375 Optional.<String> absent(), attributes, children, rpcs);
378 public boolean isRoot() {
382 public Optional<String> getKeyYangName() {
386 public Optional<String> getKeyJavaName() {
390 public Collection<AttributeIfc> getAttributes() {
391 return attributeMap.values();
394 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
398 public String getYangName() {
402 public String getPackageName() {
406 public String getJavaNamePrefix() {
407 return javaNamePrefix;
410 public List<RuntimeBeanEntry> getChildren() {
414 public Set<Rpc> getRpcs() {
418 private static class AttributesRpcsAndRuntimeBeans {
419 private final List<RuntimeBeanEntry> runtimeBeanEntries;
420 private final List<AttributeIfc> attributes;
421 private final Set<Rpc> rpcs;
423 public AttributesRpcsAndRuntimeBeans(
424 List<RuntimeBeanEntry> runtimeBeanEntries,
425 List<AttributeIfc> attributes, Set<Rpc> rpcs) {
426 this.runtimeBeanEntries = runtimeBeanEntries;
427 this.attributes = attributes;
431 private List<AttributeIfc> getAttributes() {
435 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
436 return runtimeBeanEntries;
439 public boolean isEmpty() {
440 return attributes.isEmpty() && rpcs.isEmpty();
443 private Set<Rpc> getRpcs() {
448 public static class Rpc {
449 private final String name;
450 private final List<JavaAttribute> parameters;
451 private final String returnType;
452 private final String yangName;
454 Rpc(String returnType, String name, String yangName,
455 List<JavaAttribute> parameters) {
456 this.returnType = returnType;
458 this.parameters = parameters;
459 this.yangName = yangName;
462 public String getYangName() {
466 public String getName() {
470 public List<JavaAttribute> getParameters() {
474 public String getReturnType() {
479 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
481 public String getJavaNameOfRuntimeMXBean() {
482 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
485 public String getFullyQualifiedName(String typeName) {
486 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
490 private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
491 return javaNamePrefix + MXBEAN_SUFFIX;
495 public String toString() {
496 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
497 + yangName + '\'' + ", packageName='" + packageName + '\''
498 + ", keyYangName=" + keyYangName + '}';