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;
14 import com.google.common.annotations.VisibleForTesting;
15 import com.google.common.base.Function;
16 import com.google.common.base.Optional;
17 import com.google.common.collect.Collections2;
18 import com.google.common.collect.Maps;
19 import com.google.common.collect.Sets;
20 import java.util.Collection;
21 import java.util.Collections;
22 import java.util.HashMap;
24 import java.util.Objects;
26 import java.util.regex.Matcher;
27 import java.util.regex.Pattern;
28 import javax.annotation.Nullable;
29 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
30 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
31 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
32 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
33 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
34 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
35 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
36 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.controller.config.rev130405.ServiceRef;
38 import org.opendaylight.yangtools.yang.common.QName;
39 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
40 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
41 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
43 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
44 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
46 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.Module;
49 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
50 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
52 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
54 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
55 import org.opendaylight.yangtools.yang.model.api.UsesNode;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
59 final class ModuleMXBeanEntryBuilder {
61 private Module currentModule;
62 private Map<QName, ServiceInterfaceEntry> qNamesToSIEs;
63 private SchemaContext schemaContext;
64 private TypeProviderWrapper typeProviderWrapper;
65 private String packageName;
67 public ModuleMXBeanEntryBuilder setModule(final Module module) {
68 this.currentModule = module;
72 public ModuleMXBeanEntryBuilder setqNamesToSIEs(final Map<QName, ServiceInterfaceEntry> qNamesToSIEs) {
73 this.qNamesToSIEs = qNamesToSIEs;
77 public ModuleMXBeanEntryBuilder setSchemaContext(final SchemaContext schemaContext) {
78 this.schemaContext = schemaContext;
82 public ModuleMXBeanEntryBuilder setTypeProviderWrapper(final TypeProviderWrapper typeProviderWrapper) {
83 this.typeProviderWrapper = typeProviderWrapper;
87 public ModuleMXBeanEntryBuilder setPackageName(final String packageName) {
88 this.packageName = packageName;
92 private static final Logger LOG = LoggerFactory
93 .getLogger(ModuleMXBeanEntryBuilder.class);
95 // TODO: the XPath should be parsed by code generator IMO
96 private static final String MAGIC_STRING = "MAGIC_STRING";
97 private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
98 private static final SchemaPath EXPECTED_CONFIGURATION_AUGMENTATION_SCHEMA_PATH = SchemaPath.create(true,
99 createConfigQName("modules"), createConfigQName("module"), createConfigQName("configuration"));
100 private static final SchemaPath EXPECTED_STATE_AUGMENTATION_SCHEMA_PATH = SchemaPath.create(true,
101 createConfigQName("modules"), createConfigQName("module"), createConfigQName("state"));
102 private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern
103 .compile("^(.+):(.+)$");
106 public Map<String, ModuleMXBeanEntry> build() {
107 LOG.debug("Generating ModuleMXBeans of {} to package {}",
108 currentModule.getNamespace(), packageName);
110 String configModulePrefix;
112 configModulePrefix = getConfigModulePrefixFromImport(currentModule);
113 } catch (IllegalArgumentException e) {
114 // this currentModule does not import config currentModule
115 return Collections.emptyMap();
118 // get identities of base config:currentModule-type
119 Map<String, IdentitySchemaNode> moduleIdentities = getIdentityMap();
121 Map<String, QName> uniqueGeneratedClassesNames = new HashMap<>();
123 // each currentModule name should have an augmentation defined
124 Map<String, IdentitySchemaNode> unaugmentedModuleIdentities = new HashMap<>(
127 Map<String, ModuleMXBeanEntry> result = new HashMap<>();
129 for (AugmentationSchema augmentation : currentModule.getAugmentations()) {
130 Collection<DataSchemaNode> childNodes = augmentation.getChildNodes();
131 if (areAllChildrenChoiceCaseNodes(childNodes)) {
132 for (ChoiceCaseNode childCase : castChildNodesToChoiceCases(childNodes)) {
133 // TODO refactor, extract to standalone builder class
134 processChoiceCaseNode(result, uniqueGeneratedClassesNames, configModulePrefix, moduleIdentities,
135 unaugmentedModuleIdentities, augmentation, childCase);
137 } // skip if child nodes are not all cases
140 cleanUpNulls(result);
141 // check attributes name uniqueness
142 checkAttributeNamesUniqueness(uniqueGeneratedClassesNames, result);
143 checkUnaugumentedIdentities(unaugmentedModuleIdentities);
145 LOG.debug("Number of ModuleMXBeans to be generated: {}", result.size());
150 private static void cleanUpNulls(final Map<String, ModuleMXBeanEntry> result) {
151 for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
152 ModuleMXBeanEntry module = entry.getValue();
153 if (module.getAttributes() == null) {
154 module.setYangToAttributes(Collections
155 .<String, AttributeIfc> emptyMap());
156 } else if (module.getRuntimeBeans() == null) {
157 module.setRuntimeBeans(Collections
158 .<RuntimeBeanEntry> emptyList());
163 private static void checkUnaugumentedIdentities(final Map<String, IdentitySchemaNode> unaugmentedModuleIdentities) {
164 if (unaugmentedModuleIdentities.size() > 0) {
165 LOG.warn("Augmentation not found for all currentModule identities: {}",
166 unaugmentedModuleIdentities.keySet());
170 private static void checkAttributeNamesUniqueness(final Map<String, QName> uniqueGeneratedClassesNames, final Map<String, ModuleMXBeanEntry> result) {
171 for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
172 checkUniqueRuntimeBeanAttributesName(entry.getValue(),
173 uniqueGeneratedClassesNames);
177 private Map<String, IdentitySchemaNode> getIdentityMap() {
178 Map<String, IdentitySchemaNode> moduleIdentities = Maps.newHashMap();
180 for (IdentitySchemaNode id : currentModule.getIdentities()) {
181 if (id.getBaseIdentity() != null
182 && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id.getBaseIdentity().getQName())) {
183 String identityLocalName = id.getQName().getLocalName();
184 if (moduleIdentities.containsKey(identityLocalName)) {
185 throw new IllegalStateException("Module name already defined in this currentModule: "
186 + identityLocalName);
188 moduleIdentities.put(identityLocalName, id);
189 LOG.debug("Found identity {}", identityLocalName);
191 // validation check on unknown schema nodes
192 boolean providedServiceWasSet = false;
193 for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
195 boolean unknownNodeIsProvidedServiceExtension = ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType());
196 // true => no op: 0 or more provided identities are allowed
198 if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
201 providedServiceWasSet == false,
202 format("More than one language extension %s is not allowed here: %s",
203 ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME, id));
204 providedServiceWasSet = true;
205 } else if (unknownNodeIsProvidedServiceExtension == false) {
206 throw new IllegalStateException("Unexpected language extension " + unknownNode.getNodeType());
212 return moduleIdentities;
215 private Collection<ChoiceCaseNode> castChildNodesToChoiceCases(final Collection<DataSchemaNode> childNodes) {
216 return Collections2.transform(childNodes, new Function<DataSchemaNode, ChoiceCaseNode>() {
219 public ChoiceCaseNode apply(@Nullable final DataSchemaNode input) {
220 return (ChoiceCaseNode) input;
225 private boolean areAllChildrenChoiceCaseNodes(final Iterable<DataSchemaNode> childNodes) {
226 for (DataSchemaNode childNode : childNodes) {
227 if (childNode instanceof ChoiceCaseNode == false) {
234 private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> void processChoiceCaseNode(final Map<String, ModuleMXBeanEntry> result,
235 final Map<String, QName> uniqueGeneratedClassesNames, final String configModulePrefix,
236 final Map<String, IdentitySchemaNode> moduleIdentities,
237 final Map<String, IdentitySchemaNode> unaugmentedModuleIdentities, final AugmentationSchema augmentation,
238 final DataSchemaNode when) {
240 ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when;
241 if (choiceCaseNode.getConstraints() == null || choiceCaseNode.getConstraints().getWhenCondition() == null) {
244 RevisionAwareXPath xPath = choiceCaseNode.getConstraints().getWhenCondition();
245 Matcher matcher = getWhenConditionMatcher(configModulePrefix, xPath);
246 if (matcher.matches() == false) {
249 String moduleLocalNameFromXPath = matcher.group(1);
250 IdentitySchemaNode moduleIdentity = moduleIdentities.get(moduleLocalNameFromXPath);
251 unaugmentedModuleIdentities.remove(moduleLocalNameFromXPath);
252 checkState(moduleIdentity != null, "Cannot find identity " + moduleLocalNameFromXPath
253 + " matching augmentation " + augmentation);
254 Map<String, QName> providedServices = findProvidedServices(moduleIdentity, currentModule, qNamesToSIEs,
257 if (moduleIdentity == null) {
258 throw new IllegalStateException("Cannot find identity specified by augmentation xpath constraint: "
259 + moduleLocalNameFromXPath + " of " + augmentation);
261 String javaNamePrefix = TypeProviderWrapper.findJavaNamePrefix(moduleIdentity);
263 Map<String, AttributeIfc> yangToAttributes = null;
265 Collection<RuntimeBeanEntry> runtimeBeans = null;
267 HAS_CHILDREN_AND_QNAME dataNodeContainer = getDataNodeContainer(choiceCaseNode);
269 if (EXPECTED_CONFIGURATION_AUGMENTATION_SCHEMA_PATH.equals(augmentation.getTargetPath())) {
270 LOG.debug("Parsing configuration of {}", moduleLocalNameFromXPath);
271 yangToAttributes = fillConfiguration(dataNodeContainer, currentModule, typeProviderWrapper, qNamesToSIEs,
272 schemaContext, packageName);
273 checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(), yangToAttributes);
274 } else if (EXPECTED_STATE_AUGMENTATION_SCHEMA_PATH.equals(augmentation.getTargetPath())) {
275 LOG.debug("Parsing state of {}", moduleLocalNameFromXPath);
277 runtimeBeans = fillRuntimeBeans(dataNodeContainer, currentModule, typeProviderWrapper, packageName,
278 moduleLocalNameFromXPath, javaNamePrefix);
279 } catch (NameConflictException e) {
280 throw new NameConflictException(e.getConflictingName(), when.getQName(), when.getQName());
282 checkUniqueRuntimeBeansGeneratedClasses(uniqueGeneratedClassesNames, when, runtimeBeans);
283 Set<RuntimeBeanEntry> runtimeBeanEntryValues = Sets.newHashSet(runtimeBeans);
284 for (RuntimeBeanEntry entry : runtimeBeanEntryValues) {
285 checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(),
286 entry.getYangPropertiesToTypesMap());
290 throw new IllegalArgumentException("Cannot parse augmentation " + augmentation);
292 boolean hasDummyContainer = choiceCaseNode.equals(dataNodeContainer) == false;
294 String nullableDummyContainerName = hasDummyContainer ? dataNodeContainer.getQName().getLocalName() : null;
295 if (result.containsKey(moduleLocalNameFromXPath)) {
296 // either fill runtimeBeans or yangToAttributes, merge
297 ModuleMXBeanEntry moduleMXBeanEntry = result.get(moduleLocalNameFromXPath);
298 if (yangToAttributes != null && moduleMXBeanEntry.getAttributes() == null) {
299 moduleMXBeanEntry.setYangToAttributes(yangToAttributes);
300 } else if (runtimeBeans != null && moduleMXBeanEntry.getRuntimeBeans() == null) {
301 moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
303 checkState(Objects.equals(nullableDummyContainerName, moduleMXBeanEntry.getNullableDummyContainerName()),
304 "Mismatch in module " + moduleMXBeanEntry.toString() + " - dummy container must be present/missing in" +
305 " both state and configuration");
307 ModuleMXBeanEntry.ModuleMXBeanEntryInitial initial = new ModuleMXBeanEntry.ModuleMXBeanEntryInitialBuilder()
308 .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix)
309 .setNamespace(currentModule.getNamespace().toString()).setqName(ModuleUtil.getQName(currentModule))
312 // construct ModuleMXBeanEntry
313 ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(initial, yangToAttributes, providedServices,
316 moduleMXBeanEntry.setYangModuleName(currentModule.getName());
317 moduleMXBeanEntry.setYangModuleLocalname(moduleLocalNameFromXPath);
318 moduleMXBeanEntry.setNullableDummyContainerName(nullableDummyContainerName);
319 result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
323 private void checkUniqueRuntimeBeansGeneratedClasses(final Map<String, QName> uniqueGeneratedClassesNames,
324 final DataSchemaNode when, final Collection<RuntimeBeanEntry> runtimeBeans) {
325 for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
326 final String javaNameOfRuntimeMXBean = runtimeBean.getJavaNameOfRuntimeMXBean();
327 if (uniqueGeneratedClassesNames.containsKey(javaNameOfRuntimeMXBean)) {
328 QName firstDefinedQName = uniqueGeneratedClassesNames.get(javaNameOfRuntimeMXBean);
329 throw new NameConflictException(javaNameOfRuntimeMXBean, firstDefinedQName, when.getQName());
331 uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean, when.getQName());
335 private static void checkUniqueRuntimeBeanAttributesName(final ModuleMXBeanEntry mxBeanEntry,
336 final Map<String, QName> uniqueGeneratedClassesNames) {
337 for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) {
338 for (String runtimeAttName : runtimeBeanEntry.getYangPropertiesToTypesMap().keySet()) {
339 if (mxBeanEntry.getAttributes().keySet().contains(runtimeAttName)) {
340 QName qName1 = uniqueGeneratedClassesNames.get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean());
341 QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry.getGloballyUniqueName());
342 throw new NameConflictException(runtimeAttName, qName1, qName2);
348 private static void checkUniqueAttributesWithGeneratedClass(final Map<String, QName> uniqueGeneratedClassNames,
349 final QName parentQName, final Map<String, AttributeIfc> yangToAttributes) {
350 for (Map.Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
351 if (attr.getValue() instanceof TOAttribute) {
352 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, (TOAttribute) attr.getValue());
353 } else if (attr.getValue() instanceof ListAttribute
354 && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
355 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
356 (TOAttribute) ((ListAttribute) attr.getValue()).getInnerAttribute());
361 private static void checkUniqueTOAttr(final Map<String, QName> uniqueGeneratedClassNames, final QName parentQName, final TOAttribute attr) {
362 final String upperCaseCamelCase = attr.getUpperCaseCammelCase();
363 if (uniqueGeneratedClassNames.containsKey(upperCaseCamelCase)) {
364 QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCamelCase);
365 throw new NameConflictException(upperCaseCamelCase, firstDefinedQName, parentQName);
367 uniqueGeneratedClassNames.put(upperCaseCamelCase, parentQName);
371 private Collection<RuntimeBeanEntry> fillRuntimeBeans(final DataNodeContainer dataNodeContainer, final Module currentModule,
372 final TypeProviderWrapper typeProviderWrapper, final String packageName, final String moduleLocalNameFromXPath,
373 final String javaNamePrefix) {
375 return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, dataNodeContainer, moduleLocalNameFromXPath,
376 typeProviderWrapper, javaNamePrefix, currentModule, schemaContext).values();
381 * Since each case statement within a module must provide unique child nodes, it is allowed to wrap
382 * the actual configuration with a container node with name equal to case name.
384 * @param choiceCaseNode state or configuration case statement
385 * @return either choiceCaseNode or its only child container
387 private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> HAS_CHILDREN_AND_QNAME getDataNodeContainer(final ChoiceCaseNode choiceCaseNode) {
388 Collection<DataSchemaNode> childNodes = choiceCaseNode.getChildNodes();
389 if (childNodes.size() == 1) {
390 DataSchemaNode onlyChild = childNodes.iterator().next();
391 if (onlyChild instanceof ContainerSchemaNode) {
392 ContainerSchemaNode onlyContainer = (ContainerSchemaNode) onlyChild;
393 if (Objects.equals(onlyContainer.getQName().getLocalName(), choiceCaseNode.getQName().getLocalName())) {
394 // the actual configuration is inside dummy container
395 return (HAS_CHILDREN_AND_QNAME) onlyContainer;
399 return (HAS_CHILDREN_AND_QNAME) choiceCaseNode;
402 private Map<String, AttributeIfc> fillConfiguration(final DataNodeContainer dataNodeContainer, final Module currentModule,
403 final TypeProviderWrapper typeProviderWrapper, final Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
404 final SchemaContext schemaContext, final String packageName) {
405 Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
406 for (DataSchemaNode attrNode : dataNodeContainer.getChildNodes()) {
407 AttributeIfc attributeValue = getAttributeValue(attrNode, currentModule, qNamesToSIEs, typeProviderWrapper,
408 schemaContext, packageName);
409 yangToAttributes.put(attributeValue.getAttributeYangName(), attributeValue);
411 return yangToAttributes;
414 private Map<String, QName> findProvidedServices(final IdentitySchemaNode moduleIdentity, final Module currentModule,
415 final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final SchemaContext schemaContext) {
416 Map<String, QName> result = new HashMap<>();
417 for (UnknownSchemaNode unknownNode : moduleIdentity.getUnknownSchemaNodes()) {
418 if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
419 String prefixAndIdentityLocalName = unknownNode.getNodeParameter();
420 ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName, currentModule, qNamesToSIEs,
422 result.put(sie.getFullyQualifiedName(), sie.getQName());
428 private AttributeIfc getAttributeValue(final DataSchemaNode attrNode, final Module currentModule,
429 final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final TypeProviderWrapper typeProviderWrapper,
430 final SchemaContext schemaContext, final String packageName) {
432 if (attrNode instanceof LeafSchemaNode) {
434 LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
435 return new JavaAttribute(leaf, typeProviderWrapper);
436 } else if (attrNode instanceof ContainerSchemaNode) {
438 ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
439 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
440 containerSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext);
441 if (dependencyAttributeOptional.isPresent()) {
442 return dependencyAttributeOptional.get();
444 return TOAttribute.create(containerSchemaNode, typeProviderWrapper, packageName);
447 } else if (attrNode instanceof LeafListSchemaNode) {
448 return ListAttribute.create((LeafListSchemaNode) attrNode, typeProviderWrapper);
449 } else if (attrNode instanceof ListSchemaNode) {
450 ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode;
451 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
452 listSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext);
453 if (dependencyAttributeOptional.isPresent()) {
454 return dependencyAttributeOptional.get();
456 return ListAttribute.create(listSchemaNode, typeProviderWrapper, packageName);
459 throw new UnsupportedOperationException("Unknown configuration node " + attrNode.toString());
463 private Optional<? extends AbstractDependencyAttribute> extractDependency(final DataNodeContainer dataNodeContainer,
464 final DataSchemaNode attrNode, final Module currentModule, final Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
465 final SchemaContext schemaContext) {
466 if (isDependencyContainer(dataNodeContainer)) {
468 UsesNode usesNode = dataNodeContainer.getUses().iterator().next();
469 checkState(usesNode.getRefines().size() == 1, "Unexpected 'refine' child node size of " + dataNodeContainer);
470 LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines().values().iterator().next();
471 checkState(refine.getUnknownSchemaNodes().size() == 1, "Unexpected unknown schema node size of " + refine);
472 UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes().iterator().next();
473 checkState(ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity.getNodeType()),
474 "Unexpected language extension " + requiredIdentity);
475 String prefixAndIdentityLocalName = requiredIdentity.getNodeParameter();
476 // import should point to a module
477 ServiceInterfaceEntry serviceInterfaceEntry = findSIE(prefixAndIdentityLocalName, currentModule,
478 qNamesToSIEs, schemaContext);
479 boolean mandatory = refine.getConstraints().isMandatory();
480 AbstractDependencyAttribute reference;
481 if (dataNodeContainer instanceof ContainerSchemaNode) {
482 reference = new DependencyAttribute(attrNode, serviceInterfaceEntry, mandatory,
483 attrNode.getDescription());
485 reference = new ListDependenciesAttribute(attrNode, serviceInterfaceEntry, mandatory,
486 attrNode.getDescription());
488 return Optional.of(reference);
490 return Optional.absent();
493 private boolean isDependencyContainer(final DataNodeContainer dataNodeContainer) {
494 if(dataNodeContainer.getUses().size() != 1) {
497 UsesNode onlyUses = dataNodeContainer.getUses().iterator().next();
498 if(onlyUses.getGroupingPath().getLastComponent().equals(ServiceRef.QNAME) == false) {
502 return getChildNodeSizeWithoutUses(dataNodeContainer) == 0;
505 private int getChildNodeSizeWithoutUses(final DataNodeContainer csn) {
507 for (DataSchemaNode dsn : csn.getChildNodes()) {
508 if (dsn.isAddedByUses() == false) {
515 private ServiceInterfaceEntry findSIE(final String prefixAndIdentityLocalName, final Module currentModule,
516 final Map<QName, ServiceInterfaceEntry> qNamesToSIEs, final SchemaContext schemaContext) {
518 Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
522 // if there is a prefix, look for ModuleImport with this prefix. Get
523 // Module from SchemaContext
524 String prefix = m.group(1);
525 ModuleImport moduleImport = findModuleImport(currentModule, prefix);
526 foundModule = schemaContext.findModuleByName(moduleImport.getModuleName(), moduleImport.getRevision());
527 checkNotNull(foundModule, format("Module not found in SchemaContext by %s", moduleImport));
528 localSIName = m.group(2);
530 foundModule = currentModule; // no prefix => SIE is in currentModule
531 localSIName = prefixAndIdentityLocalName;
533 QName siQName = QName.create(foundModule.getNamespace(), foundModule.getRevision(), localSIName);
534 ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
535 checkState(sie != null, "Cannot find referenced Service Interface by " + prefixAndIdentityLocalName);
539 private ModuleImport findModuleImport(final Module module, final String prefix) {
540 for (ModuleImport moduleImport : module.getImports()) {
541 if (moduleImport.getPrefix().equals(prefix)) {
545 throw new IllegalStateException(format("Import not found with prefix %s in %s", prefix, module));
549 static Matcher getWhenConditionMatcher(final String prefix, final RevisionAwareXPath whenConstraint) {
550 String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(MAGIC_STRING, prefix);
551 Pattern pattern = Pattern.compile(xpathRegex);
552 return pattern.matcher(whenConstraint.toString());
555 String getConfigModulePrefixFromImport(final Module currentModule) {
556 for (ModuleImport currentImport : currentModule.getImports()) {
557 if (currentImport.getModuleName().equals(ConfigConstants.CONFIG_MODULE)) {
558 return currentImport.getPrefix();
561 throw new IllegalArgumentException("Cannot find import " + ConfigConstants.CONFIG_MODULE + " in "