Added support for resolving augmentations.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / yang-model-parser-impl / src / main / java / org / opendaylight / controller / yang / parser / impl / YangParserImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.yang.parser.impl;
9
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.net.URI;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Date;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.LinkedHashMap;
23 import java.util.LinkedHashSet;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.NoSuchElementException;
27 import java.util.Set;
28 import java.util.TreeMap;
29
30 import org.antlr.v4.runtime.ANTLRInputStream;
31 import org.antlr.v4.runtime.CommonTokenStream;
32 import org.antlr.v4.runtime.tree.ParseTree;
33 import org.antlr.v4.runtime.tree.ParseTreeWalker;
34 import org.opendaylight.controller.antlrv4.code.gen.YangLexer;
35 import org.opendaylight.controller.antlrv4.code.gen.YangParser;
36 import org.opendaylight.controller.yang.common.QName;
37 import org.opendaylight.controller.yang.model.api.DataSchemaNode;
38 import org.opendaylight.controller.yang.model.api.ExtensionDefinition;
39 import org.opendaylight.controller.yang.model.api.Module;
40 import org.opendaylight.controller.yang.model.api.ModuleImport;
41 import org.opendaylight.controller.yang.model.api.MustDefinition;
42 import org.opendaylight.controller.yang.model.api.NotificationDefinition;
43 import org.opendaylight.controller.yang.model.api.RpcDefinition;
44 import org.opendaylight.controller.yang.model.api.SchemaContext;
45 import org.opendaylight.controller.yang.model.api.SchemaPath;
46 import org.opendaylight.controller.yang.model.api.TypeDefinition;
47 import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;
48 import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;
49 import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;
50 import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
51 import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
52 import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
53 import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;
54 import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
55 import org.opendaylight.controller.yang.model.util.ExtendedType;
56 import org.opendaylight.controller.yang.model.util.IdentityrefType;
57 import org.opendaylight.controller.yang.model.util.UnknownType;
58 import org.opendaylight.controller.yang.parser.builder.api.AugmentationSchemaBuilder;
59 import org.opendaylight.controller.yang.parser.builder.api.AugmentationTargetBuilder;
60 import org.opendaylight.controller.yang.parser.builder.api.Builder;
61 import org.opendaylight.controller.yang.parser.builder.api.ChildNodeBuilder;
62 import org.opendaylight.controller.yang.parser.builder.api.DataSchemaNodeBuilder;
63 import org.opendaylight.controller.yang.parser.builder.api.GroupingBuilder;
64 import org.opendaylight.controller.yang.parser.builder.api.TypeAwareBuilder;
65 import org.opendaylight.controller.yang.parser.builder.api.TypeDefinitionBuilder;
66 import org.opendaylight.controller.yang.parser.builder.api.UsesNodeBuilder;
67 import org.opendaylight.controller.yang.parser.builder.impl.AnyXmlBuilder;
68 import org.opendaylight.controller.yang.parser.builder.impl.ChoiceBuilder;
69 import org.opendaylight.controller.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
70 import org.opendaylight.controller.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
71 import org.opendaylight.controller.yang.parser.builder.impl.IdentityrefTypeBuilder;
72 import org.opendaylight.controller.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
73 import org.opendaylight.controller.yang.parser.builder.impl.LeafSchemaNodeBuilder;
74 import org.opendaylight.controller.yang.parser.builder.impl.ListSchemaNodeBuilder;
75 import org.opendaylight.controller.yang.parser.builder.impl.ModuleBuilder;
76 import org.opendaylight.controller.yang.parser.builder.impl.TypedefBuilder;
77 import org.opendaylight.controller.yang.parser.builder.impl.UnionTypeBuilder;
78 import org.opendaylight.controller.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
79 import org.opendaylight.controller.yang.parser.util.ModuleDependencySort;
80 import org.opendaylight.controller.yang.parser.util.ParserUtils;
81 import org.opendaylight.controller.yang.parser.util.RefineHolder;
82 import org.opendaylight.controller.yang.parser.util.TypeConstraints;
83 import org.opendaylight.controller.yang.parser.util.YangParseException;
84 import org.opendaylight.controller.yang.validator.YangModelBasicValidator;
85 import org.slf4j.Logger;
86 import org.slf4j.LoggerFactory;
87
88 public class YangParserImpl implements YangModelParser {
89
90     private static final Logger logger = LoggerFactory
91             .getLogger(YangParserImpl.class);
92
93     @Override
94     public Set<Module> parseYangModels(final List<File> yangFiles) {
95         if (yangFiles != null) {
96             final List<InputStream> inputStreams = new ArrayList<InputStream>();
97
98             for (final File yangFile : yangFiles) {
99                 try {
100                     inputStreams.add(new FileInputStream(yangFile));
101                 } catch (FileNotFoundException e) {
102                     logger.warn("Exception while reading yang file: "
103                             + yangFile.getName(), e);
104                 }
105             }
106             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(inputStreams);
107             return build(modules);
108         }
109         return Collections.emptySet();
110     }
111
112     @Override
113     public Set<Module> parseYangModelsFromStreams(
114             final List<InputStream> yangModelStreams) {
115         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams);
116         return build(modules);
117     }
118
119     @Override
120     public SchemaContext resolveSchemaContext(final Set<Module> modules) {
121         return new SchemaContextImpl(modules);
122     }
123
124     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(
125             final List<InputStream> yangFileStreams) {
126         //Linked Hash Map MUST be used because Linked Hash Map preserves ORDER of items stored in map.
127         final Map<String, TreeMap<Date, ModuleBuilder>> modules = new LinkedHashMap<String, TreeMap<Date, ModuleBuilder>>();
128         final ParseTreeWalker walker = new ParseTreeWalker();
129         final List<ParseTree> trees = parseStreams(yangFileStreams);
130         final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
131
132         // validate yang
133         new YangModelBasicValidator(walker).validate(trees);
134
135         YangParserListenerImpl yangModelParser = null;
136         for (int i = 0; i < trees.size(); i++) {
137             yangModelParser = new YangParserListenerImpl();
138             walker.walk(yangModelParser, trees.get(i));
139             builders[i] = yangModelParser.getModuleBuilder();
140         }
141
142         // module dependency graph sorted
143         List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
144         
145         for (ModuleBuilder builder : sorted) {
146             final String builderName = builder.getName();
147             Date builderRevision = builder.getRevision();
148             if (builderRevision == null) {
149                 builderRevision = new Date(0L);
150             }
151             TreeMap<Date, ModuleBuilder> builderByRevision = modules
152                     .get(builderName);
153             if (builderByRevision == null) {
154                 builderByRevision = new TreeMap<Date, ModuleBuilder>();
155             }
156             builderByRevision.put(builderRevision, builder);
157             modules.put(builderName, builderByRevision);
158         }
159         return modules;
160     }
161
162     private List<ParseTree> parseStreams(final List<InputStream> yangStreams) {
163         final List<ParseTree> trees = new ArrayList<ParseTree>();
164         for (InputStream yangStream : yangStreams) {
165             trees.add(parseStream(yangStream));
166         }
167         return trees;
168     }
169
170     private ParseTree parseStream(final InputStream yangStream) {
171         ParseTree result = null;
172         try {
173             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
174             final YangLexer lexer = new YangLexer(input);
175             final CommonTokenStream tokens = new CommonTokenStream(lexer);
176             final YangParser parser = new YangParser(tokens);
177             result = parser.yang();
178         } catch (IOException e) {
179             logger.warn("Exception while reading yang file: " + yangStream, e);
180         }
181         return result;
182     }
183
184     private Set<Module> build(
185             final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
186         // fix unresolved nodes
187         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
188                 .entrySet()) {
189             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
190                     .entrySet()) {
191                 final ModuleBuilder moduleBuilder = childEntry.getValue();
192                 fixUnresolvedNodes(modules, moduleBuilder);
193             }
194         }
195         resolveAugments(modules);
196
197         // build
198         // Linked Hash Set MUST be used otherwise the Set will not maintain order!
199         // http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashSet.html
200         final Set<Module> result = new LinkedHashSet<Module>();
201         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
202                 .entrySet()) {
203             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
204             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
205                     .entrySet()) {
206                 final ModuleBuilder moduleBuilder = childEntry.getValue();
207                 final Module module = moduleBuilder.build();
208                 modulesByRevision.put(childEntry.getKey(), module);
209                 result.add(module);
210             }
211         }
212         return result;
213     }
214
215     private void fixUnresolvedNodes(
216             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
217             final ModuleBuilder builder) {
218         resolveDirtyNodes(modules, builder);
219         resolveIdentities(modules, builder);
220         resolveUses(modules, builder);
221         resolveUnknownNodes(modules, builder);
222     }
223
224     /**
225      * Search for dirty nodes (node which contains UnknownType) and resolve
226      * unknown types.
227      * 
228      * @param modules
229      *            all available modules
230      * @param module
231      *            current module
232      */
233     private void resolveDirtyNodes(
234             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
235             final ModuleBuilder module) {
236         final Map<List<String>, TypeAwareBuilder> dirtyNodes = module
237                 .getDirtyNodes();
238         if (!dirtyNodes.isEmpty()) {
239             for (Map.Entry<List<String>, TypeAwareBuilder> entry : dirtyNodes
240                     .entrySet()) {
241
242                 final TypeAwareBuilder nodeToResolve = entry.getValue();
243                 // different handling for union types
244                 if (nodeToResolve instanceof UnionTypeBuilder) {
245                     final UnionTypeBuilder union = (UnionTypeBuilder) nodeToResolve;
246                     final List<TypeDefinition<?>> unionTypes = union.getTypes();
247                     final List<UnknownType> toRemove = new ArrayList<UnknownType>();
248                     for (TypeDefinition<?> td : unionTypes) {
249                         if (td instanceof UnknownType) {
250                             final UnknownType unknownType = (UnknownType) td;
251                             final TypeDefinitionBuilder resolvedType = resolveTypeUnion(
252                                     nodeToResolve, unknownType, modules, module);
253                             union.setType(resolvedType);
254                             toRemove.add(unknownType);
255                         }
256                     }
257                     unionTypes.removeAll(toRemove);
258                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
259                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve
260                             .getTypedef();
261                     nodeToResolve.setType(new IdentityrefType(findFullQName(
262                             modules, module, idref), idref.getPath()));
263                 } else {
264                     final TypeDefinitionBuilder resolvedType = resolveType(
265                             nodeToResolve, modules, module);
266                     nodeToResolve.setType(resolvedType);
267                 }
268             }
269         }
270     }
271
272     private TypeDefinitionBuilder resolveType(
273             final TypeAwareBuilder typeToResolve,
274             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
275             final ModuleBuilder builder) {
276         final TypeConstraints constraints = new TypeConstraints();
277
278         final TypeDefinitionBuilder targetType = getTypedefBuilder(
279                 typeToResolve, modules, builder);
280         final TypeConstraints tConstraints = findConstraints(typeToResolve,
281                 constraints, modules, builder);
282         targetType.setRanges(tConstraints.getRange());
283         targetType.setLengths(tConstraints.getLength());
284         targetType.setPatterns(tConstraints.getPatterns());
285         targetType.setFractionDigits(tConstraints.getFractionDigits());
286
287         return targetType;
288     }
289
290     private TypeDefinitionBuilder resolveTypeUnion(
291             final TypeAwareBuilder typeToResolve,
292             final UnknownType unknownType,
293             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
294             final ModuleBuilder builder) {
295         final TypeConstraints constraints = new TypeConstraints();
296
297         final TypeDefinitionBuilder targetType = getUnionBuilder(typeToResolve,
298                 unknownType, modules, builder);
299         final TypeConstraints tConstraints = findConstraints(typeToResolve,
300                 constraints, modules, builder);
301         targetType.setRanges(tConstraints.getRange());
302         targetType.setLengths(tConstraints.getLength());
303         targetType.setPatterns(tConstraints.getPatterns());
304         targetType.setFractionDigits(tConstraints.getFractionDigits());
305
306         return targetType;
307     }
308
309     private TypeDefinitionBuilder getTypedefBuilder(
310             final TypeAwareBuilder nodeToResolve,
311             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
312             final ModuleBuilder builder) {
313
314         final TypeDefinition<?> nodeToResolveBase = nodeToResolve.getType();
315         if (nodeToResolveBase != null
316                 && !(nodeToResolveBase instanceof UnknownType)) {
317             return (TypeDefinitionBuilder) nodeToResolve;
318         }
319
320         final UnknownType unknownType = (UnknownType) nodeToResolve.getType();
321         final QName unknownTypeQName = unknownType.getQName();
322
323         // search for module which contains referenced typedef
324         final ModuleBuilder dependentModule = findDependentModule(modules,
325                 builder, unknownTypeQName.getPrefix(), nodeToResolve.getLine());
326         final TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilderByName(
327                 dependentModule, unknownTypeQName.getLocalName(),
328                 builder.getName(), nodeToResolve.getLine());
329
330         final TypeDefinitionBuilder lookedUpBuilderCopy = copyTypedefBuilder(
331                 lookedUpBuilder, nodeToResolve instanceof TypeDefinitionBuilder);
332         final TypeDefinitionBuilder resolvedCopy = resolveCopiedBuilder(
333                 lookedUpBuilderCopy, modules, dependentModule);
334         return resolvedCopy;
335     }
336
337     private TypeDefinitionBuilder getUnionBuilder(
338             final TypeAwareBuilder nodeToResolve,
339             final UnknownType unknownType,
340             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
341             final ModuleBuilder module) {
342
343         final TypeDefinition<?> baseTypeToResolve = nodeToResolve.getType();
344         if (baseTypeToResolve != null
345                 && !(baseTypeToResolve instanceof UnknownType)) {
346             return (TypeDefinitionBuilder) nodeToResolve;
347         }
348
349         final QName unknownTypeQName = unknownType.getQName();
350         // search for module which contains referenced typedef
351         final ModuleBuilder dependentModule = findDependentModule(modules,
352                 module, unknownTypeQName.getPrefix(), nodeToResolve.getLine());
353         final TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilderByName(
354                 dependentModule, unknownTypeQName.getLocalName(),
355                 module.getName(), nodeToResolve.getLine());
356
357         final TypeDefinitionBuilder lookedUpBuilderCopy = copyTypedefBuilder(
358                 lookedUpBuilder, nodeToResolve instanceof TypeDefinitionBuilder);
359         final TypeDefinitionBuilder resolvedCopy = resolveCopiedBuilder(
360                 lookedUpBuilderCopy, modules, dependentModule);
361         return resolvedCopy;
362     }
363
364     private TypeDefinitionBuilder copyTypedefBuilder(
365             final TypeDefinitionBuilder old, final boolean seekByTypedefBuilder) {
366         if (old instanceof UnionTypeBuilder) {
367             final UnionTypeBuilder oldUnion = (UnionTypeBuilder) old;
368             final UnionTypeBuilder newUnion = new UnionTypeBuilder(
369                     oldUnion.getActualPath(), oldUnion.getNamespace(),
370                     oldUnion.getRevision(), old.getLine());
371             for (TypeDefinition<?> td : oldUnion.getTypes()) {
372                 newUnion.setType(td);
373             }
374             for (TypeDefinitionBuilder tdb : oldUnion.getTypedefs()) {
375                 newUnion.setType(copyTypedefBuilder(tdb, true));
376             }
377             return newUnion;
378         }
379
380         final QName oldName = old.getQName();
381         final QName newName = new QName(oldName.getNamespace(),
382                 oldName.getRevision(), oldName.getPrefix(),
383                 oldName.getLocalName());
384         final TypeDefinitionBuilder tdb = new TypedefBuilder(newName,
385                 old.getLine());
386
387         tdb.setRanges(old.getRanges());
388         tdb.setLengths(old.getLengths());
389         tdb.setPatterns(old.getPatterns());
390         tdb.setFractionDigits(old.getFractionDigits());
391         tdb.setPath(old.getPath());
392
393         final TypeDefinition<?> oldType = old.getType();
394         if (oldType == null) {
395             tdb.setType(old.getTypedef());
396         } else {
397             tdb.setType(oldType);
398         }
399
400         if (!seekByTypedefBuilder) {
401             tdb.setDescription(old.getDescription());
402             tdb.setReference(old.getReference());
403             tdb.setStatus(old.getStatus());
404             tdb.setDefaultValue(old.getDefaultValue());
405             tdb.setUnits(old.getUnits());
406         }
407         return tdb;
408     }
409
410     private TypeDefinitionBuilder resolveCopiedBuilder(
411             final TypeDefinitionBuilder copy,
412             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
413             final ModuleBuilder builder) {
414
415         if (copy instanceof UnionTypeBuilder) {
416             final UnionTypeBuilder union = (UnionTypeBuilder) copy;
417             final List<TypeDefinition<?>> unionTypes = union.getTypes();
418             final List<UnknownType> toRemove = new ArrayList<UnknownType>();
419             for (TypeDefinition<?> td : unionTypes) {
420                 if (td instanceof UnknownType) {
421                     final UnknownType unknownType = (UnknownType) td;
422                     final TypeDefinitionBuilder resolvedType = resolveTypeUnion(
423                             union, unknownType, modules, builder);
424                     union.setType(resolvedType);
425                     toRemove.add(unknownType);
426                 }
427             }
428             unionTypes.removeAll(toRemove);
429
430             return union;
431         }
432
433         final TypeDefinition<?> base = copy.getType();
434         final TypeDefinitionBuilder baseTdb = copy.getTypedef();
435         if (base != null && !(base instanceof UnknownType)) {
436             return copy;
437         } else if (base instanceof UnknownType) {
438             final UnknownType unknownType = (UnknownType) base;
439             final QName unknownTypeQName = unknownType.getQName();
440             final String unknownTypePrefix = unknownTypeQName.getPrefix();
441             final ModuleBuilder dependentModule = findDependentModule(modules,
442                     builder, unknownTypePrefix, copy.getLine());
443             final TypeDefinitionBuilder utBuilder = getTypedefBuilder(copy,
444                     modules, dependentModule);
445             copy.setType(utBuilder);
446             return copy;
447         } else if (base == null && baseTdb != null) {
448             // make a copy of baseTypeDef and call again
449             final TypeDefinitionBuilder baseTdbCopy = copyTypedefBuilder(
450                     baseTdb, true);
451             final TypeDefinitionBuilder baseTdbCopyResolved = resolveCopiedBuilder(
452                     baseTdbCopy, modules, builder);
453             copy.setType(baseTdbCopyResolved);
454             return copy;
455         } else {
456             throw new IllegalStateException("Failed to resolve type "
457                     + copy.getQName().getLocalName());
458         }
459     }
460
461     private TypeDefinitionBuilder findTypedefBuilder(
462             final QName unknownTypeQName,
463             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
464             final ModuleBuilder builder, int line) {
465         // search for module which contains referenced typedef
466         final ModuleBuilder dependentModule = findDependentModule(modules,
467                 builder, unknownTypeQName.getPrefix(), line);
468         final TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilderByName(
469                 dependentModule, unknownTypeQName.getLocalName(),
470                 builder.getName(), line);
471         return copyTypedefBuilder(lookedUpBuilder, true);
472     }
473
474     private TypeConstraints findConstraints(
475             final TypeAwareBuilder nodeToResolve,
476             final TypeConstraints constraints,
477             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
478             final ModuleBuilder builder) {
479         // union type cannot be restricted
480         if (nodeToResolve instanceof UnionTypeBuilder) {
481             return constraints;
482         }
483
484         // if referenced type is UnknownType again, search recursively with
485         // current constraints
486         final TypeDefinition<?> referencedType = nodeToResolve.getType();
487         List<RangeConstraint> ranges = Collections.emptyList();
488         List<LengthConstraint> lengths = Collections.emptyList();
489         List<PatternConstraint> patterns = Collections.emptyList();
490         Integer fractionDigits = null;
491         if (referencedType == null) {
492             final TypeDefinitionBuilder tdb = nodeToResolve.getTypedef();
493             ranges = tdb.getRanges();
494             constraints.addRanges(ranges);
495             lengths = tdb.getLengths();
496             constraints.addLengths(lengths);
497             patterns = tdb.getPatterns();
498             constraints.addPatterns(patterns);
499             fractionDigits = tdb.getFractionDigits();
500             constraints.setFractionDigits(fractionDigits);
501             return constraints;
502         } else if (referencedType instanceof ExtendedType) {
503             final ExtendedType ext = (ExtendedType) referencedType;
504             ranges = ext.getRanges();
505             constraints.addRanges(ranges);
506             lengths = ext.getLengths();
507             constraints.addLengths(lengths);
508             patterns = ext.getPatterns();
509             constraints.addPatterns(patterns);
510             fractionDigits = ext.getFractionDigits();
511             constraints.setFractionDigits(fractionDigits);
512             return findConstraints(
513                     findTypedefBuilder(ext.getQName(), modules, builder,
514                             nodeToResolve.getLine()), constraints, modules,
515                     builder);
516         } else if (referencedType instanceof UnknownType) {
517             final UnknownType unknown = (UnknownType) referencedType;
518             ranges = unknown.getRangeStatements();
519             constraints.addRanges(ranges);
520             lengths = unknown.getLengthStatements();
521             constraints.addLengths(lengths);
522             patterns = unknown.getPatterns();
523             constraints.addPatterns(patterns);
524             fractionDigits = unknown.getFractionDigits();
525             constraints.setFractionDigits(fractionDigits);
526
527             String unknownTypePrefix = unknown.getQName().getPrefix();
528             if (unknownTypePrefix == null || "".equals(unknownTypePrefix)) {
529                 unknownTypePrefix = builder.getPrefix();
530             }
531             final ModuleBuilder dependentModule = findDependentModule(modules,
532                     builder, unknown.getQName().getPrefix(),
533                     nodeToResolve.getLine());
534             final TypeDefinitionBuilder utBuilder = findTypedefBuilder(
535                     unknown.getQName(), modules, builder,
536                     nodeToResolve.getLine());
537             return findConstraints(utBuilder, constraints, modules,
538                     dependentModule);
539         } else {
540             // HANDLE BASE YANG TYPE
541             mergeConstraints(referencedType, constraints);
542             return constraints;
543         }
544     }
545
546     /**
547      * Go through all typedef statements from given module and search for one
548      * with given name
549      * 
550      * @param typedefs
551      *            typedef statements to search
552      * @param name
553      *            name of searched typedef
554      * @return typedef with name equals to given name
555      */
556     private TypeDefinitionBuilder findTypedefBuilderByName(
557             final ModuleBuilder dependentModule, final String name,
558             final String currentModuleName, final int line) {
559         TypeDefinitionBuilder result = null;
560         final Set<TypeDefinitionBuilder> typedefs = dependentModule
561                 .getModuleTypedefs();
562         for (TypeDefinitionBuilder td : typedefs) {
563             if (td.getQName().getLocalName().equals(name)) {
564                 result = td;
565                 break;
566             }
567         }
568         if (result == null) {
569             throw new YangParseException(currentModuleName, line,
570                     "Target module '" + dependentModule.getName()
571                             + "' does not contain typedef '" + name + "'.");
572         }
573         return result;
574     }
575
576     /**
577      * Pull restriction from referenced type and add them to given constraints
578      * 
579      * @param referencedType
580      * @param constraints
581      */
582     private void mergeConstraints(final TypeDefinition<?> referencedType,
583             final TypeConstraints constraints) {
584
585         if (referencedType instanceof DecimalTypeDefinition) {
586             constraints.addRanges(((DecimalTypeDefinition) referencedType)
587                     .getRangeStatements());
588             constraints
589                     .setFractionDigits(((DecimalTypeDefinition) referencedType)
590                             .getFractionDigits());
591         } else if (referencedType instanceof IntegerTypeDefinition) {
592             constraints.addRanges(((IntegerTypeDefinition) referencedType)
593                     .getRangeStatements());
594         } else if (referencedType instanceof StringTypeDefinition) {
595             constraints.addPatterns(((StringTypeDefinition) referencedType)
596                     .getPatterns());
597             constraints.addLengths(((StringTypeDefinition) referencedType)
598                     .getLengthStatements());
599         } else if (referencedType instanceof BinaryTypeDefinition) {
600             constraints.addLengths(((BinaryTypeDefinition) referencedType)
601                     .getLengthConstraints());
602         }
603     }
604
605     /**
606      * Go through all augmentation definitions and resolve them. This method
607      * also finds referenced node and add child nodes to it.
608      * 
609      * @param modules
610      *            all available modules
611      */
612     private void resolveAugments(
613             final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
614         final List<ModuleBuilder> allModulesList = new ArrayList<ModuleBuilder>();
615         final Set<ModuleBuilder> allModulesSet = new HashSet<ModuleBuilder>();
616         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
617                 .entrySet()) {
618             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue()
619                     .entrySet()) {
620                 allModulesList.add(inner.getValue());
621                 allModulesSet.add(inner.getValue());
622             }
623         }
624
625         for (int i = 0; i < allModulesList.size(); i++) {
626             final ModuleBuilder module = allModulesList.get(i);
627             // try to resolve augments in module
628             resolveAugment(modules, module);
629             // while all augments are not resolved
630             final Iterator<ModuleBuilder> allModulesIterator = allModulesSet
631                     .iterator();
632             while (!(module.getAugmentsResolved() == module.getAddedAugments()
633                     .size())) {
634                 ModuleBuilder nextModule = null;
635                 // try resolve other module augments
636                 try {
637                     nextModule = allModulesIterator.next();
638                     resolveAugment(modules, nextModule);
639                 } catch (NoSuchElementException e) {
640                     throw new YangParseException(
641                             "Failed to resolve augments in module '"
642                                     + module.getName() + "'.", e);
643                 }
644                 // then try to resolve first module again
645                 resolveAugment(modules, module);
646             }
647         }
648     }
649
650     /**
651      * 
652      * @param modules
653      *            all available modules
654      * @param module
655      *            current module
656      */
657     private void resolveAugment(
658             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
659             final ModuleBuilder module) {
660         if (module.getAugmentsResolved() < module.getAddedAugments().size()) {
661             for (AugmentationSchemaBuilder augmentBuilder : module
662                     .getAddedAugments()) {
663
664                 if (!augmentBuilder.isResolved()) {
665                     final SchemaPath augmentTargetSchemaPath = augmentBuilder
666                             .getTargetPath();
667                     final List<QName> path = augmentTargetSchemaPath.getPath();
668
669                     int i = 0;
670                     final QName qname = path.get(i);
671                     String prefix = qname.getPrefix();
672                     if (prefix == null) {
673                         prefix = module.getPrefix();
674                     }
675
676                     DataSchemaNodeBuilder currentParent = null;
677                     final ModuleBuilder dependentModule = findDependentModule(
678                             modules, module, prefix, augmentBuilder.getLine());
679                     for (DataSchemaNodeBuilder child : dependentModule
680                             .getChildNodes()) {
681                         final QName childQName = child.getQName();
682                         if (childQName.getLocalName().equals(
683                                 qname.getLocalName())) {
684                             currentParent = child;
685                             i++;
686                             break;
687                         }
688                     }
689
690                     for (; i < path.size(); i++) {
691                         final QName currentQName = path.get(i);
692                         DataSchemaNodeBuilder newParent = null;
693                         for (DataSchemaNodeBuilder child : ((ChildNodeBuilder) currentParent)
694                                 .getChildNodes()) {
695                             final QName childQName = child.getQName();
696                             if (childQName.getLocalName().equals(
697                                     currentQName.getLocalName())) {
698                                 newParent = child;
699                                 break;
700                             }
701                         }
702                         if (newParent == null) {
703                             break; // node not found, quit search
704                         } else {
705                             currentParent = newParent;
706                         }
707                     }
708
709                     final QName currentQName = currentParent.getQName();
710                     final QName lastAugmentPathElement = path
711                             .get(path.size() - 1);
712                     if (currentQName.getLocalName().equals(
713                             lastAugmentPathElement.getLocalName())) {
714                         ParserUtils.fillAugmentTarget(augmentBuilder,
715                                 (ChildNodeBuilder) currentParent);
716                         ((AugmentationTargetBuilder) currentParent)
717                                 .addAugmentation(augmentBuilder);
718                         SchemaPath oldPath = currentParent.getPath();
719                         augmentBuilder.setTargetPath(new SchemaPath(oldPath
720                                 .getPath(), oldPath.isAbsolute()));
721                         augmentBuilder.setResolved(true);
722                         module.augmentResolved();
723                     }
724                 }
725
726             }
727         }
728     }
729
730     /**
731      * Go through identity statements defined in current module and resolve
732      * their 'base' statement if present.
733      * 
734      * @param modules
735      *            all modules
736      * @param module
737      *            module being resolved
738      */
739     private void resolveIdentities(
740             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
741             final ModuleBuilder module) {
742         final Set<IdentitySchemaNodeBuilder> identities = module
743                 .getAddedIdentities();
744         for (IdentitySchemaNodeBuilder identity : identities) {
745             final String baseIdentityName = identity.getBaseIdentityName();
746             if (baseIdentityName != null) {
747                 String baseIdentityPrefix = null;
748                 String baseIdentityLocalName = null;
749                 if (baseIdentityName.contains(":")) {
750                     final String[] splitted = baseIdentityName.split(":");
751                     baseIdentityPrefix = splitted[0];
752                     baseIdentityLocalName = splitted[1];
753                 } else {
754                     baseIdentityPrefix = module.getPrefix();
755                     baseIdentityLocalName = baseIdentityName;
756                 }
757                 final ModuleBuilder dependentModule = findDependentModule(
758                         modules, module, baseIdentityPrefix, identity.getLine());
759
760                 final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule
761                         .getAddedIdentities();
762                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
763                     if (idBuilder.getQName().getLocalName()
764                             .equals(baseIdentityLocalName)) {
765                         identity.setBaseIdentity(idBuilder);
766                     }
767                 }
768             }
769         }
770     }
771
772     /**
773      * Go through uses statements defined in current module and resolve their
774      * refine statements.
775      * 
776      * @param modules
777      *            all modules
778      * @param module
779      *            module being resolved
780      */
781     private void resolveUses(
782             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
783             final ModuleBuilder module) {
784         final Map<List<String>, UsesNodeBuilder> moduleUses = module
785                 .getAddedUsesNodes();
786         for (Map.Entry<List<String>, UsesNodeBuilder> entry : moduleUses
787                 .entrySet()) {
788             final List<String> key = entry.getKey();
789             final UsesNodeBuilder usesNode = entry.getValue();
790
791             final String groupingName = key.get(key.size() - 1);
792
793             for (RefineHolder refine : usesNode.getRefines()) {
794                 // refine statements
795                 final String defaultStr = refine.getDefaultStr();
796                 final Boolean mandatory = refine.isMandatory();
797                 final MustDefinition must = refine.getMust();
798                 final Boolean presence = refine.isPresence();
799                 final Integer min = refine.getMinElements();
800                 final Integer max = refine.getMaxElements();
801                 final List<UnknownSchemaNodeBuilder> unknownNodes = refine
802                         .getUnknownNodes();
803
804                 Builder refineTarget = getRefineTargetBuilder(groupingName,
805                         refine, modules, module);
806                 if (refineTarget instanceof LeafSchemaNodeBuilder) {
807                     final LeafSchemaNodeBuilder leaf = (LeafSchemaNodeBuilder) refineTarget;
808                     if (defaultStr != null && !("".equals(defaultStr))) {
809                         leaf.setDefaultStr(defaultStr);
810                     }
811                     if (mandatory != null) {
812                         leaf.getConstraints().setMandatory(mandatory);
813                     }
814                     if (must != null) {
815                         leaf.getConstraints().addMustDefinition(must);
816                     }
817                     if (unknownNodes != null) {
818                         for (UnknownSchemaNodeBuilder unknown : unknownNodes) {
819                             leaf.addUnknownSchemaNode(unknown);
820                         }
821                     }
822                     usesNode.addRefineNode(leaf);
823                 } else if (refineTarget instanceof ContainerSchemaNodeBuilder) {
824                     final ContainerSchemaNodeBuilder container = (ContainerSchemaNodeBuilder) refineTarget;
825                     if (presence != null) {
826                         container.setPresence(presence);
827                     }
828                     if (must != null) {
829                         container.getConstraints().addMustDefinition(must);
830                     }
831                     if (unknownNodes != null) {
832                         for (UnknownSchemaNodeBuilder unknown : unknownNodes) {
833                             container.addUnknownSchemaNode(unknown);
834                         }
835                     }
836                     usesNode.addRefineNode(container);
837                 } else if (refineTarget instanceof ListSchemaNodeBuilder) {
838                     final ListSchemaNodeBuilder list = (ListSchemaNodeBuilder) refineTarget;
839                     if (must != null) {
840                         list.getConstraints().addMustDefinition(must);
841                     }
842                     if (min != null) {
843                         list.getConstraints().setMinElements(min);
844                     }
845                     if (max != null) {
846                         list.getConstraints().setMaxElements(max);
847                     }
848                     if (unknownNodes != null) {
849                         for (UnknownSchemaNodeBuilder unknown : unknownNodes) {
850                             list.addUnknownSchemaNode(unknown);
851                         }
852                     }
853                 } else if (refineTarget instanceof LeafListSchemaNodeBuilder) {
854                     final LeafListSchemaNodeBuilder leafList = (LeafListSchemaNodeBuilder) getRefineTargetBuilder(
855                             groupingName, refine, modules, module);
856                     if (must != null) {
857                         leafList.getConstraints().addMustDefinition(must);
858                     }
859                     if (min != null) {
860                         leafList.getConstraints().setMinElements(min);
861                     }
862                     if (max != null) {
863                         leafList.getConstraints().setMaxElements(max);
864                     }
865                     if (unknownNodes != null) {
866                         for (UnknownSchemaNodeBuilder unknown : unknownNodes) {
867                             leafList.addUnknownSchemaNode(unknown);
868                         }
869                     }
870                 } else if (refineTarget instanceof ChoiceBuilder) {
871                     final ChoiceBuilder choice = (ChoiceBuilder) refineTarget;
872                     if (defaultStr != null) {
873                         choice.setDefaultCase(defaultStr);
874                     }
875                     if (mandatory != null) {
876                         choice.getConstraints().setMandatory(mandatory);
877                     }
878                     if (unknownNodes != null) {
879                         for (UnknownSchemaNodeBuilder unknown : unknownNodes) {
880                             choice.addUnknownSchemaNode(unknown);
881                         }
882                     }
883                 } else if (refineTarget instanceof AnyXmlBuilder) {
884                     final AnyXmlBuilder anyXml = (AnyXmlBuilder) refineTarget;
885                     if (mandatory != null) {
886                         anyXml.getConstraints().setMandatory(mandatory);
887                     }
888                     if (must != null) {
889                         anyXml.getConstraints().addMustDefinition(must);
890                     }
891                     if (unknownNodes != null) {
892                         for (UnknownSchemaNodeBuilder unknown : unknownNodes) {
893                             anyXml.addUnknownSchemaNode(unknown);
894                         }
895                     }
896                 }
897             }
898         }
899     }
900
901     /**
902      * Find original builder of refine node and return copy of this builder.
903      * 
904      * @param groupingPath
905      *            path to grouping which contains node to refine
906      * @param refine
907      *            refine object containing informations about refine
908      * @param modules
909      *            all loaded modules
910      * @param module
911      *            current module
912      * @return copy of Builder object of node to be refined if it is present in
913      *         grouping, null otherwise
914      */
915     private Builder getRefineTargetBuilder(final String groupingPath,
916             final RefineHolder refine,
917             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
918             final ModuleBuilder module) {
919         Builder result = null;
920         final Builder lookedUpBuilder = findRefineTargetBuilder(groupingPath,
921                 refine, modules, module);
922         if (lookedUpBuilder instanceof LeafSchemaNodeBuilder) {
923             result = ParserUtils
924                     .copyLeafBuilder((LeafSchemaNodeBuilder) lookedUpBuilder);
925         } else if (lookedUpBuilder instanceof ContainerSchemaNodeBuilder) {
926             result = ParserUtils
927                     .copyContainerBuilder((ContainerSchemaNodeBuilder) lookedUpBuilder);
928         } else if (lookedUpBuilder instanceof ListSchemaNodeBuilder) {
929             result = ParserUtils
930                     .copyListBuilder((ListSchemaNodeBuilder) lookedUpBuilder);
931         } else if (lookedUpBuilder instanceof LeafListSchemaNodeBuilder) {
932             result = ParserUtils
933                     .copyLeafListBuilder((LeafListSchemaNodeBuilder) lookedUpBuilder);
934         } else if (lookedUpBuilder instanceof ChoiceBuilder) {
935             result = ParserUtils
936                     .copyChoiceBuilder((ChoiceBuilder) lookedUpBuilder);
937         } else if (lookedUpBuilder instanceof AnyXmlBuilder) {
938             result = ParserUtils
939                     .copyAnyXmlBuilder((AnyXmlBuilder) lookedUpBuilder);
940         } else {
941             throw new YangParseException(module.getName(), refine.getLine(),
942                     "Target '" + refine.getName() + "' can not be refined");
943         }
944         return result;
945     }
946
947     /**
948      * Find builder of refine node.
949      * 
950      * @param groupingPath
951      *            path to grouping which contains node to refine
952      * @param refineNodeName
953      *            name of node to be refined
954      * @param modules
955      *            all loaded modules
956      * @param module
957      *            current module
958      * @return Builder object of refine node if it is present in grouping, null
959      *         otherwise
960      */
961     private Builder findRefineTargetBuilder(final String groupingPath,
962             final RefineHolder refine,
963             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
964             final ModuleBuilder module) {
965         final SchemaPath path = ParserUtils.parseUsesPath(groupingPath);
966         final List<String> builderPath = new ArrayList<String>();
967         String prefix = null;
968         for (QName qname : path.getPath()) {
969             builderPath.add(qname.getLocalName());
970             prefix = qname.getPrefix();
971         }
972         if (prefix == null) {
973             prefix = module.getPrefix();
974         }
975
976         final ModuleBuilder dependentModule = findDependentModule(modules,
977                 module, prefix, refine.getLine());
978         builderPath.add(0, "grouping");
979         builderPath.add(0, dependentModule.getName());
980         final GroupingBuilder builder = (GroupingBuilder) dependentModule
981                 .getNode(builderPath);
982
983         return builder.getChildNode(refine.getName());
984     }
985
986     private QName findFullQName(
987             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
988             final ModuleBuilder module, final IdentityrefTypeBuilder idref) {
989         QName result = null;
990         String baseString = idref.getBaseString();
991         if (baseString.contains(":")) {
992             String[] splittedBase = baseString.split(":");
993             if (splittedBase.length > 2) {
994                 throw new YangParseException(module.getName(), idref.getLine(),
995                         "Failed to parse identityref base: " + baseString);
996             }
997             String prefix = splittedBase[0];
998             String name = splittedBase[1];
999             ModuleBuilder dependentModule = findDependentModule(modules,
1000                     module, prefix, idref.getLine());
1001             result = new QName(dependentModule.getNamespace(),
1002                     dependentModule.getRevision(), prefix, name);
1003         } else {
1004             result = new QName(module.getNamespace(), module.getRevision(),
1005                     module.getPrefix(), baseString);
1006         }
1007         return result;
1008     }
1009
1010     private void resolveUnknownNodes(
1011             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1012             final ModuleBuilder module) {
1013         for (UnknownSchemaNodeBuilder usnb : module.getAddedUnknownNodes()) {
1014             QName nodeType = usnb.getNodeType();
1015             if (nodeType.getNamespace() == null
1016                     || nodeType.getRevision() == null) {
1017                 try {
1018                     ModuleBuilder dependentModule = findDependentModule(
1019                             modules, module, nodeType.getPrefix(),
1020                             usnb.getLine());
1021                     QName newNodeType = new QName(
1022                             dependentModule.getNamespace(),
1023                             dependentModule.getRevision(),
1024                             nodeType.getPrefix(), nodeType.getLocalName());
1025                     usnb.setNodeType(newNodeType);
1026                 } catch (YangParseException e) {
1027                     logger.debug(module.getName(), usnb.getLine(),
1028                             "Failed to find unknown node type: " + nodeType);
1029                 }
1030             }
1031         }
1032     }
1033
1034     /**
1035      * Find dependent module based on given prefix
1036      * 
1037      * @param modules
1038      *            all available modules
1039      * @param module
1040      *            current module
1041      * @param prefix
1042      *            target module prefix
1043      * @return
1044      */
1045     private ModuleBuilder findDependentModule(
1046             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1047             final ModuleBuilder module, final String prefix, final int line) {
1048         ModuleBuilder dependentModule = null;
1049         Date dependentModuleRevision = null;
1050
1051         if (prefix.equals(module.getPrefix())) {
1052             dependentModule = module;
1053         } else {
1054             final ModuleImport dependentModuleImport = ParserUtils
1055                     .getModuleImport(module, prefix);
1056             if (dependentModuleImport == null) {
1057                 throw new YangParseException(module.getName(), line,
1058                         "No import found with prefix '" + prefix + "'.");
1059             }
1060             final String dependentModuleName = dependentModuleImport
1061                     .getModuleName();
1062             dependentModuleRevision = dependentModuleImport.getRevision();
1063
1064             final TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules
1065                     .get(dependentModuleName);
1066             if (moduleBuildersByRevision == null) {
1067                 throw new YangParseException(module.getName(), line,
1068                         "Failed to find dependent module '"
1069                                 + dependentModuleName + "'.");
1070             }
1071             if (dependentModuleRevision == null) {
1072                 dependentModule = moduleBuildersByRevision.lastEntry()
1073                         .getValue();
1074             } else {
1075                 dependentModule = moduleBuildersByRevision
1076                         .get(dependentModuleRevision);
1077             }
1078         }
1079
1080         if (dependentModule == null) {
1081             throw new YangParseException(module.getName(), line,
1082                     "Failed to find dependent module with prefix '" + prefix
1083                             + "' and revision '" + dependentModuleRevision
1084                             + "'.");
1085         }
1086         return dependentModule;
1087     }
1088
1089     private static class SchemaContextImpl implements SchemaContext {
1090         private final Set<Module> modules;
1091
1092         private SchemaContextImpl(final Set<Module> modules) {
1093             this.modules = modules;
1094         }
1095
1096         @Override
1097         public Set<DataSchemaNode> getDataDefinitions() {
1098             final Set<DataSchemaNode> dataDefs = new HashSet<DataSchemaNode>();
1099             for (Module m : modules) {
1100                 dataDefs.addAll(m.getChildNodes());
1101             }
1102             return dataDefs;
1103         }
1104
1105         @Override
1106         public Set<Module> getModules() {
1107             return modules;
1108         }
1109
1110         @Override
1111         public Set<NotificationDefinition> getNotifications() {
1112             final Set<NotificationDefinition> notifications = new HashSet<NotificationDefinition>();
1113             for (Module m : modules) {
1114                 notifications.addAll(m.getNotifications());
1115             }
1116             return notifications;
1117         }
1118
1119         @Override
1120         public Set<RpcDefinition> getOperations() {
1121             final Set<RpcDefinition> rpcs = new HashSet<RpcDefinition>();
1122             for (Module m : modules) {
1123                 rpcs.addAll(m.getRpcs());
1124             }
1125             return rpcs;
1126         }
1127
1128         @Override
1129         public Set<ExtensionDefinition> getExtensions() {
1130             final Set<ExtensionDefinition> extensions = new HashSet<ExtensionDefinition>();
1131             for (Module m : modules) {
1132                 extensions.addAll(m.getExtensionSchemaNodes());
1133             }
1134             return extensions;
1135         }
1136
1137         @Override
1138         public Module findModuleByName(final String name, final Date revision) {
1139             if (name != null) {
1140                 for (final Module module : modules) {
1141                     if (revision == null) {
1142                         if (module.getName().equals(name)) {
1143                             return module;
1144                         }
1145                     } else if (module.getName().equals(name)
1146                             && module.getRevision().equals(revision)) {
1147                         return module;
1148                     }
1149                 }
1150             }
1151             return null;
1152         }
1153
1154         @Override
1155         public Module findModuleByNamespace(final URI namespace) {
1156             if (namespace != null) {
1157                 for (final Module module : modules) {
1158                     if (module.getNamespace().equals(namespace)) {
1159                         return module;
1160                     }
1161                 }
1162             }
1163             return null;
1164         }
1165     }
1166
1167 }