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;
16 import java.util.ArrayList;
17 import java.util.Collection;
18 import java.util.Collections;
19 import java.util.Comparator;
20 import java.util.Deque;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.LinkedList;
24 import java.util.List;
27 import java.util.TreeSet;
28 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
29 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
30 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
31 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
32 import org.opendaylight.controller.config.yangjmxgenerator.attribute.VoidAttribute;
33 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
34 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.Module;
44 import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
45 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.UsesNode;
50 * Holds information about runtime bean to be generated. There are two kinds of
51 * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
52 * represents state that must be present at time of configuration module
53 * instantiation. Root RB must have depthLevel set to 0 and cannot have
54 * children. There might be other RBs defined in yang, but no other RB can have
55 * isRoot set to true. At least one RB must be root and all other RBs must be
56 * lined via children so that a tree with all beans can be created.
58 public class RuntimeBeanEntry {
59 private final String packageName;
60 private final String yangName, javaNamePrefix;
61 private final boolean isRoot;
62 private final Optional<String> keyYangName, keyJavaName;
63 private final Map<String, AttributeIfc> attributeMap;
64 private final List<RuntimeBeanEntry> children;
65 private final Set<Rpc> rpcs;
68 RuntimeBeanEntry(String packageName,
69 DataNodeContainer nodeForReporting, String yangName,
70 String javaNamePrefix, boolean isRoot,
71 Optional<String> keyYangName, List<AttributeIfc> attributes,
72 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
74 checkArgument(isRoot == false || keyYangName.isPresent() == false,
75 "Root RuntimeBeanEntry must not have key " + "set");
76 this.packageName = packageName;
78 this.yangName = yangName;
79 this.javaNamePrefix = javaNamePrefix;
80 this.children = Collections.unmodifiableList(children);
81 this.rpcs = Collections.unmodifiableSet(rpcs);
83 this.keyYangName = keyYangName;
84 Map<String, AttributeIfc> map = new HashMap<>();
86 for (AttributeIfc a : attributes) {
87 checkState(map.containsKey(a.getAttributeYangName()) == false,
88 "Attribute already defined: " + a.getAttributeYangName()
89 + " in " + nodeForReporting);
90 map.put(a.getAttributeYangName(), a);
93 if (keyYangName.isPresent()) {
94 AttributeIfc keyJavaName = map.get(keyYangName.get());
95 checkArgument(keyJavaName != null, "Key " + keyYangName.get()
96 + " not found in attribute " + "list " + attributes
97 + " in " + nodeForReporting);
98 this.keyJavaName = Optional
99 .of(keyJavaName.getUpperCaseCammelCase());
101 keyJavaName = Optional.absent();
103 attributeMap = Collections.unmodifiableMap(map);
107 * @return map containing all class names as key, extracted RuntimeBeans as
108 * values. If more than zero values is returned, exactly one
109 * RuntimeBeanEntry will have isRoot set to true, even if yang does
110 * not contain special configuration for it.
112 public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
113 String packageName, DataNodeContainer container,
114 String moduleYangName, TypeProviderWrapper typeProviderWrapper,
115 String javaNamePrefix, Module currentModule) {
117 Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
119 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
120 packageName, container, typeProviderWrapper, currentModule,
122 Map<String, RuntimeBeanEntry> result = new HashMap<>();
124 List<AttributeIfc> attributes;
126 if (attributesRpcsAndRuntimeBeans.isEmpty() == false) {
127 attributes = attributesRpcsAndRuntimeBeans.getAttributes();
128 rpcs = attributesRpcsAndRuntimeBeans.getRpcs();
130 // create artificial root if not defined in yang
131 attributes = Collections.emptyList();
132 rpcs = Collections.emptySet();
134 RuntimeBeanEntry rootRuntimeBeanEntry = createRoot(packageName,
135 container, moduleYangName, attributes, javaNamePrefix,
136 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(), rpcs);
138 Deque<RuntimeBeanEntry> stack = new LinkedList<>();
139 stack.add(rootRuntimeBeanEntry);
141 while (stack.isEmpty() == false) {
142 RuntimeBeanEntry first = stack.pollFirst();
143 if (result.containsKey(first.getJavaNameOfRuntimeMXBean())) {
144 throw new NameConflictException(
145 first.getJavaNameOfRuntimeMXBean(), null, null);
147 result.put(first.getJavaNameOfRuntimeMXBean(), first);
148 stack.addAll(first.getChildren());
153 private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
154 Module currentModule) {
155 // currently only looks for local identities (found in currentModule)
156 Map<QName, Set<RpcDefinition>> result = new HashMap<>();
157 for (IdentitySchemaNode identity : currentModule.getIdentities()) {
159 result.put(identity.getQName(), new HashSet<RpcDefinition>());
162 for (RpcDefinition rpc : currentModule.getRpcs()) {
163 ContainerSchemaNode input = rpc.getInput();
165 for (UsesNode uses : input.getUses()) {
167 if (uses.getGroupingPath().getPath().size() != 1)
170 // check grouping path
171 QName qname = uses.getGroupingPath().getPath().get(0);
173 .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
176 for (SchemaNode refinedNode : uses.getRefines().values()) {
178 for (UnknownSchemaNode unknownSchemaNode : refinedNode
179 .getUnknownSchemaNodes()) {
180 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
181 .equals(unknownSchemaNode.getNodeType())) {
182 String localIdentityName = unknownSchemaNode
184 QName identityQName = new QName(
185 currentModule.getNamespace(),
186 currentModule.getRevision(),
188 Set<RpcDefinition> rpcDefinitions = result
190 if (rpcDefinitions == null) {
191 throw new IllegalArgumentException(
192 "Identity referenced by rpc not found. Identity:"
193 + localIdentityName + " , rpc "
196 rpcDefinitions.add(rpc);
207 * Get direct descendants of this subtree, together with attributes defined
210 private static AttributesRpcsAndRuntimeBeans extractSubtree(
211 String packageName, DataNodeContainer subtree,
212 TypeProviderWrapper typeProviderWrapper, Module currentModule,
213 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
215 List<AttributeIfc> attributes = Lists.newArrayList();
216 // List<JavaAttribute> javaAttributes = new ArrayList<>();
217 // List<TOAttribute> toAttributes = new ArrayList<>();
218 List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
219 for (DataSchemaNode child : subtree.getChildNodes()) {
220 // child leaves can be java attributes, TO attributes, or child
222 if (child instanceof LeafSchemaNode) {
223 // just save the attribute
224 LeafSchemaNode leaf = (LeafSchemaNode) child;
225 attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
226 } else if (child instanceof ContainerSchemaNode) {
227 ContainerSchemaNode container = (ContainerSchemaNode) child;
228 // this can be either TO or hierarchical RB
229 TOAttribute toAttribute = TOAttribute.create(container,
230 typeProviderWrapper, packageName);
231 attributes.add(toAttribute);
232 } else if (child instanceof ListSchemaNode) {
233 if (isInnerStateBean(child)) {
234 ListSchemaNode listSchemaNode = (ListSchemaNode) child;
235 RuntimeBeanEntry hierarchicalChild = createHierarchical(
236 packageName, listSchemaNode, typeProviderWrapper,
237 currentModule, identitiesToRpcs);
238 runtimeBeanEntries.add(hierarchicalChild);
239 } else /* ordinary list attribute */{
240 ListAttribute listAttribute = ListAttribute.create(
241 (ListSchemaNode) child, typeProviderWrapper, packageName);
242 attributes.add(listAttribute);
245 } else if (child instanceof LeafListSchemaNode) {
246 ListAttribute listAttribute = ListAttribute.create(
247 (LeafListSchemaNode) child, typeProviderWrapper);
248 attributes.add(listAttribute);
250 throw new IllegalStateException("Unexpected running-data node "
254 Set<Rpc> rpcs = new HashSet<>();
255 SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
256 for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
257 .getUnknownSchemaNodes()) {
258 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
259 .equals(unknownSchemaNode.getNodeType())) {
260 String localIdentityName = unknownSchemaNode.getNodeParameter();
261 QName identityQName = new QName(currentModule.getNamespace(),
262 currentModule.getRevision(), localIdentityName);
263 Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
265 if (rpcDefinitions == null) {
266 throw new IllegalArgumentException("Cannot find identity "
267 + localIdentityName + " to be used as "
268 + "context reference when resolving "
269 + unknownSchemaNode);
271 // convert RpcDefinition to Rpc
272 for (RpcDefinition rpcDefinition : rpcDefinitions) {
273 String name = TypeProviderWrapper
274 .findJavaParameter(rpcDefinition);
275 AttributeIfc returnType;
276 if (rpcDefinition.getOutput() == null
277 || rpcDefinition.getOutput().getChildNodes().size() == 0) {
278 returnType = VoidAttribute.getInstance();
279 } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
280 DataSchemaNode returnDSN = rpcDefinition.getOutput()
281 .getChildNodes().iterator().next();
282 returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper, packageName);
285 throw new IllegalArgumentException(
286 "More than one child node in rpc output is not supported. "
287 + "Error occured in " + rpcDefinition);
289 List<JavaAttribute> parameters = new ArrayList<>();
290 for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
292 if (childNode.isAddedByUses() == false) { // skip
295 checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
296 + "Currently only leafs and empty output nodes are supported, got " + childNode);
297 JavaAttribute javaAttribute = new JavaAttribute(
298 (LeafSchemaNode) childNode,
299 typeProviderWrapper);
300 parameters.add(javaAttribute);
303 Rpc newRpc = new Rpc(returnType, name, rpcDefinition
304 .getQName().getLocalName(), parameters);
309 return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
313 private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper,
314 String packageName) {
315 if (child instanceof LeafSchemaNode) {
316 LeafSchemaNode leaf = (LeafSchemaNode) child;
317 return new JavaAttribute(leaf, typeProviderWrapper);
318 } else if (child instanceof ContainerSchemaNode) {
319 ContainerSchemaNode container = (ContainerSchemaNode) child;
320 TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper, packageName);
322 } else if (child instanceof ListSchemaNode) {
323 return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper, packageName);
324 } else if (child instanceof LeafListSchemaNode) {
325 return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper);
327 throw new IllegalStateException("Unknown output data node " + child + " for rpc");
331 private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
332 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
334 public int compare(DataSchemaNode o1, DataSchemaNode o2) {
335 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
338 dataSchemaNodes.addAll(childNodes);
339 return dataSchemaNodes;
342 private static boolean isInnerStateBean(DataSchemaNode child) {
343 for (UnknownSchemaNode unknownSchemaNode : child
344 .getUnknownSchemaNodes()) {
345 if (unknownSchemaNode.getNodeType().equals(
346 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
352 private static RuntimeBeanEntry createHierarchical(String packageName,
353 ListSchemaNode listSchemaNode,
354 TypeProviderWrapper typeProviderWrapper, Module currentModule,
355 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
357 // supported are numeric types, strings, enums
358 // get all attributes
359 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
360 packageName, listSchemaNode, typeProviderWrapper,
361 currentModule, identitiesToRpcs);
363 Optional<String> keyYangName;
364 if (listSchemaNode.getKeyDefinition().size() == 0) {
365 keyYangName = Optional.absent();
366 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
367 // key must be either null or one of supported key types
368 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
370 keyYangName = Optional.of(keyQName.getLocalName());
373 throw new IllegalArgumentException(
374 "More than one key is not supported in " + listSchemaNode);
377 String javaNamePrefix = TypeProviderWrapper
378 .findJavaNamePrefix(listSchemaNode);
380 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
381 listSchemaNode, listSchemaNode.getQName().getLocalName(),
382 javaNamePrefix, false, keyYangName,
383 attributesRpcsAndRuntimeBeans.getAttributes(),
384 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
385 attributesRpcsAndRuntimeBeans.getRpcs());
387 return rbFromAttributes;
390 private static RuntimeBeanEntry createRoot(String packageName,
391 DataNodeContainer nodeForReporting, String attributeYangName,
392 List<AttributeIfc> attributes, String javaNamePrefix,
393 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
394 return new RuntimeBeanEntry(packageName, nodeForReporting,
395 attributeYangName, javaNamePrefix, true,
396 Optional.<String> absent(), attributes, children, rpcs);
399 public boolean isRoot() {
403 public Optional<String> getKeyYangName() {
407 public Optional<String> getKeyJavaName() {
411 public Collection<AttributeIfc> getAttributes() {
412 return attributeMap.values();
415 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
419 public String getYangName() {
423 public String getPackageName() {
427 public String getJavaNamePrefix() {
428 return javaNamePrefix;
431 public List<RuntimeBeanEntry> getChildren() {
435 public Set<Rpc> getRpcs() {
439 private static class AttributesRpcsAndRuntimeBeans {
440 private final List<RuntimeBeanEntry> runtimeBeanEntries;
441 private final List<AttributeIfc> attributes;
442 private final Set<Rpc> rpcs;
444 public AttributesRpcsAndRuntimeBeans(
445 List<RuntimeBeanEntry> runtimeBeanEntries,
446 List<AttributeIfc> attributes, Set<Rpc> rpcs) {
447 this.runtimeBeanEntries = runtimeBeanEntries;
448 this.attributes = attributes;
452 private List<AttributeIfc> getAttributes() {
456 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
457 return runtimeBeanEntries;
460 public boolean isEmpty() {
461 return attributes.isEmpty() && rpcs.isEmpty();
464 private Set<Rpc> getRpcs() {
469 public static class Rpc {
470 private final String name;
471 private final List<JavaAttribute> parameters;
472 private final AttributeIfc returnType;
473 private final String yangName;
475 Rpc(AttributeIfc returnType, String name, String yangName,
476 List<JavaAttribute> parameters) {
477 this.returnType = returnType;
479 this.parameters = parameters;
480 this.yangName = yangName;
483 public String getYangName() {
487 public String getName() {
491 public List<JavaAttribute> getParameters() {
495 public AttributeIfc getReturnType() {
500 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
502 public String getJavaNameOfRuntimeMXBean() {
503 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
506 public String getFullyQualifiedName(String typeName) {
507 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
511 private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
512 return javaNamePrefix + MXBEAN_SUFFIX;
516 public String toString() {
517 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
518 + yangName + '\'' + ", packageName='" + packageName + '\''
519 + ", keyYangName=" + keyYangName + '}';