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.checkNotNull;
11 import static com.google.common.base.Preconditions.checkState;
12 import static java.lang.String.format;
13 import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName;
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
20 import java.util.Map.Entry;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
25 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
26 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
27 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
28 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
29 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
30 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
31 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
32 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
33 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
34 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
35 import org.opendaylight.yangtools.yang.common.QName;
36 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
37 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
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.ModuleImport;
47 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
48 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
49 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
51 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
52 import org.opendaylight.yangtools.yang.model.api.UsesNode;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
56 import com.google.common.annotations.VisibleForTesting;
57 import com.google.common.base.Optional;
58 import com.google.common.collect.Sets;
61 * Represents part of yang model that describes a module.
68 * identity threadpool-dynamic {
69 * base config:module-type;
70 * description "threadpool-dynamic description";
71 * config:provided-service "th2:threadpool";
72 * config:provided-service "th2:scheduled-threadpool";
73 * config:java-name-prefix DynamicThreadPool
75 * augment "/config:modules/config:module/config:module-type" {
76 * case threadpool-dynamic {
77 * when "/config:modules/config:module/config:module-type = 'threadpool-dynamic'";
79 * container "configuration" {
80 * // regular java attribute
87 * container threadfactory {
88 * uses config:service-ref {
90 * config:required-identity th:threadfactory;
101 public class ModuleMXBeanEntry extends AbstractEntry {
102 private static final Logger logger = LoggerFactory
103 .getLogger(ModuleMXBeanEntry.class);
105 // TODO: the XPath should be parsed by code generator IMO
106 private static final String MAGIC_STRING = "MAGIC_STRING";
107 private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
108 private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath(
109 Arrays.asList(createConfigQName("modules"),
110 createConfigQName("module"),
111 createConfigQName("configuration")), true);
112 private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath(
113 Arrays.asList(createConfigQName("modules"),
114 createConfigQName("module"), createConfigQName("state")),
117 private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern
118 .compile("^(.+):(.+)$");
120 private static final String MODULE_SUFFIX = "Module";
121 private static final String FACTORY_SUFFIX = MODULE_SUFFIX + "Factory";
122 private static final String CLASS_NAME_SUFFIX = MODULE_SUFFIX + "MXBean";
123 private static final String ABSTRACT_PREFIX = "Abstract";
126 * threadpool-dynamic from the example above, taken from when condition, not
129 private final String globallyUniqueName;
131 private Map<String, AttributeIfc> yangToAttributes;
133 private final String nullableDescription, packageName, javaNamePrefix,
136 private final Map<String, QName> providedServices;
138 private Collection<RuntimeBeanEntry> runtimeBeans;
140 public ModuleMXBeanEntry(IdentitySchemaNode id,
141 Map<String, AttributeIfc> yangToAttributes, String packageName,
142 Map<String, QName> providedServices2, String javaNamePrefix,
143 String namespace, Collection<RuntimeBeanEntry> runtimeBeans) {
144 this.globallyUniqueName = id.getQName().getLocalName();
145 this.yangToAttributes = yangToAttributes;
146 this.nullableDescription = id.getDescription();
147 this.packageName = packageName;
148 this.javaNamePrefix = checkNotNull(javaNamePrefix);
149 this.namespace = checkNotNull(namespace);
150 this.providedServices = Collections.unmodifiableMap(providedServices2);
151 this.runtimeBeans = runtimeBeans;
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 moduleMXBeanEntry.setYangModuleName(currentModule
402 .setYangModuleLocalname(moduleLocalNameFromXPath);
403 result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
405 } // skip if child node is not ChoiceCaseNode
406 } // skip if childNodes != 1
409 for (Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
410 ModuleMXBeanEntry module = entry.getValue();
411 if (module.getAttributes() == null) {
412 module.setYangToAttributes(Collections
413 .<String, AttributeIfc> emptyMap());
414 } else if (module.getRuntimeBeans() == null) {
415 module.setRuntimeBeans(Collections
416 .<RuntimeBeanEntry> emptyList());
419 // check attributes name uniqueness
420 for (Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
421 checkUniqueRuntimeBeanAttributesName(entry.getValue(),
422 uniqueGeneratedClassesNames);
424 if (unaugmentedModuleIdentities.size() > 0) {
425 logger.warn("Augmentation not found for all module identities: {}",
426 unaugmentedModuleIdentities.keySet());
429 logger.debug("Number of ModuleMXBeans to be generated: {}",
434 private static void checkUniqueRuntimeBeansGeneratedClasses(
435 Map<String, QName> uniqueGeneratedClassesNames,
436 DataSchemaNode when, Collection<RuntimeBeanEntry> runtimeBeans) {
437 for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
438 final String javaNameOfRuntimeMXBean = runtimeBean
439 .getJavaNameOfRuntimeMXBean();
440 if (uniqueGeneratedClassesNames
441 .containsKey(javaNameOfRuntimeMXBean)) {
442 QName firstDefinedQName = uniqueGeneratedClassesNames
443 .get(javaNameOfRuntimeMXBean);
444 throw new NameConflictException(javaNameOfRuntimeMXBean,
445 firstDefinedQName, when.getQName());
447 uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean,
452 private static void checkUniqueRuntimeBeanAttributesName(
453 ModuleMXBeanEntry mxBeanEntry,
454 Map<String, QName> uniqueGeneratedClassesNames) {
455 for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) {
456 for (String runtimeAttName : runtimeBeanEntry
457 .getYangPropertiesToTypesMap().keySet()) {
458 if (mxBeanEntry.getAttributes().keySet()
459 .contains(runtimeAttName)) {
460 QName qName1 = uniqueGeneratedClassesNames
461 .get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean());
462 QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry
463 .getGloballyUniqueName());
464 throw new NameConflictException(runtimeAttName, qName1,
471 private static void checkUniqueAttributesWithGeneratedClass(
472 Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
473 Map<String, AttributeIfc> yangToAttributes) {
474 for (Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
475 if (attr.getValue() instanceof TOAttribute) {
476 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
477 (TOAttribute) attr.getValue());
478 } else if (attr.getValue() instanceof ListAttribute
479 && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
480 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
481 (TOAttribute) ((ListAttribute) attr.getValue())
482 .getInnerAttribute());
487 private static void checkUniqueTOAttr(
488 Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
490 final String upperCaseCammelCase = attr.getUpperCaseCammelCase();
491 if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) {
492 QName firstDefinedQName = uniqueGeneratedClassNames
493 .get(upperCaseCammelCase);
494 throw new NameConflictException(upperCaseCammelCase,
495 firstDefinedQName, parentQName);
497 uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName);
501 private static Collection<RuntimeBeanEntry> fillRuntimeBeans(
502 ChoiceCaseNode choiceCaseNode, Module currentModule,
503 TypeProviderWrapper typeProviderWrapper, String packageName,
504 String moduleLocalNameFromXPath, String javaNamePrefix) {
506 return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName,
507 choiceCaseNode, moduleLocalNameFromXPath, typeProviderWrapper,
508 javaNamePrefix, currentModule).values();
512 private static Map<String, AttributeIfc> fillConfiguration(
513 ChoiceCaseNode choiceCaseNode, Module currentModule,
514 TypeProviderWrapper typeProviderWrapper,
515 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
516 SchemaContext schemaContext, String packageName) {
517 Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
518 for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) {
519 AttributeIfc attributeValue = getAttributeValue(attrNode,
520 currentModule, qNamesToSIEs, typeProviderWrapper,
521 schemaContext, packageName);
522 yangToAttributes.put(attributeValue.getAttributeYangName(),
525 return yangToAttributes;
528 private static Map<String, QName> findProvidedServices(
529 IdentitySchemaNode moduleIdentity, Module currentModule,
530 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
531 SchemaContext schemaContext) {
532 Map<String, QName> result = new HashMap<>();
533 for (UnknownSchemaNode unknownNode : moduleIdentity
534 .getUnknownSchemaNodes()) {
535 if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
536 .equals(unknownNode.getNodeType())) {
537 String prefixAndIdentityLocalName = unknownNode
539 ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName,
540 currentModule, qNamesToSIEs, schemaContext);
541 result.put(sie.getFullyQualifiedName(), sie.getQName());
548 * For input node, find if it contains config:java-name-prefix extension. If
549 * not found, convert local name of node converted to cammel case.
551 public static String findJavaNamePrefix(SchemaNode schemaNode) {
552 return convertToJavaName(schemaNode, true);
555 public static String findJavaParameter(SchemaNode schemaNode) {
556 return convertToJavaName(schemaNode, false);
559 public static String convertToJavaName(SchemaNode schemaNode,
560 boolean capitalizeFirstLetter) {
561 for (UnknownSchemaNode unknownNode : schemaNode.getUnknownSchemaNodes()) {
562 if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
563 .equals(unknownNode.getNodeType())) {
564 String value = unknownNode.getNodeParameter();
565 return convertToJavaName(value, capitalizeFirstLetter);
568 return convertToJavaName(schemaNode.getQName().getLocalName(),
569 capitalizeFirstLetter);
572 public static String convertToJavaName(String localName,
573 boolean capitalizeFirstLetter) {
574 if (capitalizeFirstLetter) {
575 return BindingGeneratorUtil.parseToClassName(localName);
577 return BindingGeneratorUtil.parseToValidParamName(localName);
581 private static int getChildNodeSizeWithoutUses(DataNodeContainer csn) {
583 for (DataSchemaNode dsn : csn.getChildNodes()) {
584 if (dsn.isAddedByUses() == false) {
591 private static AttributeIfc getAttributeValue(DataSchemaNode attrNode,
592 Module currentModule,
593 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
594 TypeProviderWrapper typeProviderWrapper,
595 SchemaContext schemaContext, String packageName) {
597 if (attrNode instanceof LeafSchemaNode) {
599 LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
600 return new JavaAttribute(leaf, typeProviderWrapper);
601 } else if (attrNode instanceof ContainerSchemaNode) {
603 ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
604 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
605 containerSchemaNode, attrNode, currentModule, qNamesToSIEs,
607 if (dependencyAttributeOptional.isPresent()) {
608 return dependencyAttributeOptional.get();
610 return TOAttribute.create(containerSchemaNode,
611 typeProviderWrapper, packageName);
614 } else if (attrNode instanceof LeafListSchemaNode) {
615 return ListAttribute.create((LeafListSchemaNode) attrNode,
616 typeProviderWrapper);
617 } else if (attrNode instanceof ListSchemaNode) {
618 ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode;
619 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
620 listSchemaNode, attrNode, currentModule, qNamesToSIEs,
622 if (dependencyAttributeOptional.isPresent()) {
623 return dependencyAttributeOptional.get();
625 return ListAttribute.create(listSchemaNode,
626 typeProviderWrapper, packageName);
629 throw new UnsupportedOperationException(
630 "Unknown configuration node " + attrNode.toString());
634 private static Optional<? extends AbstractDependencyAttribute> extractDependency(
635 DataNodeContainer dataNodeContainer, DataSchemaNode attrNode,
636 Module currentModule,
637 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
638 SchemaContext schemaContext) {
639 if (dataNodeContainer.getUses().size() == 1
640 && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) {
642 UsesNode usesNode = dataNodeContainer.getUses().iterator().next();
643 checkState(usesNode.getRefines().size() == 1,
644 "Unexpected 'refine' child node size of "
645 + dataNodeContainer);
646 LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines()
647 .values().iterator().next();
648 checkState(refine.getUnknownSchemaNodes().size() == 1,
649 "Unexpected unknown schema node size of " + refine);
650 UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes()
653 ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity
654 .getNodeType()), "Unexpected language extension "
656 String prefixAndIdentityLocalName = requiredIdentity
658 // import should point to a module
659 ServiceInterfaceEntry serviceInterfaceEntry = findSIE(
660 prefixAndIdentityLocalName, currentModule, qNamesToSIEs,
662 boolean mandatory = refine.getConstraints().isMandatory();
663 AbstractDependencyAttribute reference;
664 if (dataNodeContainer instanceof ContainerSchemaNode) {
665 reference = new DependencyAttribute(attrNode,
666 serviceInterfaceEntry, mandatory,
667 attrNode.getDescription());
669 reference = new ListDependenciesAttribute(attrNode,
670 serviceInterfaceEntry, mandatory,
671 attrNode.getDescription());
673 return Optional.of(reference);
675 return Optional.absent();
678 private static ServiceInterfaceEntry findSIE(
679 String prefixAndIdentityLocalName, Module currentModule,
680 Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
681 SchemaContext schemaContext) {
683 Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
687 // if there is a prefix, look for ModuleImport with this prefix. Get
688 // Module from SchemaContext
689 String prefix = m.group(1);
690 ModuleImport moduleImport = findModuleImport(currentModule, prefix);
691 foundModule = schemaContext.findModuleByName(
692 moduleImport.getModuleName(), moduleImport.getRevision());
695 format("Module not found in SchemaContext by %s",
697 localSIName = m.group(2);
699 foundModule = currentModule; // no prefix => SIE is in currentModule
700 localSIName = prefixAndIdentityLocalName;
702 QName siQName = new QName(foundModule.getNamespace(),
703 foundModule.getRevision(), localSIName);
704 ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
705 checkState(sie != null, "Cannot find referenced Service Interface by "
706 + prefixAndIdentityLocalName);
710 private static ModuleImport findModuleImport(Module module, String prefix) {
711 for (ModuleImport moduleImport : module.getImports()) {
712 if (moduleImport.getPrefix().equals(prefix)) {
716 throw new IllegalStateException(format(
717 "Import not found with prefix %s in %s", prefix, module));
720 public Map<String, AttributeIfc> getAttributes() {
721 return yangToAttributes;
724 private void setYangToAttributes(Map<String, AttributeIfc> newAttributes) {
725 this.yangToAttributes = newAttributes;
729 public String getNullableDescription() {
730 return nullableDescription;
734 public String toString() {
735 return "ModuleMXBeanEntry{" + "globallyUniqueName='"
736 + globallyUniqueName + '\'' + ", packageName='" + packageName