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<JavaAttribute> javaAttributes = new ArrayList<>();
221 // List<TOAttribute> toAttributes = new ArrayList<>();
222 List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
223 for (DataSchemaNode child : subtree.getChildNodes()) {
224 // child leaves can be java attributes, TO attributes, or child
226 if (child instanceof LeafSchemaNode) {
227 // just save the attribute
228 LeafSchemaNode leaf = (LeafSchemaNode) child;
229 attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
230 } else if (child instanceof ContainerSchemaNode) {
231 ContainerSchemaNode container = (ContainerSchemaNode) child;
232 // this can be either TO or hierarchical RB
233 TOAttribute toAttribute = TOAttribute.create(container,
234 typeProviderWrapper, packageName);
235 attributes.add(toAttribute);
236 } else if (child instanceof ListSchemaNode) {
237 if (isInnerStateBean(child)) {
238 ListSchemaNode listSchemaNode = (ListSchemaNode) child;
239 RuntimeBeanEntry hierarchicalChild = createHierarchical(
240 packageName, listSchemaNode, typeProviderWrapper,
241 currentModule, identitiesToRpcs);
242 runtimeBeanEntries.add(hierarchicalChild);
243 } else /* ordinary list attribute */{
244 ListAttribute listAttribute = ListAttribute.create(
245 (ListSchemaNode) child, typeProviderWrapper, packageName);
246 attributes.add(listAttribute);
249 } else if (child instanceof LeafListSchemaNode) {
250 ListAttribute listAttribute = ListAttribute.create(
251 (LeafListSchemaNode) child, typeProviderWrapper);
252 attributes.add(listAttribute);
254 throw new IllegalStateException("Unexpected running-data node "
258 Set<Rpc> rpcs = new HashSet<>();
259 SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
260 for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
261 .getUnknownSchemaNodes()) {
262 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
263 .equals(unknownSchemaNode.getNodeType())) {
264 String localIdentityName = unknownSchemaNode.getNodeParameter();
265 QName identityQName = QName.create(currentModule.getNamespace(),
266 currentModule.getRevision(), localIdentityName);
267 Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
269 if (rpcDefinitions == null) {
270 throw new IllegalArgumentException("Cannot find identity "
271 + localIdentityName + " to be used as "
272 + "context reference when resolving "
273 + unknownSchemaNode);
275 // convert RpcDefinition to Rpc
276 for (RpcDefinition rpcDefinition : rpcDefinitions) {
277 String name = TypeProviderWrapper
278 .findJavaParameter(rpcDefinition);
279 AttributeIfc returnType;
280 if (rpcDefinition.getOutput() == null
281 || rpcDefinition.getOutput().getChildNodes().size() == 0) {
282 returnType = VoidAttribute.getInstance();
283 } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
284 DataSchemaNode returnDSN = rpcDefinition.getOutput()
285 .getChildNodes().iterator().next();
286 returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper, packageName);
289 throw new IllegalArgumentException(
290 "More than one child node in rpc output is not supported. "
291 + "Error occured in " + rpcDefinition);
293 List<JavaAttribute> parameters = new ArrayList<>();
294 for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
296 if (childNode.isAddedByUses() == false) { // skip
299 checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
300 + "Currently only leafs and empty output nodes are supported, got " + childNode);
301 JavaAttribute javaAttribute = new JavaAttribute(
302 (LeafSchemaNode) childNode,
303 typeProviderWrapper);
304 parameters.add(javaAttribute);
307 Rpc newRpc = new Rpc(returnType, name, rpcDefinition
308 .getQName().getLocalName(), parameters);
313 return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
317 private static AttributeIfc getReturnTypeAttribute(final DataSchemaNode child, final TypeProviderWrapper typeProviderWrapper,
318 final String packageName) {
319 if (child instanceof LeafSchemaNode) {
320 LeafSchemaNode leaf = (LeafSchemaNode) child;
321 return new JavaAttribute(leaf, typeProviderWrapper);
322 } else if (child instanceof ContainerSchemaNode) {
323 ContainerSchemaNode container = (ContainerSchemaNode) child;
324 TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper, packageName);
326 } else if (child instanceof ListSchemaNode) {
327 return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper, packageName);
328 } else if (child instanceof LeafListSchemaNode) {
329 return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper);
331 throw new IllegalStateException("Unknown output data node " + child + " for rpc");
335 private static Collection<DataSchemaNode> sortAttributes(final Set<DataSchemaNode> childNodes) {
336 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
338 public int compare(final DataSchemaNode o1, final DataSchemaNode o2) {
339 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
342 dataSchemaNodes.addAll(childNodes);
343 return dataSchemaNodes;
346 private static boolean isInnerStateBean(final DataSchemaNode child) {
347 for (UnknownSchemaNode unknownSchemaNode : child
348 .getUnknownSchemaNodes()) {
349 if (unknownSchemaNode.getNodeType().equals(
350 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME)) {
357 private static RuntimeBeanEntry createHierarchical(final String packageName,
358 final ListSchemaNode listSchemaNode,
359 final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
360 final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
362 // supported are numeric types, strings, enums
363 // get all attributes
364 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
365 packageName, listSchemaNode, typeProviderWrapper,
366 currentModule, identitiesToRpcs);
368 Optional<String> keyYangName;
369 if (listSchemaNode.getKeyDefinition().size() == 0) {
370 keyYangName = Optional.absent();
371 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
372 // key must be either null or one of supported key types
373 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
375 keyYangName = Optional.of(keyQName.getLocalName());
378 throw new IllegalArgumentException(
379 "More than one key is not supported in " + listSchemaNode);
382 String javaNamePrefix = TypeProviderWrapper
383 .findJavaNamePrefix(listSchemaNode);
385 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
386 listSchemaNode, listSchemaNode.getQName().getLocalName(),
387 javaNamePrefix, false, keyYangName,
388 attributesRpcsAndRuntimeBeans.getAttributes(),
389 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
390 attributesRpcsAndRuntimeBeans.getRpcs());
392 return rbFromAttributes;
395 private static RuntimeBeanEntry createRoot(final String packageName,
396 final DataNodeContainer nodeForReporting, final String attributeYangName,
397 final List<AttributeIfc> attributes, final String javaNamePrefix,
398 final List<RuntimeBeanEntry> children, final Set<Rpc> rpcs) {
399 return new RuntimeBeanEntry(packageName, nodeForReporting,
400 attributeYangName, javaNamePrefix, true,
401 Optional.<String> absent(), attributes, children, rpcs);
404 public boolean isRoot() {
408 public Optional<String> getKeyYangName() {
412 public Optional<String> getKeyJavaName() {
416 public Collection<AttributeIfc> getAttributes() {
417 return attributeMap.values();
420 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
424 public String getYangName() {
428 public String getPackageName() {
432 public String getJavaNamePrefix() {
433 return javaNamePrefix;
436 public List<RuntimeBeanEntry> getChildren() {
440 public Set<Rpc> getRpcs() {
444 private static class AttributesRpcsAndRuntimeBeans {
445 private final List<RuntimeBeanEntry> runtimeBeanEntries;
446 private final List<AttributeIfc> attributes;
447 private final Set<Rpc> rpcs;
449 public AttributesRpcsAndRuntimeBeans(
450 final List<RuntimeBeanEntry> runtimeBeanEntries,
451 final List<AttributeIfc> attributes, final Set<Rpc> rpcs) {
452 this.runtimeBeanEntries = runtimeBeanEntries;
453 this.attributes = attributes;
457 private List<AttributeIfc> getAttributes() {
461 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
462 return runtimeBeanEntries;
465 public boolean isEmpty() {
466 return attributes.isEmpty() && rpcs.isEmpty();
469 private Set<Rpc> getRpcs() {
474 public static class Rpc {
475 private final String name;
476 private final List<JavaAttribute> parameters;
477 private final AttributeIfc returnType;
478 private final String yangName;
480 Rpc(final AttributeIfc returnType, final String name, final String yangName,
481 final List<JavaAttribute> parameters) {
482 this.returnType = returnType;
484 this.parameters = parameters;
485 this.yangName = yangName;
488 public String getYangName() {
492 public String getName() {
496 public List<JavaAttribute> getParameters() {
500 public AttributeIfc getReturnType() {
505 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
507 public String getJavaNameOfRuntimeMXBean() {
508 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
511 public String getFullyQualifiedName(final String typeName) {
512 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
516 private static String getJavaNameOfRuntimeMXBean(final String javaNamePrefix) {
517 return javaNamePrefix + MXBEAN_SUFFIX;
521 public String toString() {
522 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
523 + yangName + '\'' + ", packageName='" + packageName + '\''
524 + ", keyYangName=" + keyYangName + '}';