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