2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.controller.yang.model.parser.impl;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.util.ArrayList;
16 import java.util.Calendar;
17 import java.util.Collections;
18 import java.util.Date;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
24 import java.util.TreeMap;
26 import org.antlr.v4.runtime.ANTLRInputStream;
27 import org.antlr.v4.runtime.CommonTokenStream;
28 import org.antlr.v4.runtime.tree.ParseTree;
29 import org.antlr.v4.runtime.tree.ParseTreeWalker;
30 import org.opendaylight.controller.antlrv4.code.gen.YangLexer;
31 import org.opendaylight.controller.antlrv4.code.gen.YangParser;
32 import org.opendaylight.controller.yang.common.QName;
33 import org.opendaylight.controller.yang.model.api.AugmentationSchema;
34 import org.opendaylight.controller.yang.model.api.DataSchemaNode;
35 import org.opendaylight.controller.yang.model.api.ExtensionDefinition;
36 import org.opendaylight.controller.yang.model.api.Module;
37 import org.opendaylight.controller.yang.model.api.ModuleImport;
38 import org.opendaylight.controller.yang.model.api.NotificationDefinition;
39 import org.opendaylight.controller.yang.model.api.RpcDefinition;
40 import org.opendaylight.controller.yang.model.api.SchemaContext;
41 import org.opendaylight.controller.yang.model.api.SchemaPath;
42 import org.opendaylight.controller.yang.model.api.TypeDefinition;
43 import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;
44 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition;
45 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition.Bit;
46 import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;
47 import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;
48 import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
49 import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
50 import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
51 import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;
52 import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
53 import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationSchemaBuilder;
54 import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationTargetBuilder;
55 import org.opendaylight.controller.yang.model.parser.builder.api.ChildNodeBuilder;
56 import org.opendaylight.controller.yang.model.parser.builder.api.DataSchemaNodeBuilder;
57 import org.opendaylight.controller.yang.model.parser.builder.api.TypeAwareBuilder;
58 import org.opendaylight.controller.yang.model.parser.builder.api.TypeDefinitionBuilder;
59 import org.opendaylight.controller.yang.model.parser.builder.impl.IdentitySchemaNodeBuilder;
60 import org.opendaylight.controller.yang.model.parser.builder.impl.ModuleBuilder;
61 import org.opendaylight.controller.yang.model.parser.builder.impl.UnionTypeBuilder;
62 import org.opendaylight.controller.yang.model.parser.util.TypeConstraints;
63 import org.opendaylight.controller.yang.model.parser.util.YangParseException;
64 import org.opendaylight.controller.yang.model.util.BinaryType;
65 import org.opendaylight.controller.yang.model.util.BitsType;
66 import org.opendaylight.controller.yang.model.util.StringType;
67 import org.opendaylight.controller.yang.model.util.UnknownType;
68 import org.opendaylight.controller.yang.model.util.YangTypesConverter;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
72 public class YangModelParserImpl implements YangModelParser {
74 private static final Logger logger = LoggerFactory
75 .getLogger(YangModelParserImpl.class);
78 public Module parseYangModel(String yangFile) {
79 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFile);
80 Set<Module> result = build(modules);
81 return result.iterator().next();
85 public Set<Module> parseYangModels(String... yangFiles) {
86 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFiles);
87 Set<Module> result = build(modules);
92 public Set<Module> parseYangModelsFromStreams(
93 InputStream... yangModelStreams) {
94 final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangModelStreams);
95 Set<Module> result = build(modules);
100 public SchemaContext resolveSchemaContext(Set<Module> modules) {
101 return new SchemaContextImpl(modules);
104 private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(
105 String... yangFiles) {
106 InputStream[] streams = new InputStream[yangFiles.length];
107 for (int i = 0; i < yangFiles.length; i++) {
108 final String yangFileName = yangFiles[i];
109 final File yangFile = new File(yangFileName);
110 FileInputStream inStream = null;
112 inStream = new FileInputStream(yangFile);
113 } catch (FileNotFoundException e) {
114 logger.warn("Exception while reading yang stream: " + inStream,
117 streams[i] = inStream;
119 return resolveModuleBuildersFromStreams(streams);
122 private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(
123 InputStream... yangFiles) {
124 final Map<String, TreeMap<Date, ModuleBuilder>> modules = new HashMap<String, TreeMap<Date, ModuleBuilder>>();
125 final ParseTreeWalker walker = new ParseTreeWalker();
126 final List<ParseTree> trees = parseStreams(yangFiles);
127 final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
129 for (int i = 0; i < trees.size(); i++) {
130 final YangModelParserListenerImpl yangModelParser = new YangModelParserListenerImpl();
131 walker.walk(yangModelParser, trees.get(i));
132 builders[i] = yangModelParser.getModuleBuilder();
135 for (ModuleBuilder builder : builders) {
136 final String builderName = builder.getName();
137 Date builderRevision = builder.getRevision();
138 if (builderRevision == null) {
139 builderRevision = createEpochTime();
141 TreeMap<Date, ModuleBuilder> builderByRevision = modules
143 if (builderByRevision == null) {
144 builderByRevision = new TreeMap<Date, ModuleBuilder>();
146 builderByRevision.put(builderRevision, builder);
147 modules.put(builderName, builderByRevision);
152 private List<ParseTree> parseStreams(InputStream... yangStreams) {
153 List<ParseTree> trees = new ArrayList<ParseTree>();
154 for (InputStream yangStream : yangStreams) {
155 trees.add(parseStream(yangStream));
160 private ParseTree parseStream(InputStream yangStream) {
161 ParseTree result = null;
163 final ANTLRInputStream input = new ANTLRInputStream(yangStream);
164 final YangLexer lexer = new YangLexer(input);
165 final CommonTokenStream tokens = new CommonTokenStream(lexer);
166 final YangParser parser = new YangParser(tokens);
167 result = parser.yang();
168 } catch (IOException e) {
169 logger.warn("Exception while reading yang file: " + yangStream, e);
174 private Set<Module> build(Map<String, TreeMap<Date, ModuleBuilder>> modules) {
176 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
178 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
180 ModuleBuilder moduleBuilder = childEntry.getValue();
181 validateBuilder(modules, moduleBuilder);
185 final Set<Module> result = new HashSet<Module>();
186 for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
188 final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
189 for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
191 ModuleBuilder moduleBuilder = childEntry.getValue();
192 modulesByRevision.put(childEntry.getKey(),
193 moduleBuilder.build());
194 result.add(moduleBuilder.build());
201 private void validateBuilder(
202 Map<String, TreeMap<Date, ModuleBuilder>> modules,
203 ModuleBuilder builder) {
204 resolveTypedefs(modules, builder);
205 resolveAugments(modules, builder);
206 resolveIdentities(modules, builder);
210 * Search for dirty nodes (node which contains UnknownType) and resolve
214 * all available modules
218 private void resolveTypedefs(
219 Map<String, TreeMap<Date, ModuleBuilder>> modules,
220 ModuleBuilder module) {
221 Map<List<String>, TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
222 if (dirtyNodes.size() == 0) {
225 for (Map.Entry<List<String>, TypeAwareBuilder> entry : dirtyNodes
227 TypeAwareBuilder typeToResolve = entry.getValue();
229 if (typeToResolve instanceof UnionTypeBuilder) {
230 resolveUnionTypeBuilder(modules, module,
231 (UnionTypeBuilder) typeToResolve);
233 UnknownType ut = (UnknownType) typeToResolve.getType();
234 TypeDefinition<?> resolvedType = findTargetType(ut,
236 typeToResolve.setType(resolvedType);
242 private UnionTypeBuilder resolveUnionTypeBuilder(
243 Map<String, TreeMap<Date, ModuleBuilder>> modules,
244 ModuleBuilder builder, UnionTypeBuilder unionTypeBuilderToResolve) {
245 List<TypeDefinition<?>> resolvedTypes = new ArrayList<TypeDefinition<?>>();
246 List<TypeDefinition<?>> typesToRemove = new ArrayList<TypeDefinition<?>>();
248 for (TypeDefinition<?> td : unionTypeBuilderToResolve.getTypes()) {
249 if (td instanceof UnknownType) {
250 TypeDefinition<?> resolvedType = findTargetType(
251 (UnknownType) td, modules, builder);
252 resolvedTypes.add(resolvedType);
253 typesToRemove.add(td);
257 List<TypeDefinition<?>> unionTypeBuilderTypes = unionTypeBuilderToResolve
259 unionTypeBuilderTypes.addAll(resolvedTypes);
260 unionTypeBuilderTypes.removeAll(typesToRemove);
262 return unionTypeBuilderToResolve;
265 private TypeDefinition<?> findTargetType(UnknownType ut,
266 Map<String, TreeMap<Date, ModuleBuilder>> modules,
267 ModuleBuilder builder) {
269 TypeConstraints constraints = new TypeConstraints();
271 List<RangeConstraint> ranges = ut.getRangeStatements();
272 constraints.addRanges(ranges);
274 List<LengthConstraint> lengths = ut.getLengthStatements();
275 constraints.addLengths(lengths);
277 List<PatternConstraint> patterns = ut.getPatterns();
278 constraints.addPatterns(patterns);
280 Integer fractionDigits = ut.getFractionDigits();
282 Map<TypeDefinitionBuilder, TypeConstraints> foundedTypeDefinitionBuilder = findTypeDefinitionBuilderWithConstraints(
283 constraints, modules, ut, builder);
284 TypeDefinitionBuilder targetType = foundedTypeDefinitionBuilder
285 .entrySet().iterator().next().getKey();
287 TypeDefinition<?> targetTypeBaseType = targetType.getBaseType();
288 targetTypeBaseType = mergeConstraints(targetTypeBaseType, constraints,
291 return targetTypeBaseType;
295 * Traverse through all referenced types chain until base YANG type is
299 * current type constraints
301 * all available modules
306 * @return map, where key is type referenced and value is its constraints
308 private Map<TypeDefinitionBuilder, TypeConstraints> findTypeDefinitionBuilderWithConstraints(
309 TypeConstraints constraints,
310 Map<String, TreeMap<Date, ModuleBuilder>> modules,
311 UnknownType unknownType, ModuleBuilder builder) {
312 Map<TypeDefinitionBuilder, TypeConstraints> result = new HashMap<TypeDefinitionBuilder, TypeConstraints>();
313 QName unknownTypeQName = unknownType.getQName();
314 String unknownTypeName = unknownTypeQName.getLocalName();
315 String unknownTypePrefix = unknownTypeQName.getPrefix();
317 // search for module which contains referenced typedef
318 ModuleBuilder dependentModuleBuilder = findDependentModule(modules,
319 builder, unknownTypePrefix);
321 TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilder(
322 dependentModuleBuilder.getModuleTypedefs(), unknownTypeName);
324 // if referenced type is UnknownType again, search recursively with
325 // current constraints
326 TypeDefinition<?> referencedType = lookedUpBuilder.getBaseType();
327 if (referencedType instanceof UnknownType) {
328 UnknownType unknown = (UnknownType) referencedType;
330 final List<RangeConstraint> ranges = unknown.getRangeStatements();
331 constraints.addRanges(ranges);
332 final List<LengthConstraint> lengths = unknown
333 .getLengthStatements();
334 constraints.addLengths(lengths);
335 final List<PatternConstraint> patterns = unknown.getPatterns();
336 constraints.addPatterns(patterns);
337 return findTypeDefinitionBuilderWithConstraints(constraints,
338 modules, unknown, dependentModuleBuilder);
340 mergeConstraints(referencedType, constraints);
341 result.put(lookedUpBuilder, constraints);
347 * Go through all typedef statements from given module and search for one
351 * typedef statements to search
353 * name of searched typedef
354 * @return typedef with name equals to given name
356 private TypeDefinitionBuilder findTypedefBuilder(
357 Set<TypeDefinitionBuilder> typedefs, String name) {
358 TypeDefinitionBuilder result = null;
359 for (TypeDefinitionBuilder td : typedefs) {
360 if (td.getQName().getLocalName().equals(name)) {
365 if (result == null) {
366 throw new YangParseException(
367 "Target module does not contain typedef '" + name + "'.");
373 * Merge curent constraints with founded type constraints
375 * @param targetTypeBaseType
377 * @param fractionDigits
380 private TypeDefinition<?> mergeConstraints(
381 TypeDefinition<?> targetTypeBaseType, TypeConstraints constraints,
382 Integer fractionDigits) {
383 String targetTypeBaseTypeName = targetTypeBaseType.getQName()
385 // enumeration, leafref and identityref omitted because they have no
387 if (targetTypeBaseType instanceof DecimalTypeDefinition) {
388 List<RangeConstraint> ranges = constraints.getRange();
389 Integer fd = fractionDigits == null ? constraints
390 .getFractionDigits() : fractionDigits;
391 targetTypeBaseType = YangTypesConverter
392 .javaTypeForBaseYangDecimal64Type(ranges, fd);
393 } else if (targetTypeBaseType instanceof IntegerTypeDefinition) {
394 List<RangeConstraint> ranges = constraints.getRange();
395 if (targetTypeBaseTypeName.startsWith("int")) {
396 targetTypeBaseType = YangTypesConverter
397 .javaTypeForBaseYangSignedIntegerType(
398 targetTypeBaseTypeName, ranges);
400 targetTypeBaseType = YangTypesConverter
401 .javaTypeForBaseYangUnsignedIntegerType(
402 targetTypeBaseTypeName, ranges);
404 } else if (targetTypeBaseType instanceof StringTypeDefinition) {
405 List<LengthConstraint> lengths = constraints.getLength();
406 List<PatternConstraint> patterns = constraints.getPatterns();
407 targetTypeBaseType = new StringType(lengths, patterns);
408 } else if (targetTypeBaseType instanceof BitsTypeDefinition) {
409 BitsTypeDefinition bitsType = (BitsTypeDefinition) targetTypeBaseType;
410 List<Bit> bits = bitsType.getBits();
411 targetTypeBaseType = new BitsType(bits);
412 } else if (targetTypeBaseType instanceof BinaryTypeDefinition) {
413 List<LengthConstraint> lengths = constraints.getLength();
414 List<Byte> bytes = Collections.emptyList();
415 targetTypeBaseType = new BinaryType(bytes, lengths, null);
417 return targetTypeBaseType;
421 * Pull restriction from base type and add them to given constraints
423 * @param referencedType
426 private void mergeConstraints(TypeDefinition<?> referencedType,
427 TypeConstraints constraints) {
429 if (referencedType instanceof DecimalTypeDefinition) {
430 constraints.addRanges(((DecimalTypeDefinition) referencedType)
431 .getRangeStatements());
433 .setFractionDigits(((DecimalTypeDefinition) referencedType)
434 .getFractionDigits());
435 } else if (referencedType instanceof IntegerTypeDefinition) {
436 constraints.addRanges(((IntegerTypeDefinition) referencedType)
437 .getRangeStatements());
438 } else if (referencedType instanceof StringTypeDefinition) {
439 constraints.addPatterns(((StringTypeDefinition) referencedType)
441 constraints.addLengths(((StringTypeDefinition) referencedType)
442 .getLengthStatements());
443 } else if (referencedType instanceof BinaryTypeDefinition) {
444 constraints.addLengths(((BinaryTypeDefinition) referencedType)
445 .getLengthConstraints());
450 * Go through all augmentation definitions and resolve them. This means find
451 * referenced node and add child nodes to it.
454 * all available modules
458 private void resolveAugments(
459 Map<String, TreeMap<Date, ModuleBuilder>> modules,
460 ModuleBuilder module) {
461 Set<AugmentationSchemaBuilder> augmentBuilders = module
464 Set<AugmentationSchema> augments = new HashSet<AugmentationSchema>();
465 for (AugmentationSchemaBuilder augmentBuilder : augmentBuilders) {
466 SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath();
467 String prefix = null;
468 List<String> augmentTargetPath = new ArrayList<String>();
470 for (QName pathPart : augmentTargetSchemaPath.getPath()) {
471 prefix = pathPart.getPrefix();
472 augmentTargetPath.add(pathPart.getLocalName());
474 ModuleBuilder dependentModule = findDependentModule(modules,
476 augmentTargetPath.add(0, dependentModule.getName());
478 AugmentationTargetBuilder augmentTarget = (AugmentationTargetBuilder) dependentModule
479 .getNode(augmentTargetPath);
480 AugmentationSchema result = augmentBuilder.build();
481 augmentTarget.addAugmentation(result);
482 fillAugmentTarget(augmentBuilder, (ChildNodeBuilder) augmentTarget);
483 augments.add(result);
485 module.setAugmentations(augments);
489 * Add all augment's child nodes to given target.
494 private void fillAugmentTarget(AugmentationSchemaBuilder augment,
495 ChildNodeBuilder target) {
496 for (DataSchemaNodeBuilder builder : augment.getChildNodes()) {
497 builder.setAugmenting(true);
498 target.addChildNode(builder);
503 * Go through identity statements defined in current module and resolve
504 * their 'base' statement if present.
509 * module being resolved
511 private void resolveIdentities(
512 Map<String, TreeMap<Date, ModuleBuilder>> modules,
513 ModuleBuilder module) {
514 Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
515 for (IdentitySchemaNodeBuilder identity : identities) {
516 String baseIdentityName = identity.getBaseIdentityName();
517 if (baseIdentityName != null) {
518 String baseIdentityPrefix = null;
519 String baseIdentityLocalName = null;
520 if (baseIdentityName.contains(":")) {
521 String[] splitted = baseIdentityName.split(":");
522 baseIdentityPrefix = splitted[0];
523 baseIdentityLocalName = splitted[1];
525 baseIdentityPrefix = module.getPrefix();
526 baseIdentityLocalName = baseIdentityName;
528 ModuleBuilder dependentModule = findDependentModule(modules,
529 module, baseIdentityPrefix);
531 Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule
532 .getAddedIdentities();
533 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
534 if (idBuilder.getQName().getLocalName()
535 .equals(baseIdentityLocalName)) {
536 identity.setBaseIdentity(idBuilder);
544 * Find dependent module based on given prefix
547 * all available modules
551 * target module prefix
554 private ModuleBuilder findDependentModule(
555 Map<String, TreeMap<Date, ModuleBuilder>> modules,
556 ModuleBuilder module, String prefix) {
557 ModuleBuilder dependentModule = null;
558 Date dependentModuleRevision = null;
560 if (prefix.equals(module.getPrefix())) {
561 dependentModule = module;
563 ModuleImport dependentModuleImport = getModuleImport(module, prefix);
564 if (dependentModuleImport == null) {
565 throw new YangParseException("No import found with prefix '"
566 + prefix + "' in module " + module.getName() + "'.");
568 String dependentModuleName = dependentModuleImport.getModuleName();
569 dependentModuleRevision = dependentModuleImport.getRevision();
571 TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules
572 .get(dependentModuleName);
573 if (dependentModuleRevision == null) {
574 dependentModule = moduleBuildersByRevision.lastEntry()
577 dependentModule = moduleBuildersByRevision
578 .get(dependentModuleRevision);
582 if (dependentModule == null) {
583 throw new YangParseException(
584 "Failed to find dependent module with prefix '" + prefix
585 + "' and revision '" + dependentModuleRevision
588 return dependentModule;
592 * Get module import referenced by given prefix.
597 * prefix associated with import
598 * @return ModuleImport based on given prefix
600 private ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
601 ModuleImport moduleImport = null;
602 for (ModuleImport mi : builder.getModuleImports()) {
603 if (mi.getPrefix().equals(prefix)) {
611 private Date createEpochTime() {
612 Calendar c = Calendar.getInstance();
613 c.setTimeInMillis(0);
617 private static class SchemaContextImpl implements SchemaContext {
618 private final Set<Module> modules;
620 private SchemaContextImpl(Set<Module> modules) {
621 this.modules = modules;
625 public Set<DataSchemaNode> getDataDefinitions() {
626 final Set<DataSchemaNode> dataDefs = new HashSet<DataSchemaNode>();
627 for (Module m : modules) {
628 dataDefs.addAll(m.getChildNodes());
634 public Set<Module> getModules() {
639 public Set<NotificationDefinition> getNotifications() {
640 final Set<NotificationDefinition> notifications = new HashSet<NotificationDefinition>();
641 for (Module m : modules) {
642 notifications.addAll(m.getNotifications());
644 return notifications;
648 public Set<RpcDefinition> getOperations() {
649 final Set<RpcDefinition> rpcs = new HashSet<RpcDefinition>();
650 for (Module m : modules) {
651 rpcs.addAll(m.getRpcs());
657 public Set<ExtensionDefinition> getExtensions() {
658 final Set<ExtensionDefinition> extensions = new HashSet<ExtensionDefinition>();
659 for (Module m : modules) {
660 extensions.addAll(m.getExtensionSchemaNodes());