Yang validation moved to validator package and validation listener refactored.
[controller.git] / opendaylight / sal / yang-prototype / code-generator / yang-model-parser-impl / src / main / java / org / opendaylight / controller / yang / model / parser / impl / YangModelParserImpl.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.yang.model.parser.impl;
9
10 import java.io.File;
11 import java.io.FileInputStream;
12 import java.io.FileNotFoundException;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.net.URI;
16 import java.util.ArrayList;
17 import java.util.Collections;
18 import java.util.Date;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.NoSuchElementException;
25 import java.util.Set;
26 import java.util.TreeMap;
27
28 import org.antlr.v4.runtime.ANTLRInputStream;
29 import org.antlr.v4.runtime.CommonTokenStream;
30 import org.antlr.v4.runtime.tree.ParseTree;
31 import org.antlr.v4.runtime.tree.ParseTreeWalker;
32 import org.opendaylight.controller.antlrv4.code.gen.YangLexer;
33 import org.opendaylight.controller.antlrv4.code.gen.YangParser;
34 import org.opendaylight.controller.yang.common.QName;
35 import org.opendaylight.controller.yang.model.api.DataSchemaNode;
36 import org.opendaylight.controller.yang.model.api.ExtensionDefinition;
37 import org.opendaylight.controller.yang.model.api.Module;
38 import org.opendaylight.controller.yang.model.api.ModuleImport;
39 import org.opendaylight.controller.yang.model.api.MustDefinition;
40 import org.opendaylight.controller.yang.model.api.NotificationDefinition;
41 import org.opendaylight.controller.yang.model.api.RpcDefinition;
42 import org.opendaylight.controller.yang.model.api.SchemaContext;
43 import org.opendaylight.controller.yang.model.api.SchemaPath;
44 import org.opendaylight.controller.yang.model.api.TypeDefinition;
45 import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;
46 import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;
47 import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;
48 import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
49 import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
50 import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
51 import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;
52 import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
53 import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationSchemaBuilder;
54 import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationTargetBuilder;
55 import org.opendaylight.controller.yang.model.parser.builder.api.Builder;
56 import org.opendaylight.controller.yang.model.parser.builder.api.ChildNodeBuilder;
57 import org.opendaylight.controller.yang.model.parser.builder.api.DataSchemaNodeBuilder;
58 import org.opendaylight.controller.yang.model.parser.builder.api.GroupingBuilder;
59 import org.opendaylight.controller.yang.model.parser.builder.api.TypeAwareBuilder;
60 import org.opendaylight.controller.yang.model.parser.builder.api.TypeDefinitionBuilder;
61 import org.opendaylight.controller.yang.model.parser.builder.api.UsesNodeBuilder;
62 import org.opendaylight.controller.yang.model.parser.builder.impl.AnyXmlBuilder;
63 import org.opendaylight.controller.yang.model.parser.builder.impl.ChoiceBuilder;
64 import org.opendaylight.controller.yang.model.parser.builder.impl.ContainerSchemaNodeBuilder;
65 import org.opendaylight.controller.yang.model.parser.builder.impl.IdentitySchemaNodeBuilder;
66 import org.opendaylight.controller.yang.model.parser.builder.impl.IdentityrefTypeBuilder;
67 import org.opendaylight.controller.yang.model.parser.builder.impl.LeafListSchemaNodeBuilder;
68 import org.opendaylight.controller.yang.model.parser.builder.impl.LeafSchemaNodeBuilder;
69 import org.opendaylight.controller.yang.model.parser.builder.impl.ListSchemaNodeBuilder;
70 import org.opendaylight.controller.yang.model.parser.builder.impl.ModuleBuilder;
71 import org.opendaylight.controller.yang.model.parser.builder.impl.TypedefBuilder;
72 import org.opendaylight.controller.yang.model.parser.builder.impl.UnionTypeBuilder;
73 import org.opendaylight.controller.yang.model.parser.util.ParserUtils;
74 import org.opendaylight.controller.yang.model.parser.util.RefineHolder;
75 import org.opendaylight.controller.yang.model.parser.util.RefineHolder.Refine;
76 import org.opendaylight.controller.yang.model.parser.util.TypeConstraints;
77 import org.opendaylight.controller.yang.model.parser.util.YangParseException;
78 import org.opendaylight.controller.yang.model.util.ExtendedType;
79 import org.opendaylight.controller.yang.model.util.IdentityrefType;
80 import org.opendaylight.controller.yang.model.util.UnknownType;
81 import org.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.getTypedef();
287                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref)));
288                 } else {
289                     final TypeDefinitionBuilder resolvedType = resolveType(
290                             nodeToResolve, modules, module);
291                     nodeToResolve.setType(resolvedType);
292                 }
293             }
294         }
295     }
296
297     private TypeDefinitionBuilder resolveType(
298             final TypeAwareBuilder typeToResolve,
299             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
300             final ModuleBuilder builder) {
301         final TypeConstraints constraints = new TypeConstraints();
302
303         final TypeDefinitionBuilder targetType = getTypedefBuilder(
304                 typeToResolve, modules, builder);
305         final TypeConstraints tConstraints = findConstraints(typeToResolve,
306                 constraints, modules, builder);
307         targetType.setRanges(tConstraints.getRange());
308         targetType.setLengths(tConstraints.getLength());
309         targetType.setPatterns(tConstraints.getPatterns());
310         targetType.setFractionDigits(tConstraints.getFractionDigits());
311
312         return targetType;
313     }
314
315     private TypeDefinitionBuilder resolveTypeUnion(
316             final TypeAwareBuilder typeToResolve,
317             final UnknownType unknownType,
318             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
319             final ModuleBuilder builder) {
320         final TypeConstraints constraints = new TypeConstraints();
321
322         final TypeDefinitionBuilder targetType = getUnionBuilder(typeToResolve,
323                 unknownType, modules, builder);
324         final TypeConstraints tConstraints = findConstraints(typeToResolve,
325                 constraints, modules, builder);
326         targetType.setRanges(tConstraints.getRange());
327         targetType.setLengths(tConstraints.getLength());
328         targetType.setPatterns(tConstraints.getPatterns());
329         targetType.setFractionDigits(tConstraints.getFractionDigits());
330
331         return targetType;
332     }
333
334     private TypeDefinitionBuilder getTypedefBuilder(
335             final TypeAwareBuilder nodeToResolve,
336             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
337             final ModuleBuilder builder) {
338
339         final TypeDefinition<?> nodeToResolveBase = nodeToResolve.getType();
340         if (nodeToResolveBase != null
341                 && !(nodeToResolveBase instanceof UnknownType)) {
342             return (TypeDefinitionBuilder) nodeToResolve;
343         }
344
345         final UnknownType unknownType = (UnknownType) nodeToResolve.getType();
346         final QName unknownTypeQName = unknownType.getQName();
347
348         // search for module which contains referenced typedef
349         final ModuleBuilder dependentModule = findDependentModule(modules,
350                 builder, unknownTypeQName.getPrefix());
351         final TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilderByName(
352                 dependentModule, unknownTypeQName.getLocalName());
353
354         final TypeDefinitionBuilder lookedUpBuilderCopy = copyTypedefBuilder(
355                 lookedUpBuilder, nodeToResolve instanceof TypeDefinitionBuilder);
356         final TypeDefinitionBuilder resolvedCopy = resolveCopiedBuilder(
357                 lookedUpBuilderCopy, modules, dependentModule);
358         return resolvedCopy;
359     }
360
361     private TypeDefinitionBuilder getUnionBuilder(
362             final TypeAwareBuilder nodeToResolve,
363             final UnknownType unknownType,
364             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
365             final ModuleBuilder module) {
366
367         final TypeDefinition<?> baseTypeToResolve = nodeToResolve.getType();
368         if (baseTypeToResolve != null
369                 && !(baseTypeToResolve instanceof UnknownType)) {
370             return (TypeDefinitionBuilder) nodeToResolve;
371         }
372
373         final QName unknownTypeQName = unknownType.getQName();
374         // search for module which contains referenced typedef
375         final ModuleBuilder dependentModule = findDependentModule(modules,
376                 module, unknownTypeQName.getPrefix());
377         final TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilderByName(
378                 dependentModule, unknownTypeQName.getLocalName());
379
380         final TypeDefinitionBuilder lookedUpBuilderCopy = copyTypedefBuilder(
381                 lookedUpBuilder, nodeToResolve instanceof TypeDefinitionBuilder);
382         final TypeDefinitionBuilder resolvedCopy = resolveCopiedBuilder(
383                 lookedUpBuilderCopy, modules, dependentModule);
384         return resolvedCopy;
385     }
386
387     private TypeDefinitionBuilder copyTypedefBuilder(
388             final TypeDefinitionBuilder old, final boolean seekByTypedefBuilder) {
389         if (old instanceof UnionTypeBuilder) {
390             final UnionTypeBuilder oldUnion = (UnionTypeBuilder) old;
391             final UnionTypeBuilder newUnion = new UnionTypeBuilder();
392             for (TypeDefinition<?> td : oldUnion.getTypes()) {
393                 newUnion.setType(td);
394             }
395             for (TypeDefinitionBuilder tdb : oldUnion.getTypedefs()) {
396                 newUnion.setType(copyTypedefBuilder(tdb, true));
397             }
398             return newUnion;
399         }
400
401         final QName oldName = old.getQName();
402         final QName newName = new QName(oldName.getNamespace(),
403                 oldName.getRevision(), oldName.getPrefix(),
404                 oldName.getLocalName());
405         final TypeDefinitionBuilder tdb = new TypedefBuilder(newName);
406
407         tdb.setRanges(old.getRanges());
408         tdb.setLengths(old.getLengths());
409         tdb.setPatterns(old.getPatterns());
410         tdb.setFractionDigits(old.getFractionDigits());
411
412         final TypeDefinition<?> oldType = old.getType();
413         if (oldType == null) {
414             tdb.setType(old.getTypedef());
415         } else {
416             tdb.setType(oldType);
417         }
418
419         if (!seekByTypedefBuilder) {
420             tdb.setDescription(old.getDescription());
421             tdb.setReference(old.getReference());
422             tdb.setStatus(old.getStatus());
423             tdb.setDefaultValue(old.getDefaultValue());
424             tdb.setUnits(old.getUnits());
425         }
426         return tdb;
427     }
428
429     private TypeDefinitionBuilder resolveCopiedBuilder(
430             final TypeDefinitionBuilder copy,
431             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
432             final ModuleBuilder builder) {
433
434         if (copy instanceof UnionTypeBuilder) {
435             final UnionTypeBuilder union = (UnionTypeBuilder) copy;
436             final List<TypeDefinition<?>> unionTypes = union.getTypes();
437             final List<UnknownType> toRemove = new ArrayList<UnknownType>();
438             for (TypeDefinition<?> td : unionTypes) {
439                 if (td instanceof UnknownType) {
440                     final UnknownType unknownType = (UnknownType) td;
441                     final TypeDefinitionBuilder resolvedType = resolveTypeUnion(
442                             union, unknownType, modules, builder);
443                     union.setType(resolvedType);
444                     toRemove.add(unknownType);
445                 }
446             }
447             unionTypes.removeAll(toRemove);
448
449             return union;
450         }
451
452         final TypeDefinition<?> base = copy.getType();
453         final TypeDefinitionBuilder baseTdb = copy.getTypedef();
454         if (base != null && !(base instanceof UnknownType)) {
455             return copy;
456         } else if (base instanceof UnknownType) {
457             final UnknownType unknownType = (UnknownType) base;
458             final QName unknownTypeQName = unknownType.getQName();
459             final String unknownTypePrefix = unknownTypeQName.getPrefix();
460             final ModuleBuilder dependentModule = findDependentModule(modules,
461                     builder, unknownTypePrefix);
462             final TypeDefinitionBuilder utBuilder = getTypedefBuilder(copy,
463                     modules, dependentModule);
464             copy.setType(utBuilder);
465             return copy;
466         } else if (base == null && baseTdb != null) {
467             // make a copy of baseTypeDef and call again
468             final TypeDefinitionBuilder baseTdbCopy = copyTypedefBuilder(
469                     baseTdb, true);
470             final TypeDefinitionBuilder baseTdbCopyResolved = resolveCopiedBuilder(
471                     baseTdbCopy, modules, builder);
472             copy.setType(baseTdbCopyResolved);
473             return copy;
474         } else {
475             throw new IllegalStateException(
476                     "TypeDefinitionBuilder in unexpected state");
477         }
478     }
479
480     private TypeDefinitionBuilder findTypedefBuilder(
481             final QName unknownTypeQName,
482             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
483             final ModuleBuilder builder) {
484
485         // search for module which contains referenced typedef
486         final ModuleBuilder dependentModule = findDependentModule(modules,
487                 builder, unknownTypeQName.getPrefix());
488
489         final TypeDefinitionBuilder lookedUpBuilder = findTypedefBuilderByName(
490                 dependentModule, unknownTypeQName.getLocalName());
491
492         return copyTypedefBuilder(lookedUpBuilder, true);
493     }
494
495     private TypeConstraints findConstraints(
496             final TypeAwareBuilder nodeToResolve,
497             final TypeConstraints constraints,
498             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
499             final ModuleBuilder builder) {
500
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     /**
567      * Go through all typedef statements from given module and search for one
568      * with given name
569      *
570      * @param typedefs
571      *            typedef statements to search
572      * @param name
573      *            name of searched typedef
574      * @return typedef with name equals to given name
575      */
576     private TypeDefinitionBuilder findTypedefBuilderByName(
577             final ModuleBuilder dependentModule, final String name) {
578         TypeDefinitionBuilder result = null;
579         final Set<TypeDefinitionBuilder> typedefs = dependentModule
580                 .getModuleTypedefs();
581         for (TypeDefinitionBuilder td : typedefs) {
582             if (td.getQName().getLocalName().equals(name)) {
583                 result = td;
584                 break;
585             }
586         }
587         if (result == null) {
588             throw new YangParseException("Target module '"
589                     + dependentModule.getName()
590                     + "' does not contain typedef '" + name + "'.");
591         }
592         return result;
593     }
594
595     /**
596      * Pull restriction from referenced type and add them to given constraints
597      *
598      * @param referencedType
599      * @param constraints
600      */
601     private void mergeConstraints(final TypeDefinition<?> referencedType,
602             final TypeConstraints constraints) {
603
604         if (referencedType instanceof DecimalTypeDefinition) {
605             constraints.addRanges(((DecimalTypeDefinition) referencedType)
606                     .getRangeStatements());
607             constraints
608                     .setFractionDigits(((DecimalTypeDefinition) referencedType)
609                             .getFractionDigits());
610         } else if (referencedType instanceof IntegerTypeDefinition) {
611             constraints.addRanges(((IntegerTypeDefinition) referencedType)
612                     .getRangeStatements());
613         } else if (referencedType instanceof StringTypeDefinition) {
614             constraints.addPatterns(((StringTypeDefinition) referencedType)
615                     .getPatterns());
616             constraints.addLengths(((StringTypeDefinition) referencedType)
617                     .getLengthStatements());
618         } else if (referencedType instanceof BinaryTypeDefinition) {
619             constraints.addLengths(((BinaryTypeDefinition) referencedType)
620                     .getLengthConstraints());
621         }
622     }
623
624     /**
625      * Go through all augmentation definitions and resolve them. This method
626      * also finds referenced node and add child nodes to it.
627      *
628      * @param modules
629      *            all available modules
630      */
631     private void resolveAugments(
632             final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
633         final List<ModuleBuilder> allModulesList = new ArrayList<ModuleBuilder>();
634         final Set<ModuleBuilder> allModulesSet = new HashSet<ModuleBuilder>();
635         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
636                 .entrySet()) {
637             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue()
638                     .entrySet()) {
639                 allModulesList.add(inner.getValue());
640                 allModulesSet.add(inner.getValue());
641             }
642         }
643
644         for (int i = 0; i < allModulesList.size(); i++) {
645             final ModuleBuilder module = allModulesList.get(i);
646             // try to resolve augments in module
647             resolveAugment(modules, module);
648             // while all augments are not resolved
649             final Iterator<ModuleBuilder> allModulesIterator = allModulesSet
650                     .iterator();
651             while (!(module.getAugmentsResolved() == module.getAddedAugments()
652                     .size())) {
653                 ModuleBuilder nextModule = null;
654                 // try resolve other module augments
655                 try {
656                     nextModule = allModulesIterator.next();
657                     resolveAugment(modules, nextModule);
658                 } catch (NoSuchElementException e) {
659                     throw new YangParseException(
660                             "Failed to resolve augments in module '"
661                                     + module.getName() + "'.", e);
662                 }
663                 // then try to resolve first module again
664                 resolveAugment(modules, module);
665             }
666         }
667     }
668
669     private void resolveAugment(
670             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
671             final ModuleBuilder module) {
672         if (module.getAugmentsResolved() < module.getAddedAugments().size()) {
673             for (AugmentationSchemaBuilder augmentBuilder : module
674                     .getAddedAugments()) {
675                 final SchemaPath augmentTargetSchemaPath = augmentBuilder
676                         .getTargetPath();
677                 final List<QName> path = augmentTargetSchemaPath.getPath();
678
679                 int i = 0;
680                 final QName qname = path.get(i);
681                 String prefix = qname.getPrefix();
682                 if(prefix == null) {
683                     prefix = module.getPrefix();
684                 }
685
686                 DataSchemaNodeBuilder currentParent = null;
687                 final ModuleBuilder dependentModule = findDependentModule(
688                         modules, module, prefix);
689                 for (DataSchemaNodeBuilder child : dependentModule
690                         .getChildNodes()) {
691                     final QName childQName = child.getQName();
692                     if (childQName.getLocalName().equals(qname.getLocalName())) {
693                         currentParent = child;
694                         i++;
695                         break;
696                     }
697                 }
698
699                 for (; i < path.size(); i++) {
700                     final QName currentQName = path.get(i);
701                     DataSchemaNodeBuilder newParent = null;
702                     for (DataSchemaNodeBuilder child : ((ChildNodeBuilder) currentParent)
703                             .getChildNodes()) {
704                         final QName childQName = child.getQName();
705                         if (childQName.getLocalName().equals(
706                                 currentQName.getLocalName())) {
707                             newParent = child;
708                             break;
709                         }
710                     }
711                     if (newParent == null) {
712                         break; // node not found, quit search
713                     } else {
714                         currentParent = newParent;
715                     }
716                 }
717
718                 final QName currentQName = currentParent.getQName();
719                 final QName lastAugmentPathElement = path.get(path.size() - 1);
720
721                 if (currentQName.getLocalName().equals(
722                         lastAugmentPathElement.getLocalName())) {
723                     fillAugmentTarget(augmentBuilder,
724                             (ChildNodeBuilder) currentParent);
725                     ((AugmentationTargetBuilder) currentParent)
726                             .addAugmentation(augmentBuilder);
727                     module.augmentResolved();
728                 }
729             }
730         }
731     }
732
733     /**
734      * Add all augment's child nodes to given target.
735      *
736      * @param augment
737      * @param target
738      */
739     private void fillAugmentTarget(final AugmentationSchemaBuilder augment,
740             final ChildNodeBuilder target) {
741         for (DataSchemaNodeBuilder builder : augment.getChildNodes()) {
742             builder.setAugmenting(true);
743             target.addChildNode(builder);
744         }
745     }
746
747     /**
748      * Go through identity statements defined in current module and resolve
749      * their 'base' statement if present.
750      *
751      * @param modules
752      *            all modules
753      * @param module
754      *            module being resolved
755      */
756     private void resolveIdentities(
757             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
758             final ModuleBuilder module) {
759         final Set<IdentitySchemaNodeBuilder> identities = module
760                 .getAddedIdentities();
761         for (IdentitySchemaNodeBuilder identity : identities) {
762             final String baseIdentityName = identity.getBaseIdentityName();
763             if (baseIdentityName != null) {
764                 String baseIdentityPrefix = null;
765                 String baseIdentityLocalName = null;
766                 if (baseIdentityName.contains(":")) {
767                     final String[] splitted = baseIdentityName.split(":");
768                     baseIdentityPrefix = splitted[0];
769                     baseIdentityLocalName = splitted[1];
770                 } else {
771                     baseIdentityPrefix = module.getPrefix();
772                     baseIdentityLocalName = baseIdentityName;
773                 }
774                 final ModuleBuilder dependentModule = findDependentModule(
775                         modules, module, baseIdentityPrefix);
776
777                 final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule
778                         .getAddedIdentities();
779                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
780                     if (idBuilder.getQName().getLocalName()
781                             .equals(baseIdentityLocalName)) {
782                         identity.setBaseIdentity(idBuilder);
783                     }
784                 }
785             }
786         }
787     }
788
789     /**
790      * Go through uses statements defined in current module and resolve their
791      * refine statements.
792      *
793      * @param modules
794      *            all modules
795      * @param module
796      *            module being resolved
797      */
798     private void resolveUses(
799             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
800             final ModuleBuilder module) {
801         final Map<List<String>, UsesNodeBuilder> moduleUses = module
802                 .getAddedUsesNodes();
803         for (Map.Entry<List<String>, UsesNodeBuilder> entry : moduleUses
804                 .entrySet()) {
805             final List<String> key = entry.getKey();
806             final UsesNodeBuilder usesNode = entry.getValue();
807
808             final String groupingName = key.get(key.size() - 1);
809
810             final List<RefineHolder> refines = usesNode.getRefines();
811             for (RefineHolder refine : refines) {
812                 final Refine refineType = refine.getType();
813                 // refine statements
814                 final String defaultStr = refine.getDefaultStr();
815                 final Boolean mandatory = refine.isMandatory();
816                 final MustDefinition must = refine.getMust();
817                 final Boolean presence = refine.isPresence();
818                 final Integer min = refine.getMinElements();
819                 final Integer max = refine.getMaxElements();
820
821                 switch (refineType) {
822                 case LEAF:
823                     final LeafSchemaNodeBuilder leaf = (LeafSchemaNodeBuilder) getRefineTargetBuilder(
824                             groupingName, refine, modules, module);
825                     if (defaultStr != null && !("".equals(defaultStr))) {
826                         leaf.setDefaultStr(defaultStr);
827                     }
828                     if (mandatory != null) {
829                         leaf.getConstraints().setMandatory(mandatory);
830                     }
831                     if (must != null) {
832                         leaf.getConstraints().addMustDefinition(must);
833                     }
834                     usesNode.addRefineNode(leaf);
835                     break;
836                 case CONTAINER:
837                     final ContainerSchemaNodeBuilder container = (ContainerSchemaNodeBuilder) getRefineTargetBuilder(
838                             groupingName, refine, modules, module);
839                     if (presence != null) {
840                         container.setPresence(presence);
841                     }
842                     if (must != null) {
843                         container.getConstraints().addMustDefinition(must);
844                     }
845                     usesNode.addRefineNode(container);
846                     break;
847                 case LIST:
848                     final ListSchemaNodeBuilder list = (ListSchemaNodeBuilder) getRefineTargetBuilder(
849                             groupingName, refine, modules, module);
850                     if (must != null) {
851                         list.getConstraints().addMustDefinition(must);
852                     }
853                     if (min != null) {
854                         list.getConstraints().setMinElements(min);
855                     }
856                     if (max != null) {
857                         list.getConstraints().setMaxElements(max);
858                     }
859                     break;
860                 case LEAF_LIST:
861                     final LeafListSchemaNodeBuilder leafList = (LeafListSchemaNodeBuilder) getRefineTargetBuilder(
862                             groupingName, refine, modules, module);
863                     if (must != null) {
864                         leafList.getConstraints().addMustDefinition(must);
865                     }
866                     if (min != null) {
867                         leafList.getConstraints().setMinElements(min);
868                     }
869                     if (max != null) {
870                         leafList.getConstraints().setMaxElements(max);
871                     }
872                     break;
873                 case CHOICE:
874                     final ChoiceBuilder choice = (ChoiceBuilder) getRefineTargetBuilder(
875                             groupingName, refine, modules, module);
876                     if (defaultStr != null) {
877                         choice.setDefaultCase(defaultStr);
878                     }
879                     if (mandatory != null) {
880                         choice.getConstraints().setMandatory(mandatory);
881                     }
882                     break;
883                 case ANYXML:
884                     final AnyXmlBuilder anyXml = (AnyXmlBuilder) getRefineTargetBuilder(
885                             groupingName, refine, modules, module);
886                     if (mandatory != null) {
887                         anyXml.getConstraints().setMandatory(mandatory);
888                     }
889                     if (must != null) {
890                         anyXml.getConstraints().addMustDefinition(must);
891                     }
892                 }
893             }
894         }
895
896     }
897
898     /**
899      * Find original builder of refine node and return copy of this builder.
900      *
901      * @param groupingPath
902      *            path to grouping which contains node to refine
903      * @param refine
904      *            refine object containing informations about refine
905      * @param modules
906      *            all loaded modules
907      * @param module
908      *            current module
909      * @return copy of Builder object of node to be refined if it is present in
910      *         grouping, null otherwise
911      */
912     private Builder getRefineTargetBuilder(final String groupingPath,
913             final RefineHolder refine,
914             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
915             final ModuleBuilder module) {
916         Builder result = null;
917         final Builder lookedUpBuilder = findRefineTargetBuilder(groupingPath,
918                 refine.getName(), modules, module);
919         if (lookedUpBuilder instanceof LeafSchemaNodeBuilder) {
920             result = ParserUtils
921                     .copyLeafBuilder((LeafSchemaNodeBuilder) lookedUpBuilder);
922         } else if (lookedUpBuilder instanceof ContainerSchemaNodeBuilder) {
923             result = ParserUtils
924                     .copyContainerBuilder((ContainerSchemaNodeBuilder) lookedUpBuilder);
925         } else if (lookedUpBuilder instanceof ListSchemaNodeBuilder) {
926             result = ParserUtils
927                     .copyListBuilder((ListSchemaNodeBuilder) lookedUpBuilder);
928         } else if (lookedUpBuilder instanceof LeafListSchemaNodeBuilder) {
929             result = ParserUtils
930                     .copyLeafListBuilder((LeafListSchemaNodeBuilder) lookedUpBuilder);
931         } else if (lookedUpBuilder instanceof ChoiceBuilder) {
932             result = ParserUtils
933                     .copyChoiceBuilder((ChoiceBuilder) lookedUpBuilder);
934         } else if (lookedUpBuilder instanceof AnyXmlBuilder) {
935             result = ParserUtils
936                     .copyAnyXmlBuilder((AnyXmlBuilder) lookedUpBuilder);
937         } else {
938             throw new YangParseException("Target '" + refine.getName()
939                     + "' can not be refined");
940         }
941         return result;
942     }
943
944     /**
945      * Find builder of refine node.
946      *
947      * @param groupingPath
948      *            path to grouping which contains node to refine
949      * @param refineNodeName
950      *            name of node to be refined
951      * @param modules
952      *            all loaded modules
953      * @param module
954      *            current module
955      * @return Builder object of refine node if it is present in grouping, null
956      *         otherwise
957      */
958     private Builder findRefineTargetBuilder(final String groupingPath,
959             final String refineNodeName,
960             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
961             final ModuleBuilder module) {
962         final SchemaPath path = ParserUtils.parseUsesPath(groupingPath);
963         final List<String> builderPath = new ArrayList<String>();
964         String prefix = null;
965         for (QName qname : path.getPath()) {
966             builderPath.add(qname.getLocalName());
967             prefix = qname.getPrefix();
968         }
969         if (prefix == null) {
970             prefix = module.getPrefix();
971         }
972
973         final ModuleBuilder dependentModule = findDependentModule(modules,
974                 module, prefix);
975         builderPath.add(0, "grouping");
976         builderPath.add(0, dependentModule.getName());
977         final GroupingBuilder builder = (GroupingBuilder) dependentModule
978                 .getNode(builderPath);
979
980         return builder.getChildNode(refineNodeName);
981     }
982
983     private QName findFullQName(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
984             final ModuleBuilder module, final IdentityrefTypeBuilder idref) {
985         QName result = null;
986         String baseString = idref.getBaseString();
987         if(baseString.contains(":")) {
988             String[] splittedBase = baseString.split(":");
989             if(splittedBase.length > 2) {
990                 throw new YangParseException("Failed to parse identity base: "+ baseString);
991             }
992             String prefix = splittedBase[0];
993             String name = splittedBase[1];
994             ModuleBuilder dependentModule = findDependentModule(modules, module, prefix);
995             result = new QName(dependentModule.getNamespace(), dependentModule.getRevision(), prefix, name);
996         } else {
997             result = new QName(module.getNamespace(), module.getRevision(), module.getPrefix(), baseString);
998         }
999         return result;
1000     }
1001
1002     /**
1003      * Find dependent module based on given prefix
1004      *
1005      * @param modules
1006      *            all available modules
1007      * @param module
1008      *            current module
1009      * @param prefix
1010      *            target module prefix
1011      * @return
1012      */
1013     private ModuleBuilder findDependentModule(
1014             final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1015             final ModuleBuilder module, final String prefix) {
1016         ModuleBuilder dependentModule = null;
1017         Date dependentModuleRevision = null;
1018
1019         if (prefix.equals(module.getPrefix())) {
1020             dependentModule = module;
1021         } else {
1022             final ModuleImport dependentModuleImport = getModuleImport(module,
1023                     prefix);
1024             if (dependentModuleImport == null) {
1025                 throw new YangParseException("No import found with prefix '"
1026                         + prefix + "' in module " + module.getName() + "'.");
1027             }
1028             final String dependentModuleName = dependentModuleImport
1029                     .getModuleName();
1030             dependentModuleRevision = dependentModuleImport.getRevision();
1031
1032             final TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules
1033                     .get(dependentModuleName);
1034             if (moduleBuildersByRevision == null) {
1035                 throw new YangParseException(
1036                         "Failed to find dependent module '"
1037                                 + dependentModuleName + "' needed by module '"
1038                                 + module.getName() + "'.");
1039             }
1040             if (dependentModuleRevision == null) {
1041                 dependentModule = moduleBuildersByRevision.lastEntry()
1042                         .getValue();
1043             } else {
1044                 dependentModule = moduleBuildersByRevision
1045                         .get(dependentModuleRevision);
1046             }
1047         }
1048
1049         if (dependentModule == null) {
1050             throw new YangParseException(
1051                     "Failed to find dependent module with prefix '" + prefix
1052                             + "' and revision '" + dependentModuleRevision
1053                             + "'.");
1054         }
1055         return dependentModule;
1056     }
1057
1058     /**
1059      * Get module import referenced by given prefix.
1060      *
1061      * @param builder
1062      *            module to search
1063      * @param prefix
1064      *            prefix associated with import
1065      * @return ModuleImport based on given prefix
1066      */
1067     private ModuleImport getModuleImport(final ModuleBuilder builder,
1068             final String prefix) {
1069         ModuleImport moduleImport = null;
1070         for (ModuleImport mi : builder.getModuleImports()) {
1071             if (mi.getPrefix().equals(prefix)) {
1072                 moduleImport = mi;
1073                 break;
1074             }
1075         }
1076         return moduleImport;
1077     }
1078
1079     private static class SchemaContextImpl implements SchemaContext {
1080         private final Set<Module> modules;
1081
1082         private SchemaContextImpl(final Set<Module> modules) {
1083             this.modules = modules;
1084         }
1085
1086         @Override
1087         public Set<DataSchemaNode> getDataDefinitions() {
1088             final Set<DataSchemaNode> dataDefs = new HashSet<DataSchemaNode>();
1089             for (Module m : modules) {
1090                 dataDefs.addAll(m.getChildNodes());
1091             }
1092             return dataDefs;
1093         }
1094
1095         @Override
1096         public Set<Module> getModules() {
1097             return modules;
1098         }
1099
1100         @Override
1101         public Set<NotificationDefinition> getNotifications() {
1102             final Set<NotificationDefinition> notifications = new HashSet<NotificationDefinition>();
1103             for (Module m : modules) {
1104                 notifications.addAll(m.getNotifications());
1105             }
1106             return notifications;
1107         }
1108
1109         @Override
1110         public Set<RpcDefinition> getOperations() {
1111             final Set<RpcDefinition> rpcs = new HashSet<RpcDefinition>();
1112             for (Module m : modules) {
1113                 rpcs.addAll(m.getRpcs());
1114             }
1115             return rpcs;
1116         }
1117
1118         @Override
1119         public Set<ExtensionDefinition> getExtensions() {
1120             final Set<ExtensionDefinition> extensions = new HashSet<ExtensionDefinition>();
1121             for (Module m : modules) {
1122                 extensions.addAll(m.getExtensionSchemaNodes());
1123             }
1124             return extensions;
1125         }
1126
1127         @Override
1128         public Module findModuleByName(final String name, final Date revision) {
1129             if ((name != null) && (revision != null)) {
1130                 for (final Module module : modules) {
1131                     if (module.getName().equals(name)
1132                             && module.getRevision().equals(revision)) {
1133                         return module;
1134                     }
1135                 }
1136             }
1137             return null;
1138         }
1139
1140         @Override
1141         public Module findModuleByNamespace(final URI namespace) {
1142             if (namespace != null) {
1143                 for (final Module module : modules) {
1144                     if (module.getNamespace().equals(namespace)) {
1145                         return module;
1146                     }
1147                 }
1148             }
1149             return null;
1150         }
1151     }
1152
1153 }