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