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(final String packageName,
69 final DataNodeContainer nodeForReporting, final String yangName,
70 final String javaNamePrefix, final boolean isRoot,
71 final Optional<String> keyYangName, final List<AttributeIfc> attributes,
72 final List<RuntimeBeanEntry> children, final 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 final String packageName, final DataNodeContainer container,
114 final String moduleYangName, final TypeProviderWrapper typeProviderWrapper,
115 final String javaNamePrefix, final 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 final 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) {
171 // check grouping path
172 QName qname = uses.getGroupingPath().getPath().get(0);
174 .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 = QName.create(
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 final String packageName, final DataNodeContainer subtree,
214 final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
215 final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
217 List<AttributeIfc> attributes = Lists.newArrayList();
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 = QName.create(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().isEmpty()) {
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(final DataSchemaNode child, final TypeProviderWrapper typeProviderWrapper,
314 final 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(final Collection<DataSchemaNode> childNodes) {
332 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
334 public int compare(final DataSchemaNode o1, final DataSchemaNode o2) {
335 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
338 dataSchemaNodes.addAll(childNodes);
339 return dataSchemaNodes;
342 private static boolean isInnerStateBean(final DataSchemaNode child) {
343 for (UnknownSchemaNode unknownSchemaNode : child
344 .getUnknownSchemaNodes()) {
345 if (unknownSchemaNode.getNodeType().equals(
346 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME)) {
353 private static RuntimeBeanEntry createHierarchical(final String packageName,
354 final ListSchemaNode listSchemaNode,
355 final TypeProviderWrapper typeProviderWrapper, final Module currentModule,
356 final Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
358 // supported are numeric types, strings, enums
359 // get all attributes
360 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
361 packageName, listSchemaNode, typeProviderWrapper,
362 currentModule, identitiesToRpcs);
364 Optional<String> keyYangName;
365 if (listSchemaNode.getKeyDefinition().isEmpty()) {
366 keyYangName = Optional.absent();
367 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
368 // key must be either null or one of supported key types
369 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
371 keyYangName = Optional.of(keyQName.getLocalName());
374 throw new IllegalArgumentException(
375 "More than one key is not supported in " + listSchemaNode);
378 String javaNamePrefix = TypeProviderWrapper
379 .findJavaNamePrefix(listSchemaNode);
381 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
382 listSchemaNode, listSchemaNode.getQName().getLocalName(),
383 javaNamePrefix, false, keyYangName,
384 attributesRpcsAndRuntimeBeans.getAttributes(),
385 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
386 attributesRpcsAndRuntimeBeans.getRpcs());
388 return rbFromAttributes;
391 private static RuntimeBeanEntry createRoot(final String packageName,
392 final DataNodeContainer nodeForReporting, final String attributeYangName,
393 final List<AttributeIfc> attributes, final String javaNamePrefix,
394 final List<RuntimeBeanEntry> children, final Set<Rpc> rpcs) {
395 return new RuntimeBeanEntry(packageName, nodeForReporting,
396 attributeYangName, javaNamePrefix, true,
397 Optional.<String> absent(), attributes, children, rpcs);
400 public boolean isRoot() {
404 public Optional<String> getKeyYangName() {
408 public Optional<String> getKeyJavaName() {
412 public Collection<AttributeIfc> getAttributes() {
413 return attributeMap.values();
416 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
420 public String getYangName() {
424 public String getPackageName() {
428 public String getJavaNamePrefix() {
429 return javaNamePrefix;
432 public List<RuntimeBeanEntry> getChildren() {
436 public Set<Rpc> getRpcs() {
440 private static class AttributesRpcsAndRuntimeBeans {
441 private final List<RuntimeBeanEntry> runtimeBeanEntries;
442 private final List<AttributeIfc> attributes;
443 private final Set<Rpc> rpcs;
445 public AttributesRpcsAndRuntimeBeans(
446 final List<RuntimeBeanEntry> runtimeBeanEntries,
447 final List<AttributeIfc> attributes, final Set<Rpc> rpcs) {
448 this.runtimeBeanEntries = runtimeBeanEntries;
449 this.attributes = attributes;
453 private List<AttributeIfc> getAttributes() {
457 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
458 return runtimeBeanEntries;
461 public boolean isEmpty() {
462 return attributes.isEmpty() && rpcs.isEmpty();
465 private Set<Rpc> getRpcs() {
470 public static class Rpc {
471 private final String name;
472 private final List<JavaAttribute> parameters;
473 private final AttributeIfc returnType;
474 private final String yangName;
476 Rpc(final AttributeIfc returnType, final String name, final String yangName,
477 final List<JavaAttribute> parameters) {
478 this.returnType = returnType;
480 this.parameters = parameters;
481 this.yangName = yangName;
484 public String getYangName() {
488 public String getName() {
492 public List<JavaAttribute> getParameters() {
496 public AttributeIfc getReturnType() {
501 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
503 public String getJavaNameOfRuntimeMXBean() {
504 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
507 public String getFullyQualifiedName(final String typeName) {
508 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
512 private static String getJavaNameOfRuntimeMXBean(final String javaNamePrefix) {
513 return javaNamePrefix + MXBEAN_SUFFIX;
517 public String toString() {
518 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
519 + yangName + '\'' + ", packageName='" + packageName + '\''
520 + ", keyYangName=" + keyYangName + '}';