/* * 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.IOException; 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.model.util.UnknownType; 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.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.ModuleBuilder; 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(String yangFile) { final Map> modules = loadFiles(yangFile); Set result = build(modules); return result.iterator().next(); } @Override public Set parseYangModels(String... yangFiles) { final Map> modules = loadFiles(yangFiles); Set result = build(modules); return result; } @Override public SchemaContext resolveSchemaContext(Set modules) { return new SchemaContextImpl(modules); } private Set build(Map> modules) { // first validate for (Map.Entry> entry : modules.entrySet()) { for (Map.Entry childEntry : entry.getValue().entrySet()) { ModuleBuilder moduleBuilder = childEntry.getValue(); validateBuilder(modules, moduleBuilder); } } // then build final Set result = new HashSet(); for (Map.Entry> entry : modules.entrySet()) { final Map modulesByRevision = new HashMap(); for (Map.Entry childEntry : entry.getValue().entrySet()) { ModuleBuilder moduleBuilder = childEntry.getValue(); modulesByRevision.put(childEntry.getKey(),moduleBuilder.build()); result.add(moduleBuilder.build()); } } return result; } private void validateBuilder(Map> modules, ModuleBuilder builder) { resolveTypedefs(modules, builder); resolveAugments(modules, builder); } private void resolveTypedefs(Map> modules, ModuleBuilder builder) { Map, TypeAwareBuilder> dirtyNodes = builder.getDirtyNodes(); if (dirtyNodes.size() == 0) { return; } else { for (Map.Entry, TypeAwareBuilder> entry : dirtyNodes.entrySet()) { TypeAwareBuilder tab = entry.getValue(); TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(modules,entry.getValue(), builder); tab.setType(tdb.build()); } } } private TypeDefinitionBuilder findTypeDefinitionBuilder(Map> modules, TypeAwareBuilder typeBuilder, ModuleBuilder builder) { UnknownType type = (UnknownType) typeBuilder.getType(); QName typeQName = type.getQName(); String typeName = type.getQName().getLocalName(); String prefix = typeQName.getPrefix(); ModuleBuilder dependentModuleBuilder; if (prefix.equals(builder.getPrefix())) { dependentModuleBuilder = builder; } else { ModuleImport dependentModuleImport = getDependentModuleImport(builder, prefix); String dependentModuleName = dependentModuleImport.getModuleName(); Date dependentModuleRevision = dependentModuleImport.getRevision(); TreeMap moduleBuildersByRevision = modules.get(dependentModuleName); if(dependentModuleRevision == null) { dependentModuleBuilder = moduleBuildersByRevision.lastEntry().getValue(); } else { dependentModuleBuilder = moduleBuildersByRevision.get(dependentModuleRevision); } } final Set typedefs = dependentModuleBuilder.getModuleTypedefs(); TypeDefinitionBuilder lookedUpBuilder = null; for (TypeDefinitionBuilder tdb : typedefs) { QName qname = tdb.getQName(); if (qname.getLocalName().equals(typeName)) { lookedUpBuilder = tdb; break; } } if (lookedUpBuilder.getBaseType() instanceof UnknownType) { return findTypeDefinitionBuilder(modules, (TypeAwareBuilder) lookedUpBuilder, dependentModuleBuilder); } else { return lookedUpBuilder; } } private void resolveAugments(Map> modules, ModuleBuilder builder) { Set augmentBuilders = builder.getAddedAugments(); Set augments = new HashSet(); for (AugmentationSchemaBuilder augmentBuilder : augmentBuilders) { SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath(); String prefix = null; List augmentTargetPath = new ArrayList(); for (QName pathPart : augmentTargetSchemaPath.getPath()) { prefix = pathPart.getPrefix(); augmentTargetPath.add(pathPart.getLocalName()); } ModuleImport dependentModuleImport = getDependentModuleImport(builder, prefix); String dependentModuleName = dependentModuleImport.getModuleName(); augmentTargetPath.add(0, dependentModuleName); Date dependentModuleRevision = dependentModuleImport.getRevision(); TreeMap moduleBuildersByRevision = modules.get(dependentModuleName); ModuleBuilder dependentModule; if(dependentModuleRevision == null) { dependentModule = moduleBuildersByRevision.lastEntry().getValue(); } else { dependentModule = moduleBuildersByRevision.get(dependentModuleRevision); } AugmentationTargetBuilder augmentTarget = (AugmentationTargetBuilder) dependentModule.getNode(augmentTargetPath); AugmentationSchema result = augmentBuilder.build(); augmentTarget.addAugmentation(result); fillAugmentTarget(augmentBuilder, (ChildNodeBuilder) augmentTarget); augments.add(result); } builder.setAugmentations(augments); } private void fillAugmentTarget(AugmentationSchemaBuilder augment, ChildNodeBuilder target) { for (DataSchemaNodeBuilder builder : augment.getChildNodes()) { builder.setAugmenting(true); target.addChildNode(builder); } } private Map> loadFiles(String... yangFiles) { final Map> modules = new HashMap>(); final YangModelParserListenerImpl yangModelParser = new YangModelParserListenerImpl(); final ParseTreeWalker walker = new ParseTreeWalker(); List trees = parseFiles(yangFiles); ModuleBuilder[] builders = new ModuleBuilder[trees.size()]; for (int i = 0; i < trees.size(); i++) { 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 builderByRevision = modules.get(builderName); if (builderByRevision == null) { builderByRevision = new TreeMap(); } builderByRevision.put(builderRevision, builder); modules.put(builderName, builderByRevision); } return modules; } private List parseFiles(String... yangFileNames) { List trees = new ArrayList(); for (String fileName : yangFileNames) { trees.add(parseFile(fileName)); } return trees; } private ParseTree parseFile(String yangFileName) { ParseTree result = null; try { final File yangFile = new File(yangFileName); final FileInputStream inStream = new FileInputStream(yangFile); final ANTLRInputStream input = new ANTLRInputStream(inStream); 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: " + yangFileName, e); } return result; } private ModuleImport getDependentModuleImport(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 c = Calendar.getInstance(); c.setTimeInMillis(0); return c.getTime(); } private static class SchemaContextImpl implements SchemaContext { private final Set modules; private SchemaContextImpl(Set modules) { this.modules = modules; } @Override public Set getDataDefinitions() { final Set dataDefs = new HashSet(); for (Module m : modules) { dataDefs.addAll(m.getChildNodes()); } return dataDefs; } @Override public Set getModules() { return modules; } @Override public Set getNotifications() { final Set notifications = new HashSet(); for (Module m : modules) { notifications.addAll(m.getNotifications()); } return notifications; } @Override public Set getOperations() { final Set rpcs = new HashSet(); for (Module m : modules) { rpcs.addAll(m.getRpcs()); } return rpcs; } @Override public Set getExtensions() { final Set extensions = new HashSet(); for (Module m : modules) { extensions.addAll(m.getExtensionSchemaNodes()); } return extensions; } } }