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