Initial code drop of yang model driven configuration system
[controller.git] / opendaylight / config / yang-jmx-generator / src / main / java / org / opendaylight / controller / config / yangjmxgenerator / ModuleMXBeanEntry.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.controller.config.yangjmxgenerator;
9
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
15 import java.util.Arrays;
16 import java.util.Collection;
17 import java.util.Collections;
18 import java.util.HashMap;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Set;
22 import java.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25 import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc;
26 import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute;
27 import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute;
28 import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute;
29 import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute;
30 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper;
31 import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException;
32 import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil;
33 import org.opendaylight.yangtools.yang.common.QName;
34 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
35 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
36 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
39 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
42 import org.opendaylight.yangtools.yang.model.api.Module;
43 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
44 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
45 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
46 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
48 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.UsesNode;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52
53 import com.google.common.annotations.VisibleForTesting;
54 import com.google.common.collect.Sets;
55
56 /**
57  * Represents part of yang model that describes a module.
58  *
59  * Example:
60  * <p>
61  * <blockquote>
62  *
63  * <pre>
64  *  identity threadpool-dynamic {
65  *      base config:module-type;
66  *      description "threadpool-dynamic description";
67  *      config:provided-service "th2:threadpool";
68  *      config:provided-service "th2:scheduled-threadpool";
69  *      config:java-name-prefix DynamicThreadPool
70  *  }
71  *  augment "/config:modules/config:module/config:module-type" {
72  *     case threadpool-dynamic {
73  *         when "/config:modules/config:module/config:module-type = 'threadpool-dynamic'";
74  *
75  *         container "configuration" {
76  *             // regular java attribute
77  *             leaf core-size {
78  *                 type uint32;
79  *          }
80  *
81  *             ...
82  *          // dependency
83  *             container threadfactory {
84  *                 uses config:service-ref {
85  *                     refine type {
86  *                         config:required-identity th:threadfactory;
87  *                  }
88  *              }
89  *          }
90  *      }
91  * }
92  * </pre>
93  *
94  * </blockquote>
95  * </p>
96  */
97 public class ModuleMXBeanEntry extends AbstractEntry {
98     private static final Logger logger = LoggerFactory
99             .getLogger(ModuleMXBeanEntry.class);
100
101     // TODO: the XPath should be parsed by code generator IMO
102     private static final String MAGIC_STRING = "MAGIC_STRING";
103     private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$";
104     private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath(
105             Arrays.asList(createConfigQName("modules"),
106                     createConfigQName("module"),
107                     createConfigQName("configuration")), true);
108     private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath(
109             Arrays.asList(createConfigQName("modules"),
110                     createConfigQName("module"), createConfigQName("state")),
111             true);
112
113     private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern
114             .compile("^(.+):(.+)$");
115
116     private static final String MODULE_SUFFIX = "Module";
117     private static final String FACTORY_SUFFIX = MODULE_SUFFIX + "Factory";
118     private static final String CLASS_NAME_SUFFIX = MODULE_SUFFIX + "MXBean";
119     private static final String ABSTRACT_PREFIX = "Abstract";
120
121     /*
122      * threadpool-dynamic from the example above, taken from when condition, not
123      * the case name
124      */
125     private final String globallyUniqueName;
126
127     private Map<String, AttributeIfc> yangToAttributes;
128
129     private final String nullableDescription, packageName, javaNamePrefix,
130             namespace;
131
132     private final Map<String, String> providedServices;
133
134     private Collection<RuntimeBeanEntry> runtimeBeans;
135
136     public ModuleMXBeanEntry(IdentitySchemaNode id,
137             Map<String, AttributeIfc> yangToAttributes, String packageName,
138             Map<String, String> providedServices2, String javaNamePrefix,
139             String namespace, Collection<RuntimeBeanEntry> runtimeBeans) {
140         this.globallyUniqueName = id.getQName().getLocalName();
141         this.yangToAttributes = yangToAttributes;
142         this.nullableDescription = id.getDescription();
143         this.packageName = packageName;
144         this.javaNamePrefix = checkNotNull(javaNamePrefix);
145         this.namespace = checkNotNull(namespace);
146         this.providedServices = Collections.unmodifiableMap(providedServices2);
147         this.runtimeBeans = runtimeBeans;
148     }
149
150     public String getMXBeanInterfaceName() {
151         return javaNamePrefix + CLASS_NAME_SUFFIX;
152     }
153
154     public String getStubFactoryName() {
155         return javaNamePrefix + FACTORY_SUFFIX;
156     }
157
158     public String getAbstractFactoryName() {
159         return ABSTRACT_PREFIX + getStubFactoryName();
160     }
161
162     public String getStubModuleName() {
163         return javaNamePrefix + MODULE_SUFFIX;
164     }
165
166     public String getAbstractModuleName() {
167         return ABSTRACT_PREFIX + getStubModuleName();
168     }
169
170     public String getFullyQualifiedName(String typeName) {
171         return FullyQualifiedNameHelper.getFullyQualifiedName(packageName,
172                 typeName);
173     }
174
175     public String getGloballyUniqueName() {
176         return globallyUniqueName;
177     }
178
179     public String getPackageName() {
180         return packageName;
181     }
182
183     public Map<String, String> getProvidedServices() {
184         return providedServices;
185     }
186
187     public void setRuntimeBeans(Collection<RuntimeBeanEntry> newRuntimeBeans) {
188         runtimeBeans = newRuntimeBeans;
189     }
190
191     public Collection<RuntimeBeanEntry> getRuntimeBeans() {
192         return runtimeBeans;
193     }
194
195     public String getJavaNamePrefix() {
196         return javaNamePrefix;
197     }
198
199     public String getNamespace() {
200         return namespace;
201     }
202
203     @VisibleForTesting
204     static Matcher getWhenConditionMatcher(String prefix,
205             RevisionAwareXPath whenConstraint) {
206         String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(
207                 MAGIC_STRING, prefix);
208         Pattern pattern = Pattern.compile(xpathRegex);
209         return pattern.matcher(whenConstraint.toString());
210     }
211
212     static String getConfigModulePrefixFromImport(Module currentModule) {
213         for (ModuleImport currentImport : currentModule.getImports()) {
214             if (currentImport.getModuleName().equals(
215                     ConfigConstants.CONFIG_MODULE)) {
216                 return currentImport.getPrefix();
217             }
218         }
219         throw new IllegalArgumentException("Cannot find import "
220                 + ConfigConstants.CONFIG_MODULE + " in " + currentModule);
221     }
222
223     /**
224      * Transform module to zero or more ModuleMXBeanEntry instances. Each
225      * instance must have a globally unique local name.
226      *
227      * @return Map of identity local names as keys, and ModuleMXBeanEntry
228      *         instances as values
229      */
230     public static Map<String/* identity local name */, ModuleMXBeanEntry> create(
231             Module currentModule,
232             Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
233             SchemaContext schemaContext,
234             TypeProviderWrapper typeProviderWrapper, String packageName) {
235         Map<String, QName> uniqueGeneratedClassesNames = new HashMap<>();
236         logger.debug("Generating ModuleMXBeans of {} to package {}",
237                 currentModule.getNamespace(), packageName);
238         String configModulePrefix;
239         try {
240             configModulePrefix = getConfigModulePrefixFromImport(currentModule);
241         } catch (IllegalArgumentException e) {
242             // this module does not import config module
243             return Collections.emptyMap();
244         }
245
246         // get identities of base config:module-type
247         Map<String, IdentitySchemaNode> moduleIdentities = new HashMap<>();
248
249         for (IdentitySchemaNode id : currentModule.getIdentities()) {
250             if (id.getBaseIdentity() != null
251                     && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id
252                             .getBaseIdentity().getQName())) {
253                 String identityLocalName = id.getQName().getLocalName();
254                 if (moduleIdentities.containsKey(identityLocalName)) {
255                     throw new IllegalStateException(
256                             "Module name already defined in this module: "
257                                     + identityLocalName);
258                 } else {
259                     moduleIdentities.put(identityLocalName, id);
260                     logger.debug("Found identity {}", identityLocalName);
261                 }
262                 // validation check on unknown schema nodes
263                 boolean providedServiceWasSet = false;
264                 for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) {
265                     // TODO: test this
266                     if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
267                             .equals(unknownNode.getNodeType())) {
268                         // no op: 0 or more provided identities are allowed
269                     } else if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
270                             .equals(unknownNode.getNodeType())) {
271                         // 0..1 allowed
272                         checkState(
273                                 providedServiceWasSet == false,
274                                 format("More than one language extension %s is not allowed here: %s",
275                                         ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME,
276                                         id));
277                         providedServiceWasSet = true;
278                     } else {
279                         throw new IllegalStateException(
280                                 "Unexpected language extension "
281                                         + unknownNode.getNodeType());
282                     }
283                 }
284             }
285         }
286         Map<String, ModuleMXBeanEntry> result = new HashMap<>();
287         // each module name should have an augmentation defined
288         Map<String, IdentitySchemaNode> unaugmentedModuleIdentities = new HashMap<>(
289                 moduleIdentities);
290         for (AugmentationSchema augmentation : currentModule.getAugmentations()) {
291             Set<DataSchemaNode> childNodes = augmentation.getChildNodes();
292             if (childNodes.size() == 1) {
293                 DataSchemaNode when = childNodes.iterator().next();
294                 if (when instanceof ChoiceCaseNode) {
295                     ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when;
296                     if (choiceCaseNode.getConstraints() == null
297                             || choiceCaseNode.getConstraints()
298                                     .getWhenCondition() == null) {
299                         continue;
300                     }
301                     RevisionAwareXPath xPath = choiceCaseNode.getConstraints()
302                             .getWhenCondition();
303                     Matcher matcher = getWhenConditionMatcher(
304                             configModulePrefix, xPath);
305                     if (matcher.matches() == false) {
306                         continue;
307                     }
308                     String moduleLocalNameFromXPath = matcher.group(1);
309                     IdentitySchemaNode moduleIdentity = moduleIdentities
310                             .get(moduleLocalNameFromXPath);
311                     unaugmentedModuleIdentities
312                             .remove(moduleLocalNameFromXPath);
313                     checkState(moduleIdentity != null, "Cannot find identity "
314                             + moduleLocalNameFromXPath
315                             + " matching augmentation " + augmentation);
316                     Map<String, String> providedServices = findProvidedServices(
317                             moduleIdentity, currentModule, qNamesToSIEs,
318                             schemaContext);
319
320                     if (moduleIdentity == null) {
321                         throw new IllegalStateException(
322                                 "Cannot find identity specified by augmentation xpath constraint: "
323                                         + moduleLocalNameFromXPath + " of "
324                                         + augmentation);
325                     }
326                     String javaNamePrefix = findJavaNamePrefix(moduleIdentity);
327
328                     Map<String, AttributeIfc> yangToAttributes = null;
329                     // runtime-data
330                     Collection<RuntimeBeanEntry> runtimeBeans = null;
331
332                     if (expectedConfigurationAugmentationSchemaPath
333                             .equals(augmentation.getTargetPath())) {
334                         logger.debug("Parsing configuration of {}",
335                                 moduleLocalNameFromXPath);
336                         yangToAttributes = fillConfiguration(choiceCaseNode,
337                                 currentModule, typeProviderWrapper,
338                                 qNamesToSIEs, schemaContext);
339                         checkUniqueAttributesWithGeneratedClass(
340                                 uniqueGeneratedClassesNames, when.getQName(),
341                                 yangToAttributes);
342                     } else if (expectedStateAugmentationSchemaPath
343                             .equals(augmentation.getTargetPath())) {
344                         logger.debug("Parsing state of {}",
345                                 moduleLocalNameFromXPath);
346                         try {
347                             runtimeBeans = fillRuntimeBeans(choiceCaseNode,
348                                     currentModule, typeProviderWrapper,
349                                     packageName, moduleLocalNameFromXPath,
350                                     javaNamePrefix);
351                         } catch (NameConflictException e) {
352                             throw new NameConflictException(
353                                     e.getConflictingName(), when.getQName(),
354                                     when.getQName());
355                         }
356
357                         checkUniqueRuntimeBeansGeneratedClasses(
358                                 uniqueGeneratedClassesNames, when, runtimeBeans);
359                         Set<RuntimeBeanEntry> runtimeBeanEntryValues = Sets
360                                 .newHashSet(runtimeBeans);
361                         for (RuntimeBeanEntry entry : runtimeBeanEntryValues) {
362                             checkUniqueAttributesWithGeneratedClass(
363                                     uniqueGeneratedClassesNames,
364                                     when.getQName(),
365                                     entry.getYangPropertiesToTypesMap());
366                         }
367
368                     } else {
369                         throw new IllegalArgumentException(
370                                 "Cannot parse augmentation " + augmentation);
371                     }
372                     if (result.containsKey(moduleLocalNameFromXPath)) {
373                         // either fill runtimeBeans or yangToAttributes
374                         ModuleMXBeanEntry moduleMXBeanEntry = result
375                                 .get(moduleLocalNameFromXPath);
376                         if (yangToAttributes != null
377                                 && moduleMXBeanEntry.getAttributes() == null) {
378                             moduleMXBeanEntry
379                                     .setYangToAttributes(yangToAttributes);
380                         } else if (runtimeBeans != null
381                                 && moduleMXBeanEntry.getRuntimeBeans() == null) {
382                             moduleMXBeanEntry.setRuntimeBeans(runtimeBeans);
383                         }
384                     } else {
385                         // construct ModuleMXBeanEntry
386                         ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(
387                                 moduleIdentity, yangToAttributes, packageName,
388                                 providedServices, javaNamePrefix, currentModule
389                                         .getNamespace().toString(),
390                                 runtimeBeans);
391                         moduleMXBeanEntry.setYangModuleName(currentModule
392                                 .getName());
393                         moduleMXBeanEntry
394                                 .setYangModuleLocalname(moduleLocalNameFromXPath);
395                         result.put(moduleLocalNameFromXPath, moduleMXBeanEntry);
396                     }
397                 } // skip if child node is not ChoiceCaseNode
398             } // skip if childNodes != 1
399         }
400         // clean up nulls
401         for (Entry<String, ModuleMXBeanEntry> entry : result.entrySet()) {
402             ModuleMXBeanEntry module = entry.getValue();
403             if (module.getAttributes() == null) {
404                 module.setYangToAttributes(Collections
405                         .<String, AttributeIfc> emptyMap());
406             } else if (module.getRuntimeBeans() == null) {
407                 module.setRuntimeBeans(Collections
408                         .<RuntimeBeanEntry> emptyList());
409             }
410         }
411         if (unaugmentedModuleIdentities.size() > 0) {
412             logger.warn("Augmentation not found for all module identities: {}",
413                     unaugmentedModuleIdentities.keySet());
414         }
415
416         logger.debug("Number of ModuleMXBeans to be generated: {}",
417                 result.size());
418         return result;
419     }
420
421     private static void checkUniqueRuntimeBeansGeneratedClasses(
422             Map<String, QName> uniqueGeneratedClassesNames,
423             DataSchemaNode when, Collection<RuntimeBeanEntry> runtimeBeans) {
424         for (RuntimeBeanEntry runtimeBean : runtimeBeans) {
425             final String javaNameOfRuntimeMXBean = runtimeBean
426                     .getJavaNameOfRuntimeMXBean();
427             if (uniqueGeneratedClassesNames
428                     .containsKey(javaNameOfRuntimeMXBean)) {
429                 QName firstDefinedQName = uniqueGeneratedClassesNames
430                         .get(javaNameOfRuntimeMXBean);
431                 throw new NameConflictException(javaNameOfRuntimeMXBean,
432                         firstDefinedQName, when.getQName());
433             }
434             uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean,
435                     when.getQName());
436         }
437     }
438
439     private static void checkUniqueAttributesWithGeneratedClass(
440             Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
441             Map<String, AttributeIfc> yangToAttributes) {
442         for (Entry<String, AttributeIfc> attr : yangToAttributes.entrySet()) {
443             if (attr.getValue() instanceof TOAttribute) {
444                 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
445                         (TOAttribute) attr.getValue());
446             } else if (attr.getValue() instanceof ListAttribute
447                     && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) {
448                 checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName,
449                         (TOAttribute) ((ListAttribute) attr.getValue())
450                                 .getInnerAttribute());
451             }
452         }
453     }
454
455     private static void checkUniqueTOAttr(
456             Map<String, QName> uniqueGeneratedClassNames, QName parentQName,
457             TOAttribute attr) {
458         final String upperCaseCammelCase = attr.getUpperCaseCammelCase();
459         if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) {
460             QName firstDefinedQName = uniqueGeneratedClassNames
461                     .get(upperCaseCammelCase);
462             throw new NameConflictException(upperCaseCammelCase,
463                     firstDefinedQName, parentQName);
464         } else {
465             uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName);
466         }
467     }
468
469     private static Collection<RuntimeBeanEntry> fillRuntimeBeans(
470             ChoiceCaseNode choiceCaseNode, Module currentModule,
471             TypeProviderWrapper typeProviderWrapper, String packageName,
472             String moduleLocalNameFromXPath, String javaNamePrefix) {
473
474         return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName,
475                 choiceCaseNode, moduleLocalNameFromXPath, typeProviderWrapper,
476                 javaNamePrefix, currentModule).values();
477
478     }
479
480     private static Map<String, AttributeIfc> fillConfiguration(
481             ChoiceCaseNode choiceCaseNode, Module currentModule,
482             TypeProviderWrapper typeProviderWrapper,
483             Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
484             SchemaContext schemaContext) {
485         Map<String, AttributeIfc> yangToAttributes = new HashMap<>();
486         for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) {
487             AttributeIfc attributeValue = getAttributeValue(attrNode,
488                     currentModule, qNamesToSIEs, typeProviderWrapper,
489                     schemaContext);
490             yangToAttributes.put(attributeValue.getAttributeYangName(),
491                     attributeValue);
492         }
493         return yangToAttributes;
494     }
495
496     private static Map<String, String> findProvidedServices(
497             IdentitySchemaNode moduleIdentity, Module currentModule,
498             Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
499             SchemaContext schemaContext) {
500         Map<String, String> result = new HashMap<>();
501         for (UnknownSchemaNode unknownNode : moduleIdentity
502                 .getUnknownSchemaNodes()) {
503             if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME
504                     .equals(unknownNode.getNodeType())) {
505                 String prefixAndIdentityLocalName = unknownNode
506                         .getNodeParameter();
507                 ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName,
508                         currentModule, qNamesToSIEs, schemaContext);
509                 result.put(sie.getFullyQualifiedName(), sie.getQName()
510                         .getLocalName());
511             }
512         }
513         return result;
514     }
515
516     /**
517      * For input node, find if it contains config:java-name-prefix extension. If
518      * not found, convert local name of node converted to cammel case.
519      */
520     public static String findJavaNamePrefix(SchemaNode schemaNode) {
521         return convertToJavaName(schemaNode, true);
522     }
523
524     public static String findJavaParameter(SchemaNode schemaNode) {
525         return convertToJavaName(schemaNode, false);
526     }
527
528     public static String convertToJavaName(SchemaNode schemaNode,
529             boolean capitalizeFirstLetter) {
530         for (UnknownSchemaNode unknownNode : schemaNode.getUnknownSchemaNodes()) {
531             if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME
532                     .equals(unknownNode.getNodeType())) {
533                 String value = unknownNode.getNodeParameter();
534                 return convertToJavaName(value, capitalizeFirstLetter);
535             }
536         }
537         return convertToJavaName(schemaNode.getQName().getLocalName(),
538                 capitalizeFirstLetter);
539     }
540
541     public static String convertToJavaName(String localName,
542             boolean capitalizeFirstLetter) {
543         if (capitalizeFirstLetter) {
544             return BindingGeneratorUtil.parseToClassName(localName);
545         } else {
546             return BindingGeneratorUtil.parseToValidParamName(localName);
547         }
548     }
549
550     private static int getChildNodeSizeWithoutUses(ContainerSchemaNode csn) {
551         int result = 0;
552         for (DataSchemaNode dsn : csn.getChildNodes()) {
553             if (dsn.isAddedByUses() == false)
554                 result++;
555         }
556         return result;
557     }
558
559     private static AttributeIfc getAttributeValue(DataSchemaNode attrNode,
560             Module currentModule,
561             Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
562             TypeProviderWrapper typeProviderWrapper, SchemaContext schemaContext) {
563
564         if (attrNode instanceof LeafSchemaNode) {
565             // simple type
566             LeafSchemaNode leaf = (LeafSchemaNode) attrNode;
567             return new JavaAttribute(leaf, typeProviderWrapper);
568         } else if (attrNode instanceof ContainerSchemaNode) {
569             // reference or TO
570             ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode;
571             if (containerSchemaNode.getUses().size() == 1
572                     && getChildNodeSizeWithoutUses(containerSchemaNode) == 0) {
573                 // reference
574                 UsesNode usesNode = containerSchemaNode.getUses().iterator()
575                         .next();
576                 checkState(usesNode.getRefines().size() == 1,
577                         "Unexpected 'refine' child node size of "
578                                 + containerSchemaNode);
579                 LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines()
580                         .values().iterator().next();
581                 checkState(refine.getUnknownSchemaNodes().size() == 1,
582                         "Unexpected unknown schema node size of " + refine);
583                 UnknownSchemaNode requiredIdentity = refine
584                         .getUnknownSchemaNodes().iterator().next();
585                 checkState(
586                         ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity
587                                 .getNodeType()),
588                         "Unexpected language extension " + requiredIdentity);
589                 String prefixAndIdentityLocalName = requiredIdentity
590                         .getNodeParameter();
591                 // import should point to a module
592                 ServiceInterfaceEntry serviceInterfaceEntry = findSIE(
593                         prefixAndIdentityLocalName, currentModule,
594                         qNamesToSIEs, schemaContext);
595                 boolean mandatory = refine.getConstraints().isMandatory();
596                 return new DependencyAttribute(attrNode, serviceInterfaceEntry,
597                         mandatory, attrNode.getDescription());
598             } else {
599                 return TOAttribute.create(containerSchemaNode,
600                         typeProviderWrapper);
601             }
602         } else if (attrNode instanceof LeafListSchemaNode) {
603             return ListAttribute.create((LeafListSchemaNode) attrNode,
604                     typeProviderWrapper);
605         } else if (attrNode instanceof ListSchemaNode) {
606             return ListAttribute.create((ListSchemaNode) attrNode,
607                     typeProviderWrapper);
608         } else {
609             throw new UnsupportedOperationException(
610                     "Unknown configuration node " + attrNode.toString());
611         }
612     }
613
614     private static ServiceInterfaceEntry findSIE(
615             String prefixAndIdentityLocalName, Module currentModule,
616             Map<QName, ServiceInterfaceEntry> qNamesToSIEs,
617             SchemaContext schemaContext) {
618
619         Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName);
620         Module foundModule;
621         String localSIName;
622         if (m.matches()) {
623             // if there is a prefix, look for ModuleImport with this prefix. Get
624             // Module from SchemaContext
625             String prefix = m.group(1);
626             ModuleImport moduleImport = findModuleImport(currentModule, prefix);
627             foundModule = schemaContext.findModuleByName(
628                     moduleImport.getModuleName(), moduleImport.getRevision());
629             checkState(
630                     foundModule != null,
631                     format("Module not found in SchemaContext by %s",
632                             moduleImport));
633             localSIName = m.group(2);
634         } else {
635             foundModule = currentModule; // no prefix => SIE is in currentModule
636             localSIName = prefixAndIdentityLocalName;
637         }
638         QName siQName = new QName(foundModule.getNamespace(),
639                 foundModule.getRevision(), localSIName);
640         ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName);
641         checkState(sie != null, "Cannot find referenced Service Interface by "
642                 + prefixAndIdentityLocalName);
643         return sie;
644     }
645
646     private static ModuleImport findModuleImport(Module module, String prefix) {
647         for (ModuleImport moduleImport : module.getImports()) {
648             if (moduleImport.getPrefix().equals(prefix)) {
649                 return moduleImport;
650             }
651         }
652         throw new IllegalStateException(format(
653                 "Import not found with prefix %s in %s", prefix, module));
654     }
655
656     public Map<String, AttributeIfc> getAttributes() {
657         return yangToAttributes;
658     }
659
660     private void setYangToAttributes(Map<String, AttributeIfc> newAttributes) {
661         this.yangToAttributes = newAttributes;
662
663     }
664
665     public String getNullableDescription() {
666         return nullableDescription;
667     }
668
669     @Override
670     public String toString() {
671         return "ModuleMXBeanEntry{" + "globallyUniqueName='"
672                 + globallyUniqueName + '\'' + ", packageName='" + packageName
673                 + '\'' + '}';
674     }
675 }