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.Arrays;
22 import java.util.Collection;
23 import java.util.Collections;
24 import java.util.HashMap;
26 import java.util.Objects;
28 import java.util.regex.Matcher;
29 import java.util.regex.Pattern;
30 import javax.annotation.Nullable;
31 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute;
32 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
33 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
34 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
35 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
36 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute;
37 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
38 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
41 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
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 Module currentModule;
63 private Map<QName, ServiceInterfaceEntry> qNamesToSIEs;
64 private SchemaContext schemaContext;
65 private TypeProviderWrapper typeProviderWrapper;
66 private String packageName;
68 public ModuleMXBeanEntryBuilder setModule(Module module) {
69 this.currentModule = module;
73 public ModuleMXBeanEntryBuilder setqNamesToSIEs(Map<QName, ServiceInterfaceEntry> qNamesToSIEs) {
74 this.qNamesToSIEs = qNamesToSIEs;
78 public ModuleMXBeanEntryBuilder setSchemaContext(SchemaContext schemaContext) {
79 this.schemaContext = schemaContext;
83 public ModuleMXBeanEntryBuilder setTypeProviderWrapper(TypeProviderWrapper typeProviderWrapper) {
84 this.typeProviderWrapper = typeProviderWrapper;
88 public ModuleMXBeanEntryBuilder setPackageName(String packageName) {
89 this.packageName = packageName;
93 private static final Logger logger = LoggerFactory
94 .getLogger(ModuleMXBeanEntryBuilder.class);
96 // TODO: the XPath should be parsed by code generator IMO
97 private static final String MAGIC_STRING = "MAGIC_STRING";
98 private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
99 private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath(
100 Arrays.asList(createConfigQName("modules"),
101 createConfigQName("module"),
102 createConfigQName("configuration")), true);
103 private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath(
104 Arrays.asList(createConfigQName("modules"),
105 createConfigQName("module"), createConfigQName("state")),
107 private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern
108 .compile("^(.+):(.+)$");
111 public Map<String, ModuleMXBeanEntry> build() {
112 logger.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 (AugmentationSchema augmentation : currentModule.getAugmentations()) {
135 Set<DataSchemaNode> childNodes = augmentation.getChildNodes();
136 if (areAllChildrenChoiceCaseNodes(childNodes)) {
137 for (ChoiceCaseNode childCase : castChildNodesToChoiceCases(childNodes)) {
138 // TODO refactor, extract to standalone builder class
139 processChoiceCaseNode(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 logger.debug("Number of ModuleMXBeans to be generated: {}", result.size());
155 private static void cleanUpNulls(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(Map<String, IdentitySchemaNode> unaugmentedModuleIdentities) {
169 if (unaugmentedModuleIdentities.size() > 0) {
170 logger.warn("Augmentation not found for all currentModule identities: {}",
171 unaugmentedModuleIdentities.keySet());
175 private static void checkAttributeNamesUniqueness(Map<String, QName> uniqueGeneratedClassesNames, Map<String, ModuleMXBeanEntry> result) {
176 for (Map.Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
177 checkUniqueRuntimeBeanAttributesName(entry.getValue(),
178 uniqueGeneratedClassesNames);
182 private Map<String, IdentitySchemaNode> getIdentityMap() {
183 Map<String, IdentitySchemaNode> moduleIdentities = Maps.newHashMap();
185 for (IdentitySchemaNode id : currentModule.getIdentities()) {
186 if (id.getBaseIdentity() != null
187 && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id.getBaseIdentity().getQName())) {
188 String identityLocalName = id.getQName().getLocalName();
189 if (moduleIdentities.containsKey(identityLocalName)) {
190 throw new IllegalStateException("Module name already defined in this currentModule: "
191 + identityLocalName);
193 moduleIdentities.put(identityLocalName, id);
194 logger.debug("Found identity {}", identityLocalName);
196 // validation check on unknown schema nodes
197 boolean providedServiceWasSet = false;
198 for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
200 boolean unknownNodeIsProvidedServiceExtension = ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType());
201 // true => no op: 0 or more provided identities are allowed
203 if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
206 providedServiceWasSet == false,
207 format("More than one language extension %s is not allowed here: %s",
208 ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME, id));
209 providedServiceWasSet = true;
210 } else if (unknownNodeIsProvidedServiceExtension == false) {
211 throw new IllegalStateException("Unexpected language extension " + unknownNode.getNodeType());
217 return moduleIdentities;
220 private Collection<ChoiceCaseNode> castChildNodesToChoiceCases(Set<DataSchemaNode> childNodes) {
221 return Collections2.transform(childNodes, new Function<DataSchemaNode, ChoiceCaseNode>() {
224 public ChoiceCaseNode apply(@Nullable DataSchemaNode input) {
225 return (ChoiceCaseNode) input;
230 private boolean areAllChildrenChoiceCaseNodes(Set<DataSchemaNode> childNodes) {
231 for (DataSchemaNode childNode : childNodes) {
232 if (childNode instanceof ChoiceCaseNode == false)
238 private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> void processChoiceCaseNode(Map<String, ModuleMXBeanEntry> result,
239 Map<String, QName> uniqueGeneratedClassesNames, String configModulePrefix,
240 Map<String, IdentitySchemaNode> moduleIdentities,
241 Map<String, IdentitySchemaNode> unaugmentedModuleIdentities, AugmentationSchema augmentation,
242 DataSchemaNode when) {
244 ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when;
245 if (choiceCaseNode.getConstraints() == null || choiceCaseNode.getConstraints().getWhenCondition() == null) {
248 RevisionAwareXPath xPath = choiceCaseNode.getConstraints().getWhenCondition();
249 Matcher matcher = getWhenConditionMatcher(configModulePrefix, xPath);
250 if (matcher.matches() == false) {
253 String moduleLocalNameFromXPath = matcher.group(1);
254 IdentitySchemaNode moduleIdentity = moduleIdentities.get(moduleLocalNameFromXPath);
255 unaugmentedModuleIdentities.remove(moduleLocalNameFromXPath);
256 checkState(moduleIdentity != null, "Cannot find identity " + moduleLocalNameFromXPath
257 + " matching augmentation " + augmentation);
258 Map<String, QName> providedServices = findProvidedServices(moduleIdentity, currentModule, qNamesToSIEs,
261 if (moduleIdentity == null) {
262 throw new IllegalStateException("Cannot find identity specified by augmentation xpath constraint: "
263 + moduleLocalNameFromXPath + " of " + augmentation);
265 String javaNamePrefix = TypeProviderWrapper.findJavaNamePrefix(moduleIdentity);
267 Map<String, AttributeIfc> yangToAttributes = null;
269 Collection<RuntimeBeanEntry> runtimeBeans = null;
271 HAS_CHILDREN_AND_QNAME dataNodeContainer = getDataNodeContainer(choiceCaseNode);
273 if (expectedConfigurationAugmentationSchemaPath.equals(augmentation.getTargetPath())) {
274 logger.debug("Parsing configuration of {}", moduleLocalNameFromXPath);
275 yangToAttributes = fillConfiguration(dataNodeContainer, currentModule, typeProviderWrapper, qNamesToSIEs,
276 schemaContext, packageName);
277 checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(), yangToAttributes);
278 } else if (expectedStateAugmentationSchemaPath.equals(augmentation.getTargetPath())) {
279 logger.debug("Parsing state of {}", moduleLocalNameFromXPath);
281 runtimeBeans = fillRuntimeBeans(dataNodeContainer, currentModule, typeProviderWrapper, packageName,
282 moduleLocalNameFromXPath, javaNamePrefix);
283 } catch (NameConflictException e) {
284 throw new NameConflictException(e.getConflictingName(), when.getQName(), when.getQName());
286 checkUniqueRuntimeBeansGeneratedClasses(uniqueGeneratedClassesNames, when, runtimeBeans);
287 Set<RuntimeBeanEntry> runtimeBeanEntryValues = Sets.newHashSet(runtimeBeans);
288 for (RuntimeBeanEntry entry : runtimeBeanEntryValues) {
289 checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(),
290 entry.getYangPropertiesToTypesMap());
294 throw new IllegalArgumentException("Cannot parse augmentation " + augmentation);
296 boolean hasDummyContainer = choiceCaseNode.equals(dataNodeContainer) == false;
298 String nullableDummyContainerName = hasDummyContainer ? dataNodeContainer.getQName().getLocalName() : null;
299 if (result.containsKey(moduleLocalNameFromXPath)) {
300 // either fill runtimeBeans or yangToAttributes, merge
301 ModuleMXBeanEntry moduleMXBeanEntry = result.get(moduleLocalNameFromXPath);
302 if (yangToAttributes != null && moduleMXBeanEntry.getAttributes() == null) {
303 moduleMXBeanEntry.setYangToAttributes(yangToAttributes);
304 } else if (runtimeBeans != null && moduleMXBeanEntry.getRuntimeBeans() == null) {
305 moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
307 checkState(Objects.equals(nullableDummyContainerName, moduleMXBeanEntry.getNullableDummyContainerName()),
308 "Mismatch in module " + moduleMXBeanEntry.toString() + " - dummy container must be present/missing in" +
309 " both state and configuration");
311 ModuleMXBeanEntry.ModuleMXBeanEntryInitial initial = new ModuleMXBeanEntry.ModuleMXBeanEntryInitialBuilder()
312 .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix)
313 .setNamespace(currentModule.getNamespace().toString()).setqName(ModuleUtil.getQName(currentModule))
316 // construct ModuleMXBeanEntry
317 ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(initial, yangToAttributes, providedServices,
320 moduleMXBeanEntry.setYangModuleName(currentModule.getName());
321 moduleMXBeanEntry.setYangModuleLocalname(moduleLocalNameFromXPath);
322 moduleMXBeanEntry.setNullableDummyContainerName(nullableDummyContainerName);
323 result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
327 private void checkUniqueRuntimeBeansGeneratedClasses(Map<String, QName> uniqueGeneratedClassesNames,
328 DataSchemaNode when, Collection<RuntimeBeanEntry> runtimeBeans) {
329 for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
330 final String javaNameOfRuntimeMXBean = runtimeBean.getJavaNameOfRuntimeMXBean();
331 if (uniqueGeneratedClassesNames.containsKey(javaNameOfRuntimeMXBean)) {
332 QName firstDefinedQName = uniqueGeneratedClassesNames.get(javaNameOfRuntimeMXBean);
333 throw new NameConflictException(javaNameOfRuntimeMXBean, firstDefinedQName, when.getQName());
335 uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean, when.getQName());
339 private static void checkUniqueRuntimeBeanAttributesName(ModuleMXBeanEntry mxBeanEntry,
340 Map<String, QName> uniqueGeneratedClassesNames) {
341 for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) {
342 for (String runtimeAttName : runtimeBeanEntry.getYangPropertiesToTypesMap().keySet()) {
343 if (mxBeanEntry.getAttributes().keySet().contains(runtimeAttName)) {
344 QName qName1 = uniqueGeneratedClassesNames.get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean());
345 QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry.getGloballyUniqueName());
346 throw new NameConflictException(runtimeAttName, qName1, qName2);
352 private void checkUniqueAttributesWithGeneratedClass(Map<String, QName> uniqueGeneratedClassNames,
353 QName parentQName, Map<String, AttributeIfc> yangToAttributes) {
354 for (Map.Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
355 if (attr.getValue() instanceof TOAttribute) {
356 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, (TOAttribute) attr.getValue());
357 } else if (attr.getValue() instanceof ListAttribute
358 && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
359 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
360 (TOAttribute) ((ListAttribute) attr.getValue()).getInnerAttribute());
365 private void checkUniqueTOAttr(Map<String, QName> uniqueGeneratedClassNames, QName parentQName, TOAttribute attr) {
366 final String upperCaseCamelCase = attr.getUpperCaseCammelCase();
367 if (uniqueGeneratedClassNames.containsKey(upperCaseCamelCase)) {
368 QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCamelCase);
369 throw new NameConflictException(upperCaseCamelCase, firstDefinedQName, parentQName);
371 uniqueGeneratedClassNames.put(upperCaseCamelCase, parentQName);
375 private Collection<RuntimeBeanEntry> fillRuntimeBeans(DataNodeContainer dataNodeContainer, Module currentModule,
376 TypeProviderWrapper typeProviderWrapper, String packageName, String moduleLocalNameFromXPath,
377 String javaNamePrefix) {
379 return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, dataNodeContainer, moduleLocalNameFromXPath,
380 typeProviderWrapper, javaNamePrefix, currentModule).values();
385 * Since each case statement within a module must provide unique child nodes, it is allowed to wrap
386 * the actual configuration with a container node with name equal to case name.
388 * @param choiceCaseNode state or configuration case statement
389 * @return either choiceCaseNode or its only child container
391 private <HAS_CHILDREN_AND_QNAME extends DataNodeContainer & SchemaNode> HAS_CHILDREN_AND_QNAME getDataNodeContainer(ChoiceCaseNode choiceCaseNode) {
392 Set<DataSchemaNode> childNodes = choiceCaseNode.getChildNodes();
393 if (childNodes.size() == 1) {
394 DataSchemaNode onlyChild = childNodes.iterator().next();
395 if (onlyChild instanceof ContainerSchemaNode) {
396 ContainerSchemaNode onlyContainer = (ContainerSchemaNode) onlyChild;
397 if (Objects.equals(onlyContainer.getQName().getLocalName(), choiceCaseNode.getQName().getLocalName())) {
398 // the actual configuration is inside dummy container
399 return (HAS_CHILDREN_AND_QNAME) onlyContainer;
403 return (HAS_CHILDREN_AND_QNAME) choiceCaseNode;
406 private Map<String, AttributeIfc> fillConfiguration(DataNodeContainer dataNodeContainer, Module currentModule,
407 TypeProviderWrapper typeProviderWrapper, Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
408 SchemaContext schemaContext, String packageName) {
409 Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
410 Set<DataSchemaNode> childNodes = dataNodeContainer.getChildNodes();
411 for (DataSchemaNode attrNode : childNodes) {
412 AttributeIfc attributeValue = getAttributeValue(attrNode, currentModule, qNamesToSIEs, typeProviderWrapper,
413 schemaContext, packageName);
414 yangToAttributes.put(attributeValue.getAttributeYangName(), attributeValue);
416 return yangToAttributes;
419 private Map<String, QName> findProvidedServices(IdentitySchemaNode moduleIdentity, Module currentModule,
420 Map<QName, ServiceInterfaceEntry> qNamesToSIEs, SchemaContext schemaContext) {
421 Map<String, QName> result = new HashMap<>();
422 for (UnknownSchemaNode unknownNode : moduleIdentity.getUnknownSchemaNodes()) {
423 if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) {
424 String prefixAndIdentityLocalName = unknownNode.getNodeParameter();
425 ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName, currentModule, qNamesToSIEs,
427 result.put(sie.getFullyQualifiedName(), sie.getQName());
433 private AttributeIfc getAttributeValue(DataSchemaNode attrNode, Module currentModule,
434 Map<QName, ServiceInterfaceEntry> qNamesToSIEs, TypeProviderWrapper typeProviderWrapper,
435 SchemaContext schemaContext, String packageName) {
437 if (attrNode instanceof LeafSchemaNode) {
439 LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
440 return new JavaAttribute(leaf, typeProviderWrapper);
441 } else if (attrNode instanceof ContainerSchemaNode) {
443 ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
444 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
445 containerSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext);
446 if (dependencyAttributeOptional.isPresent()) {
447 return dependencyAttributeOptional.get();
449 return TOAttribute.create(containerSchemaNode, typeProviderWrapper, packageName);
452 } else if (attrNode instanceof LeafListSchemaNode) {
453 return ListAttribute.create((LeafListSchemaNode) attrNode, typeProviderWrapper);
454 } else if (attrNode instanceof ListSchemaNode) {
455 ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode;
456 Optional<? extends AbstractDependencyAttribute> dependencyAttributeOptional = extractDependency(
457 listSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext);
458 if (dependencyAttributeOptional.isPresent()) {
459 return dependencyAttributeOptional.get();
461 return ListAttribute.create(listSchemaNode, typeProviderWrapper, packageName);
464 throw new UnsupportedOperationException("Unknown configuration node " + attrNode.toString());
468 private Optional<? extends AbstractDependencyAttribute> extractDependency(DataNodeContainer dataNodeContainer,
469 DataSchemaNode attrNode, Module currentModule, Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
470 SchemaContext schemaContext) {
471 if (dataNodeContainer.getUses().size() == 1 && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) {
473 UsesNode usesNode = dataNodeContainer.getUses().iterator().next();
474 checkState(usesNode.getRefines().size() == 1, "Unexpected 'refine' child node size of " + dataNodeContainer);
475 LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines().values().iterator().next();
476 checkState(refine.getUnknownSchemaNodes().size() == 1, "Unexpected unknown schema node size of " + refine);
477 UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes().iterator().next();
478 checkState(ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity.getNodeType()),
479 "Unexpected language extension " + requiredIdentity);
480 String prefixAndIdentityLocalName = requiredIdentity.getNodeParameter();
481 // import should point to a module
482 ServiceInterfaceEntry serviceInterfaceEntry = findSIE(prefixAndIdentityLocalName, currentModule,
483 qNamesToSIEs, schemaContext);
484 boolean mandatory = refine.getConstraints().isMandatory();
485 AbstractDependencyAttribute reference;
486 if (dataNodeContainer instanceof ContainerSchemaNode) {
487 reference = new DependencyAttribute(attrNode, serviceInterfaceEntry, mandatory,
488 attrNode.getDescription());
490 reference = new ListDependenciesAttribute(attrNode, serviceInterfaceEntry, mandatory,
491 attrNode.getDescription());
493 return Optional.of(reference);
495 return Optional.absent();
498 private int getChildNodeSizeWithoutUses(DataNodeContainer csn) {
500 for (DataSchemaNode dsn : csn.getChildNodes()) {
501 if (dsn.isAddedByUses() == false) {
508 private ServiceInterfaceEntry findSIE(String prefixAndIdentityLocalName, Module currentModule,
509 Map<QName, ServiceInterfaceEntry> qNamesToSIEs, SchemaContext schemaContext) {
511 Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
515 // if there is a prefix, look for ModuleImport with this prefix. Get
516 // Module from SchemaContext
517 String prefix = m.group(1);
518 ModuleImport moduleImport = findModuleImport(currentModule, prefix);
519 foundModule = schemaContext.findModuleByName(moduleImport.getModuleName(), moduleImport.getRevision());
520 checkNotNull(foundModule, format("Module not found in SchemaContext by %s", moduleImport));
521 localSIName = m.group(2);
523 foundModule = currentModule; // no prefix => SIE is in currentModule
524 localSIName = prefixAndIdentityLocalName;
526 QName siQName = new QName(foundModule.getNamespace(), foundModule.getRevision(), localSIName);
527 ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
528 checkState(sie != null, "Cannot find referenced Service Interface by " + prefixAndIdentityLocalName);
532 private ModuleImport findModuleImport(Module module, String prefix) {
533 for (ModuleImport moduleImport : module.getImports()) {
534 if (moduleImport.getPrefix().equals(prefix)) {
538 throw new IllegalStateException(format("Import not found with prefix %s in %s", prefix, module));
542 static Matcher getWhenConditionMatcher(String prefix, RevisionAwareXPath whenConstraint) {
543 String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(MAGIC_STRING, prefix);
544 Pattern pattern = Pattern.compile(xpathRegex);
545 return pattern.matcher(whenConstraint.toString());
548 String getConfigModulePrefixFromImport(Module currentModule) {
549 for (ModuleImport currentImport : currentModule.getImports()) {
550 if (currentImport.getModuleName().equals(ConfigConstants.CONFIG_MODULE)) {
551 return currentImport.getPrefix();
554 throw new IllegalArgumentException("Cannot find import " + ConfigConstants.CONFIG_MODULE + " in "