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 com.google.common.annotations.VisibleForTesting;
16 import com.google.common.base.Function;
17 import com.google.common.base.Optional;
18 import com.google.common.collect.Collections2;
19 import com.google.common.collect.Maps;
20 import com.google.common.collect.Sets;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
25 import java.util.Objects;
27 import java.util.regex.Matcher;
28 import java.util.regex.Pattern;
29 import javax.annotation.Nullable;
30 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
31 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
32 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
33 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
34 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
35 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
36 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
37 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.rev130405.ServiceRef;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
44 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.Module;
50 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
51 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
52 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
53 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
54 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
55 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
56 import org.opendaylight.yangtools.yang.model.api.UsesNode;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
60 final class ModuleMXBeanEntryBuilder {
62 private static final String TYPE = "type";
64 private Module currentModule;
65 private Map<QName, ServiceInterfaceEntry> qNamesToSIEs;
66 private SchemaContext schemaContext;
67 private TypeProviderWrapper typeProviderWrapper;
68 private String packageName;
70 public ModuleMXBeanEntryBuilder setModule(final Module module) {
71 this.currentModule = module;
75 public ModuleMXBeanEntryBuilder setqNamesToSIEs(final Map<QName, ServiceInterfaceEntry> qNamesToSIEs) {
76 this.qNamesToSIEs = qNamesToSIEs;
80 public ModuleMXBeanEntryBuilder setSchemaContext(final SchemaContext schemaContext) {
81 this.schemaContext = schemaContext;
85 public ModuleMXBeanEntryBuilder setTypeProviderWrapper(final TypeProviderWrapper typeProviderWrapper) {
86 this.typeProviderWrapper = typeProviderWrapper;
90 public ModuleMXBeanEntryBuilder setPackageName(final String packageName) {
91 this.packageName = packageName;
95 private static final Logger LOG = LoggerFactory
96 .getLogger(ModuleMXBeanEntryBuilder.class);
98 // TODO: the XPath should be parsed by code generator IMO
99 private static final String MAGIC_STRING = "MAGIC_STRING";
100 private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
102 private static final SchemaPath AUGMENT_SCHEMAPATH = SchemaPath.create(true,
103 createConfigQName("modules"), createConfigQName("module"));
105 private static final SchemaPath EXPECTED_CONFIGURATION_AUGMENTATION_SCHEMA_PATH =
106 AUGMENT_SCHEMAPATH.createChild(createConfigQName("configuration"));
107 private static final SchemaPath EXPECTED_STATE_AUGMENTATION_SCHEMA_PATH =
108 AUGMENT_SCHEMAPATH.createChild(createConfigQName("state"));
109 private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern.compile("^(.+):(.+)$");
111 public Map<String, ModuleMXBeanEntry> build() {
112 LOG.debug("Generating ModuleMXBeans of {} to package {}",
113 currentModule.getNamespace(), packageName);
115 String configModulePrefix;
117 configModulePrefix = getConfigModulePrefixFromImport(currentModule);
118 } catch (IllegalArgumentException e) {
119 // this currentModule does not import config currentModule
120 return Collections.emptyMap();
123 // get identities of base config:currentModule-type
124 Map<String, IdentitySchemaNode> moduleIdentities = getIdentityMap();
126 Map<String, QName> uniqueGeneratedClassesNames = new HashMap<>();
128 // each currentModule name should have an augmentation defined
129 Map<String, IdentitySchemaNode> unaugmentedModuleIdentities = new HashMap<>(
132 Map<String, ModuleMXBeanEntry> result = new HashMap<>();
134 for (AugmentationSchemaNode augmentation : currentModule.getAugmentations()) {
135 Collection<DataSchemaNode> childNodes = augmentation.getChildNodes();
136 if (areAllChildrenCaseSchemaNodes(childNodes)) {
137 for (CaseSchemaNode childCase : castChildNodesToChoiceCases(childNodes)) {
138 // TODO refactor, extract to standalone builder class
139 processCaseSchemaNode(result, uniqueGeneratedClassesNames, configModulePrefix, moduleIdentities,
140 unaugmentedModuleIdentities, augmentation, childCase);
142 } // skip if child nodes are not all cases
145 cleanUpNulls(result);
146 // check attributes name uniqueness
147 checkAttributeNamesUniqueness(uniqueGeneratedClassesNames, result);
148 checkUnaugumentedIdentities(unaugmentedModuleIdentities);
150 LOG.debug("Number of ModuleMXBeans to be generated: {}", result.size());
155 private static void cleanUpNulls(final Map<String, ModuleMXBeanEntry> result) {
156 for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
157 ModuleMXBeanEntry module = entry.getValue();
158 if (module.getAttributes() == null) {
159 module.setYangToAttributes(Collections
160 .<String, AttributeIfc> emptyMap());
161 } else if (module.getRuntimeBeans() == null) {
162 module.setRuntimeBeans(Collections
163 .<RuntimeBeanEntry> emptyList());
168 private static void checkUnaugumentedIdentities(final Map<String, IdentitySchemaNode> unaugmentedModuleIdentities) {
169 if (unaugmentedModuleIdentities.size() > 0) {
170 LOG.warn("Augmentation not found for all currentModule identities: {}",
171 unaugmentedModuleIdentities.keySet());
175 private static void checkAttributeNamesUniqueness(final Map<String, QName> uniqueGeneratedClassesNames,
176 final Map<String, ModuleMXBeanEntry> result) {
177 for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
178 checkUniqueRuntimeBeanAttributesName(entry.getValue(),
179 uniqueGeneratedClassesNames);
183 private Map<String, IdentitySchemaNode> getIdentityMap() {
184 Map<String, IdentitySchemaNode> moduleIdentities = Maps.newHashMap();
186 for (IdentitySchemaNode id : currentModule.getIdentities()) {
187 if (!id.getBaseIdentities().isEmpty()
188 && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id.getBaseIdentities().iterator().next().getQName())) {
189 String identityLocalName = id.getQName().getLocalName();
190 if (moduleIdentities.containsKey(identityLocalName)) {
191 throw new IllegalStateException("Module name already defined in this currentModule: "
192 + identityLocalName);
194 moduleIdentities.put(identityLocalName, id);
195 LOG.debug("Found identity {}", identityLocalName);
197 // validation check on unknown schema nodes
198 boolean providedServiceWasSet = false;
199 for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
201 boolean unknownNodeIsProvidedServiceExtension = ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType());
202 // true => no op: 0 or more provided identities are allowed
204 if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
207 providedServiceWasSet == false,
208 format("More than one language extension %s is not allowed here: %s",
209 ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME, id));
210 providedServiceWasSet = true;
211 } else if (unknownNodeIsProvidedServiceExtension == false) {
212 throw new IllegalStateException("Unexpected language extension " + unknownNode.getNodeType());
218 return moduleIdentities;
221 private static Collection<CaseSchemaNode> castChildNodesToChoiceCases(final Collection<DataSchemaNode> childNodes) {
222 return Collections2.transform(childNodes, new Function<DataSchemaNode, CaseSchemaNode>() {
225 public CaseSchemaNode apply(@Nullable final DataSchemaNode input) {
226 return (CaseSchemaNode) input;
231 private static boolean areAllChildrenCaseSchemaNodes(final Iterable<DataSchemaNode> childNodes) {
232 for (DataSchemaNode childNode : childNodes) {
233 if (childNode instanceof CaseSchemaNode == false) {
240 private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> void processCaseSchemaNode(
241 final Map<String, ModuleMXBeanEntry> result,
242 final Map<String, QName> uniqueGeneratedClassesNames, final String configModulePrefix,
243 final Map<String, IdentitySchemaNode> moduleIdentities,
244 final Map<String, IdentitySchemaNode> unaugmentedModuleIdentities,
245 final AugmentationSchemaNode augmentation, final DataSchemaNode when) {
247 CaseSchemaNode choiceCaseNode = (CaseSchemaNode) when;
248 if (!choiceCaseNode.getWhenCondition().isPresent()) {
251 java.util.Optional<RevisionAwareXPath> xPath = choiceCaseNode.getWhenCondition();
252 checkState(xPath.isPresent(), "Choice node %s does not have a when condition", choiceCaseNode);
253 Matcher matcher = getWhenConditionMatcher(configModulePrefix, xPath.get());
254 if (matcher.matches() == false) {
257 String moduleLocalNameFromXPath = matcher.group(1);
258 IdentitySchemaNode moduleIdentity = moduleIdentities.get(moduleLocalNameFromXPath);
259 unaugmentedModuleIdentities.remove(moduleLocalNameFromXPath);
260 checkState(moduleIdentity != null, "Cannot find identity %s matching augmentation %s", moduleLocalNameFromXPath, augmentation);
261 Map<String, QName> providedServices = findProvidedServices(moduleIdentity, currentModule, qNamesToSIEs,
264 String javaNamePrefix = TypeProviderWrapper.findJavaNamePrefix(moduleIdentity);
266 Map<String, AttributeIfc> yangToAttributes = null;
268 Collection<RuntimeBeanEntry> runtimeBeans = null;
270 HAS_CHILDREN_AND_QNAME dataNodeContainer = getDataNodeContainer(choiceCaseNode);
272 if (EXPECTED_CONFIGURATION_AUGMENTATION_SCHEMA_PATH.equals(augmentation.getTargetPath())) {
273 LOG.debug("Parsing configuration of {}", moduleLocalNameFromXPath);
274 yangToAttributes = fillConfiguration(dataNodeContainer, currentModule, typeProviderWrapper, qNamesToSIEs,
275 schemaContext, packageName);
276 checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(), yangToAttributes);
277 } else if (EXPECTED_STATE_AUGMENTATION_SCHEMA_PATH.equals(augmentation.getTargetPath())) {
278 LOG.debug("Parsing state of {}", moduleLocalNameFromXPath);
280 runtimeBeans = fillRuntimeBeans(dataNodeContainer, currentModule, typeProviderWrapper, packageName,
281 moduleLocalNameFromXPath, javaNamePrefix);
282 } catch (NameConflictException e) {
283 throw new NameConflictException(e.getConflictingName(), when.getQName(), when.getQName());
285 checkUniqueRuntimeBeansGeneratedClasses(uniqueGeneratedClassesNames, when, runtimeBeans);
286 Set<RuntimeBeanEntry> runtimeBeanEntryValues = Sets.newHashSet(runtimeBeans);
287 for (RuntimeBeanEntry entry : runtimeBeanEntryValues) {
288 checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(),
289 entry.getYangPropertiesToTypesMap());
293 throw new IllegalArgumentException("Cannot parse augmentation " + augmentation);
295 boolean hasDummyContainer = choiceCaseNode.equals(dataNodeContainer) == false;
297 String nullableDummyContainerName = hasDummyContainer ? dataNodeContainer.getQName().getLocalName() : null;
298 if (result.containsKey(moduleLocalNameFromXPath)) {
299 // either fill runtimeBeans or yangToAttributes, merge
300 ModuleMXBeanEntry moduleMXBeanEntry = result.get(moduleLocalNameFromXPath);
301 if (yangToAttributes != null && moduleMXBeanEntry.getAttributes() == null) {
302 moduleMXBeanEntry.setYangToAttributes(yangToAttributes);
303 } else if (runtimeBeans != null && moduleMXBeanEntry.getRuntimeBeans() == null) {
304 moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
306 checkState(Objects.equals(nullableDummyContainerName, moduleMXBeanEntry.getNullableDummyContainerName()),
307 "Mismatch in module " + moduleMXBeanEntry.toString() + " - dummy container must be present/missing in" +
308 " both state and configuration");
310 ModuleMXBeanEntry.ModuleMXBeanEntryInitial initial = new ModuleMXBeanEntry.ModuleMXBeanEntryInitialBuilder()
311 .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix)
312 .setNamespace(currentModule.getNamespace().toString()).setqName(ModuleUtil.getQName(currentModule))
315 // construct ModuleMXBeanEntry
316 ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(initial, yangToAttributes, providedServices,
319 moduleMXBeanEntry.setYangModuleName(currentModule.getName());
320 moduleMXBeanEntry.setYangModuleLocalname(moduleLocalNameFromXPath);
321 moduleMXBeanEntry.setNullableDummyContainerName(nullableDummyContainerName);
322 result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
326 private static void checkUniqueRuntimeBeansGeneratedClasses(final Map<String, QName> uniqueGeneratedClassesNames,
327 final DataSchemaNode when, final Collection<RuntimeBeanEntry> runtimeBeans) {
328 for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
329 final String javaNameOfRuntimeMXBean = runtimeBean.getJavaNameOfRuntimeMXBean();
330 if (uniqueGeneratedClassesNames.containsKey(javaNameOfRuntimeMXBean)) {
331 QName firstDefinedQName = uniqueGeneratedClassesNames.get(javaNameOfRuntimeMXBean);
332 throw new NameConflictException(javaNameOfRuntimeMXBean, firstDefinedQName, when.getQName());
334 uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean, when.getQName());
338 private static void checkUniqueRuntimeBeanAttributesName(final ModuleMXBeanEntry mxBeanEntry,
339 final Map<String, QName> uniqueGeneratedClassesNames) {
340 for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) {
341 for (String runtimeAttName : runtimeBeanEntry.getYangPropertiesToTypesMap().keySet()) {
342 if (mxBeanEntry.getAttributes().keySet().contains(runtimeAttName)) {
343 QName qName1 = uniqueGeneratedClassesNames.get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean());
344 QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry.getGloballyUniqueName());
345 throw new NameConflictException(runtimeAttName, qName1, qName2);
351 private static void checkUniqueAttributesWithGeneratedClass(final Map<String, QName> uniqueGeneratedClassNames,
352 final QName parentQName, final Map<String, AttributeIfc> yangToAttributes) {
353 for (Map.Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
354 if (attr.getValue() instanceof TOAttribute) {
355 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, (TOAttribute) attr.getValue());
356 } else if (attr.getValue() instanceof ListAttribute
357 && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
358 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
359 (TOAttribute) ((ListAttribute) attr.getValue()).getInnerAttribute());
364 private static void checkUniqueTOAttr(final Map<String, QName> uniqueGeneratedClassNames, final QName parentQName, final TOAttribute attr) {
365 final String upperCaseCamelCase = attr.getUpperCaseCammelCase();
366 if (uniqueGeneratedClassNames.containsKey(upperCaseCamelCase)) {
367 QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCamelCase);
368 throw new NameConflictException(upperCaseCamelCase, firstDefinedQName, parentQName);
370 uniqueGeneratedClassNames.put(upperCaseCamelCase, parentQName);
374 private Collection<RuntimeBeanEntry> fillRuntimeBeans(final DataNodeContainer dataNodeContainer, final Module currentModule,
375 final TypeProviderWrapper typeProviderWrapper, final String packageName, final String moduleLocalNameFromXPath,
376 final String javaNamePrefix) {
378 return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, dataNodeContainer, moduleLocalNameFromXPath,
379 typeProviderWrapper, javaNamePrefix, currentModule, schemaContext).values();
384 * Since each case statement within a module must provide unique child nodes, it is allowed to wrap
385 * the actual configuration with a container node with name equal to case name.
387 * @param choiceCaseNode state or configuration case statement
388 * @return either choiceCaseNode or its only child container
390 private static <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> HAS_CHILDREN_AND_QNAME getDataNodeContainer(final CaseSchemaNode choiceCaseNode) {
391 Collection<DataSchemaNode> childNodes = choiceCaseNode.getChildNodes();
392 if (childNodes.size() == 1) {
393 DataSchemaNode onlyChild = childNodes.iterator().next();
394 if (onlyChild instanceof ContainerSchemaNode) {
395 ContainerSchemaNode onlyContainer = (ContainerSchemaNode) onlyChild;
396 if (Objects.equals(onlyContainer.getQName().getLocalName(), choiceCaseNode.getQName().getLocalName())) {
397 // the actual configuration is inside dummy container
398 return (HAS_CHILDREN_AND_QNAME) onlyContainer;
402 return (HAS_CHILDREN_AND_QNAME) choiceCaseNode;
405 private static Map<String, AttributeIfc> fillConfiguration(final DataNodeContainer dataNodeContainer, final Module currentModule,
406 final TypeProviderWrapper typeProviderWrapper, final Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
407 final SchemaContext schemaContext, final String packageName) {
408 Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
409 for (DataSchemaNode attrNode : dataNodeContainer.getChildNodes()) {
410 AttributeIfc attributeValue = getAttributeValue(attrNode, currentModule, qNamesToSIEs, typeProviderWrapper,
411 schemaContext, packageName);
412 yangToAttributes.put(attributeValue.getAttributeYangName(), attributeValue);
414 return yangToAttributes;
417 private static Map<String, QName> findProvidedServices(final IdentitySchemaNode moduleIdentity, final Module currentModule,
418 final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final SchemaContext schemaContext) {
419 Map<String, QName> result = new HashMap<>();
420 for (UnknownSchemaNode unknownNode : moduleIdentity.getUnknownSchemaNodes()) {
421 if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
422 String prefixAndIdentityLocalName = unknownNode.getNodeParameter();
423 ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName, currentModule, qNamesToSIEs,
425 result.put(sie.getFullyQualifiedName(), sie.getQName());
431 private static AttributeIfc getAttributeValue(final DataSchemaNode attrNode, final Module currentModule,
432 final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final TypeProviderWrapper typeProviderWrapper,
433 final SchemaContext schemaContext, final String packageName) {
435 if (attrNode instanceof LeafSchemaNode) {
437 LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
438 return new JavaAttribute(leaf, typeProviderWrapper);
439 } else if (attrNode instanceof ContainerSchemaNode) {
441 ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
442 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
443 containerSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext);
444 if (dependencyAttributeOptional.isPresent()) {
445 return dependencyAttributeOptional.get();
447 return TOAttribute.create(containerSchemaNode, typeProviderWrapper, packageName);
450 } else if (attrNode instanceof LeafListSchemaNode) {
451 return ListAttribute.create((LeafListSchemaNode) attrNode, typeProviderWrapper);
452 } else if (attrNode instanceof ListSchemaNode) {
453 ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode;
454 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
455 listSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext);
456 if (dependencyAttributeOptional.isPresent()) {
457 return dependencyAttributeOptional.get();
459 return ListAttribute.create(listSchemaNode, typeProviderWrapper, packageName);
462 throw new UnsupportedOperationException("Unknown configuration node " + attrNode.toString());
466 private static Optional<? extends AbstractDependencyAttribute> extractDependency(final DataNodeContainer dataNodeContainer,
467 final DataSchemaNode attrNode, final Module currentModule, final Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
468 final SchemaContext schemaContext) {
469 if (isDependencyContainer(dataNodeContainer)) {
471 UsesNode usesNode = dataNodeContainer.getUses().iterator().next();
472 for (SchemaNode refineNode : usesNode.getRefines().values()) {
473 // this will ignore name nodes, since they are not needed here
474 if (TYPE.equals(refineNode.getQName().getLocalName())){
475 checkState(refineNode.getUnknownSchemaNodes().size() == 1, "Unexpected unknown schema node size of " + refineNode);
476 UnknownSchemaNode requiredIdentity = refineNode.getUnknownSchemaNodes().iterator().next();
477 checkState(ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity.getNodeType()),
478 "Unexpected language extension " + requiredIdentity);
479 String prefixAndIdentityLocalName = requiredIdentity.getNodeParameter();
480 // import should point to a module
481 ServiceInterfaceEntry serviceInterfaceEntry = findSIE(prefixAndIdentityLocalName, currentModule,
482 qNamesToSIEs, schemaContext);
483 LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines().values().iterator().next();
485 boolean mandatory = refine.isMandatory();
486 AbstractDependencyAttribute reference;
487 if (dataNodeContainer instanceof ContainerSchemaNode) {
488 reference = new DependencyAttribute(attrNode, serviceInterfaceEntry, mandatory,
489 attrNode.getDescription().orElse(null));
491 reference = new ListDependenciesAttribute(attrNode, serviceInterfaceEntry, mandatory,
492 attrNode.getDescription().orElse(null));
494 return Optional.of(reference);
498 return Optional.absent();
501 private static boolean isDependencyContainer(final DataNodeContainer dataNodeContainer) {
502 if(dataNodeContainer.getUses().size() != 1) {
505 UsesNode onlyUses = dataNodeContainer.getUses().iterator().next();
506 if(onlyUses.getGroupingPath().getLastComponent().equals(ServiceRef.QNAME) == false) {
510 return getChildNodeSizeWithoutUses(dataNodeContainer) == 0;
513 private static int getChildNodeSizeWithoutUses(final DataNodeContainer csn) {
515 for (DataSchemaNode dsn : csn.getChildNodes()) {
516 if (dsn.isAddedByUses() == false) {
523 private static ServiceInterfaceEntry findSIE(final String prefixAndIdentityLocalName, final Module currentModule,
524 final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final SchemaContext schemaContext) {
526 Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
530 // if there is a prefix, look for ModuleImport with this prefix. Get
531 // Module from SchemaContext
532 String prefix = m.group(1);
533 ModuleImport moduleImport = findModuleImport(currentModule, prefix);
534 foundModule = schemaContext.findModule(moduleImport.getModuleName(), moduleImport.getRevision()).orElse(null);
535 checkNotNull(foundModule, format("Module not found in SchemaContext by %s", moduleImport));
536 localSIName = m.group(2);
538 foundModule = currentModule; // no prefix => SIE is in currentModule
539 localSIName = prefixAndIdentityLocalName;
541 QName siQName = QName.create(foundModule.getNamespace(), foundModule.getRevision(), localSIName);
542 ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
543 checkState(sie != null, "Cannot find referenced Service Interface by " + prefixAndIdentityLocalName);
547 private static ModuleImport findModuleImport(final Module module, final String prefix) {
548 for (ModuleImport moduleImport : module.getImports()) {
549 if (moduleImport.getPrefix().equals(prefix)) {
553 throw new IllegalStateException(format("Import not found with prefix %s in %s", prefix, module));
557 static Matcher getWhenConditionMatcher(final String prefix, final RevisionAwareXPath whenConstraint) {
558 String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(MAGIC_STRING, prefix);
559 Pattern pattern = Pattern.compile(xpathRegex);
560 return pattern.matcher(whenConstraint.toString());
563 private static String getConfigModulePrefixFromImport(final Module currentModule) {
564 for (ModuleImport currentImport : currentModule.getImports()) {
565 if (currentImport.getModuleName().equals(ConfigConstants.CONFIG_MODULE)) {
566 return currentImport.getPrefix();
569 throw new IllegalArgumentException("Cannot find import " + ConfigConstants.CONFIG_MODULE + " in "