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.*;
14 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
15 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
16 import org.opendaylight.yangtools.yang.common.QName;
17 import org.opendaylight.yangtools.yang.model.api.*;
21 import static com.google.common.base.Preconditions.checkArgument;
22 import static com.google.common.base.Preconditions.checkState;
25 * Holds information about runtime bean to be generated. There are two kinds of
26 * RuntimeBeanEntry instances: if isRoot flag is set to true, this bean
27 * represents state that must be present at time of configuration module
28 * instantiation. Root RB must have depthLevel set to 0 and cannot have
29 * children. There might be other RBs defined in yang, but no other RB can have
30 * isRoot set to true. At least one RB must be root and all other RBs must be
31 * lined via children so that a tree with all beans can be created.
33 public class RuntimeBeanEntry {
34 private final String packageName;
35 private final String yangName, javaNamePrefix;
36 private final boolean isRoot;
37 private final Optional<String> keyYangName, keyJavaName;
38 private final Map<String, AttributeIfc> attributeMap;
39 private final List<RuntimeBeanEntry> children;
40 private final Set<Rpc> rpcs;
43 public RuntimeBeanEntry(String packageName,
44 DataSchemaNode nodeForReporting, String yangName,
45 String javaNamePrefix, boolean isRoot,
46 Optional<String> keyYangName, List<AttributeIfc> attributes,
47 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
49 checkArgument(isRoot == false || keyYangName.isPresent() == false,
50 "Root RuntimeBeanEntry must not have key " + "set");
51 this.packageName = packageName;
53 this.yangName = yangName;
54 this.javaNamePrefix = javaNamePrefix;
55 this.children = Collections.unmodifiableList(children);
56 this.rpcs = Collections.unmodifiableSet(rpcs);
58 this.keyYangName = keyYangName;
59 Map<String, AttributeIfc> map = new HashMap<>();
61 for (AttributeIfc a : attributes) {
62 checkState(map.containsKey(a.getAttributeYangName()) == false,
63 "Attribute already defined: " + a.getAttributeYangName()
64 + " in " + nodeForReporting);
65 map.put(a.getAttributeYangName(), a);
68 if (keyYangName.isPresent()) {
69 AttributeIfc keyJavaName = map.get(keyYangName.get());
70 checkArgument(keyJavaName != null, "Key " + keyYangName.get()
71 + " not found in attribute " + "list " + attributes
72 + " in " + nodeForReporting);
73 this.keyJavaName = Optional
74 .of(keyJavaName.getUpperCaseCammelCase());
76 keyJavaName = Optional.absent();
78 attributeMap = Collections.unmodifiableMap(map);
82 * @return map containing all class names as key, extracted RuntimeBeans as
83 * values. If more than zero values is returned, exactly one
84 * RuntimeBeanEntry will have isRoot set to true, even if yang does
85 * not contain special configuration for it.
87 public static Map<String, RuntimeBeanEntry> extractClassNameToRuntimeBeanMap(
88 String packageName, ChoiceCaseNode container,
89 String moduleYangName, TypeProviderWrapper typeProviderWrapper,
90 String javaNamePrefix, Module currentModule) {
92 Map<QName, Set<RpcDefinition>> identitiesToRpcs = getIdentitiesToRpcs(currentModule);
94 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
95 packageName, container, typeProviderWrapper, currentModule,
97 Map<String, RuntimeBeanEntry> result = new HashMap<>();
99 List<AttributeIfc> attributes;
101 if (attributesRpcsAndRuntimeBeans.isEmpty() == false) {
102 attributes = attributesRpcsAndRuntimeBeans.getAttributes();
103 rpcs = attributesRpcsAndRuntimeBeans.getRpcs();
105 // create artificial root if not defined in yang
106 attributes = Collections.emptyList();
107 rpcs = Collections.emptySet();
109 RuntimeBeanEntry rootRuntimeBeanEntry = createRoot(packageName,
110 container, moduleYangName, attributes, javaNamePrefix,
111 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(), rpcs);
113 Deque<RuntimeBeanEntry> stack = new LinkedList<>();
114 stack.add(rootRuntimeBeanEntry);
116 while (stack.isEmpty() == false) {
117 RuntimeBeanEntry first = stack.pollFirst();
118 if (result.containsKey(first.getJavaNameOfRuntimeMXBean())) {
119 throw new NameConflictException(
120 first.getJavaNameOfRuntimeMXBean(), null, null);
122 result.put(first.getJavaNameOfRuntimeMXBean(), first);
123 stack.addAll(first.getChildren());
128 private static Map<QName/* of identity */, Set<RpcDefinition>> getIdentitiesToRpcs(
129 Module currentModule) {
130 // currently only looks for local identities (found in currentModule)
131 Map<QName, Set<RpcDefinition>> result = new HashMap<>();
132 for (IdentitySchemaNode identity : currentModule.getIdentities()) {
134 result.put(identity.getQName(), new HashSet<RpcDefinition>());
137 for (RpcDefinition rpc : currentModule.getRpcs()) {
138 ContainerSchemaNode input = rpc.getInput();
139 for (UsesNode uses : input.getUses()) {
141 if (uses.getGroupingPath().getPath().size() != 1)
144 // check grouping path
145 QName qname = uses.getGroupingPath().getPath().get(0);
147 .equals(ConfigConstants.RPC_CONTEXT_REF_GROUPING_QNAME))
150 for (SchemaNode refinedNode : uses.getRefines().values()) {
152 for (UnknownSchemaNode unknownSchemaNode : refinedNode
153 .getUnknownSchemaNodes()) {
154 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
155 .equals(unknownSchemaNode.getNodeType())) {
156 String localIdentityName = unknownSchemaNode
158 QName identityQName = new QName(
159 currentModule.getNamespace(),
160 currentModule.getRevision(),
162 Set<RpcDefinition> rpcDefinitions = result
164 if (rpcDefinitions == null) {
165 throw new IllegalArgumentException(
166 "Identity referenced by rpc not found. Identity:"
167 + localIdentityName + " , rpc "
170 rpcDefinitions.add(rpc);
181 * Get direct descendants of this subtree, together with attributes defined
184 private static AttributesRpcsAndRuntimeBeans extractSubtree(
185 String packageName, DataNodeContainer subtree,
186 TypeProviderWrapper typeProviderWrapper, Module currentModule,
187 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
189 List<AttributeIfc> attributes = Lists.newArrayList();
190 // List<JavaAttribute> javaAttributes = new ArrayList<>();
191 // List<TOAttribute> toAttributes = new ArrayList<>();
192 List<RuntimeBeanEntry> runtimeBeanEntries = new ArrayList<>();
193 for (DataSchemaNode child : subtree.getChildNodes()) {
194 // child leaves can be java attributes, TO attributes, or child
196 if (child instanceof LeafSchemaNode) {
197 // just save the attribute
198 LeafSchemaNode leaf = (LeafSchemaNode) child;
199 attributes.add(new JavaAttribute(leaf, typeProviderWrapper));
200 } else if (child instanceof ContainerSchemaNode) {
201 ContainerSchemaNode container = (ContainerSchemaNode) child;
202 // this can be either TO or hierarchical RB
203 TOAttribute toAttribute = TOAttribute.create(container,
204 typeProviderWrapper);
205 attributes.add(toAttribute);
206 } else if (child instanceof ListSchemaNode) {
207 if (isInnerStateBean(child)) {
208 ListSchemaNode listSchemaNode = (ListSchemaNode) child;
209 RuntimeBeanEntry hierarchicalChild = createHierarchical(
210 packageName, listSchemaNode, typeProviderWrapper,
211 currentModule, identitiesToRpcs);
212 runtimeBeanEntries.add(hierarchicalChild);
213 } else /* ordinary list attribute */{
214 ListAttribute listAttribute = ListAttribute.create(
215 (ListSchemaNode) child, typeProviderWrapper);
216 attributes.add(listAttribute);
219 } else if (child instanceof LeafListSchemaNode) {
220 ListAttribute listAttribute = ListAttribute.create(
221 (LeafListSchemaNode) child, typeProviderWrapper);
222 attributes.add(listAttribute);
224 throw new IllegalStateException("Unexpected running-data node "
228 Set<Rpc> rpcs = new HashSet<>();
229 SchemaNode subtreeSchemaNode = (SchemaNode) subtree;
230 for (UnknownSchemaNode unknownSchemaNode : subtreeSchemaNode
231 .getUnknownSchemaNodes()) {
232 if (ConfigConstants.RPC_CONTEXT_INSTANCE_EXTENSION_QNAME
233 .equals(unknownSchemaNode.getNodeType())) {
234 String localIdentityName = unknownSchemaNode.getNodeParameter();
235 QName identityQName = new QName(currentModule.getNamespace(),
236 currentModule.getRevision(), localIdentityName);
237 Set<RpcDefinition> rpcDefinitions = identitiesToRpcs
239 if (rpcDefinitions == null) {
240 throw new IllegalArgumentException("Cannot find identity "
241 + localIdentityName + " to be used as "
242 + "context reference when resolving "
243 + unknownSchemaNode);
245 // convert RpcDefinition to Rpc
246 for (RpcDefinition rpcDefinition : rpcDefinitions) {
247 String name = ModuleMXBeanEntry
248 .findJavaParameter(rpcDefinition);
249 AttributeIfc returnType;
250 if (rpcDefinition.getOutput() == null
251 || rpcDefinition.getOutput().getChildNodes().size() == 0) {
252 returnType = VoidAttribute.getInstance();
253 } else if (rpcDefinition.getOutput().getChildNodes().size() == 1) {
254 DataSchemaNode returnDSN = rpcDefinition.getOutput()
255 .getChildNodes().iterator().next();
256 returnType = getReturnTypeAttribute(returnDSN, typeProviderWrapper);
259 throw new IllegalArgumentException(
260 "More than one child node in rpc output is not supported. "
261 + "Error occured in " + rpcDefinition);
263 List<JavaAttribute> parameters = new ArrayList<>();
264 for (DataSchemaNode childNode : sortAttributes(rpcDefinition.getInput()
266 if (childNode.isAddedByUses() == false) { // skip
269 checkArgument(childNode instanceof LeafSchemaNode, "Unexpected type of rpc input type. "
270 + "Currently only leafs and empty output nodes are supported, got " + childNode);
271 JavaAttribute javaAttribute = new JavaAttribute(
272 (LeafSchemaNode) childNode,
273 typeProviderWrapper);
274 parameters.add(javaAttribute);
277 Rpc newRpc = new Rpc(returnType, name, rpcDefinition
278 .getQName().getLocalName(), parameters);
283 return new AttributesRpcsAndRuntimeBeans(runtimeBeanEntries,
287 private static AttributeIfc getReturnTypeAttribute(DataSchemaNode child, TypeProviderWrapper typeProviderWrapper) {
288 if (child instanceof LeafSchemaNode) {
289 LeafSchemaNode leaf = (LeafSchemaNode) child;
290 return new JavaAttribute(leaf, typeProviderWrapper);
291 } else if (child instanceof ContainerSchemaNode) {
292 ContainerSchemaNode container = (ContainerSchemaNode) child;
293 TOAttribute toAttribute = TOAttribute.create(container, typeProviderWrapper);
295 } else if (child instanceof ListSchemaNode) {
296 return ListAttribute.create((ListSchemaNode) child, typeProviderWrapper);
297 } else if (child instanceof LeafListSchemaNode) {
298 return ListAttribute.create((LeafListSchemaNode) child, typeProviderWrapper);
300 throw new IllegalStateException("Unknown output data node " + child + " for rpc");
304 private static Collection<DataSchemaNode> sortAttributes(Set<DataSchemaNode> childNodes) {
305 final TreeSet<DataSchemaNode> dataSchemaNodes = new TreeSet<>(new Comparator<DataSchemaNode>() {
307 public int compare(DataSchemaNode o1, DataSchemaNode o2) {
308 return o1.getQName().getLocalName().compareTo(o2.getQName().getLocalName());
311 dataSchemaNodes.addAll(childNodes);
312 return dataSchemaNodes;
315 private static boolean isInnerStateBean(DataSchemaNode child) {
316 for (UnknownSchemaNode unknownSchemaNode : child
317 .getUnknownSchemaNodes()) {
318 if (unknownSchemaNode.getNodeType().equals(
319 ConfigConstants.INNER_STATE_BEAN_EXTENSION_QNAME))
325 private static RuntimeBeanEntry createHierarchical(String packageName,
326 ListSchemaNode listSchemaNode,
327 TypeProviderWrapper typeProviderWrapper, Module currentModule,
328 Map<QName, Set<RpcDefinition>> identitiesToRpcs) {
330 // supported are numeric types, strings, enums
331 // get all attributes
332 AttributesRpcsAndRuntimeBeans attributesRpcsAndRuntimeBeans = extractSubtree(
333 packageName, listSchemaNode, typeProviderWrapper,
334 currentModule, identitiesToRpcs);
336 Optional<String> keyYangName;
337 if (listSchemaNode.getKeyDefinition().size() == 0) {
338 keyYangName = Optional.absent();
339 } else if (listSchemaNode.getKeyDefinition().size() == 1) {
340 // key must be either null or one of supported key types
341 QName keyQName = listSchemaNode.getKeyDefinition().iterator()
343 keyYangName = Optional.of(keyQName.getLocalName());
346 throw new IllegalArgumentException(
347 "More than one key is not supported in " + listSchemaNode);
350 String javaNamePrefix = ModuleMXBeanEntry
351 .findJavaNamePrefix(listSchemaNode);
353 RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName,
354 listSchemaNode, listSchemaNode.getQName().getLocalName(),
355 javaNamePrefix, false, keyYangName,
356 attributesRpcsAndRuntimeBeans.getAttributes(),
357 attributesRpcsAndRuntimeBeans.getRuntimeBeanEntries(),
358 attributesRpcsAndRuntimeBeans.getRpcs());
360 return rbFromAttributes;
363 private static RuntimeBeanEntry createRoot(String packageName,
364 DataSchemaNode nodeForReporting, String attributeYangName,
365 List<AttributeIfc> attributes, String javaNamePrefix,
366 List<RuntimeBeanEntry> children, Set<Rpc> rpcs) {
367 return new RuntimeBeanEntry(packageName, nodeForReporting,
368 attributeYangName, javaNamePrefix, true,
369 Optional.<String> absent(), attributes, children, rpcs);
372 public boolean isRoot() {
376 public Optional<String> getKeyYangName() {
380 public Optional<String> getKeyJavaName() {
384 public Collection<AttributeIfc> getAttributes() {
385 return attributeMap.values();
388 public Map<String, AttributeIfc> getYangPropertiesToTypesMap() {
392 public String getYangName() {
396 public String getPackageName() {
400 public String getJavaNamePrefix() {
401 return javaNamePrefix;
404 public List<RuntimeBeanEntry> getChildren() {
408 public Set<Rpc> getRpcs() {
412 private static class AttributesRpcsAndRuntimeBeans {
413 private final List<RuntimeBeanEntry> runtimeBeanEntries;
414 private final List<AttributeIfc> attributes;
415 private final Set<Rpc> rpcs;
417 public AttributesRpcsAndRuntimeBeans(
418 List<RuntimeBeanEntry> runtimeBeanEntries,
419 List<AttributeIfc> attributes, Set<Rpc> rpcs) {
420 this.runtimeBeanEntries = runtimeBeanEntries;
421 this.attributes = attributes;
425 private List<AttributeIfc> getAttributes() {
429 public List<RuntimeBeanEntry> getRuntimeBeanEntries() {
430 return runtimeBeanEntries;
433 public boolean isEmpty() {
434 return attributes.isEmpty() && rpcs.isEmpty();
437 private Set<Rpc> getRpcs() {
442 public static class Rpc {
443 private final String name;
444 private final List<JavaAttribute> parameters;
445 private final AttributeIfc returnType;
446 private final String yangName;
448 Rpc(AttributeIfc returnType, String name, String yangName,
449 List<JavaAttribute> parameters) {
450 this.returnType = returnType;
452 this.parameters = parameters;
453 this.yangName = yangName;
456 public String getYangName() {
460 public String getName() {
464 public List<JavaAttribute> getParameters() {
468 public AttributeIfc getReturnType() {
473 private static final String MXBEAN_SUFFIX = "RuntimeMXBean";
475 public String getJavaNameOfRuntimeMXBean() {
476 return getJavaNameOfRuntimeMXBean(javaNamePrefix);
479 public String getFullyQualifiedName(String typeName) {
480 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
484 private static String getJavaNameOfRuntimeMXBean(String javaNamePrefix) {
485 return javaNamePrefix + MXBEAN_SUFFIX;
489 public String toString() {
490 return "RuntimeBeanEntry{" + "isRoot=" + isRoot + ", yangName='"
491 + yangName + '\'' + ", packageName='" + packageName + '\''
492 + ", keyYangName=" + keyYangName + '}';