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