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