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