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