From: Maros Marsalek Date: Tue, 4 Feb 2014 09:54:04 +0000 (+0100) Subject: Add support for multiple choice case statements within one augument in config yang... X-Git-Tag: autorelease-tag-v20140601202136_82eb3f9~427^2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?p=controller.git;a=commitdiff_plain;h=bb14f3f60be546ba6dc99f6b87d6cbce9003c5e3 Add support for multiple choice case statements within one augument in config yang files Change-Id: Ic2111f4f3b1a63160279c1bd36b9c4799dccf159 Signed-off-by: Maros Marsalek --- diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java index 5bcc5402af..0cc950af65 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntry.java @@ -7,52 +7,16 @@ */ package org.opendaylight.controller.config.yangjmxgenerator; -import com.google.common.annotations.VisibleForTesting; -import com.google.common.base.Optional; -import com.google.common.collect.Sets; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute; + import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; -import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.FullyQualifiedNameHelper; -import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException; -import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.ModuleImport; -import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; import org.opendaylight.yangtools.yang.model.api.SchemaContext; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; -import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; -import org.opendaylight.yangtools.yang.model.api.UsesNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import static com.google.common.base.Preconditions.checkNotNull; -import static com.google.common.base.Preconditions.checkState; -import static java.lang.String.format; -import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName; /** * Represents part of yang model that describes a module. @@ -96,67 +60,34 @@ import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstant *

*/ public class ModuleMXBeanEntry extends AbstractEntry { - private static final Logger logger = LoggerFactory - .getLogger(ModuleMXBeanEntry.class); - - // TODO: the XPath should be parsed by code generator IMO - private static final String MAGIC_STRING = "MAGIC_STRING"; - private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$"; - private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath( - Arrays.asList(createConfigQName("modules"), - createConfigQName("module"), - createConfigQName("configuration")), true); - private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath( - Arrays.asList(createConfigQName("modules"), - createConfigQName("module"), createConfigQName("state")), - true); - - private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern - .compile("^(.+):(.+)$"); private static final String MODULE_SUFFIX = "Module"; private static final String FACTORY_SUFFIX = MODULE_SUFFIX + "Factory"; private static final String CLASS_NAME_SUFFIX = MODULE_SUFFIX + "MXBean"; private static final String ABSTRACT_PREFIX = "Abstract"; - /* - * threadpool-dynamic from the example above, taken from when condition, not - * the case name - */ - private final String globallyUniqueName; + private final ModuleMXBeanEntryInitial initial; private Map yangToAttributes; - private final String nullableDescription, packageName, javaNamePrefix, - namespace; - private final Map providedServices; private Collection runtimeBeans; - private final QName yangModuleQName; - - public ModuleMXBeanEntry(IdentitySchemaNode id, - Map yangToAttributes, String packageName, - Map providedServices2, String javaNamePrefix, - String namespace, Collection runtimeBeans, - QName yangModuleQName) { - this.globallyUniqueName = id.getQName().getLocalName(); + + ModuleMXBeanEntry(ModuleMXBeanEntryInitial initials, Map yangToAttributes, + Map providedServices2, Collection runtimeBeans) { this.yangToAttributes = yangToAttributes; - this.nullableDescription = id.getDescription(); - this.packageName = packageName; - this.javaNamePrefix = checkNotNull(javaNamePrefix); - this.namespace = checkNotNull(namespace); this.providedServices = Collections.unmodifiableMap(providedServices2); this.runtimeBeans = runtimeBeans; - this.yangModuleQName = yangModuleQName; + this.initial = initials; } public String getMXBeanInterfaceName() { - return javaNamePrefix + CLASS_NAME_SUFFIX; + return initial.javaNamePrefix + CLASS_NAME_SUFFIX; } public String getStubFactoryName() { - return javaNamePrefix + FACTORY_SUFFIX; + return initial.javaNamePrefix + FACTORY_SUFFIX; } public String getAbstractFactoryName() { @@ -164,7 +95,7 @@ public class ModuleMXBeanEntry extends AbstractEntry { } public String getStubModuleName() { - return javaNamePrefix + MODULE_SUFFIX; + return initial.javaNamePrefix + MODULE_SUFFIX; } public String getAbstractModuleName() { @@ -172,16 +103,16 @@ public class ModuleMXBeanEntry extends AbstractEntry { } public String getFullyQualifiedName(String typeName) { - return FullyQualifiedNameHelper.getFullyQualifiedName(packageName, + return FullyQualifiedNameHelper.getFullyQualifiedName(initial.packageName, typeName); } public String getGloballyUniqueName() { - return globallyUniqueName; + return initial.localName; } public String getPackageName() { - return packageName; + return initial.packageName; } /** @@ -202,31 +133,11 @@ public class ModuleMXBeanEntry extends AbstractEntry { } public String getJavaNamePrefix() { - return javaNamePrefix; + return initial.javaNamePrefix; } public String getNamespace() { - return namespace; - } - - @VisibleForTesting - static Matcher getWhenConditionMatcher(String prefix, - RevisionAwareXPath whenConstraint) { - String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace( - MAGIC_STRING, prefix); - Pattern pattern = Pattern.compile(xpathRegex); - return pattern.matcher(whenConstraint.toString()); - } - - static String getConfigModulePrefixFromImport(Module currentModule) { - for (ModuleImport currentImport : currentModule.getImports()) { - if (currentImport.getModuleName().equals( - ConfigConstants.CONFIG_MODULE)) { - return currentImport.getPrefix(); - } - } - throw new IllegalArgumentException("Cannot find import " - + ConfigConstants.CONFIG_MODULE + " in " + currentModule); + return initial.namespace; } /** @@ -241,504 +152,93 @@ public class ModuleMXBeanEntry extends AbstractEntry { Map qNamesToSIEs, SchemaContext schemaContext, TypeProviderWrapper typeProviderWrapper, String packageName) { - Map uniqueGeneratedClassesNames = new HashMap<>(); - logger.debug("Generating ModuleMXBeans of {} to package {}", - currentModule.getNamespace(), packageName); - String configModulePrefix; - try { - configModulePrefix = getConfigModulePrefixFromImport(currentModule); - } catch (IllegalArgumentException e) { - // this module does not import config module - return Collections.emptyMap(); - } - // get identities of base config:module-type - Map moduleIdentities = new HashMap<>(); - - for (IdentitySchemaNode id : currentModule.getIdentities()) { - if (id.getBaseIdentity() != null - && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id - .getBaseIdentity().getQName())) { - String identityLocalName = id.getQName().getLocalName(); - if (moduleIdentities.containsKey(identityLocalName)) { - throw new IllegalStateException( - "Module name already defined in this module: " - + identityLocalName); - } else { - moduleIdentities.put(identityLocalName, id); - logger.debug("Found identity {}", identityLocalName); - } - // validation check on unknown schema nodes - boolean providedServiceWasSet = false; - for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) { - // TODO: test this - if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME - .equals(unknownNode.getNodeType())) { - // no op: 0 or more provided identities are allowed - } else if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME - .equals(unknownNode.getNodeType())) { - // 0..1 allowed - checkState( - providedServiceWasSet == false, - format("More than one language extension %s is not allowed here: %s", - ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME, - id)); - providedServiceWasSet = true; - } else { - throw new IllegalStateException( - "Unexpected language extension " - + unknownNode.getNodeType()); - } - } - } - } - Map result = new HashMap<>(); - // each module name should have an augmentation defined - Map unaugmentedModuleIdentities = new HashMap<>( - moduleIdentities); - for (AugmentationSchema augmentation : currentModule.getAugmentations()) { - Set childNodes = augmentation.getChildNodes(); - if (childNodes.size() == 1) { - DataSchemaNode when = childNodes.iterator().next(); - if (when instanceof ChoiceCaseNode) { - ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when; - if (choiceCaseNode.getConstraints() == null - || choiceCaseNode.getConstraints() - .getWhenCondition() == null) { - continue; - } - RevisionAwareXPath xPath = choiceCaseNode.getConstraints() - .getWhenCondition(); - Matcher matcher = getWhenConditionMatcher( - configModulePrefix, xPath); - if (matcher.matches() == false) { - continue; - } - String moduleLocalNameFromXPath = matcher.group(1); - IdentitySchemaNode moduleIdentity = moduleIdentities - .get(moduleLocalNameFromXPath); - unaugmentedModuleIdentities - .remove(moduleLocalNameFromXPath); - checkState(moduleIdentity != null, "Cannot find identity " - + moduleLocalNameFromXPath - + " matching augmentation " + augmentation); - Map providedServices = findProvidedServices( - moduleIdentity, currentModule, qNamesToSIEs, - schemaContext); - - if (moduleIdentity == null) { - throw new IllegalStateException( - "Cannot find identity specified by augmentation xpath constraint: " - + moduleLocalNameFromXPath + " of " - + augmentation); - } - String javaNamePrefix = findJavaNamePrefix(moduleIdentity); - - Map yangToAttributes = null; - // runtime-data - Collection runtimeBeans = null; - - if (expectedConfigurationAugmentationSchemaPath - .equals(augmentation.getTargetPath())) { - logger.debug("Parsing configuration of {}", - moduleLocalNameFromXPath); - yangToAttributes = fillConfiguration(choiceCaseNode, - currentModule, typeProviderWrapper, - qNamesToSIEs, schemaContext, packageName); - checkUniqueAttributesWithGeneratedClass( - uniqueGeneratedClassesNames, when.getQName(), - yangToAttributes); - } else if (expectedStateAugmentationSchemaPath - .equals(augmentation.getTargetPath())) { - logger.debug("Parsing state of {}", - moduleLocalNameFromXPath); - try { - runtimeBeans = fillRuntimeBeans(choiceCaseNode, - currentModule, typeProviderWrapper, - packageName, moduleLocalNameFromXPath, - javaNamePrefix); - } catch (NameConflictException e) { - throw new NameConflictException( - e.getConflictingName(), when.getQName(), - when.getQName()); - } - checkUniqueRuntimeBeansGeneratedClasses( - uniqueGeneratedClassesNames, when, runtimeBeans); - Set runtimeBeanEntryValues = Sets - .newHashSet(runtimeBeans); - for (RuntimeBeanEntry entry : runtimeBeanEntryValues) { - checkUniqueAttributesWithGeneratedClass( - uniqueGeneratedClassesNames, - when.getQName(), - entry.getYangPropertiesToTypesMap()); - } - - } else { - throw new IllegalArgumentException( - "Cannot parse augmentation " + augmentation); - } - if (result.containsKey(moduleLocalNameFromXPath)) { - // either fill runtimeBeans or yangToAttributes - ModuleMXBeanEntry moduleMXBeanEntry = result - .get(moduleLocalNameFromXPath); - if (yangToAttributes != null - && moduleMXBeanEntry.getAttributes() == null) { - moduleMXBeanEntry - .setYangToAttributes(yangToAttributes); - } else if (runtimeBeans != null - && moduleMXBeanEntry.getRuntimeBeans() == null) { - moduleMXBeanEntry.setRuntimeBeans(runtimeBeans); - } - } else { - // construct ModuleMXBeanEntry - ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry( - moduleIdentity, yangToAttributes, packageName, - providedServices, javaNamePrefix, currentModule - .getNamespace().toString(), - runtimeBeans, - ModuleUtil.getQName(currentModule)); - moduleMXBeanEntry.setYangModuleName(currentModule - .getName()); - moduleMXBeanEntry - .setYangModuleLocalname(moduleLocalNameFromXPath); - result.put(moduleLocalNameFromXPath, moduleMXBeanEntry); - } - } // skip if child node is not ChoiceCaseNode - } // skip if childNodes != 1 - } - // clean up nulls - for (Entry entry : result.entrySet()) { - ModuleMXBeanEntry module = entry.getValue(); - if (module.getAttributes() == null) { - module.setYangToAttributes(Collections - . emptyMap()); - } else if (module.getRuntimeBeans() == null) { - module.setRuntimeBeans(Collections - . emptyList()); - } - } - // check attributes name uniqueness - for (Entry entry : result.entrySet()) { - checkUniqueRuntimeBeanAttributesName(entry.getValue(), - uniqueGeneratedClassesNames); - } - if (unaugmentedModuleIdentities.size() > 0) { - logger.warn("Augmentation not found for all module identities: {}", - unaugmentedModuleIdentities.keySet()); - } + ModuleMXBeanEntryBuilder builder = new ModuleMXBeanEntryBuilder().setModule(currentModule).setqNamesToSIEs(qNamesToSIEs) + .setSchemaContext(schemaContext).setTypeProviderWrapper(typeProviderWrapper) + .setPackageName(packageName); - logger.debug("Number of ModuleMXBeans to be generated: {}", - result.size()); - return result; + return builder.build(); } - private static void checkUniqueRuntimeBeansGeneratedClasses( - Map uniqueGeneratedClassesNames, - DataSchemaNode when, Collection runtimeBeans) { - for (RuntimeBeanEntry runtimeBean : runtimeBeans) { - final String javaNameOfRuntimeMXBean = runtimeBean - .getJavaNameOfRuntimeMXBean(); - if (uniqueGeneratedClassesNames - .containsKey(javaNameOfRuntimeMXBean)) { - QName firstDefinedQName = uniqueGeneratedClassesNames - .get(javaNameOfRuntimeMXBean); - throw new NameConflictException(javaNameOfRuntimeMXBean, - firstDefinedQName, when.getQName()); - } - uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean, - when.getQName()); - } - } - - private static void checkUniqueRuntimeBeanAttributesName( - ModuleMXBeanEntry mxBeanEntry, - Map uniqueGeneratedClassesNames) { - for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) { - for (String runtimeAttName : runtimeBeanEntry - .getYangPropertiesToTypesMap().keySet()) { - if (mxBeanEntry.getAttributes().keySet() - .contains(runtimeAttName)) { - QName qName1 = uniqueGeneratedClassesNames - .get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean()); - QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry - .getGloballyUniqueName()); - throw new NameConflictException(runtimeAttName, qName1, - qName2); - } - } - } - } - - private static void checkUniqueAttributesWithGeneratedClass( - Map uniqueGeneratedClassNames, QName parentQName, - Map yangToAttributes) { - for (Entry attr : yangToAttributes.entrySet()) { - if (attr.getValue() instanceof TOAttribute) { - checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, - (TOAttribute) attr.getValue()); - } else if (attr.getValue() instanceof ListAttribute - && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) { - checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, - (TOAttribute) ((ListAttribute) attr.getValue()) - .getInnerAttribute()); - } - } + public Map getAttributes() { + return yangToAttributes; } - private static void checkUniqueTOAttr( - Map uniqueGeneratedClassNames, QName parentQName, - TOAttribute attr) { - final String upperCaseCammelCase = attr.getUpperCaseCammelCase(); - if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) { - QName firstDefinedQName = uniqueGeneratedClassNames - .get(upperCaseCammelCase); - throw new NameConflictException(upperCaseCammelCase, - firstDefinedQName, parentQName); - } else { - uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName); - } + void setYangToAttributes(Map newAttributes) { + this.yangToAttributes = newAttributes; } - private static Collection fillRuntimeBeans( - ChoiceCaseNode choiceCaseNode, Module currentModule, - TypeProviderWrapper typeProviderWrapper, String packageName, - String moduleLocalNameFromXPath, String javaNamePrefix) { - - return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, - choiceCaseNode, moduleLocalNameFromXPath, typeProviderWrapper, - javaNamePrefix, currentModule).values(); - + public String getNullableDescription() { + return initial.description; } - private static Map fillConfiguration( - ChoiceCaseNode choiceCaseNode, Module currentModule, - TypeProviderWrapper typeProviderWrapper, - Map qNamesToSIEs, - SchemaContext schemaContext, String packageName) { - Map yangToAttributes = new HashMap<>(); - for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) { - AttributeIfc attributeValue = getAttributeValue(attrNode, - currentModule, qNamesToSIEs, typeProviderWrapper, - schemaContext, packageName); - yangToAttributes.put(attributeValue.getAttributeYangName(), - attributeValue); - } - return yangToAttributes; + public QName getYangModuleQName() { + return initial.qName; } - private static Map findProvidedServices( - IdentitySchemaNode moduleIdentity, Module currentModule, - Map qNamesToSIEs, - SchemaContext schemaContext) { - Map result = new HashMap<>(); - for (UnknownSchemaNode unknownNode : moduleIdentity - .getUnknownSchemaNodes()) { - if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME - .equals(unknownNode.getNodeType())) { - String prefixAndIdentityLocalName = unknownNode - .getNodeParameter(); - ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName, - currentModule, qNamesToSIEs, schemaContext); - result.put(sie.getFullyQualifiedName(), sie.getQName()); - } - } - return result; + @Override + public String toString() { + return "ModuleMXBeanEntry{" + "globallyUniqueName='" + + initial.localName + '\'' + ", packageName='" + initial.packageName + + '\'' + '}'; } - /** - * For input node, find if it contains config:java-name-prefix extension. If - * not found, convert local name of node converted to cammel case. - */ - public static String findJavaNamePrefix(SchemaNode schemaNode) { - return convertToJavaName(schemaNode, true); - } + static final class ModuleMXBeanEntryInitial { - public static String findJavaParameter(SchemaNode schemaNode) { - return convertToJavaName(schemaNode, false); - } + private String localName; + private String description; + private String packageName; + private String javaNamePrefix; + private String namespace; + private QName qName; - public static String convertToJavaName(SchemaNode schemaNode, - boolean capitalizeFirstLetter) { - for (UnknownSchemaNode unknownNode : schemaNode.getUnknownSchemaNodes()) { - if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME - .equals(unknownNode.getNodeType())) { - String value = unknownNode.getNodeParameter(); - return convertToJavaName(value, capitalizeFirstLetter); - } + ModuleMXBeanEntryInitial(String localName, String description, String packageName, String javaNamePrefix, String namespace, QName qName) { + this.localName = localName; + this.description = description; + this.packageName = packageName; + this.javaNamePrefix = javaNamePrefix; + this.namespace = namespace; + this.qName = qName; } - return convertToJavaName(schemaNode.getQName().getLocalName(), - capitalizeFirstLetter); } - public static String convertToJavaName(String localName, - boolean capitalizeFirstLetter) { - if (capitalizeFirstLetter) { - return BindingGeneratorUtil.parseToClassName(localName); - } else { - return BindingGeneratorUtil.parseToValidParamName(localName); - } - } + static final class ModuleMXBeanEntryInitialBuilder { + private String localName; + private String description; + private String packageName; + private String javaNamePrefix; + private String namespace; + private QName qName; - private static int getChildNodeSizeWithoutUses(DataNodeContainer csn) { - int result = 0; - for (DataSchemaNode dsn : csn.getChildNodes()) { - if (dsn.isAddedByUses() == false) { - result++; - } + public ModuleMXBeanEntryInitialBuilder setPackageName(String packageName) { + this.packageName = packageName; + return this; } - return result; - } - private static AttributeIfc getAttributeValue(DataSchemaNode attrNode, - Module currentModule, - Map qNamesToSIEs, - TypeProviderWrapper typeProviderWrapper, - SchemaContext schemaContext, String packageName) { - - if (attrNode instanceof LeafSchemaNode) { - // simple type - LeafSchemaNode leaf = (LeafSchemaNode) attrNode; - return new JavaAttribute(leaf, typeProviderWrapper); - } else if (attrNode instanceof ContainerSchemaNode) { - // reference or TO - ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode; - Optional dependencyAttributeOptional = extractDependency( - containerSchemaNode, attrNode, currentModule, qNamesToSIEs, - schemaContext); - if (dependencyAttributeOptional.isPresent()) { - return dependencyAttributeOptional.get(); - } else { - return TOAttribute.create(containerSchemaNode, - typeProviderWrapper, packageName); - } - - } else if (attrNode instanceof LeafListSchemaNode) { - return ListAttribute.create((LeafListSchemaNode) attrNode, - typeProviderWrapper); - } else if (attrNode instanceof ListSchemaNode) { - ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode; - Optional dependencyAttributeOptional = extractDependency( - listSchemaNode, attrNode, currentModule, qNamesToSIEs, - schemaContext); - if (dependencyAttributeOptional.isPresent()) { - return dependencyAttributeOptional.get(); - } else { - return ListAttribute.create(listSchemaNode, - typeProviderWrapper, packageName); - } - } else { - throw new UnsupportedOperationException( - "Unknown configuration node " + attrNode.toString()); + public ModuleMXBeanEntryInitialBuilder setJavaNamePrefix(String javaNamePrefix) { + this.javaNamePrefix = javaNamePrefix; + return this; } - } - private static Optional extractDependency( - DataNodeContainer dataNodeContainer, DataSchemaNode attrNode, - Module currentModule, - Map qNamesToSIEs, - SchemaContext schemaContext) { - if (dataNodeContainer.getUses().size() == 1 - && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) { - // reference - UsesNode usesNode = dataNodeContainer.getUses().iterator().next(); - checkState(usesNode.getRefines().size() == 1, - "Unexpected 'refine' child node size of " - + dataNodeContainer); - LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines() - .values().iterator().next(); - checkState(refine.getUnknownSchemaNodes().size() == 1, - "Unexpected unknown schema node size of " + refine); - UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes() - .iterator().next(); - checkState( - ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity - .getNodeType()), "Unexpected language extension " - + requiredIdentity); - String prefixAndIdentityLocalName = requiredIdentity - .getNodeParameter(); - // import should point to a module - ServiceInterfaceEntry serviceInterfaceEntry = findSIE( - prefixAndIdentityLocalName, currentModule, qNamesToSIEs, - schemaContext); - boolean mandatory = refine.getConstraints().isMandatory(); - AbstractDependencyAttribute reference; - if (dataNodeContainer instanceof ContainerSchemaNode) { - reference = new DependencyAttribute(attrNode, - serviceInterfaceEntry, mandatory, - attrNode.getDescription()); - } else { - reference = new ListDependenciesAttribute(attrNode, - serviceInterfaceEntry, mandatory, - attrNode.getDescription()); - } - return Optional.of(reference); + public ModuleMXBeanEntryInitialBuilder setNamespace(String namespace) { + this.namespace = namespace; + return this; } - return Optional.absent(); - } - private static ServiceInterfaceEntry findSIE( - String prefixAndIdentityLocalName, Module currentModule, - Map qNamesToSIEs, - SchemaContext schemaContext) { - - Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName); - Module foundModule; - String localSIName; - if (m.matches()) { - // if there is a prefix, look for ModuleImport with this prefix. Get - // Module from SchemaContext - String prefix = m.group(1); - ModuleImport moduleImport = findModuleImport(currentModule, prefix); - foundModule = schemaContext.findModuleByName( - moduleImport.getModuleName(), moduleImport.getRevision()); - checkState( - foundModule != null, - format("Module not found in SchemaContext by %s", - moduleImport)); - localSIName = m.group(2); - } else { - foundModule = currentModule; // no prefix => SIE is in currentModule - localSIName = prefixAndIdentityLocalName; + public ModuleMXBeanEntryInitialBuilder setqName(QName qName) { + this.qName = qName; + return this; } - QName siQName = new QName(foundModule.getNamespace(), - foundModule.getRevision(), localSIName); - ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName); - checkState(sie != null, "Cannot find referenced Service Interface by " - + prefixAndIdentityLocalName); - return sie; - } - private static ModuleImport findModuleImport(Module module, String prefix) { - for (ModuleImport moduleImport : module.getImports()) { - if (moduleImport.getPrefix().equals(prefix)) { - return moduleImport; - } + public ModuleMXBeanEntry.ModuleMXBeanEntryInitial build() { + return new ModuleMXBeanEntry.ModuleMXBeanEntryInitial(localName, description, packageName, javaNamePrefix, namespace, qName); } - throw new IllegalStateException(format( - "Import not found with prefix %s in %s", prefix, module)); - } - public Map getAttributes() { - return yangToAttributes; - } - - private void setYangToAttributes(Map newAttributes) { - this.yangToAttributes = newAttributes; - - } - - public String getNullableDescription() { - return nullableDescription; - } - - public QName getYangModuleQName() { - return yangModuleQName; - } + public ModuleMXBeanEntryInitialBuilder setIdSchemaNode(IdentitySchemaNode idSchemaNode) { + this.localName = idSchemaNode.getQName().getLocalName(); + this.description = idSchemaNode.getDescription(); + return this; + } - @Override - public String toString() { - return "ModuleMXBeanEntry{" + "globallyUniqueName='" - + globallyUniqueName + '\'' + ", packageName='" + packageName - + '\'' + '}'; } } diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryBuilder.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryBuilder.java new file mode 100644 index 0000000000..4a9d5512d3 --- /dev/null +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryBuilder.java @@ -0,0 +1,523 @@ +/* + * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.controller.config.yangjmxgenerator; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Function; +import com.google.common.base.Optional; +import com.google.common.collect.Collections2; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AbstractDependencyAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.AttributeIfc; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.DependencyAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.JavaAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.ListDependenciesAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.attribute.TOAttribute; +import org.opendaylight.controller.config.yangjmxgenerator.plugin.util.NameConflictException; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; +import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; +import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.ModuleImport; +import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; +import org.opendaylight.yangtools.yang.model.api.UsesNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static com.google.common.base.Preconditions.checkState; +import static java.lang.String.format; +import static org.opendaylight.controller.config.yangjmxgenerator.ConfigConstants.createConfigQName; + +final class ModuleMXBeanEntryBuilder { + + private Module currentModule; + private Map qNamesToSIEs; + private SchemaContext schemaContext; + private TypeProviderWrapper typeProviderWrapper; + private String packageName; + + public ModuleMXBeanEntryBuilder setModule(Module module) { + this.currentModule = module; + return this; + } + + public ModuleMXBeanEntryBuilder setqNamesToSIEs(Map qNamesToSIEs) { + this.qNamesToSIEs = qNamesToSIEs; + return this; + } + + public ModuleMXBeanEntryBuilder setSchemaContext(SchemaContext schemaContext) { + this.schemaContext = schemaContext; + return this; + } + + public ModuleMXBeanEntryBuilder setTypeProviderWrapper(TypeProviderWrapper typeProviderWrapper) { + this.typeProviderWrapper = typeProviderWrapper; + return this; + } + + public ModuleMXBeanEntryBuilder setPackageName(String packageName) { + this.packageName = packageName; + return this; + } + + private static final Logger logger = LoggerFactory + .getLogger(ModuleMXBeanEntryBuilder.class); + + // TODO: the XPath should be parsed by code generator IMO + private static final String MAGIC_STRING = "MAGIC_STRING"; + private static final String MODULE_CONDITION_XPATH_TEMPLATE = "^/MAGIC_STRING:modules/MAGIC_STRING:module/MAGIC_STRING:type\\s*=\\s*['\"](.+)['\"]$"; + private static final SchemaPath expectedConfigurationAugmentationSchemaPath = new SchemaPath( + Arrays.asList(createConfigQName("modules"), + createConfigQName("module"), + createConfigQName("configuration")), true); + private static final SchemaPath expectedStateAugmentationSchemaPath = new SchemaPath( + Arrays.asList(createConfigQName("modules"), + createConfigQName("module"), createConfigQName("state")), + true); + private static final Pattern PREFIX_COLON_LOCAL_NAME = Pattern + .compile("^(.+):(.+)$"); + + + public Map build() { + logger.debug("Generating ModuleMXBeans of {} to package {}", + currentModule.getNamespace(), packageName); + + String configModulePrefix; + try { + configModulePrefix = getConfigModulePrefixFromImport(currentModule); + } catch (IllegalArgumentException e) { + // this currentModule does not import config currentModule + return Collections.emptyMap(); + } + + // get identities of base config:currentModule-type + Map moduleIdentities = getIdentityMap(); + + Map uniqueGeneratedClassesNames = new HashMap<>(); + + // each currentModule name should have an augmentation defined + Map unaugmentedModuleIdentities = new HashMap<>( + moduleIdentities); + + Map result = new HashMap<>(); + + for (AugmentationSchema augmentation : currentModule.getAugmentations()) { + Set childNodes = augmentation.getChildNodes(); + if (areAllChildrenChoiceCaseNodes(childNodes)) { + for (ChoiceCaseNode childCase : castChildNodesToChoiceCases(childNodes)) { + // TODO refactor, extract to standalone builder class + processChoiceCaseNode(result, uniqueGeneratedClassesNames, configModulePrefix, moduleIdentities, + unaugmentedModuleIdentities, augmentation, childCase); + } + } // skip if child nodes are not all cases + } + // clean up nulls + cleanUpNulls(result); + // check attributes name uniqueness + checkAttributeNamesUniqueness(uniqueGeneratedClassesNames, result); + checkUnaugumentedIdentities(unaugmentedModuleIdentities); + + logger.debug("Number of ModuleMXBeans to be generated: {}", result.size()); + + return result; + } + + private static void cleanUpNulls(Map result) { + for (Map.Entry entry : result.entrySet()) { + ModuleMXBeanEntry module = entry.getValue(); + if (module.getAttributes() == null) { + module.setYangToAttributes(Collections + . emptyMap()); + } else if (module.getRuntimeBeans() == null) { + module.setRuntimeBeans(Collections + . emptyList()); + } + } + } + + private static void checkUnaugumentedIdentities(Map unaugmentedModuleIdentities) { + if (unaugmentedModuleIdentities.size() > 0) { + logger.warn("Augmentation not found for all currentModule identities: {}", + unaugmentedModuleIdentities.keySet()); + } + } + + private static void checkAttributeNamesUniqueness(Map uniqueGeneratedClassesNames, Map result) { + for (Map.Entry entry : result.entrySet()) { + checkUniqueRuntimeBeanAttributesName(entry.getValue(), + uniqueGeneratedClassesNames); + } + } + + private Map getIdentityMap() { + Map moduleIdentities = Maps.newHashMap(); + + for (IdentitySchemaNode id : currentModule.getIdentities()) { + if (id.getBaseIdentity() != null + && ConfigConstants.MODULE_TYPE_Q_NAME.equals(id.getBaseIdentity().getQName())) { + String identityLocalName = id.getQName().getLocalName(); + if (moduleIdentities.containsKey(identityLocalName)) { + throw new IllegalStateException("Module name already defined in this currentModule: " + + identityLocalName); + } else { + moduleIdentities.put(identityLocalName, id); + logger.debug("Found identity {}", identityLocalName); + } + // validation check on unknown schema nodes + boolean providedServiceWasSet = false; + for (UnknownSchemaNode unknownNode : id.getUnknownSchemaNodes()) { + // TODO: test this + if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) { + // no op: 0 or more provided identities are allowed + } else if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME.equals(unknownNode.getNodeType())) { + // 0..1 allowed + checkState( + providedServiceWasSet == false, + format("More than one language extension %s is not allowed here: %s", + ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME, id)); + providedServiceWasSet = true; + } else { + throw new IllegalStateException("Unexpected language extension " + unknownNode.getNodeType()); + } + } + } + } + + return moduleIdentities; + } + + private Collection castChildNodesToChoiceCases(Set childNodes) { + return Collections2.transform(childNodes, new Function() { + @Nullable + @Override + public ChoiceCaseNode apply(@Nullable DataSchemaNode input) { + return (ChoiceCaseNode) input; + } + }); + } + + private boolean areAllChildrenChoiceCaseNodes(Set childNodes) { + for (DataSchemaNode childNode : childNodes) { + if (childNode instanceof ChoiceCaseNode == false) + return false; + } + return true; + } + + private void processChoiceCaseNode(Map result, + Map uniqueGeneratedClassesNames, String configModulePrefix, + Map moduleIdentities, + Map unaugmentedModuleIdentities, AugmentationSchema augmentation, + DataSchemaNode when) { + + ChoiceCaseNode choiceCaseNode = (ChoiceCaseNode) when; + if (choiceCaseNode.getConstraints() == null || choiceCaseNode.getConstraints().getWhenCondition() == null) { + return; + } + RevisionAwareXPath xPath = choiceCaseNode.getConstraints().getWhenCondition(); + Matcher matcher = getWhenConditionMatcher(configModulePrefix, xPath); + if (matcher.matches() == false) { + return; + } + String moduleLocalNameFromXPath = matcher.group(1); + IdentitySchemaNode moduleIdentity = moduleIdentities.get(moduleLocalNameFromXPath); + unaugmentedModuleIdentities.remove(moduleLocalNameFromXPath); + checkState(moduleIdentity != null, "Cannot find identity " + moduleLocalNameFromXPath + + " matching augmentation " + augmentation); + Map providedServices = findProvidedServices(moduleIdentity, currentModule, qNamesToSIEs, + schemaContext); + + if (moduleIdentity == null) { + throw new IllegalStateException("Cannot find identity specified by augmentation xpath constraint: " + + moduleLocalNameFromXPath + " of " + augmentation); + } + String javaNamePrefix = TypeProviderWrapper.findJavaNamePrefix(moduleIdentity); + + Map yangToAttributes = null; + // runtime-data + Collection runtimeBeans = null; + + if (expectedConfigurationAugmentationSchemaPath.equals(augmentation.getTargetPath())) { + logger.debug("Parsing configuration of {}", moduleLocalNameFromXPath); + yangToAttributes = fillConfiguration(choiceCaseNode, currentModule, typeProviderWrapper, qNamesToSIEs, + schemaContext, packageName); + checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(), yangToAttributes); + } else if (expectedStateAugmentationSchemaPath.equals(augmentation.getTargetPath())) { + logger.debug("Parsing state of {}", moduleLocalNameFromXPath); + try { + runtimeBeans = fillRuntimeBeans(choiceCaseNode, currentModule, typeProviderWrapper, packageName, + moduleLocalNameFromXPath, javaNamePrefix); + } catch (NameConflictException e) { + throw new NameConflictException(e.getConflictingName(), when.getQName(), when.getQName()); + } + checkUniqueRuntimeBeansGeneratedClasses(uniqueGeneratedClassesNames, when, runtimeBeans); + Set runtimeBeanEntryValues = Sets.newHashSet(runtimeBeans); + for (RuntimeBeanEntry entry : runtimeBeanEntryValues) { + checkUniqueAttributesWithGeneratedClass(uniqueGeneratedClassesNames, when.getQName(), + entry.getYangPropertiesToTypesMap()); + } + + } else { + throw new IllegalArgumentException("Cannot parse augmentation " + augmentation); + } + if (result.containsKey(moduleLocalNameFromXPath)) { + // either fill runtimeBeans or yangToAttributes + ModuleMXBeanEntry moduleMXBeanEntry = result.get(moduleLocalNameFromXPath); + if (yangToAttributes != null && moduleMXBeanEntry.getAttributes() == null) { + moduleMXBeanEntry.setYangToAttributes(yangToAttributes); + } else if (runtimeBeans != null && moduleMXBeanEntry.getRuntimeBeans() == null) { + moduleMXBeanEntry.setRuntimeBeans(runtimeBeans); + } + } else { + ModuleMXBeanEntry.ModuleMXBeanEntryInitial initial = new ModuleMXBeanEntry.ModuleMXBeanEntryInitialBuilder() + .setIdSchemaNode(moduleIdentity).setPackageName(packageName).setJavaNamePrefix(javaNamePrefix) + .setNamespace(currentModule.getNamespace().toString()).setqName(ModuleUtil.getQName(currentModule)) + .build(); + + // construct ModuleMXBeanEntry + ModuleMXBeanEntry moduleMXBeanEntry = new ModuleMXBeanEntry(initial, yangToAttributes, providedServices, + runtimeBeans); + + moduleMXBeanEntry.setYangModuleName(currentModule.getName()); + moduleMXBeanEntry.setYangModuleLocalname(moduleLocalNameFromXPath); + result.put(moduleLocalNameFromXPath, moduleMXBeanEntry); + } + } + + private void checkUniqueRuntimeBeansGeneratedClasses(Map uniqueGeneratedClassesNames, + DataSchemaNode when, Collection runtimeBeans) { + for (RuntimeBeanEntry runtimeBean : runtimeBeans) { + final String javaNameOfRuntimeMXBean = runtimeBean.getJavaNameOfRuntimeMXBean(); + if (uniqueGeneratedClassesNames.containsKey(javaNameOfRuntimeMXBean)) { + QName firstDefinedQName = uniqueGeneratedClassesNames.get(javaNameOfRuntimeMXBean); + throw new NameConflictException(javaNameOfRuntimeMXBean, firstDefinedQName, when.getQName()); + } + uniqueGeneratedClassesNames.put(javaNameOfRuntimeMXBean, when.getQName()); + } + } + + private static void checkUniqueRuntimeBeanAttributesName(ModuleMXBeanEntry mxBeanEntry, + Map uniqueGeneratedClassesNames) { + for (RuntimeBeanEntry runtimeBeanEntry : mxBeanEntry.getRuntimeBeans()) { + for (String runtimeAttName : runtimeBeanEntry.getYangPropertiesToTypesMap().keySet()) { + if (mxBeanEntry.getAttributes().keySet().contains(runtimeAttName)) { + QName qName1 = uniqueGeneratedClassesNames.get(runtimeBeanEntry.getJavaNameOfRuntimeMXBean()); + QName qName2 = uniqueGeneratedClassesNames.get(mxBeanEntry.getGloballyUniqueName()); + throw new NameConflictException(runtimeAttName, qName1, qName2); + } + } + } + } + + private void checkUniqueAttributesWithGeneratedClass(Map uniqueGeneratedClassNames, + QName parentQName, Map yangToAttributes) { + for (Map.Entry attr : yangToAttributes.entrySet()) { + if (attr.getValue() instanceof TOAttribute) { + checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, (TOAttribute) attr.getValue()); + } else if (attr.getValue() instanceof ListAttribute + && ((ListAttribute) attr.getValue()).getInnerAttribute() instanceof TOAttribute) { + checkUniqueTOAttr(uniqueGeneratedClassNames, parentQName, + (TOAttribute) ((ListAttribute) attr.getValue()).getInnerAttribute()); + } + } + } + + private void checkUniqueTOAttr(Map uniqueGeneratedClassNames, QName parentQName, TOAttribute attr) { + final String upperCaseCammelCase = attr.getUpperCaseCammelCase(); + if (uniqueGeneratedClassNames.containsKey(upperCaseCammelCase)) { + QName firstDefinedQName = uniqueGeneratedClassNames.get(upperCaseCammelCase); + throw new NameConflictException(upperCaseCammelCase, firstDefinedQName, parentQName); + } else { + uniqueGeneratedClassNames.put(upperCaseCammelCase, parentQName); + } + } + + private Collection fillRuntimeBeans(ChoiceCaseNode choiceCaseNode, Module currentModule, + TypeProviderWrapper typeProviderWrapper, String packageName, String moduleLocalNameFromXPath, + String javaNamePrefix) { + + return RuntimeBeanEntry.extractClassNameToRuntimeBeanMap(packageName, choiceCaseNode, moduleLocalNameFromXPath, + typeProviderWrapper, javaNamePrefix, currentModule).values(); + + } + + private Map fillConfiguration(ChoiceCaseNode choiceCaseNode, Module currentModule, + TypeProviderWrapper typeProviderWrapper, Map qNamesToSIEs, + SchemaContext schemaContext, String packageName) { + Map yangToAttributes = new HashMap<>(); + for (DataSchemaNode attrNode : choiceCaseNode.getChildNodes()) { + AttributeIfc attributeValue = getAttributeValue(attrNode, currentModule, qNamesToSIEs, typeProviderWrapper, + schemaContext, packageName); + yangToAttributes.put(attributeValue.getAttributeYangName(), attributeValue); + } + return yangToAttributes; + } + + private Map findProvidedServices(IdentitySchemaNode moduleIdentity, Module currentModule, + Map qNamesToSIEs, SchemaContext schemaContext) { + Map result = new HashMap<>(); + for (UnknownSchemaNode unknownNode : moduleIdentity.getUnknownSchemaNodes()) { + if (ConfigConstants.PROVIDED_SERVICE_EXTENSION_QNAME.equals(unknownNode.getNodeType())) { + String prefixAndIdentityLocalName = unknownNode.getNodeParameter(); + ServiceInterfaceEntry sie = findSIE(prefixAndIdentityLocalName, currentModule, qNamesToSIEs, + schemaContext); + result.put(sie.getFullyQualifiedName(), sie.getQName()); + } + } + return result; + } + + private AttributeIfc getAttributeValue(DataSchemaNode attrNode, Module currentModule, + Map qNamesToSIEs, TypeProviderWrapper typeProviderWrapper, + SchemaContext schemaContext, String packageName) { + + if (attrNode instanceof LeafSchemaNode) { + // simple type + LeafSchemaNode leaf = (LeafSchemaNode) attrNode; + return new JavaAttribute(leaf, typeProviderWrapper); + } else if (attrNode instanceof ContainerSchemaNode) { + // reference or TO + ContainerSchemaNode containerSchemaNode = (ContainerSchemaNode) attrNode; + Optional dependencyAttributeOptional = extractDependency( + containerSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext); + if (dependencyAttributeOptional.isPresent()) { + return dependencyAttributeOptional.get(); + } else { + return TOAttribute.create(containerSchemaNode, typeProviderWrapper, packageName); + } + + } else if (attrNode instanceof LeafListSchemaNode) { + return ListAttribute.create((LeafListSchemaNode) attrNode, typeProviderWrapper); + } else if (attrNode instanceof ListSchemaNode) { + ListSchemaNode listSchemaNode = (ListSchemaNode) attrNode; + Optional dependencyAttributeOptional = extractDependency( + listSchemaNode, attrNode, currentModule, qNamesToSIEs, schemaContext); + if (dependencyAttributeOptional.isPresent()) { + return dependencyAttributeOptional.get(); + } else { + return ListAttribute.create(listSchemaNode, typeProviderWrapper, packageName); + } + } else { + throw new UnsupportedOperationException("Unknown configuration node " + attrNode.toString()); + } + } + + private Optional extractDependency(DataNodeContainer dataNodeContainer, + DataSchemaNode attrNode, Module currentModule, Map qNamesToSIEs, + SchemaContext schemaContext) { + if (dataNodeContainer.getUses().size() == 1 && getChildNodeSizeWithoutUses(dataNodeContainer) == 0) { + // reference + UsesNode usesNode = dataNodeContainer.getUses().iterator().next(); + checkState(usesNode.getRefines().size() == 1, "Unexpected 'refine' child node size of " + dataNodeContainer); + LeafSchemaNode refine = (LeafSchemaNode) usesNode.getRefines().values().iterator().next(); + checkState(refine.getUnknownSchemaNodes().size() == 1, "Unexpected unknown schema node size of " + refine); + UnknownSchemaNode requiredIdentity = refine.getUnknownSchemaNodes().iterator().next(); + checkState(ConfigConstants.REQUIRED_IDENTITY_EXTENSION_QNAME.equals(requiredIdentity.getNodeType()), + "Unexpected language extension " + requiredIdentity); + String prefixAndIdentityLocalName = requiredIdentity.getNodeParameter(); + // import should point to a module + ServiceInterfaceEntry serviceInterfaceEntry = findSIE(prefixAndIdentityLocalName, currentModule, + qNamesToSIEs, schemaContext); + boolean mandatory = refine.getConstraints().isMandatory(); + AbstractDependencyAttribute reference; + if (dataNodeContainer instanceof ContainerSchemaNode) { + reference = new DependencyAttribute(attrNode, serviceInterfaceEntry, mandatory, + attrNode.getDescription()); + } else { + reference = new ListDependenciesAttribute(attrNode, serviceInterfaceEntry, mandatory, + attrNode.getDescription()); + } + return Optional.of(reference); + } + return Optional.absent(); + } + + private int getChildNodeSizeWithoutUses(DataNodeContainer csn) { + int result = 0; + for (DataSchemaNode dsn : csn.getChildNodes()) { + if (dsn.isAddedByUses() == false) { + result++; + } + } + return result; + } + + private ServiceInterfaceEntry findSIE(String prefixAndIdentityLocalName, Module currentModule, + Map qNamesToSIEs, SchemaContext schemaContext) { + + Matcher m = PREFIX_COLON_LOCAL_NAME.matcher(prefixAndIdentityLocalName); + Module foundModule; + String localSIName; + if (m.matches()) { + // if there is a prefix, look for ModuleImport with this prefix. Get + // Module from SchemaContext + String prefix = m.group(1); + ModuleImport moduleImport = findModuleImport(currentModule, prefix); + foundModule = schemaContext.findModuleByName(moduleImport.getModuleName(), moduleImport.getRevision()); + checkState(foundModule != null, format("Module not found in SchemaContext by %s", moduleImport)); + localSIName = m.group(2); + } else { + foundModule = currentModule; // no prefix => SIE is in currentModule + localSIName = prefixAndIdentityLocalName; + } + QName siQName = new QName(foundModule.getNamespace(), foundModule.getRevision(), localSIName); + ServiceInterfaceEntry sie = qNamesToSIEs.get(siQName); + checkState(sie != null, "Cannot find referenced Service Interface by " + prefixAndIdentityLocalName); + return sie; + } + + private ModuleImport findModuleImport(Module module, String prefix) { + for (ModuleImport moduleImport : module.getImports()) { + if (moduleImport.getPrefix().equals(prefix)) { + return moduleImport; + } + } + throw new IllegalStateException(format("Import not found with prefix %s in %s", prefix, module)); + } + + @VisibleForTesting + static Matcher getWhenConditionMatcher(String prefix, RevisionAwareXPath whenConstraint) { + String xpathRegex = MODULE_CONDITION_XPATH_TEMPLATE.replace(MAGIC_STRING, prefix); + Pattern pattern = Pattern.compile(xpathRegex); + return pattern.matcher(whenConstraint.toString()); + } + + String getConfigModulePrefixFromImport(Module currentModule) { + for (ModuleImport currentImport : currentModule.getImports()) { + if (currentImport.getModuleName().equals(ConfigConstants.CONFIG_MODULE)) { + return currentImport.getPrefix(); + } + } + throw new IllegalArgumentException("Cannot find import " + ConfigConstants.CONFIG_MODULE + " in " + + currentModule); + } + +} diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java index 4831545b39..cd14458b0f 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/RuntimeBeanEntry.java @@ -272,7 +272,7 @@ public class RuntimeBeanEntry { } // convert RpcDefinition to Rpc for (RpcDefinition rpcDefinition : rpcDefinitions) { - String name = ModuleMXBeanEntry + String name = TypeProviderWrapper .findJavaParameter(rpcDefinition); AttributeIfc returnType; if (rpcDefinition.getOutput() == null @@ -376,7 +376,7 @@ public class RuntimeBeanEntry { "More than one key is not supported in " + listSchemaNode); } - String javaNamePrefix = ModuleMXBeanEntry + String javaNamePrefix = TypeProviderWrapper .findJavaNamePrefix(listSchemaNode); RuntimeBeanEntry rbFromAttributes = new RuntimeBeanEntry(packageName, diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java index c43fead0fc..3c8c8aa2f4 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/TypeProviderWrapper.java @@ -7,12 +7,14 @@ */ package org.opendaylight.controller.config.yangjmxgenerator; +import org.opendaylight.yangtools.binding.generator.util.BindingGeneratorUtil; import org.opendaylight.yangtools.sal.binding.generator.spi.TypeProvider; import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; public class TypeProviderWrapper { private final TypeProvider typeProvider; @@ -21,6 +23,40 @@ public class TypeProviderWrapper { this.typeProvider = typeProvider; } + /** + * For input node, find if it contains config:java-name-prefix extension. If + * not found, convert local name of node converted to cammel case. + */ + public static String findJavaNamePrefix(SchemaNode schemaNode) { + return convertToJavaName(schemaNode, true); + } + + public static String findJavaParameter(SchemaNode schemaNode) { + return convertToJavaName(schemaNode, false); + } + + public static String convertToJavaName(SchemaNode schemaNode, + boolean capitalizeFirstLetter) { + for (UnknownSchemaNode unknownNode : schemaNode.getUnknownSchemaNodes()) { + if (ConfigConstants.JAVA_NAME_PREFIX_EXTENSION_QNAME + .equals(unknownNode.getNodeType())) { + String value = unknownNode.getNodeParameter(); + return convertToJavaName(value, capitalizeFirstLetter); + } + } + return convertToJavaName(schemaNode.getQName().getLocalName(), + capitalizeFirstLetter); + } + + public static String convertToJavaName(String localName, + boolean capitalizeFirstLetter) { + if (capitalizeFirstLetter) { + return BindingGeneratorUtil.parseToClassName(localName); + } else { + return BindingGeneratorUtil.parseToValidParamName(localName); + } + } + public Type getType(LeafSchemaNode leaf) { TypeDefinition type = leaf.getType(); return getType(leaf, type); diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java index ba2edc4e31..edfb4c59af 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/AbstractAttribute.java @@ -7,7 +7,7 @@ */ package org.opendaylight.controller.config.yangjmxgenerator.attribute; -import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; +import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; public abstract class AbstractAttribute implements AttributeIfc { @@ -22,8 +22,8 @@ public abstract class AbstractAttribute implements AttributeIfc { AbstractAttribute(DataSchemaNode attrNode) { this.attributeYangName = getLocalName(attrNode); this.node = attrNode; - this.upperCaseCammelCase = ModuleMXBeanEntry.findJavaNamePrefix(node); - this.lowerCaseCammelCase = ModuleMXBeanEntry.findJavaParameter(node); + this.upperCaseCammelCase = TypeProviderWrapper.findJavaNamePrefix(node); + this.lowerCaseCammelCase = TypeProviderWrapper.findJavaParameter(node); } @Override diff --git a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java index 6a540b50b2..84300cb81d 100644 --- a/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java +++ b/opendaylight/config/yang-jmx-generator/src/main/java/org/opendaylight/controller/config/yangjmxgenerator/attribute/TOAttribute.java @@ -11,7 +11,6 @@ import com.google.common.base.Function; import com.google.common.collect.Collections2; import com.google.common.collect.Maps; import com.google.common.collect.Sets; -import org.opendaylight.controller.config.yangjmxgenerator.ModuleMXBeanEntry; import org.opendaylight.controller.config.yangjmxgenerator.TypeProviderWrapper; import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl; import org.opendaylight.yangtools.sal.binding.model.api.Type; @@ -121,7 +120,7 @@ public class TOAttribute extends AbstractAttribute implements TypedAttribute { .entrySet()) { capitalizedPropertiesToTypesMap.put( - ModuleMXBeanEntry.convertToJavaName(entry.getKey(), true), + TypeProviderWrapper.convertToJavaName(entry.getKey(), true), entry.getValue()); } return capitalizedPropertiesToTypesMap; @@ -133,7 +132,7 @@ public class TOAttribute extends AbstractAttribute implements TypedAttribute { .entrySet()) { jmxPropertiesToTypesMap.put( - ModuleMXBeanEntry.convertToJavaName(entry.getKey(), false), + TypeProviderWrapper.convertToJavaName(entry.getKey(), false), entry.getValue()); } return jmxPropertiesToTypesMap; diff --git a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java index 9ea34ca2b8..8ca2bb5bc9 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java +++ b/opendaylight/config/yang-jmx-generator/src/test/java/org/opendaylight/controller/config/yangjmxgenerator/ModuleMXBeanEntryTest.java @@ -113,6 +113,9 @@ public class ModuleMXBeanEntryTest extends AbstractYangTest { , PACKAGE_NAME); Map attributes = namesToMBEs.get("impl-netconf") .getAttributes(); + + assertCorrectAttributesSize(namesToMBEs, attributes); + // DependencyAttribute threadFactoryAttribute = (DependencyAttribute) attributes .get("thread-factory"); @@ -132,6 +135,16 @@ public class ModuleMXBeanEntryTest extends AbstractYangTest { assertThat(threadFactoryAttribute.getType().getName(), is("ObjectName")); } + private void assertCorrectAttributesSize(Map namesToMBEs, Map attributes) { + assertEquals(14, attributes.size()); + assertEquals(1, namesToMBEs.get("impl-netconf").getRuntimeBeans().size()); + assertEquals(2, namesToMBEs.get("impl-netconf").getRuntimeBeans().iterator().next().getAttributes().size()); + + assertEquals(4, namesToMBEs.get("impl").getAttributes().size()); + assertEquals(1, namesToMBEs.get("impl").getRuntimeBeans().size()); + assertEquals(1, namesToMBEs.get("impl").getRuntimeBeans().iterator().next().getAttributes().size()); + } + protected RuntimeBeanEntry findFirstByYangName( Collection runtimeBeans, String yangName) { for (RuntimeBeanEntry rb : runtimeBeans) { @@ -155,7 +168,7 @@ public class ModuleMXBeanEntryTest extends AbstractYangTest { private void assertMatches(String prefix, String input) { RevisionAwareXPath whenConstraint = mock(RevisionAwareXPath.class); doReturn(input).when(whenConstraint).toString(); - Matcher output = ModuleMXBeanEntry.getWhenConditionMatcher(prefix, + Matcher output = ModuleMXBeanEntryBuilder.getWhenConditionMatcher(prefix, whenConstraint); assertTrue(output.matches()); assertEquals("threadpool-dynamic", output.group(1)); diff --git a/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang b/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang index 97078e033a..16085efbba 100644 --- a/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang +++ b/opendaylight/config/yang-jmx-generator/src/test/resources/config-jmx-it-impl.yang @@ -8,8 +8,6 @@ module config-jmx-it-impl { import ietf-inet-types { prefix inet; revision-date 2010-09-24;} import config-threads { prefix th; revision-date 2013-04-09; } - - description "Testing IMPL"; @@ -67,19 +65,7 @@ module config-jmx-it-impl { } } - } - - augment "/config:modules/config:module/config:state" { - case impl { - when "/config:modules/config:module/config:type = 'impl'"; - // root runtime bean - leaf created-sessions { - type uint32; - } - } - } - augment "/config:modules/config:module/config:configuration" { case impl-netconf { when "/config:modules/config:module/config:type = 'impl-netconf'"; @@ -205,6 +191,14 @@ module config-jmx-it-impl { } augment "/config:modules/config:module/config:state" { + case impl { + when "/config:modules/config:module/config:type = 'impl'"; + // root runtime bean + leaf created-sessions { + type uint32; + } + } + case impl-netconf { when "/config:modules/config:module/config:type = 'impl-netconf'"; // root runtime bean @@ -212,6 +206,11 @@ module config-jmx-it-impl { type uint32; } + leaf created-sessions-2 { + type uint32; + } + } } + }