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