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