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