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