-/*\r
- * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.\r
- *\r
- * This program and the accompanying materials are made available under the\r
- * terms of the Eclipse Public License v1.0 which accompanies this distribution,\r
- * and is available at http://www.eclipse.org/legal/epl-v10.html\r
- */\r
-package org.opendaylight.controller.yang.model.parser.impl;\r
-\r
-import java.io.File;\r
-import java.io.FileInputStream;\r
-import java.io.FileNotFoundException;\r
-import java.io.IOException;\r
-import java.io.InputStream;\r
-import java.util.ArrayList;\r
-import java.util.Calendar;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-import java.util.TreeMap;\r
-\r
-import org.antlr.v4.runtime.ANTLRInputStream;\r
-import org.antlr.v4.runtime.CommonTokenStream;\r
-import org.antlr.v4.runtime.tree.ParseTree;\r
-import org.antlr.v4.runtime.tree.ParseTreeWalker;\r
-import org.opendaylight.controller.antlrv4.code.gen.YangLexer;\r
-import org.opendaylight.controller.antlrv4.code.gen.YangParser;\r
-import org.opendaylight.controller.yang.common.QName;\r
-import org.opendaylight.controller.yang.model.api.AugmentationSchema;\r
-import org.opendaylight.controller.yang.model.api.DataSchemaNode;\r
-import org.opendaylight.controller.yang.model.api.ExtensionDefinition;\r
-import org.opendaylight.controller.yang.model.api.Module;\r
-import org.opendaylight.controller.yang.model.api.ModuleImport;\r
-import org.opendaylight.controller.yang.model.api.NotificationDefinition;\r
-import org.opendaylight.controller.yang.model.api.RpcDefinition;\r
-import org.opendaylight.controller.yang.model.api.SchemaContext;\r
-import org.opendaylight.controller.yang.model.api.SchemaPath;\r
-import org.opendaylight.controller.yang.model.api.TypeDefinition;\r
-import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;\r
-import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition;\r
-import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition.Bit;\r
-import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;\r
-import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;\r
-import org.opendaylight.controller.yang.model.api.type.LengthConstraint;\r
-import org.opendaylight.controller.yang.model.api.type.PatternConstraint;\r
-import org.opendaylight.controller.yang.model.api.type.RangeConstraint;\r
-import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;\r
-import org.opendaylight.controller.yang.model.parser.api.YangModelParser;\r
-import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationSchemaBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationTargetBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.api.ChildNodeBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.api.DataSchemaNodeBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.api.TypeAwareBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.api.TypeDefinitionBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.impl.IdentitySchemaNodeBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.impl.ModuleBuilder;\r
-import org.opendaylight.controller.yang.model.parser.builder.impl.UnionTypeBuilder;\r
-import org.opendaylight.controller.yang.model.util.BaseConstraints;\r
-import org.opendaylight.controller.yang.model.util.BinaryType;\r
-import org.opendaylight.controller.yang.model.util.BitsType;\r
-import org.opendaylight.controller.yang.model.util.StringType;\r
-import org.opendaylight.controller.yang.model.util.UnknownType;\r
-import org.opendaylight.controller.yang.model.util.YangTypesConverter;\r
-import org.slf4j.Logger;\r
-import org.slf4j.LoggerFactory;\r
-\r
-public class YangModelParserImpl implements YangModelParser {\r
-\r
- private static final Logger logger = LoggerFactory\r
- .getLogger(YangModelParserImpl.class);\r
-\r
- @Override\r
- public Module parseYangModel(String yangFile) {\r
- final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFile);\r
- Set<Module> result = build(modules);\r
- return result.iterator().next();\r
- }\r
-\r
- @Override\r
- public Set<Module> parseYangModels(String... yangFiles) {\r
- final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFiles);\r
- Set<Module> result = build(modules);\r
- return result;\r
- }\r
-\r
- @Override\r
- public Set<Module> parseYangModelsFromStreams(\r
- InputStream... yangModelStreams) {\r
- final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangModelStreams);\r
- Set<Module> result = build(modules);\r
- return result;\r
- }\r
-\r
- @Override\r
- public SchemaContext resolveSchemaContext(Set<Module> modules) {\r
- return new SchemaContextImpl(modules);\r
- }\r
-\r
- private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(\r
- String... yangFiles) {\r
- InputStream[] streams = new InputStream[yangFiles.length];\r
- for (int i = 0; i < yangFiles.length; i++) {\r
- final String yangFileName = yangFiles[i];\r
- final File yangFile = new File(yangFileName);\r
- FileInputStream inStream = null;\r
- try {\r
- inStream = new FileInputStream(yangFile);\r
- } catch (FileNotFoundException e) {\r
- logger.warn("Exception while reading yang stream: " + inStream,\r
- e);\r
- }\r
- streams[i] = inStream;\r
- }\r
- return resolveModuleBuildersFromStreams(streams);\r
- }\r
-\r
- private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(\r
- InputStream... yangFiles) {\r
- final Map<String, TreeMap<Date, ModuleBuilder>> modules = new HashMap<String, TreeMap<Date, ModuleBuilder>>();\r
- final ParseTreeWalker walker = new ParseTreeWalker();\r
- final List<ParseTree> trees = parseStreams(yangFiles);\r
- final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];\r
-\r
- for (int i = 0; i < trees.size(); i++) {\r
- final YangModelParserListenerImpl yangModelParser = new YangModelParserListenerImpl();\r
- walker.walk(yangModelParser, trees.get(i));\r
- builders[i] = yangModelParser.getModuleBuilder();\r
- }\r
-\r
- for (ModuleBuilder builder : builders) {\r
- final String builderName = builder.getName();\r
- Date builderRevision = builder.getRevision();\r
- if (builderRevision == null) {\r
- builderRevision = createEpochTime();\r
- }\r
- TreeMap<Date, ModuleBuilder> builderByRevision = modules\r
- .get(builderName);\r
- if (builderByRevision == null) {\r
- builderByRevision = new TreeMap<Date, ModuleBuilder>();\r
- }\r
- builderByRevision.put(builderRevision, builder);\r
- modules.put(builderName, builderByRevision);\r
- }\r
- return modules;\r
- }\r
-\r
- private List<ParseTree> parseStreams(InputStream... yangStreams) {\r
- List<ParseTree> trees = new ArrayList<ParseTree>();\r
- for (InputStream yangStream : yangStreams) {\r
- trees.add(parseStream(yangStream));\r
- }\r
- return trees;\r
- }\r
-\r
- private ParseTree parseStream(InputStream yangStream) {\r
- ParseTree result = null;\r
- try {\r
- final ANTLRInputStream input = new ANTLRInputStream(yangStream);\r
- final YangLexer lexer = new YangLexer(input);\r
- final CommonTokenStream tokens = new CommonTokenStream(lexer);\r
- final YangParser parser = new YangParser(tokens);\r
- result = parser.yang();\r
- } catch (IOException e) {\r
- logger.warn("Exception while reading yang file: " + yangStream, e);\r
- }\r
- return result;\r
- }\r
-\r
- private Set<Module> build(Map<String, TreeMap<Date, ModuleBuilder>> modules) {\r
- // first validate\r
- for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules\r
- .entrySet()) {\r
- for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()\r
- .entrySet()) {\r
- ModuleBuilder moduleBuilder = childEntry.getValue();\r
- validateBuilder(modules, moduleBuilder);\r
- }\r
- }\r
- // then build\r
- final Set<Module> result = new HashSet<Module>();\r
- for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules\r
- .entrySet()) {\r
- final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();\r
- for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()\r
- .entrySet()) {\r
- ModuleBuilder moduleBuilder = childEntry.getValue();\r
- modulesByRevision.put(childEntry.getKey(),\r
- moduleBuilder.build());\r
- result.add(moduleBuilder.build());\r
- }\r
- }\r
-\r
- return result;\r
- }\r
-\r
- private void validateBuilder(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder builder) {\r
- resolveTypedefs(modules, builder);\r
- resolveAugments(modules, builder);\r
- resolveIdentities(modules, builder);\r
- }\r
-\r
- /**\r
- * Search for dirty nodes (node which contains UnknownType) and resolve\r
- * unknown types.\r
- *\r
- * @param modules\r
- * all available modules\r
- * @param module\r
- * current module\r
- */\r
- private void resolveTypedefs(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder module) {\r
- Map<List<String>, TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();\r
- if (dirtyNodes.size() == 0) {\r
- return;\r
- } else {\r
- for (Map.Entry<List<String>, TypeAwareBuilder> entry : dirtyNodes\r
- .entrySet()) {\r
- TypeAwareBuilder typeToResolve = entry.getValue();\r
-\r
- if (typeToResolve instanceof UnionTypeBuilder) {\r
- resolveUnionTypeBuilder(modules, module,\r
- (UnionTypeBuilder) typeToResolve);\r
- } else {\r
- UnknownType ut = (UnknownType) typeToResolve.getType();\r
- TypeDefinition<?> resolvedType = findTargetType(ut,\r
- modules, module);\r
- typeToResolve.setType(resolvedType);\r
- }\r
- }\r
- }\r
- }\r
-\r
- private UnionTypeBuilder resolveUnionTypeBuilder(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder builder, UnionTypeBuilder unionTypeBuilderToResolve) {\r
- List<TypeDefinition<?>> resolvedTypes = new ArrayList<TypeDefinition<?>>();\r
- List<TypeDefinition<?>> typesToRemove = new ArrayList<TypeDefinition<?>>();\r
-\r
- for (TypeDefinition<?> td : unionTypeBuilderToResolve.getTypes()) {\r
- if (td instanceof UnknownType) {\r
- TypeDefinition<?> resolvedType = findTargetType(\r
- (UnknownType) td, modules, builder);\r
- resolvedTypes.add(resolvedType);\r
- typesToRemove.add(td);\r
- }\r
- }\r
-\r
- List<TypeDefinition<?>> unionTypeBuilderTypes = unionTypeBuilderToResolve\r
- .getTypes();\r
- unionTypeBuilderTypes.addAll(resolvedTypes);\r
- unionTypeBuilderTypes.removeAll(typesToRemove);\r
-\r
- return unionTypeBuilderToResolve;\r
- }\r
-\r
- private TypeDefinition<?> findTargetType(UnknownType ut,\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder builder) {\r
-\r
- Map<TypeDefinitionBuilder, TypeConstraints> foundedTypeDefinitionBuilder = findTypeDefinitionBuilderWithConstraints(\r
- modules, ut, builder);\r
- TypeDefinitionBuilder targetType = foundedTypeDefinitionBuilder\r
- .entrySet().iterator().next().getKey();\r
- TypeConstraints constraints = foundedTypeDefinitionBuilder.entrySet()\r
- .iterator().next().getValue();\r
-\r
- TypeDefinition<?> targetTypeBaseType = targetType.getBaseType();\r
-\r
- // RANGE\r
- List<RangeConstraint> ranges = ut.getRangeStatements();\r
- resolveRanges(ranges, targetType, modules, builder);\r
- // LENGTH\r
- List<LengthConstraint> lengths = ut.getLengthStatements();\r
- resolveLengths(lengths, targetType, modules, builder);\r
- // PATTERN\r
- List<PatternConstraint> patterns = ut.getPatterns();\r
- // Fraction Digits\r
- Integer fractionDigits = ut.getFractionDigits();\r
-\r
- targetTypeBaseType = mergeConstraints(targetTypeBaseType, constraints, ranges, lengths,\r
- patterns, fractionDigits);\r
-\r
- return targetTypeBaseType;\r
- }\r
-\r
- /**\r
- * Merge curent constraints with founded type constraints\r
- *\r
- * @param targetTypeBaseType\r
- * @param constraints\r
- * @param ranges\r
- * @param lengths\r
- * @param patterns\r
- * @param fractionDigits\r
- */\r
- private TypeDefinition<?> mergeConstraints(TypeDefinition<?> targetTypeBaseType,\r
- TypeConstraints constraints, List<RangeConstraint> ranges,\r
- List<LengthConstraint> lengths, List<PatternConstraint> patterns,\r
- Integer fractionDigits) {\r
- String targetTypeBaseTypeName = targetTypeBaseType.getQName()\r
- .getLocalName();\r
- // enumeration, leafref and identityref omitted because they have no\r
- // restrictions\r
- if (targetTypeBaseType instanceof DecimalTypeDefinition) {\r
- List<RangeConstraint> fullRanges = new ArrayList<RangeConstraint>();\r
- fullRanges.addAll(constraints.getRanges());\r
- fullRanges.addAll(ranges);\r
- Integer fd = fractionDigits == null ? constraints\r
- .getFractionDigits() : fractionDigits;\r
- targetTypeBaseType = YangTypesConverter\r
- .javaTypeForBaseYangDecimal64Type(fullRanges, fd);\r
- } else if (targetTypeBaseType instanceof IntegerTypeDefinition) {\r
- List<RangeConstraint> fullRanges = new ArrayList<RangeConstraint>();\r
- fullRanges.addAll(constraints.getRanges());\r
- fullRanges.addAll(ranges);\r
- if (targetTypeBaseTypeName.startsWith("int")) {\r
- targetTypeBaseType = YangTypesConverter\r
- .javaTypeForBaseYangSignedIntegerType(\r
- targetTypeBaseTypeName, fullRanges);\r
- } else {\r
- targetTypeBaseType = YangTypesConverter\r
- .javaTypeForBaseYangUnsignedIntegerType(\r
- targetTypeBaseTypeName, fullRanges);\r
- }\r
- } else if (targetTypeBaseType instanceof StringTypeDefinition) {\r
- List<LengthConstraint> fullLengths = new ArrayList<LengthConstraint>();\r
- fullLengths.addAll(constraints.getLengths());\r
- fullLengths.addAll(lengths);\r
- List<PatternConstraint> fullPatterns = new ArrayList<PatternConstraint>();\r
- fullPatterns.addAll(constraints.getPatterns());\r
- fullPatterns.addAll(patterns);\r
- targetTypeBaseType = new StringType(fullLengths, fullPatterns);\r
- } else if (targetTypeBaseType instanceof BitsTypeDefinition) {\r
- BitsTypeDefinition bitsType = (BitsTypeDefinition) targetTypeBaseType;\r
- List<Bit> bits = bitsType.getBits();\r
- targetTypeBaseType = new BitsType(bits);\r
- } else if (targetTypeBaseType instanceof BinaryTypeDefinition) {\r
- targetTypeBaseType = new BinaryType(null, lengths, null);\r
- }\r
- return targetTypeBaseType;\r
- }\r
-\r
- private TypeDefinitionBuilder findTypeDefinitionBuilder(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- UnknownType unknownType, ModuleBuilder builder) {\r
- Map<TypeDefinitionBuilder, TypeConstraints> result = findTypeDefinitionBuilderWithConstraints(\r
- modules, unknownType, builder);\r
- return result.entrySet().iterator().next().getKey();\r
- }\r
-\r
- private Map<TypeDefinitionBuilder, TypeConstraints> findTypeDefinitionBuilderWithConstraints(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- UnknownType unknownType, ModuleBuilder builder) {\r
- return findTypeDefinitionBuilderWithConstraints(new TypeConstraints(),\r
- modules, unknownType, builder);\r
- }\r
-\r
- /**\r
- * Traverse through all referenced types chain until base YANG type is\r
- * founded.\r
- *\r
- * @param constraints\r
- * current type constraints\r
- * @param modules\r
- * all available modules\r
- * @param unknownType\r
- * unknown type\r
- * @param builder\r
- * current module\r
- * @return map, where key is type referenced and value is its constraints\r
- */\r
- private Map<TypeDefinitionBuilder, TypeConstraints> findTypeDefinitionBuilderWithConstraints(\r
- TypeConstraints constraints,\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- UnknownType unknownType, ModuleBuilder builder) {\r
- Map<TypeDefinitionBuilder, TypeConstraints> result = new HashMap<TypeDefinitionBuilder, TypeConstraints>();\r
- QName unknownTypeQName = unknownType.getQName();\r
- String unknownTypeName = unknownTypeQName.getLocalName();\r
- String unknownTypePrefix = unknownTypeQName.getPrefix();\r
-\r
- // search for module which contains referenced typedef\r
- ModuleBuilder dependentModuleBuilder;\r
- if (unknownTypePrefix.equals(builder.getPrefix())) {\r
- dependentModuleBuilder = builder;\r
- } else {\r
- dependentModuleBuilder = findDependentModule(modules, builder,\r
- unknownTypePrefix);\r
- }\r
-\r
- // pull all typedef statements from dependent module...\r
- final Set<TypeDefinitionBuilder> typedefs = dependentModuleBuilder\r
- .getModuleTypedefs();\r
- // and search for referenced typedef\r
- TypeDefinitionBuilder lookedUpBuilder = null;\r
- for (TypeDefinitionBuilder tdb : typedefs) {\r
- QName qname = tdb.getQName();\r
- if (qname.getLocalName().equals(unknownTypeName)) {\r
- lookedUpBuilder = tdb;\r
- break;\r
- }\r
- }\r
-\r
- // if referenced type is UnknownType again, search recursively with\r
- // current constraints\r
- TypeDefinition<?> referencedType = lookedUpBuilder.getBaseType();\r
- if (referencedType instanceof UnknownType) {\r
- UnknownType unknown = (UnknownType) lookedUpBuilder.getBaseType();\r
-\r
- final List<RangeConstraint> ranges = unknown.getRangeStatements();\r
- constraints.addRanges(ranges);\r
- final List<LengthConstraint> lengths = unknown\r
- .getLengthStatements();\r
- constraints.addLengths(lengths);\r
- final List<PatternConstraint> patterns = unknown.getPatterns();\r
- constraints.addPatterns(patterns);\r
- return findTypeDefinitionBuilderWithConstraints(constraints,\r
- modules, unknown, dependentModuleBuilder);\r
- } else {\r
- // pull restriction from this base type and add them to\r
- // 'constraints'\r
- if (referencedType instanceof DecimalTypeDefinition) {\r
- constraints.addRanges(((DecimalTypeDefinition) referencedType)\r
- .getRangeStatements());\r
- constraints\r
- .setFractionDigits(((DecimalTypeDefinition) referencedType)\r
- .getFractionDigits());\r
- } else if (referencedType instanceof IntegerTypeDefinition) {\r
- constraints.addRanges(((IntegerTypeDefinition) referencedType)\r
- .getRangeStatements());\r
- } else if (referencedType instanceof StringTypeDefinition) {\r
- constraints.addPatterns(((StringTypeDefinition) referencedType)\r
- .getPatterns());\r
- } else if (referencedType instanceof BinaryTypeDefinition) {\r
- constraints.addLengths(((BinaryTypeDefinition) referencedType)\r
- .getLengthConstraints());\r
- }\r
- result.put(lookedUpBuilder, constraints);\r
- return result;\r
- }\r
- }\r
-\r
- /**\r
- * Go through all augmentation definitions and resolve them. This means find\r
- * referenced node and add child nodes to it.\r
- *\r
- * @param modules\r
- * all available modules\r
- * @param module\r
- * current module\r
- */\r
- private void resolveAugments(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder module) {\r
- Set<AugmentationSchemaBuilder> augmentBuilders = module\r
- .getAddedAugments();\r
-\r
- Set<AugmentationSchema> augments = new HashSet<AugmentationSchema>();\r
- for (AugmentationSchemaBuilder augmentBuilder : augmentBuilders) {\r
- SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath();\r
- String prefix = null;\r
- List<String> augmentTargetPath = new ArrayList<String>();\r
-\r
- for (QName pathPart : augmentTargetSchemaPath.getPath()) {\r
- prefix = pathPart.getPrefix();\r
- augmentTargetPath.add(pathPart.getLocalName());\r
- }\r
- ModuleBuilder dependentModule = findDependentModule(modules,\r
- module, prefix);\r
- //\r
- augmentTargetPath.add(0, dependentModule.getName());\r
- //\r
-\r
-\r
- AugmentationTargetBuilder augmentTarget = (AugmentationTargetBuilder) dependentModule\r
- .getNode(augmentTargetPath);\r
- AugmentationSchema result = augmentBuilder.build();\r
- augmentTarget.addAugmentation(result);\r
- fillAugmentTarget(augmentBuilder, (ChildNodeBuilder) augmentTarget);\r
- augments.add(result);\r
- }\r
- module.setAugmentations(augments);\r
- }\r
-\r
- /**\r
- * Add all augment's child nodes to given target.\r
- *\r
- * @param augment\r
- * @param target\r
- */\r
- private void fillAugmentTarget(AugmentationSchemaBuilder augment,\r
- ChildNodeBuilder target) {\r
- for (DataSchemaNodeBuilder builder : augment.getChildNodes()) {\r
- builder.setAugmenting(true);\r
- target.addChildNode(builder);\r
- }\r
- }\r
-\r
- /**\r
- * Go through identity statements defined in current module and resolve\r
- * their 'base' statement if present.\r
- *\r
- * @param modules\r
- * all modules\r
- * @param module\r
- * module being resolved\r
- */\r
- private void resolveIdentities(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder module) {\r
- Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();\r
- for (IdentitySchemaNodeBuilder identity : identities) {\r
- String baseIdentityName = identity.getBaseIdentityName();\r
- if (baseIdentityName != null) {\r
- String baseIdentityPrefix = null;\r
- String baseIdentityLocalName = null;\r
- if (baseIdentityName.contains(":")) {\r
- String[] splitted = baseIdentityName.split(":");\r
- baseIdentityPrefix = splitted[0];\r
- baseIdentityLocalName = splitted[1];\r
- } else {\r
- baseIdentityPrefix = module.getPrefix();\r
- baseIdentityLocalName = baseIdentityName;\r
- }\r
- ModuleBuilder dependentModule;\r
- if (baseIdentityPrefix.equals(module.getPrefix())) {\r
- dependentModule = module;\r
- } else {\r
- dependentModule = findDependentModule(modules, module,\r
- baseIdentityPrefix);\r
- }\r
-\r
- Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule\r
- .getAddedIdentities();\r
- for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {\r
- if (idBuilder.getQName().getLocalName()\r
- .equals(baseIdentityLocalName)) {\r
- identity.setBaseIdentity(idBuilder);\r
- }\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Find dependent module based on given prefix\r
- *\r
- * @param modules\r
- * all available modules\r
- * @param module\r
- * current module\r
- * @param prefix\r
- * target module prefix\r
- * @return dependent module builder\r
- */\r
- private ModuleBuilder findDependentModule(\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder module, String prefix) {\r
- ModuleImport dependentModuleImport = getModuleImport(module, prefix);\r
- String dependentModuleName = dependentModuleImport.getModuleName();\r
- Date dependentModuleRevision = dependentModuleImport.getRevision();\r
-\r
- TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules\r
- .get(dependentModuleName);\r
- ModuleBuilder dependentModule;\r
- if (dependentModuleRevision == null) {\r
- dependentModule = moduleBuildersByRevision.lastEntry().getValue();\r
- } else {\r
- dependentModule = moduleBuildersByRevision\r
- .get(dependentModuleRevision);\r
- }\r
- return dependentModule;\r
- }\r
-\r
- /**\r
- * Get module import referenced by given prefix.\r
- *\r
- * @param builder\r
- * module to search\r
- * @param prefix\r
- * prefix associated with import\r
- * @return ModuleImport based on given prefix\r
- */\r
- private ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {\r
- ModuleImport moduleImport = null;\r
- for (ModuleImport mi : builder.getModuleImports()) {\r
- if (mi.getPrefix().equals(prefix)) {\r
- moduleImport = mi;\r
- break;\r
- }\r
- }\r
- return moduleImport;\r
- }\r
-\r
- /**\r
- * Helper method for resolving special 'min' or 'max' values in range\r
- * constraint\r
- *\r
- * @param ranges\r
- * ranges to resolve\r
- * @param targetType\r
- * target type\r
- * @param modules\r
- * all available modules\r
- * @param builder\r
- * current module\r
- */\r
- private void resolveRanges(List<RangeConstraint> ranges,\r
- TypeDefinitionBuilder targetType,\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder builder) {\r
- if (ranges != null && ranges.size() > 0) {\r
- Long min = (Long) ranges.get(0).getMin();\r
- Long max = (Long) ranges.get(ranges.size() - 1).getMax();\r
- // if range contains one of the special values 'min' or 'max'\r
- if (min.equals(Long.MIN_VALUE) || max.equals(Long.MAX_VALUE)) {\r
- Long[] values = parseRangeConstraint(targetType, modules,\r
- builder);\r
- if (min.equals(Long.MIN_VALUE)) {\r
- min = values[0];\r
- RangeConstraint oldFirst = ranges.get(0);\r
- RangeConstraint newFirst = BaseConstraints.rangeConstraint(\r
- min, oldFirst.getMax(), oldFirst.getDescription(),\r
- oldFirst.getReference());\r
- ranges.set(0, newFirst);\r
- }\r
- if (max.equals(Long.MAX_VALUE)) {\r
- max = values[1];\r
- RangeConstraint oldLast = ranges.get(ranges.size() - 1);\r
- RangeConstraint newLast = BaseConstraints.rangeConstraint(\r
- oldLast.getMin(), max, oldLast.getDescription(),\r
- oldLast.getReference());\r
- ranges.set(ranges.size() - 1, newLast);\r
- }\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Helper method for resolving special 'min' or 'max' values in length\r
- * constraint\r
- *\r
- * @param lengths\r
- * lengths to resolve\r
- * @param targetType\r
- * target type\r
- * @param modules\r
- * all available modules\r
- * @param builder\r
- * current module\r
- */\r
- private void resolveLengths(List<LengthConstraint> lengths,\r
- TypeDefinitionBuilder targetType,\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder builder) {\r
- if (lengths != null && lengths.size() > 0) {\r
- Long min = lengths.get(0).getMin().longValue();\r
- Long max = lengths.get(lengths.size() - 1).getMax().longValue();\r
- // if length contains one of the special values 'min' or 'max'\r
- if (min.equals(Long.MIN_VALUE) || max.equals(Long.MAX_VALUE)) {\r
- Long[] values = parseRangeConstraint(targetType, modules,\r
- builder);\r
- if (min.equals(Long.MIN_VALUE)) {\r
- min = values[0];\r
- LengthConstraint oldFirst = lengths.get(0);\r
- LengthConstraint newFirst = BaseConstraints\r
- .lengthConstraint(min, oldFirst.getMax(),\r
- oldFirst.getDescription(),\r
- oldFirst.getReference());\r
- lengths.set(0, newFirst);\r
- }\r
- if (max.equals(Long.MAX_VALUE)) {\r
- max = values[1];\r
- LengthConstraint oldLast = lengths.get(lengths.size() - 1);\r
- LengthConstraint newLast = BaseConstraints\r
- .lengthConstraint(oldLast.getMin(), max,\r
- oldLast.getDescription(),\r
- oldLast.getReference());\r
- lengths.set(lengths.size() - 1, newLast);\r
- }\r
- }\r
- }\r
- }\r
-\r
- private Long[] parseRangeConstraint(TypeDefinitionBuilder targetType,\r
- Map<String, TreeMap<Date, ModuleBuilder>> modules,\r
- ModuleBuilder builder) {\r
- TypeDefinition<?> targetBaseType = targetType.getBaseType();\r
-\r
- if (targetBaseType instanceof IntegerTypeDefinition) {\r
- IntegerTypeDefinition itd = (IntegerTypeDefinition) targetBaseType;\r
- List<RangeConstraint> ranges = itd.getRangeStatements();\r
- Long min = (Long) ranges.get(0).getMin();\r
- Long max = (Long) ranges.get(ranges.size() - 1).getMax();\r
- return new Long[] { min, max };\r
- } else if (targetBaseType instanceof DecimalTypeDefinition) {\r
- DecimalTypeDefinition dtd = (DecimalTypeDefinition) targetBaseType;\r
- List<RangeConstraint> ranges = dtd.getRangeStatements();\r
- Long min = (Long) ranges.get(0).getMin();\r
- Long max = (Long) ranges.get(ranges.size() - 1).getMax();\r
- return new Long[] { min, max };\r
- } else {\r
- return parseRangeConstraint(\r
- findTypeDefinitionBuilder(modules,\r
- (UnknownType) targetBaseType, builder), modules,\r
- builder);\r
- }\r
- }\r
-\r
- private Date createEpochTime() {\r
- Calendar c = Calendar.getInstance();\r
- c.setTimeInMillis(0);\r
- return c.getTime();\r
- }\r
-\r
- private static class SchemaContextImpl implements SchemaContext {\r
- private final Set<Module> modules;\r
-\r
- private SchemaContextImpl(Set<Module> modules) {\r
- this.modules = modules;\r
- }\r
-\r
- @Override\r
- public Set<DataSchemaNode> getDataDefinitions() {\r
- final Set<DataSchemaNode> dataDefs = new HashSet<DataSchemaNode>();\r
- for (Module m : modules) {\r
- dataDefs.addAll(m.getChildNodes());\r
- }\r
- return dataDefs;\r
- }\r
-\r
- @Override\r
- public Set<Module> getModules() {\r
- return modules;\r
- }\r
-\r
- @Override\r
- public Set<NotificationDefinition> getNotifications() {\r
- final Set<NotificationDefinition> notifications = new HashSet<NotificationDefinition>();\r
- for (Module m : modules) {\r
- notifications.addAll(m.getNotifications());\r
- }\r
- return notifications;\r
- }\r
-\r
- @Override\r
- public Set<RpcDefinition> getOperations() {\r
- final Set<RpcDefinition> rpcs = new HashSet<RpcDefinition>();\r
- for (Module m : modules) {\r
- rpcs.addAll(m.getRpcs());\r
- }\r
- return rpcs;\r
- }\r
-\r
- @Override\r
- public Set<ExtensionDefinition> getExtensions() {\r
- final Set<ExtensionDefinition> extensions = new HashSet<ExtensionDefinition>();\r
- for (Module m : modules) {\r
- extensions.addAll(m.getExtensionSchemaNodes());\r
- }\r
- return extensions;\r
- }\r
- }\r
-\r
- private static class TypeConstraints {\r
- private final List<RangeConstraint> ranges = new ArrayList<RangeConstraint>();\r
- private final List<LengthConstraint> lengths = new ArrayList<LengthConstraint>();\r
- private final List<PatternConstraint> patterns = new ArrayList<PatternConstraint>();\r
- private Integer fractionDigits;\r
-\r
- public List<RangeConstraint> getRanges() {\r
- return ranges;\r
- }\r
-\r
- public void addRanges(List<RangeConstraint> ranges) {\r
- this.ranges.addAll(0, ranges);\r
- }\r
-\r
- public List<LengthConstraint> getLengths() {\r
- return lengths;\r
- }\r
-\r
- public void addLengths(List<LengthConstraint> lengths) {\r
- this.lengths.addAll(0, lengths);\r
- }\r
-\r
- public List<PatternConstraint> getPatterns() {\r
- return patterns;\r
- }\r
-\r
- public void addPatterns(List<PatternConstraint> patterns) {\r
- this.patterns.addAll(0, patterns);\r
- }\r
-\r
- public Integer getFractionDigits() {\r
- return fractionDigits;\r
- }\r
-\r
- public void setFractionDigits(Integer fractionDigits) {\r
- if (fractionDigits != null) {\r
- this.fractionDigits = fractionDigits;\r
- }\r
- }\r
- }\r
-\r
-}\r
+/*
+ * 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.yang.model.parser.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTree;
+import org.antlr.v4.runtime.tree.ParseTreeWalker;
+import org.opendaylight.controller.antlrv4.code.gen.YangLexer;
+import org.opendaylight.controller.antlrv4.code.gen.YangParser;
+import org.opendaylight.controller.yang.common.QName;
+import org.opendaylight.controller.yang.model.api.AugmentationSchema;
+import org.opendaylight.controller.yang.model.api.DataSchemaNode;
+import org.opendaylight.controller.yang.model.api.ExtensionDefinition;
+import org.opendaylight.controller.yang.model.api.Module;
+import org.opendaylight.controller.yang.model.api.ModuleImport;
+import org.opendaylight.controller.yang.model.api.NotificationDefinition;
+import org.opendaylight.controller.yang.model.api.RpcDefinition;
+import org.opendaylight.controller.yang.model.api.SchemaContext;
+import org.opendaylight.controller.yang.model.api.SchemaPath;
+import org.opendaylight.controller.yang.model.api.TypeDefinition;
+import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;
+import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;
+import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;
+import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
+import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
+import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
+import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;
+import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
+import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationSchemaBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationTargetBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.api.ChildNodeBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.api.DataSchemaNodeBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.api.TypeAwareBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.api.TypeDefinitionBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.impl.IdentitySchemaNodeBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.impl.ModuleBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.impl.TypedefBuilder;
+import org.opendaylight.controller.yang.model.parser.builder.impl.UnionTypeBuilder;
+import org.opendaylight.controller.yang.model.parser.util.TypeConstraints;
+import org.opendaylight.controller.yang.model.parser.util.YangParseException;
+import org.opendaylight.controller.yang.model.util.ExtendedType;
+import org.opendaylight.controller.yang.model.util.UnknownType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class YangModelParserImpl implements YangModelParser {
+
+ private static final Logger logger = LoggerFactory
+ .getLogger(YangModelParserImpl.class);
+
+ @Override
+ public Module parseYangModel(final String yangFile) {
+ final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFile);
+ final Set<Module> result = build(modules);
+ return result.iterator().next();
+ }
+
+ @Override
+ public Set<Module> parseYangModels(final String... yangFiles) {
+ final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFiles);
+ return build(modules);
+ }
+
+ @Override
+ public Set<Module> parseYangModelsFromStreams(
+ final InputStream... yangModelStreams) {
+ final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangModelStreams);
+ return build(modules);
+ }
+
+ @Override
+ public SchemaContext resolveSchemaContext(final Set<Module> modules) {
+ return new SchemaContextImpl(modules);
+ }
+
+ private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(
+ String... yangFiles) {
+ InputStream[] streams = new InputStream[yangFiles.length];
+ FileInputStream inStream = null;
+ for (int i = 0; i < yangFiles.length; i++) {
+ final String yangFileName = yangFiles[i];
+ final File yangFile = new File(yangFileName);
+ try {
+ inStream = new FileInputStream(yangFile);
+ } catch (FileNotFoundException e) {
+ logger.warn("Exception while reading yang stream: " + inStream,
+ e);
+ }
+ streams[i] = inStream;
+ }
+ return resolveModuleBuildersFromStreams(streams);
+ }
+
+ private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(
+ InputStream... yangFiles) {
+ final Map<String, TreeMap<Date, ModuleBuilder>> modules = new HashMap<String, TreeMap<Date, ModuleBuilder>>();
+ final ParseTreeWalker walker = new ParseTreeWalker();
+ final List<ParseTree> trees = parseStreams(yangFiles);
+ final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
+
+ YangModelParserListenerImpl yangModelParser = null;
+ for (int i = 0; i < trees.size(); i++) {
+ yangModelParser = new YangModelParserListenerImpl();
+ walker.walk(yangModelParser, trees.get(i));
+ builders[i] = yangModelParser.getModuleBuilder();
+ }
+
+ for (ModuleBuilder builder : builders) {
+ final String builderName = builder.getName();
+ Date builderRevision = builder.getRevision();
+ if (builderRevision == null) {
+ builderRevision = createEpochTime();
+ }
+ TreeMap<Date, ModuleBuilder> builderByRevision = modules
+ .get(builderName);
+ if (builderByRevision == null) {
+ builderByRevision = new TreeMap<Date, ModuleBuilder>();
+ }
+ builderByRevision.put(builderRevision, builder);
+ modules.put(builderName, builderByRevision);
+ }
+ return modules;
+ }
+
+ private List<ParseTree> parseStreams(InputStream... yangStreams) {
+ final List<ParseTree> trees = new ArrayList<ParseTree>();
+ for (InputStream yangStream : yangStreams) {
+ trees.add(parseStream(yangStream));
+ }
+ return trees;
+ }
+
+ private ParseTree parseStream(InputStream yangStream) {
+ ParseTree result = null;
+ try {
+ final ANTLRInputStream input = new ANTLRInputStream(yangStream);
+ final YangLexer lexer = new YangLexer(input);
+ final CommonTokenStream tokens = new CommonTokenStream(lexer);
+ final YangParser parser = new YangParser(tokens);
+ result = parser.yang();
+ } catch (IOException e) {
+ logger.warn("Exception while reading yang file: " + yangStream, e);
+ }
+ return result;
+ }
+
+ private Set<Module> build(Map<String, TreeMap<Date, ModuleBuilder>> modules) {
+ // validate
+ for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
+ .entrySet()) {
+ for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
+ .entrySet()) {
+ ModuleBuilder moduleBuilder = childEntry.getValue();
+ validateModule(modules, moduleBuilder);
+ }
+ }
+
+ // build
+ final Set<Module> result = new HashSet<Module>();
+ for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
+ .entrySet()) {
+ final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
+ for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
+ .entrySet()) {
+ ModuleBuilder moduleBuilder = childEntry.getValue();
+ Module module = moduleBuilder.build();
+ modulesByRevision.put(childEntry.getKey(), module);
+ result.add(module);
+ }
+ }
+ return result;
+ }
+
+ private void validateModule(
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+ resolveDirtyNodes(modules, builder);
+ resolveAugments(modules, builder);
+ resolveIdentities(modules, builder);
+ }
+
+ /**
+ * Search for dirty nodes (node which contains UnknownType) and resolve
+ * unknown types.
+ *
+ * @param modules
+ * all available modules
+ * @param module
+ * current module
+ */
+ private void resolveDirtyNodes(
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder module) {
+ final Map<List<String>, TypeAwareBuilder> dirtyNodes = module
+ .getDirtyNodes();
+ if (!dirtyNodes.isEmpty()) {
+ for (Map.Entry<List<String>, TypeAwareBuilder> entry : dirtyNodes
+ .entrySet()) {
+
+ TypeAwareBuilder typeToResolve = entry.getValue();
+ if (typeToResolve instanceof UnionTypeBuilder) {
+ UnionTypeBuilder union = (UnionTypeBuilder) typeToResolve;
+ List<TypeDefinition<?>> unionTypes = union.getTypes();
+ List<UnknownType> toRemove = new ArrayList<UnknownType>();
+ for (TypeDefinition<?> td : unionTypes) {
+ if (td instanceof UnknownType) {
+ UnknownType unknownType = (UnknownType) td;
+ TypeDefinitionBuilder resolvedType = findTargetTypeUnion(
+ typeToResolve, unknownType, modules, module);
+ union.setType(resolvedType);
+ toRemove.add(unknownType);
+ }
+ }
+ unionTypes.removeAll(toRemove);
+ } else {
+ TypeDefinitionBuilder resolvedType = findTargetType(
+ typeToResolve, modules, module);
+ typeToResolve.setType(resolvedType);
+ }
+ }
+ }
+ }
+
+ private TypeDefinitionBuilder findTargetType(
+ TypeAwareBuilder typeToResolve,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+ TypeConstraints constraints = new TypeConstraints();
+
+ TypeDefinitionBuilder targetType = findTypedef(typeToResolve, modules,
+ builder);
+ TypeConstraints tConstraints = findConstraints(typeToResolve,
+ constraints, modules, builder);
+ targetType.setRanges(tConstraints.getRange());
+ targetType.setLengths(tConstraints.getLength());
+ targetType.setPatterns(tConstraints.getPatterns());
+ targetType.setFractionDigits(tConstraints.getFractionDigits());
+
+ return targetType;
+ }
+
+ private TypeDefinitionBuilder findTargetTypeUnion(
+ TypeAwareBuilder typeToResolve, UnknownType unknownType,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+ TypeConstraints constraints = new TypeConstraints();
+
+ TypeDefinitionBuilder targetType = findTypedefUnion(typeToResolve,
+ unknownType, modules, builder);
+ TypeConstraints tConstraints = findConstraints(typeToResolve,
+ constraints, modules, builder);
+ targetType.setRanges(tConstraints.getRange());
+ targetType.setLengths(tConstraints.getLength());
+ targetType.setPatterns(tConstraints.getPatterns());
+ targetType.setFractionDigits(tConstraints.getFractionDigits());
+
+ return targetType;
+ }
+
+ private TypeDefinitionBuilder findTypedef(TypeAwareBuilder typeToResolve,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+
+ TypeDefinition<?> baseTypeToResolve = typeToResolve.getType();
+ if (baseTypeToResolve != null
+ && !(baseTypeToResolve instanceof UnknownType)) {
+ return (TypeDefinitionBuilder) typeToResolve;
+ }
+
+ UnknownType unknownType = (UnknownType) typeToResolve.getType();
+
+ QName unknownTypeQName = unknownType.getQName();
+ String unknownTypeName = unknownTypeQName.getLocalName();
+ String unknownTypePrefix = unknownTypeQName.getPrefix();
+
+ // search for module which contains referenced typedef
+ ModuleBuilder dependentModule = findDependentModule(modules, builder,
+ unknownTypePrefix);
+ TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilder(
+ dependentModule.getModuleTypedefs(), unknownTypeName);
+
+ TypeDefinitionBuilder lookedUpBuilderCopy = copyTypedefBuilder(
+ lookedUpBuilder, typeToResolve instanceof TypeDefinitionBuilder);
+ TypeDefinitionBuilder resolvedCopy = resolveCopiedBuilder(
+ lookedUpBuilderCopy, modules, dependentModule);
+ return resolvedCopy;
+ }
+
+ private TypeDefinitionBuilder findTypedefUnion(
+ TypeAwareBuilder typeToResolve, UnknownType unknownType,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+
+ TypeDefinition<?> baseTypeToResolve = typeToResolve.getType();
+ if (baseTypeToResolve != null
+ && !(baseTypeToResolve instanceof UnknownType)) {
+ return (TypeDefinitionBuilder) typeToResolve;
+ }
+
+ QName unknownTypeQName = unknownType.getQName();
+ String unknownTypeName = unknownTypeQName.getLocalName();
+ String unknownTypePrefix = unknownTypeQName.getPrefix();
+
+ // search for module which contains referenced typedef
+ ModuleBuilder dependentModule = findDependentModule(modules, builder,
+ unknownTypePrefix);
+ TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilder(
+ dependentModule.getModuleTypedefs(), unknownTypeName);
+
+ TypeDefinitionBuilder lookedUpBuilderCopy = copyTypedefBuilder(
+ lookedUpBuilder, typeToResolve instanceof TypeDefinitionBuilder);
+ TypeDefinitionBuilder resolvedCopy = resolveCopiedBuilder(
+ lookedUpBuilderCopy, modules, dependentModule);
+ return resolvedCopy;
+ }
+
+ private TypeDefinitionBuilder copyTypedefBuilder(TypeDefinitionBuilder old,
+ boolean seekByTypedefBuilder) {
+ if (old instanceof UnionTypeBuilder) {
+ UnionTypeBuilder oldUnion = (UnionTypeBuilder) old;
+ UnionTypeBuilder newUnion = new UnionTypeBuilder();
+ for (TypeDefinition<?> td : oldUnion.getTypes()) {
+ newUnion.setType(td);
+ }
+ for (TypeDefinitionBuilder tdb : oldUnion.getTypedefs()) {
+ newUnion.setType(copyTypedefBuilder(tdb, true));
+ }
+ return newUnion;
+ }
+
+ QName oldQName = old.getQName();
+ QName newQName = new QName(oldQName.getNamespace(),
+ oldQName.getRevision(), oldQName.getPrefix(),
+ oldQName.getLocalName());
+ TypeDefinitionBuilder tdb = new TypedefBuilder(newQName);
+
+ tdb.setRanges(old.getRanges());
+ tdb.setLengths(old.getLengths());
+ tdb.setPatterns(old.getPatterns());
+
+ TypeDefinition<?> oldType = old.getType();
+ if (oldType == null) {
+ tdb.setType(old.getTypedef());
+ } else {
+ tdb.setType(oldType);
+ }
+
+ if (!seekByTypedefBuilder) {
+ tdb.setDescription(old.getDescription());
+ tdb.setReference(old.getReference());
+ tdb.setStatus(old.getStatus());
+ tdb.setDefaultValue(old.getDefaultValue());
+ tdb.setUnits(old.getUnits());
+ }
+ return tdb;
+ }
+
+ private TypeDefinitionBuilder resolveCopiedBuilder(
+ TypeDefinitionBuilder copied,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+
+ if (copied instanceof UnionTypeBuilder) {
+ UnionTypeBuilder union = (UnionTypeBuilder) copied;
+ List<TypeDefinition<?>> unionTypes = union.getTypes();
+ List<UnknownType> toRemove = new ArrayList<UnknownType>();
+ for (TypeDefinition<?> td : unionTypes) {
+ if (td instanceof UnknownType) {
+ UnknownType unknownType = (UnknownType) td;
+ TypeDefinitionBuilder resolvedType = findTargetTypeUnion(
+ union, unknownType, modules, builder);
+ union.setType(resolvedType);
+ toRemove.add(unknownType);
+ }
+ }
+ unionTypes.removeAll(toRemove);
+
+ return union;
+ }
+
+ TypeDefinition<?> base = copied.getType();
+ TypeDefinitionBuilder baseTdb = copied.getTypedef();
+ if (base != null && !(base instanceof UnknownType)) {
+ return copied;
+ } else if (base instanceof UnknownType) {
+ UnknownType unknownType = (UnknownType) base;
+ QName unknownTypeQName = unknownType.getQName();
+ String unknownTypePrefix = unknownTypeQName.getPrefix();
+ ModuleBuilder dependentModule = findDependentModule(modules,
+ builder, unknownTypePrefix);
+ TypeDefinitionBuilder unknownTypeBuilder = findTypedef(copied,
+ modules, dependentModule);
+ copied.setType(unknownTypeBuilder);
+ return copied;
+ } else if (base == null && baseTdb != null) {
+ // make a copy of baseTypeDef and call again
+ TypeDefinitionBuilder baseTdbCopy = copyTypedefBuilder(baseTdb,
+ true);
+ TypeDefinitionBuilder baseTdbCopyResolved = resolveCopiedBuilder(
+ baseTdbCopy, modules, builder);
+ copied.setType(baseTdbCopyResolved);
+ return copied;
+ } else {
+ throw new IllegalStateException(
+ "TypeDefinitionBuilder in unexpected state");
+ }
+ }
+
+ private TypeDefinitionBuilder findTypedef(QName unknownTypeQName,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+
+ String unknownTypeName = unknownTypeQName.getLocalName();
+ String unknownTypePrefix = unknownTypeQName.getPrefix();
+
+ // search for module which contains referenced typedef
+ ModuleBuilder dependentModule = findDependentModule(modules, builder,
+ unknownTypePrefix);
+
+ TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilder(
+ dependentModule.getModuleTypedefs(), unknownTypeName);
+
+ TypeDefinitionBuilder copied = copyTypedefBuilder(lookedUpBuilder, true);
+ return copied;
+ }
+
+ private TypeConstraints findConstraints(TypeAwareBuilder typeToResolve,
+ TypeConstraints constraints,
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder builder) {
+
+ // union type cannot be restricted
+ if (typeToResolve instanceof UnionTypeBuilder) {
+ return constraints;
+ }
+
+ // if referenced type is UnknownType again, search recursively with
+ // current constraints
+ TypeDefinition<?> referencedType = typeToResolve.getType();
+ if (referencedType == null) {
+ TypeDefinitionBuilder tdb = (TypeDefinitionBuilder) typeToResolve;
+ final List<RangeConstraint> ranges = tdb.getRanges();
+ constraints.addRanges(ranges);
+ final List<LengthConstraint> lengths = tdb.getLengths();
+ constraints.addLengths(lengths);
+ final List<PatternConstraint> patterns = tdb.getPatterns();
+ constraints.addPatterns(patterns);
+ final Integer fractionDigits = tdb.getFractionDigits();
+ constraints.setFractionDigits(fractionDigits);
+ return constraints;
+ } else if (referencedType instanceof ExtendedType) {
+ ExtendedType ext = (ExtendedType) referencedType;
+ final List<RangeConstraint> ranges = ext.getRanges();
+ constraints.addRanges(ranges);
+ final List<LengthConstraint> lengths = ext.getLengths();
+ constraints.addLengths(lengths);
+ final List<PatternConstraint> patterns = ext.getPatterns();
+ constraints.addPatterns(patterns);
+ final Integer fractionDigits = ext.getFractionDigits();
+ constraints.setFractionDigits(fractionDigits);
+ return findConstraints(
+ findTypedef(ext.getQName(), modules, builder), constraints,
+ modules, builder);
+ } else if (referencedType instanceof UnknownType) {
+ UnknownType unknown = (UnknownType) referencedType;
+
+ final List<RangeConstraint> ranges = unknown.getRangeStatements();
+ constraints.addRanges(ranges);
+ final List<LengthConstraint> lengths = unknown
+ .getLengthStatements();
+ constraints.addLengths(lengths);
+ final List<PatternConstraint> patterns = unknown.getPatterns();
+ constraints.addPatterns(patterns);
+ final Integer fractionDigits = unknown.getFractionDigits();
+ constraints.setFractionDigits(fractionDigits);
+
+ String unknownTypePrefix = unknown.getQName().getPrefix();
+ if (unknownTypePrefix == null || "".equals(unknownTypePrefix)) {
+ unknownTypePrefix = builder.getPrefix();
+ }
+ ModuleBuilder dependentModule = findDependentModule(modules,
+ builder, unknown.getQName().getPrefix());
+ TypeDefinitionBuilder unknownTypeBuilder = findTypedef(
+ unknown.getQName(), modules, builder);
+ return findConstraints(unknownTypeBuilder, constraints, modules,
+ dependentModule);
+ } else {
+ // HANDLE BASE YANG TYPE
+ mergeConstraints(referencedType, constraints);
+ return constraints;
+ }
+
+ }
+
+ /**
+ * Go through all typedef statements from given module and search for one
+ * with given name
+ *
+ * @param typedefs
+ * typedef statements to search
+ * @param name
+ * name of searched typedef
+ * @return typedef with name equals to given name
+ */
+ private TypeDefinitionBuilder findTypedefBuilder(
+ Set<TypeDefinitionBuilder> typedefs, String name) {
+ TypeDefinitionBuilder result = null;
+ for (TypeDefinitionBuilder td : typedefs) {
+ if (td.getQName().getLocalName().equals(name)) {
+ result = td;
+ break;
+ }
+ }
+ if (result == null) {
+ throw new YangParseException(
+ "Target module does not contain typedef '" + name + "'.");
+ }
+ return result;
+ }
+
+ /**
+ * Pull restriction from referenced type and add them to given constraints
+ *
+ * @param referencedType
+ * @param constraints
+ */
+ private void mergeConstraints(TypeDefinition<?> referencedType,
+ TypeConstraints constraints) {
+
+ if (referencedType instanceof DecimalTypeDefinition) {
+ constraints.addRanges(((DecimalTypeDefinition) referencedType)
+ .getRangeStatements());
+ constraints
+ .setFractionDigits(((DecimalTypeDefinition) referencedType)
+ .getFractionDigits());
+ } else if (referencedType instanceof IntegerTypeDefinition) {
+ constraints.addRanges(((IntegerTypeDefinition) referencedType)
+ .getRangeStatements());
+ } else if (referencedType instanceof StringTypeDefinition) {
+ constraints.addPatterns(((StringTypeDefinition) referencedType)
+ .getPatterns());
+ constraints.addLengths(((StringTypeDefinition) referencedType)
+ .getLengthStatements());
+ } else if (referencedType instanceof BinaryTypeDefinition) {
+ constraints.addLengths(((BinaryTypeDefinition) referencedType)
+ .getLengthConstraints());
+ }
+ }
+
+ /**
+ * Go through all augmentation definitions and resolve them. This means find
+ * referenced node and add child nodes to it.
+ *
+ * @param modules
+ * all available modules
+ * @param module
+ * current module
+ */
+ private void resolveAugments(
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder module) {
+ Set<AugmentationSchemaBuilder> augmentBuilders = module
+ .getAddedAugments();
+
+ Set<AugmentationSchema> augments = new HashSet<AugmentationSchema>();
+ for (AugmentationSchemaBuilder augmentBuilder : augmentBuilders) {
+ SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath();
+ String prefix = null;
+ List<String> augmentTargetPath = new ArrayList<String>();
+
+ for (QName pathPart : augmentTargetSchemaPath.getPath()) {
+ prefix = pathPart.getPrefix();
+ augmentTargetPath.add(pathPart.getLocalName());
+ }
+ ModuleBuilder dependentModule = findDependentModule(modules,
+ module, prefix);
+ augmentTargetPath.add(0, dependentModule.getName());
+
+ AugmentationTargetBuilder augmentTarget = (AugmentationTargetBuilder) dependentModule
+ .getNode(augmentTargetPath);
+ AugmentationSchema result = augmentBuilder.build();
+ augmentTarget.addAugmentation(result);
+ fillAugmentTarget(augmentBuilder, (ChildNodeBuilder) augmentTarget);
+ augments.add(result);
+ }
+ module.setAugmentations(augments);
+ }
+
+ /**
+ * Add all augment's child nodes to given target.
+ *
+ * @param augment
+ * @param target
+ */
+ private void fillAugmentTarget(AugmentationSchemaBuilder augment,
+ ChildNodeBuilder target) {
+ for (DataSchemaNodeBuilder builder : augment.getChildNodes()) {
+ builder.setAugmenting(true);
+ target.addChildNode(builder);
+ }
+ }
+
+ /**
+ * Go through identity statements defined in current module and resolve
+ * their 'base' statement if present.
+ *
+ * @param modules
+ * all modules
+ * @param module
+ * module being resolved
+ */
+ private void resolveIdentities(
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder module) {
+ Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
+ for (IdentitySchemaNodeBuilder identity : identities) {
+ String baseIdentityName = identity.getBaseIdentityName();
+ if (baseIdentityName != null) {
+ String baseIdentityPrefix = null;
+ String baseIdentityLocalName = null;
+ if (baseIdentityName.contains(":")) {
+ String[] splitted = baseIdentityName.split(":");
+ baseIdentityPrefix = splitted[0];
+ baseIdentityLocalName = splitted[1];
+ } else {
+ baseIdentityPrefix = module.getPrefix();
+ baseIdentityLocalName = baseIdentityName;
+ }
+ ModuleBuilder dependentModule = findDependentModule(modules,
+ module, baseIdentityPrefix);
+
+ Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule
+ .getAddedIdentities();
+ for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
+ if (idBuilder.getQName().getLocalName()
+ .equals(baseIdentityLocalName)) {
+ identity.setBaseIdentity(idBuilder);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Find dependent module based on given prefix
+ *
+ * @param modules
+ * all available modules
+ * @param module
+ * current module
+ * @param prefix
+ * target module prefix
+ * @return
+ */
+ private ModuleBuilder findDependentModule(
+ Map<String, TreeMap<Date, ModuleBuilder>> modules,
+ ModuleBuilder module, String prefix) {
+ ModuleBuilder dependentModule = null;
+ Date dependentModuleRevision = null;
+
+ if (prefix.equals(module.getPrefix())) {
+ dependentModule = module;
+ } else {
+ ModuleImport dependentModuleImport = getModuleImport(module, prefix);
+ if (dependentModuleImport == null) {
+ throw new YangParseException("No import found with prefix '"
+ + prefix + "' in module " + module.getName() + "'.");
+ }
+ String dependentModuleName = dependentModuleImport.getModuleName();
+ dependentModuleRevision = dependentModuleImport.getRevision();
+
+ TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules
+ .get(dependentModuleName);
+ if (dependentModuleRevision == null) {
+ dependentModule = moduleBuildersByRevision.lastEntry()
+ .getValue();
+ } else {
+ dependentModule = moduleBuildersByRevision
+ .get(dependentModuleRevision);
+ }
+ }
+
+ if (dependentModule == null) {
+ throw new YangParseException(
+ "Failed to find dependent module with prefix '" + prefix
+ + "' and revision '" + dependentModuleRevision
+ + "'.");
+ }
+ return dependentModule;
+ }
+
+ /**
+ * Get module import referenced by given prefix.
+ *
+ * @param builder
+ * module to search
+ * @param prefix
+ * prefix associated with import
+ * @return ModuleImport based on given prefix
+ */
+ private ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
+ ModuleImport moduleImport = null;
+ for (ModuleImport mi : builder.getModuleImports()) {
+ if (mi.getPrefix().equals(prefix)) {
+ moduleImport = mi;
+ break;
+ }
+ }
+ return moduleImport;
+ }
+
+ private Date createEpochTime() {
+ Calendar calendar = Calendar.getInstance();
+ calendar.setTimeInMillis(0);
+ return calendar.getTime();
+ }
+
+ private static class SchemaContextImpl implements SchemaContext {
+ private final Set<Module> modules;
+
+ private SchemaContextImpl(Set<Module> modules) {
+ this.modules = modules;
+ }
+
+ @Override
+ public Set<DataSchemaNode> getDataDefinitions() {
+ final Set<DataSchemaNode> dataDefs = new HashSet<DataSchemaNode>();
+ for (Module m : modules) {
+ dataDefs.addAll(m.getChildNodes());
+ }
+ return dataDefs;
+ }
+
+ @Override
+ public Set<Module> getModules() {
+ return modules;
+ }
+
+ @Override
+ public Set<NotificationDefinition> getNotifications() {
+ final Set<NotificationDefinition> notifications = new HashSet<NotificationDefinition>();
+ for (Module m : modules) {
+ notifications.addAll(m.getNotifications());
+ }
+ return notifications;
+ }
+
+ @Override
+ public Set<RpcDefinition> getOperations() {
+ final Set<RpcDefinition> rpcs = new HashSet<RpcDefinition>();
+ for (Module m : modules) {
+ rpcs.addAll(m.getRpcs());
+ }
+ return rpcs;
+ }
+
+ @Override
+ public Set<ExtensionDefinition> getExtensions() {
+ final Set<ExtensionDefinition> extensions = new HashSet<ExtensionDefinition>();
+ for (Module m : modules) {
+ extensions.addAll(m.getExtensionSchemaNodes());
+ }
+ return extensions;
+ }
+ }
+
+}