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.Sets;
13 import java.util.Arrays;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
18 import java.util.Map.Entry;
20 import java.util.regex.Matcher;
21 import java.util.regex.Pattern;
22 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
23 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
24 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
25 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
26 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
27 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
28 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
29 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
30 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
31 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
32 import org.opendaylight.yangtools.yang.common.QName;
33 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
34 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
35 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.Module;
43 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
44 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
48 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.UsesNode;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import static com.google.common.base.Preconditions.checkNotNull;
53 import static com.google.common.base.Preconditions.checkState;
54 import static java.lang.String.format;
55 import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName;
58 * Represents part of yang model that describes a module.
65 * identity threadpool-dynamic {
66 * base config:module-type;
67 * description "threadpool-dynamic description";
68 * config:provided-service "th2:threadpool";
69 * config:provided-service "th2:scheduled-threadpool";
70 * config:java-name-prefix DynamicThreadPool
72 * augment "/config:modules/config:module/config:module-type" {
73 * case threadpool-dynamic {
74 * when "/config:modules/config:module/config:module-type = 'threadpool-dynamic'";
76 * container "configuration" {
77 * // regular java attribute
84 * container threadfactory {
85 * uses config:service-ref {
87 * config:required-identity th:threadfactory;
98 public class ModuleMXBeanEntry extends AbstractEntry {
99 private static final Logger logger = LoggerFactory
100 .getLogger(ModuleMXBeanEntry.class);
102 // TODO: the XPath should be parsed by code generator IMO
103 private static final String MAGIC_STRING = "MAGIC_STRING";
104 private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
105 private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath(
106 Arrays.asList(createConfigQName("modules"),
107 createConfigQName("module"),
108 createConfigQName("configuration")), true);
109 private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath(
110 Arrays.asList(createConfigQName("modules"),
111 createConfigQName("module"), createConfigQName("state")),
114 private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern
115 .compile("^(.+):(.+)$");
117 private static final String MODULE_SUFFIX = "Module";
118 private static final String FACTORY_SUFFIX = MODULE_SUFFIX + "Factory";
119 private static final String CLASS_NAME_SUFFIX = MODULE_SUFFIX + "MXBean";
120 private static final String ABSTRACT_PREFIX = "Abstract";
123 * threadpool-dynamic from the example above, taken from when condition, not
126 private final String globallyUniqueName;
128 private Map<String, AttributeIfc> yangToAttributes;
130 private final String nullableDescription, packageName, javaNamePrefix,
133 private final Map<String, QName> providedServices;
135 private Collection<RuntimeBeanEntry> runtimeBeans;
136 private final QName yangModuleQName;
138 public ModuleMXBeanEntry(IdentitySchemaNode id,
139 Map<String, AttributeIfc> yangToAttributes, String packageName,
140 Map<String, QName> providedServices2, String javaNamePrefix,
141 String namespace, Collection<RuntimeBeanEntry> runtimeBeans,
142 QName yangModuleQName) {
143 this.globallyUniqueName = id.getQName().getLocalName();
144 this.yangToAttributes = yangToAttributes;
145 this.nullableDescription = id.getDescription();
146 this.packageName = packageName;
147 this.javaNamePrefix = checkNotNull(javaNamePrefix);
148 this.namespace = checkNotNull(namespace);
149 this.providedServices = Collections.unmodifiableMap(providedServices2);
150 this.runtimeBeans = runtimeBeans;
151 this.yangModuleQName = yangModuleQName;
154 public String getMXBeanInterfaceName() {
155 return javaNamePrefix + CLASS_NAME_SUFFIX;
158 public String getStubFactoryName() {
159 return javaNamePrefix + FACTORY_SUFFIX;
162 public String getAbstractFactoryName() {
163 return ABSTRACT_PREFIX + getStubFactoryName();
166 public String getStubModuleName() {
167 return javaNamePrefix + MODULE_SUFFIX;
170 public String getAbstractModuleName() {
171 return ABSTRACT_PREFIX + getStubModuleName();
174 public String getFullyQualifiedName(String typeName) {
175 return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
179 public String getGloballyUniqueName() {
180 return globallyUniqueName;
183 public String getPackageName() {
188 * @return services implemented by this module. Keys are fully qualified
189 * java names of generated ServiceInterface classes, values are
190 * identity local names.
192 public Map<String, QName> getProvidedServices() {
193 return providedServices;
196 public void setRuntimeBeans(Collection<RuntimeBeanEntry> newRuntimeBeans) {
197 runtimeBeans = newRuntimeBeans;
200 public Collection<RuntimeBeanEntry> getRuntimeBeans() {
204 public String getJavaNamePrefix() {
205 return javaNamePrefix;
208 public String getNamespace() {
213 static Matcher getWhenConditionMatcher(String prefix,
214 RevisionAwareXPath whenConstraint) {
215 String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(
216 MAGIC_STRING, prefix);
217 Pattern pattern = Pattern.compile(xpathRegex);
218 return pattern.matcher(whenConstraint.toString());
221 static String getConfigModulePrefixFromImport(Module currentModule) {
222 for (ModuleImport currentImport : currentModule.getImports()) {
223 if (currentImport.getModuleName().equals(
224 ConfigConstants.CONFIG_MODULE)) {
225 return currentImport.getPrefix();
228 throw new IllegalArgumentException("Cannot find import "
229 + ConfigConstants.CONFIG_MODULE + " in " + currentModule);
233 * Transform module to zero or more ModuleMXBeanEntry instances. Each
234 * instance must have a globally unique local name.
236 * @return Map of identity local names as keys, and ModuleMXBeanEntry
237 * instances as values
239 public static Map<String/* identity local name */, ModuleMXBeanEntry> create(
240 Module currentModule,
241 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
242 SchemaContext schemaContext,
243 TypeProviderWrapper typeProviderWrapper, String packageName) {
244 Map<String, QName> uniqueGeneratedClassesNames = new HashMap<>();
245 logger.debug("Generating ModuleMXBeans of {} to package {}",
246 currentModule.getNamespace(), packageName);
247 String configModulePrefix;
249 configModulePrefix = getConfigModulePrefixFromImport(currentModule);
250 } catch (IllegalArgumentException e) {
251 // this module does not import config module
252 return Collections.emptyMap();
255 // get identities of base config:module-type
256 Map<String, IdentitySchemaNode> moduleIdentities = new HashMap<>();
258 for (IdentitySchemaNode id : currentModule.getIdentities()) {
259 if (id.getBaseIdentity() != null
260 && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id
261 .getBaseIdentity().getQName())) {
262 String identityLocalName = id.getQName().getLocalName();
263 if (moduleIdentities.containsKey(identityLocalName)) {
264 throw new IllegalStateException(
265 "Module name already defined in this module: "
266 + identityLocalName);
268 moduleIdentities.put(identityLocalName, id);
269 logger.debug("Found identity {}", identityLocalName);
271 // validation check on unknown schema nodes
272 boolean providedServiceWasSet = false;
273 for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
275 if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
276 .equals(unknownNode.getNodeType())) {
277 // no op: 0 or more provided identities are allowed
278 } else if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
279 .equals(unknownNode.getNodeType())) {
282 providedServiceWasSet == false,
283 format("More than one language extension %s is not allowed here: %s",
284 ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME,
286 providedServiceWasSet = true;
288 throw new IllegalStateException(
289 "Unexpected language extension "
290 + unknownNode.getNodeType());
295 Map<String, ModuleMXBeanEntry> result = new HashMap<>();
296 // each module name should have an augmentation defined
297 Map<String, IdentitySchemaNode> unaugmentedModuleIdentities = new HashMap<>(
299 for (AugmentationSchema augmentation : currentModule.getAugmentations()) {
300 Set<DataSchemaNode> childNodes = augmentation.getChildNodes();
301 if (childNodes.size() == 1) {
302 DataSchemaNode when = childNodes.iterator().next();
303 if (when instanceof ChoiceCaseNode) {
304 ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when;
305 if (choiceCaseNode.getConstraints() == null
306 || choiceCaseNode.getConstraints()
307 .getWhenCondition() == null) {
310 RevisionAwareXPath xPath = choiceCaseNode.getConstraints()
312 Matcher matcher = getWhenConditionMatcher(
313 configModulePrefix, xPath);
314 if (matcher.matches() == false) {
317 String moduleLocalNameFromXPath = matcher.group(1);
318 IdentitySchemaNode moduleIdentity = moduleIdentities
319 .get(moduleLocalNameFromXPath);
320 unaugmentedModuleIdentities
321 .remove(moduleLocalNameFromXPath);
322 checkState(moduleIdentity != null, "Cannot find identity "
323 + moduleLocalNameFromXPath
324 + " matching augmentation " + augmentation);
325 Map<String, QName> providedServices = findProvidedServices(
326 moduleIdentity, currentModule, qNamesToSIEs,
329 if (moduleIdentity == null) {
330 throw new IllegalStateException(
331 "Cannot find identity specified by augmentation xpath constraint: "
332 + moduleLocalNameFromXPath + " of "
335 String javaNamePrefix = findJavaNamePrefix(moduleIdentity);
337 Map<String, AttributeIfc> yangToAttributes = null;
339 Collection<RuntimeBeanEntry> runtimeBeans = null;
341 if (expectedConfigurationAugmentationSchemaPath
342 .equals(augmentation.getTargetPath())) {
343 logger.debug("Parsing configuration of {}",
344 moduleLocalNameFromXPath);
345 yangToAttributes = fillConfiguration(choiceCaseNode,
346 currentModule, typeProviderWrapper,
347 qNamesToSIEs, schemaContext, packageName);
348 checkUniqueAttributesWithGeneratedClass(
349 uniqueGeneratedClassesNames, when.getQName(),
351 } else if (expectedStateAugmentationSchemaPath
352 .equals(augmentation.getTargetPath())) {
353 logger.debug("Parsing state of {}",
354 moduleLocalNameFromXPath);
356 runtimeBeans = fillRuntimeBeans(choiceCaseNode,
357 currentModule, typeProviderWrapper,
358 packageName, moduleLocalNameFromXPath,
360 } catch (NameConflictException e) {
361 throw new NameConflictException(
362 e.getConflictingName(), when.getQName(),
365 checkUniqueRuntimeBeansGeneratedClasses(
366 uniqueGeneratedClassesNames, when, runtimeBeans);
367 Set<RuntimeBeanEntry> runtimeBeanEntryValues = Sets
368 .newHashSet(runtimeBeans);
369 for (RuntimeBeanEntry entry : runtimeBeanEntryValues) {
370 checkUniqueAttributesWithGeneratedClass(
371 uniqueGeneratedClassesNames,
373 entry.getYangPropertiesToTypesMap());
377 throw new IllegalArgumentException(
378 "Cannot parse augmentation " + augmentation);
380 if (result.containsKey(moduleLocalNameFromXPath)) {
381 // either fill runtimeBeans or yangToAttributes
382 ModuleMXBeanEntry moduleMXBeanEntry = result
383 .get(moduleLocalNameFromXPath);
384 if (yangToAttributes != null
385 && moduleMXBeanEntry.getAttributes() == null) {
387 .setYangToAttributes(yangToAttributes);
388 } else if (runtimeBeans != null
389 && moduleMXBeanEntry.getRuntimeBeans() == null) {
390 moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
393 // construct ModuleMXBeanEntry
394 ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(
395 moduleIdentity, yangToAttributes, packageName,
396 providedServices, javaNamePrefix, currentModule
397 .getNamespace().toString(),
399 ModuleUtil.getQName(currentModule));
400 moduleMXBeanEntry.setYangModuleName(currentModule
403 .setYangModuleLocalname(moduleLocalNameFromXPath);
404 result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
406 } // skip if child node is not ChoiceCaseNode
407 } // skip if childNodes != 1
410 for (Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
411 ModuleMXBeanEntry module = entry.getValue();
412 if (module.getAttributes() == null) {
413 module.setYangToAttributes(Collections
414 .<String, AttributeIfc> emptyMap());
415 } else if (module.getRuntimeBeans() == null) {
416 module.setRuntimeBeans(Collections
417 .<RuntimeBeanEntry> emptyList());
420 // check attributes name uniqueness
421 for (Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
422 checkUniqueRuntimeBeanAttributesName(entry.getValue(),
423 uniqueGeneratedClassesNames);
425 if (unaugmentedModuleIdentities.size() > 0) {
426 logger.warn("Augmentation not found for all module identities: {}",
427 unaugmentedModuleIdentities.keySet());
430 logger.debug("Number of ModuleMXBeans to be generated: {}",
435 private static void checkUniqueRuntimeBeansGeneratedClasses(
436 Map<String, QName> uniqueGeneratedClassesNames,
437 DataSchemaNode when, Collection<RuntimeBeanEntry> runtimeBeans) {
438 for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
439 final String javaNameOfRuntimeMXBean = runtimeBean
440 .getJavaNameOfRuntimeMXBean();
441 if (uniqueGeneratedClassesNames
442 .containsKey(javaNameOfRuntimeMXBean)) {
443 QName firstDefinedQName = uniqueGeneratedClassesNames
444 .get(javaNameOfRuntimeMXBean);
445 throw new NameConflictException(javaNameOfRuntimeMXBean,
446 firstDefinedQName, when.getQName());
448 uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean,
453 private static void checkUniqueRuntimeBeanAttributesName(
454 ModuleMXBeanEntry mxBeanEntry,
455 Map<String, QName> uniqueGeneratedClassesNames) {
456 for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) {
457 for (String runtimeAttName : runtimeBeanEntry
458 .getYangPropertiesToTypesMap().keySet()) {
459 if (mxBeanEntry.getAttributes().keySet()
460 .contains(runtimeAttName)) {
461 QName qName1 = uniqueGeneratedClassesNames
462 .get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean());
463 QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry
464 .getGloballyUniqueName());
465 throw new NameConflictException(runtimeAttName, qName1,
472 private static void checkUniqueAttributesWithGeneratedClass(
473 Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
474 Map<String, AttributeIfc> yangToAttributes) {
475 for (Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
476 if (attr.getValue() instanceof TOAttribute) {
477 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
478 (TOAttribute) attr.getValue());
479 } else if (attr.getValue() instanceof ListAttribute
480 && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
481 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
482 (TOAttribute) ((ListAttribute) attr.getValue())
483 .getInnerAttribute());
488 private static void checkUniqueTOAttr(
489 Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
491 final String upperCaseCammelCase = attr.getUpperCaseCammelCase();
492 if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) {
493 QName firstDefinedQName = uniqueGeneratedClassNames
494 .get(upperCaseCammelCase);
495 throw new NameConflictException(upperCaseCammelCase,
496 firstDefinedQName, parentQName);
498 uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName);
502 private static Collection<RuntimeBeanEntry> fillRuntimeBeans(
503 ChoiceCaseNode choiceCaseNode, Module currentModule,
504 TypeProviderWrapper typeProviderWrapper, String packageName,
505 String moduleLocalNameFromXPath, String javaNamePrefix) {
507 return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName,
508 choiceCaseNode, moduleLocalNameFromXPath, typeProviderWrapper,
509 javaNamePrefix, currentModule).values();
513 private static Map<String, AttributeIfc> fillConfiguration(
514 ChoiceCaseNode choiceCaseNode, Module currentModule,
515 TypeProviderWrapper typeProviderWrapper,
516 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
517 SchemaContext schemaContext, String packageName) {
518 Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
519 for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) {
520 AttributeIfc attributeValue = getAttributeValue(attrNode,
521 currentModule, qNamesToSIEs, typeProviderWrapper,
522 schemaContext, packageName);
523 yangToAttributes.put(attributeValue.getAttributeYangName(),
526 return yangToAttributes;
529 private static Map<String, QName> findProvidedServices(
530 IdentitySchemaNode moduleIdentity, Module currentModule,
531 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
532 SchemaContext schemaContext) {
533 Map<String, QName> result = new HashMap<>();
534 for (UnknownSchemaNode unknownNode : moduleIdentity
535 .getUnknownSchemaNodes()) {
536 if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
537 .equals(unknownNode.getNodeType())) {
538 String prefixAndIdentityLocalName = unknownNode
540 ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName,
541 currentModule, qNamesToSIEs, schemaContext);
542 result.put(sie.getFullyQualifiedName(), sie.getQName());
549 * For input node, find if it contains config:java-name-prefix extension. If
550 * not found, convert local name of node converted to cammel case.
552 public static String findJavaNamePrefix(SchemaNode schemaNode) {
553 return convertToJavaName(schemaNode, true);
556 public static String findJavaParameter(SchemaNode schemaNode) {
557 return convertToJavaName(schemaNode, false);
560 public static String convertToJavaName(SchemaNode schemaNode,
561 boolean capitalizeFirstLetter) {
562 for (UnknownSchemaNode unknownNode : schemaNode.getUnknownSchemaNodes()) {
563 if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
564 .equals(unknownNode.getNodeType())) {
565 String value = unknownNode.getNodeParameter();
566 return convertToJavaName(value, capitalizeFirstLetter);
569 return convertToJavaName(schemaNode.getQName().getLocalName(),
570 capitalizeFirstLetter);
573 public static String convertToJavaName(String localName,
574 boolean capitalizeFirstLetter) {
575 if (capitalizeFirstLetter) {
576 return BindingGeneratorUtil.parseToClassName(localName);
578 return BindingGeneratorUtil.parseToValidParamName(localName);
582 private static int getChildNodeSizeWithoutUses(DataNodeContainer csn) {
584 for (DataSchemaNode dsn : csn.getChildNodes()) {
585 if (dsn.isAddedByUses() == false) {
592 private static AttributeIfc getAttributeValue(DataSchemaNode attrNode,
593 Module currentModule,
594 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
595 TypeProviderWrapper typeProviderWrapper,
596 SchemaContext schemaContext, String packageName) {
598 if (attrNode instanceof LeafSchemaNode) {
600 LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
601 return new JavaAttribute(leaf, typeProviderWrapper);
602 } else if (attrNode instanceof ContainerSchemaNode) {
604 ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
605 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
606 containerSchemaNode, attrNode, currentModule, qNamesToSIEs,
608 if (dependencyAttributeOptional.isPresent()) {
609 return dependencyAttributeOptional.get();
611 return TOAttribute.create(containerSchemaNode,
612 typeProviderWrapper, packageName);
615 } else if (attrNode instanceof LeafListSchemaNode) {
616 return ListAttribute.create((LeafListSchemaNode) attrNode,
617 typeProviderWrapper);
618 } else if (attrNode instanceof ListSchemaNode) {
619 ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode;
620 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
621 listSchemaNode, attrNode, currentModule, qNamesToSIEs,
623 if (dependencyAttributeOptional.isPresent()) {
624 return dependencyAttributeOptional.get();
626 return ListAttribute.create(listSchemaNode,
627 typeProviderWrapper, packageName);
630 throw new UnsupportedOperationException(
631 "Unknown configuration node " + attrNode.toString());
635 private static Optional<? extends AbstractDependencyAttribute> extractDependency(
636 DataNodeContainer dataNodeContainer, DataSchemaNode attrNode,
637 Module currentModule,
638 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
639 SchemaContext schemaContext) {
640 if (dataNodeContainer.getUses().size() == 1
641 && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) {
643 UsesNode usesNode = dataNodeContainer.getUses().iterator().next();
644 checkState(usesNode.getRefines().size() == 1,
645 "Unexpected 'refine' child node size of "
646 + dataNodeContainer);
647 LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines()
648 .values().iterator().next();
649 checkState(refine.getUnknownSchemaNodes().size() == 1,
650 "Unexpected unknown schema node size of " + refine);
651 UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes()
654 ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity
655 .getNodeType()), "Unexpected language extension "
657 String prefixAndIdentityLocalName = requiredIdentity
659 // import should point to a module
660 ServiceInterfaceEntry serviceInterfaceEntry = findSIE(
661 prefixAndIdentityLocalName, currentModule, qNamesToSIEs,
663 boolean mandatory = refine.getConstraints().isMandatory();
664 AbstractDependencyAttribute reference;
665 if (dataNodeContainer instanceof ContainerSchemaNode) {
666 reference = new DependencyAttribute(attrNode,
667 serviceInterfaceEntry, mandatory,
668 attrNode.getDescription());
670 reference = new ListDependenciesAttribute(attrNode,
671 serviceInterfaceEntry, mandatory,
672 attrNode.getDescription());
674 return Optional.of(reference);
676 return Optional.absent();
679 private static ServiceInterfaceEntry findSIE(
680 String prefixAndIdentityLocalName, Module currentModule,
681 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
682 SchemaContext schemaContext) {
684 Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
688 // if there is a prefix, look for ModuleImport with this prefix. Get
689 // Module from SchemaContext
690 String prefix = m.group(1);
691 ModuleImport moduleImport = findModuleImport(currentModule, prefix);
692 foundModule = schemaContext.findModuleByName(
693 moduleImport.getModuleName(), moduleImport.getRevision());
696 format("Module not found in SchemaContext by %s",
698 localSIName = m.group(2);
700 foundModule = currentModule; // no prefix => SIE is in currentModule
701 localSIName = prefixAndIdentityLocalName;
703 QName siQName = new QName(foundModule.getNamespace(),
704 foundModule.getRevision(), localSIName);
705 ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
706 checkState(sie != null, "Cannot find referenced Service Interface by "
707 + prefixAndIdentityLocalName);
711 private static ModuleImport findModuleImport(Module module, String prefix) {
712 for (ModuleImport moduleImport : module.getImports()) {
713 if (moduleImport.getPrefix().equals(prefix)) {
717 throw new IllegalStateException(format(
718 "Import not found with prefix %s in %s", prefix, module));
721 public Map<String, AttributeIfc> getAttributes() {
722 return yangToAttributes;
725 private void setYangToAttributes(Map<String, AttributeIfc> newAttributes) {
726 this.yangToAttributes = newAttributes;
730 public String getNullableDescription() {
731 return nullableDescription;
734 public QName getYangModuleQName() {
735 return yangModuleQName;
739 public String toString() {
740 return "ModuleMXBeanEntry{" + "globallyUniqueName='"
741 + globallyUniqueName + '\'' + ", packageName='" + packageName