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 com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Optional;
12 import com.google.common.collect.Lists;
13 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
14 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
15 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
16 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
17 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
18 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.model.api.*;
24 import static com.google.common.base.Preconditions.checkArgument;
25 import static com.google.common.base.Preconditions.checkState;
28 * Holds information about runtime bean to be generated. There are two kinds of
29 * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
30 * represents state that must be present at time of configuration module
31 * instantiation. Root RB must have depthLevel set to 0 and cannot have
32 * children. There might be other RBs defined in yang, but no other RB can have
33 * isRoot set to true. At least one RB must be root and all other RBs must be
34 * lined via children so that a tree with all beans can be created.
36 public class RuntimeBeanEntry {
37 private final String packageName;
38 private final String yangName, javaNamePrefix;
39 private final boolean isRoot;
40 private final Optional<String> keyYangName, keyJavaName;
41 private final Map<String, AttributeIfc> attributeMap;
42 private final List<RuntimeBeanEntry> children;
43 private final Set<Rpc> rpcs;
46 public RuntimeBeanEntry(String packageName,
47 DataSchemaNode nodeForReporting, String yangName,
48 String javaNamePrefix, boolean isRoot,
49 Optional<String> keyYangName, List<AttributeIfc> attributes,
50 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
52 checkArgument(isRoot == false || keyYangName.isPresent() == false,
53 "Root RuntimeBeanEntry must not have key " + "set");
54 this.packageName = packageName;
56 this.yangName = yangName;
57 this.javaNamePrefix = javaNamePrefix;
58 this.children = Collections.unmodifiableList(children);
59 this.rpcs = Collections.unmodifiableSet(rpcs);
61 this.keyYangName = keyYangName;
62 Map<String, AttributeIfc> map = new HashMap<>();
64 for (AttributeIfc a : attributes) {
65 checkState(map.containsKey(a.getAttributeYangName()) == false,
66 "Attribute already defined: " + a.getAttributeYangName()
67 + " in " + nodeForReporting);
68 map.put(a.getAttributeYangName(), a);
71 if (keyYangName.isPresent()) {
72 AttributeIfc keyJavaName = map.get(keyYangName.get());
73 checkArgument(keyJavaName != null, "Key " + keyYangName.get()
74 + " not found in attribute " + "list " + attributes
75 + " in " + nodeForReporting);
76 this.keyJavaName = Optional
77 .of(keyJavaName.getUpperCaseCammelCase());
79 keyJavaName = Optional.absent();
81 attributeMap = Collections.unmodifiableMap(map);
85 * @return map containing all class names as key, extracted RuntimeBeans as
86 * values. If more than zero values is returned, exactly one
87 * RuntimeBeanEntry will have isRoot set to true, even if yang does
88 * not contain special configuration for it.
90 public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
91 String packageName, ChoiceCaseNode container,
92 String moduleYangName, TypeProviderWrapper typeProviderWrapper,
93 String javaNamePrefix, Module currentModule) {
95 Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
97 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
98 packageName, container, typeProviderWrapper, currentModule,
100 Map<String, RuntimeBeanEntry> result = new HashMap<>();
102 List<AttributeIfc> attributes;
104 if (attributesRpcsAndRuntimeBeans.isEmpty() == false) {
105 attributes = attributesRpcsAndRuntimeBeans.getAttributes();
106 rpcs = attributesRpcsAndRuntimeBeans.getRpcs();
108 // create artificial root if not defined in yang
109 attributes = Collections.emptyList();
110 rpcs = Collections.emptySet();
112 RuntimeBeanEntry rootRuntimeBeanEntry = createRoot(packageName,
113 container, moduleYangName, attributes, javaNamePrefix,
114 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(), rpcs);
116 Deque<RuntimeBeanEntry> stack = new LinkedList<>();
117 stack.add(rootRuntimeBeanEntry);
119 while (stack.isEmpty() == false) {
120 RuntimeBeanEntry first = stack.pollFirst();
121 if (result.containsKey(first.getJavaNameOfRuntimeMXBean())) {
122 throw new NameConflictException(
123 first.getJavaNameOfRuntimeMXBean(), null, null);
125 result.put(first.getJavaNameOfRuntimeMXBean(), first);
126 stack.addAll(first.getChildren());
131 private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
132 Module currentModule) {
133 // currently only looks for local identities (found in currentModule)
134 Map<QName, Set<RpcDefinition>> result = new HashMap<>();
135 for (IdentitySchemaNode identity : currentModule.getIdentities()) {
137 result.put(identity.getQName(), new HashSet<RpcDefinition>());
140 for (RpcDefinition rpc : currentModule.getRpcs()) {
141 ContainerSchemaNode input = rpc.getInput();
142 for (UsesNode uses : input.getUses()) {
144 if (uses.getGroupingPath().getPath().size() != 1)
147 // check grouping path
148 QName qname = uses.getGroupingPath().getPath().get(0);
150 .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
153 for (SchemaNode refinedNode : uses.getRefines().values()) {
155 for (UnknownSchemaNode unknownSchemaNode : refinedNode
156 .getUnknownSchemaNodes()) {
157 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
158 .equals(unknownSchemaNode.getNodeType())) {
159 String localIdentityName = unknownSchemaNode
161 QName identityQName = new QName(
162 currentModule.getNamespace(),
163 currentModule.getRevision(),
165 Set<RpcDefinition> rpcDefinitions = result
167 if (rpcDefinitions == null) {
168 throw new IllegalArgumentException(
169 "Identity referenced by rpc not found. Identity:"
170 + localIdentityName + " , rpc "
173 rpcDefinitions.add(rpc);
184 * Get direct descendants of this subtree, together with attributes defined
187 private static AttributesRpcsAndRuntimeBeans extractSubtree(
188 String packageName, DataNodeContainer subtree,
189 TypeProviderWrapper typeProviderWrapper, Module currentModule,
190 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
192 List<AttributeIfc> attributes = Lists.newArrayList();
193 // List<JavaAttribute> javaAttributes = new ArrayList<>();
194 // List<TOAttribute> toAttributes = new ArrayList<>();
195 List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
196 for (DataSchemaNode child : subtree.getChildNodes()) {
197 // child leaves can be java attributes, TO attributes, or child
199 if (child instanceof LeafSchemaNode) {
200 // just save the attribute
201 LeafSchemaNode leaf = (LeafSchemaNode) child;
202 attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
203 } else if (child instanceof ContainerSchemaNode) {
204 ContainerSchemaNode container = (ContainerSchemaNode) child;
205 // this can be either TO or hierarchical RB
206 TOAttribute toAttribute = TOAttribute.create(container,
207 typeProviderWrapper);
208 attributes.add(toAttribute);
209 } else if (child instanceof ListSchemaNode) {
210 if (isInnerStateBean(child)) {
211 ListSchemaNode listSchemaNode = (ListSchemaNode) child;
212 RuntimeBeanEntry hierarchicalChild = createHierarchical(
213 packageName, listSchemaNode, typeProviderWrapper,
214 currentModule, identitiesToRpcs);
215 runtimeBeanEntries.add(hierarchicalChild);
216 } else /* ordinary list attribute */{
217 ListAttribute listAttribute = ListAttribute.create(
218 (ListSchemaNode) child, typeProviderWrapper);
219 attributes.add(listAttribute);
223 throw new IllegalStateException("Unknown running-data node "
224 + child + " , " + "" + "expected leaf or container");
227 Set<Rpc> rpcs = new HashSet<>();
228 SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
229 for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
230 .getUnknownSchemaNodes()) {
231 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
232 .equals(unknownSchemaNode.getNodeType())) {
233 String localIdentityName = unknownSchemaNode.getNodeParameter();
234 QName identityQName = new QName(currentModule.getNamespace(),
235 currentModule.getRevision(), localIdentityName);
236 Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
238 if (rpcDefinitions == null) {
239 throw new IllegalArgumentException("Cannot find identity "
240 + localIdentityName + " to be used as "
241 + "context reference when resolving "
242 + unknownSchemaNode);
244 // convert RpcDefinition to Rpc
245 for (RpcDefinition rpcDefinition : rpcDefinitions) {
246 String name = ModuleMXBeanEntry
247 .findJavaParameter(rpcDefinition);
249 if (rpcDefinition.getOutput() == null
250 || rpcDefinition.getOutput().getChildNodes().size() == 0) {
252 } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
253 DataSchemaNode returnDSN = rpcDefinition.getOutput()
254 .getChildNodes().iterator().next();
256 returnDSN instanceof LeafSchemaNode,
257 "Unexpected type of rpc return type. "
258 + "Currently only leafs and empty output nodes are supported, got "
260 LeafSchemaNode returnLeaf = (LeafSchemaNode) returnDSN;
261 // We currently expect leaf defined in output element in yang to be named result
262 // FIXME: value of result is fully qualified name - should be extended to accept TOs
263 String localName = returnLeaf.getQName().getLocalName();
265 localName.equals("result"),
266 "Unexpected name of leaf in output element, expected leaf named result, was %s at %s",
267 localName, currentModule.getName());
269 returnType = typeProviderWrapper.getType(returnLeaf)
270 .getFullyQualifiedName();
272 throw new IllegalArgumentException(
273 "More than one child node in rpc output is not supported. "
274 + "Error occured in " + rpcDefinition);
276 List<JavaAttribute> parameters = new ArrayList<>();
277 for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
279 if (childNode.isAddedByUses() == false) { // skip
282 checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
283 + "Currently only leafs and empty output nodes are supported, got " + childNode);
284 JavaAttribute javaAttribute = new JavaAttribute(
285 (LeafSchemaNode) childNode,
286 typeProviderWrapper);
287 parameters.add(javaAttribute);
290 Rpc newRpc = new Rpc(returnType, name, rpcDefinition
291 .getQName().getLocalName(), parameters);
296 return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
300 private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
301 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
303 public int compare(DataSchemaNode o1, DataSchemaNode o2) {
304 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
307 dataSchemaNodes.addAll(childNodes);
308 return dataSchemaNodes;
311 private static boolean isInnerStateBean(DataSchemaNode child) {
312 for (UnknownSchemaNode unknownSchemaNode : child
313 .getUnknownSchemaNodes()) {
314 if (unknownSchemaNode.getNodeType().equals(
315 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
321 private static RuntimeBeanEntry createHierarchical(String packageName,
322 ListSchemaNode listSchemaNode,
323 TypeProviderWrapper typeProviderWrapper, Module currentModule,
324 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
326 // supported are numeric types, strings, enums
327 // get all attributes
328 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
329 packageName, listSchemaNode, typeProviderWrapper,
330 currentModule, identitiesToRpcs);
332 Optional<String> keyYangName;
333 if (listSchemaNode.getKeyDefinition().size() == 0) {
334 keyYangName = Optional.absent();
335 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
336 // key must be either null or one of supported key types
337 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
339 keyYangName = Optional.of(keyQName.getLocalName());
342 throw new IllegalArgumentException(
343 "More than one key is not supported in " + listSchemaNode);
346 String javaNamePrefix = ModuleMXBeanEntry
347 .findJavaNamePrefix(listSchemaNode);
349 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
350 listSchemaNode, listSchemaNode.getQName().getLocalName(),
351 javaNamePrefix, false, keyYangName,
352 attributesRpcsAndRuntimeBeans.getAttributes(),
353 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
354 attributesRpcsAndRuntimeBeans.getRpcs());
356 return rbFromAttributes;
359 private static RuntimeBeanEntry createRoot(String packageName,
360 DataSchemaNode nodeForReporting, String attributeYangName,
361 List<AttributeIfc> attributes, String javaNamePrefix,
362 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
363 return new RuntimeBeanEntry(packageName, nodeForReporting,
364 attributeYangName, javaNamePrefix, true,
365 Optional.<String> absent(), attributes, children, rpcs);
368 public boolean isRoot() {
372 public Optional<String> getKeyYangName() {
376 public Optional<String> getKeyJavaName() {
380 public Collection<AttributeIfc> getAttributes() {
381 return attributeMap.values();
384 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
388 public String getYangName() {
392 public String getPackageName() {
396 public String getJavaNamePrefix() {
397 return javaNamePrefix;
400 public List<RuntimeBeanEntry> getChildren() {
404 public Set<Rpc> getRpcs() {
408 private static class AttributesRpcsAndRuntimeBeans {
409 private final List<RuntimeBeanEntry> runtimeBeanEntries;
410 private final List<AttributeIfc> attributes;
411 private final Set<Rpc> rpcs;
413 public AttributesRpcsAndRuntimeBeans(
414 List<RuntimeBeanEntry> runtimeBeanEntries,
415 List<AttributeIfc> attributes, Set<Rpc> rpcs) {
416 this.runtimeBeanEntries = runtimeBeanEntries;
417 this.attributes = attributes;
421 private List<AttributeIfc> getAttributes() {
425 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
426 return runtimeBeanEntries;
429 public boolean isEmpty() {
430 return attributes.isEmpty() && rpcs.isEmpty();
433 private Set<Rpc> getRpcs() {
438 public static class Rpc {
439 private final String name;
440 private final List<JavaAttribute> parameters;
441 private final String returnType;
442 private final String yangName;
444 Rpc(String returnType, String name, String yangName,
445 List<JavaAttribute> parameters) {
446 this.returnType = returnType;
448 this.parameters = parameters;
449 this.yangName = yangName;
452 public String getYangName() {
456 public String getName() {
460 public List<JavaAttribute> getParameters() {
464 public String getReturnType() {
469 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
471 public String getJavaNameOfRuntimeMXBean() {
472 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
475 public String getFullyQualifiedName(String typeName) {
476 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
480 private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
481 return javaNamePrefix + MXBEAN_SUFFIX;
485 public String toString() {
486 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
487 + yangName + '\'' + ", packageName='" + packageName + '\''
488 + ", keyYangName=" + keyYangName + '}';