2eff93d47200ab1f57e236a12267b1796d940790
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / 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.yangtools.yang.parser.impl;
9
10 import static org.opendaylight.yangtools.yang.parser.util.ParserUtils.*;
11 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.*;
12
13 import java.io.File;
14 import java.io.FileInputStream;
15 import java.io.FileNotFoundException;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.util.ArrayList;
19 import java.util.Collections;
20 import java.util.Date;
21 import java.util.HashMap;
22 import java.util.HashSet;
23 import java.util.LinkedHashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
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.yangtools.antlrv4.code.gen.YangLexer;
35 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
36 import org.opendaylight.yangtools.yang.common.QName;
37 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
38 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
39 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
40 import org.opendaylight.yangtools.yang.model.api.Module;
41 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
42 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
44 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
45 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
46 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
47 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
48 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
49 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
50 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
52 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
53 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
54 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
55 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
56 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
57 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
58 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
59 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
60 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
61 import org.opendaylight.yangtools.yang.parser.util.CopyUtils;
62 import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
63 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
64 import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
65 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
66 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 import com.google.common.collect.Lists;
71 import com.google.common.collect.Maps;
72 import com.google.common.collect.Sets;
73
74 public final class YangParserImpl implements YangModelParser {
75     private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
76
77     @Override
78     public Set<Module> parseYangModels(final List<File> yangFiles) {
79         return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
80     }
81
82     @Override
83     public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
84         // TODO
85         throw new YangParseException("Not yet implemented");
86         // if (yangFiles != null) {
87         // final Map<InputStream, File> inputStreams = Maps.newHashMap();
88         //
89         // for (final File yangFile : yangFiles) {
90         // try {
91         // inputStreams.put(new FileInputStream(yangFile), yangFile);
92         // } catch (FileNotFoundException e) {
93         // LOG.warn("Exception while reading yang file: " + yangFile.getName(),
94         // e);
95         // }
96         // }
97         //
98         // Map<ModuleBuilder, InputStream> builderToStreamMap =
99         // Maps.newHashMap();
100         //
101         // final Map<String, TreeMap<Date, ModuleBuilder>> modules =
102         // resolveModuleBuilders(
103         // Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
104         //
105         // for (InputStream is : inputStreams.keySet()) {
106         // try {
107         // is.close();
108         // } catch (IOException e) {
109         // LOG.debug("Failed to close stream.");
110         // }
111         // }
112         //
113         // return new LinkedHashSet<Module>(buildWithContext(modules,
114         // context).values());
115         // }
116         // return Collections.emptySet();
117     }
118
119     @Override
120     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
121         return Sets.newHashSet(parseYangModelsFromStreamsMapped(yangModelStreams).values());
122     }
123
124     @Override
125     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, SchemaContext context) {
126         // TODO
127         throw new YangParseException("Not yet implemented");
128         // if (yangModelStreams != null) {
129         // Map<ModuleBuilder, InputStream> builderToStreamMap =
130         // Maps.newHashMap();
131         // final Map<String, TreeMap<Date, ModuleBuilder>> modules =
132         // resolveModuleBuildersWithContext(
133         // yangModelStreams, builderToStreamMap, context);
134         // return new LinkedHashSet<Module>(buildWithContext(modules,
135         // context).values());
136         // }
137         // return Collections.emptySet();
138     }
139
140     @Override
141     public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
142         if (yangFiles != null) {
143             final Map<InputStream, File> inputStreams = Maps.newHashMap();
144
145             for (final File yangFile : yangFiles) {
146                 try {
147                     inputStreams.put(new FileInputStream(yangFile), yangFile);
148                 } catch (FileNotFoundException e) {
149                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
150                 }
151             }
152
153             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
154             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
155                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
156
157             for (InputStream is : inputStreams.keySet()) {
158                 try {
159                     is.close();
160                 } catch (IOException e) {
161                     LOG.debug("Failed to close stream.");
162                 }
163             }
164
165             Map<File, Module> retVal = Maps.newLinkedHashMap();
166             Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
167
168             for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
169                 retVal.put(inputStreams.get(builderToStreamMap.get(builderToModule.getKey())),
170                         builderToModule.getValue());
171             }
172
173             return retVal;
174         }
175         return Collections.emptyMap();
176     }
177
178     @Override
179     public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
180         Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
181
182         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
183                 builderToStreamMap);
184         Map<InputStream, Module> retVal = Maps.newLinkedHashMap();
185         Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
186
187         for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
188             retVal.put(builderToStreamMap.get(builderToModule.getKey()), builderToModule.getValue());
189         }
190         return retVal;
191     }
192
193     @Override
194     public SchemaContext resolveSchemaContext(final Set<Module> modules) {
195         return new SchemaContextImpl(modules);
196     }
197
198     private ModuleBuilder[] parseModuleBuilders(List<InputStream> inputStreams,
199             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
200
201         final ParseTreeWalker walker = new ParseTreeWalker();
202         final List<ParseTree> trees = parseStreams(inputStreams);
203         final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
204
205         // validate yang
206         new YangModelBasicValidator(walker).validate(trees);
207
208         YangParserListenerImpl yangModelParser = null;
209         for (int i = 0; i < trees.size(); i++) {
210             yangModelParser = new YangParserListenerImpl();
211             walker.walk(yangModelParser, trees.get(i));
212             ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
213
214             // We expect the order of trees and streams has to be the same
215             streamToBuilderMap.put(moduleBuilder, inputStreams.get(i));
216             builders[i] = moduleBuilder;
217         }
218         return builders;
219     }
220
221     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
222             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
223         return resolveModuleBuildersWithContext(yangFileStreams, streamToBuilderMap, null);
224     }
225
226     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersWithContext(
227             final List<InputStream> yangFileStreams, final Map<ModuleBuilder, InputStream> streamToBuilderMap,
228             final SchemaContext context) {
229         final ModuleBuilder[] builders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
230
231         // Linked Hash Map MUST be used because Linked Hash Map preserves ORDER
232         // of items stored in map.
233         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = new LinkedHashMap<String, TreeMap<Date, ModuleBuilder>>();
234
235         // module dependency graph sorted
236         List<ModuleBuilder> sorted = null;
237         if (context == null) {
238             sorted = ModuleDependencySort.sort(builders);
239         } else {
240             sorted = ModuleDependencySort.sortWithContext(context, builders);
241         }
242
243         for (final ModuleBuilder builder : sorted) {
244             if (builder == null) {
245                 continue;
246             }
247             final String builderName = builder.getName();
248             Date builderRevision = builder.getRevision();
249             if (builderRevision == null) {
250                 builderRevision = new Date(0L);
251             }
252             TreeMap<Date, ModuleBuilder> builderByRevision = modules.get(builderName);
253             if (builderByRevision == null) {
254                 builderByRevision = new TreeMap<Date, ModuleBuilder>();
255             }
256             builderByRevision.put(builderRevision, builder);
257             modules.put(builderName, builderByRevision);
258         }
259         return modules;
260     }
261
262     private List<ParseTree> parseStreams(final List<InputStream> yangStreams) {
263         final List<ParseTree> trees = new ArrayList<ParseTree>();
264         for (InputStream yangStream : yangStreams) {
265             trees.add(parseStream(yangStream));
266         }
267         return trees;
268     }
269
270     private ParseTree parseStream(final InputStream yangStream) {
271         ParseTree result = null;
272         try {
273             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
274             final YangLexer lexer = new YangLexer(input);
275             final CommonTokenStream tokens = new CommonTokenStream(lexer);
276             final YangParser parser = new YangParser(tokens);
277             parser.removeErrorListeners();
278             parser.addErrorListener(new YangErrorListener());
279
280             result = parser.yang();
281         } catch (IOException e) {
282             LOG.warn("Exception while reading yang file: " + yangStream, e);
283         }
284         return result;
285     }
286
287     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
288         findUsesTargets(modules, null);
289
290         // fix unresolved nodes
291         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
292             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
293                 final ModuleBuilder moduleBuilder = childEntry.getValue();
294                 fixUnresolvedNodes(modules, moduleBuilder);
295             }
296         }
297
298         finishResolveDirtyNodes(modules);
299         resolveAugments(modules);
300         resolveUses(modules);
301         resolveDeviations(modules);
302
303         // build
304         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
305         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
306             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
307             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
308                 final ModuleBuilder moduleBuilder = childEntry.getValue();
309                 final Module module = moduleBuilder.build();
310                 modulesByRevision.put(childEntry.getKey(), module);
311                 result.put(moduleBuilder, module);
312             }
313         }
314         return result;
315     }
316
317     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
318             SchemaContext context) {
319         findUsesTargets(modules, context);
320
321         // fix unresolved nodes
322         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
323             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
324                 final ModuleBuilder moduleBuilder = childEntry.getValue();
325                 fixUnresolvedNodesWithContext(modules, moduleBuilder, context);
326             }
327         }
328         // TODO finishResolveDirtyNodesWithContext(modules, context)
329         resolveAugmentsWithContext(modules, context);
330         resolveUsesWithContext(modules, context);
331         resolveDeviationsWithContext(modules, context);
332
333         // build
334         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
335         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
336             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
337             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
338                 final ModuleBuilder moduleBuilder = childEntry.getValue();
339                 final Module module = moduleBuilder.build();
340                 modulesByRevision.put(childEntry.getKey(), module);
341                 result.put(moduleBuilder, module);
342             }
343         }
344         return result;
345     }
346
347     private void fixUnresolvedNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder) {
348         resolveDirtyNodes(modules, builder);
349         resolveIdentities(modules, builder);
350         resolveUnknownNodes(modules, builder);
351     }
352
353     private void fixUnresolvedNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
354             final ModuleBuilder builder, final SchemaContext context) {
355         resolveDirtyNodesWithContext(modules, builder, context);
356         resolveIdentitiesWithContext(modules, builder, context);
357         resolveUnknownNodesWithContext(modules, builder, context);
358     }
359
360     /**
361      * Search for dirty nodes (node which contains UnknownType) and resolve
362      * unknown types.
363      *
364      * @param modules
365      *            all available modules
366      * @param module
367      *            current module
368      */
369     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
370         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
371         if (!dirtyNodes.isEmpty()) {
372             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
373                 if (nodeToResolve instanceof UnionTypeBuilder) {
374                     // special handling for union types
375                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
376                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
377                     // special handling for identityref types
378                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
379                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
380                 } else {
381                     resolveType(nodeToResolve, modules, module);
382                 }
383             }
384         }
385     }
386
387     private void finishResolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
388         final Set<TypeAwareBuilder> dirtyNodes = new HashSet<>();
389         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
390             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
391                 dirtyNodes.addAll(inner.getValue().getDirtyNodes());
392             }
393         }
394
395         if (!dirtyNodes.isEmpty()) {
396             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
397                 if (nodeToResolve instanceof UnionTypeBuilder) {
398                     List<TypeDefinitionBuilder> newTypes = new ArrayList<>();
399                     List<TypeDefinitionBuilder> oldTypes = ((UnionTypeBuilder) nodeToResolve).getTypedefs();
400                     for (TypeDefinitionBuilder tdb : oldTypes) {
401                         TypeDefinitionBuilder newType = CopyUtils.copy(tdb, nodeToResolve, false);
402                         ParserUtils.correctTypeAwareNodePath(newType);
403                         newTypes.add(newType);
404                     }
405                     oldTypes.clear();
406                     oldTypes.addAll(newTypes);
407                 } else if (nodeToResolve.getType() instanceof IdentityrefType) {
408                     TypeDefinition<?> idRef = ParserUtils.createCorrectTypeDefinition(nodeToResolve.getPath(),
409                             nodeToResolve.getType());
410                     nodeToResolve.setType(idRef);
411                 } else {
412                     TypeDefinitionBuilder tdb = CopyUtils.copy(nodeToResolve.getTypedef(), nodeToResolve, false);
413                     ParserUtils.correctTypeAwareNodePath(tdb);
414                     nodeToResolve.setTypedef(tdb);
415                 }
416             }
417         }
418     }
419
420     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
421             final ModuleBuilder module, SchemaContext context) {
422         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
423         if (!dirtyNodes.isEmpty()) {
424             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
425                 if (nodeToResolve instanceof UnionTypeBuilder) {
426                     // special handling for union types
427                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
428                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
429                     // special handling for identityref types
430                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
431                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
432                 } else {
433                     resolveTypeWithContext(nodeToResolve, modules, module, context);
434                 }
435             }
436         }
437     }
438
439     /**
440      * Go through all augment definitions and perform augmentation. It is
441      * expected that modules are already sorted by their dependencies.
442      *
443      * @param modules
444      *            all loaded modules
445      */
446     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
447         // collect augments from all loaded modules
448         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
449         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
450             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
451                 allAugments.addAll(inner.getValue().getAllAugments());
452             }
453         }
454
455         for (int i = 0; i < allAugments.size(); i++) {
456             // pick one augment
457             final AugmentationSchemaBuilder augment = allAugments.get(i);
458             // create collection of others
459             List<AugmentationSchemaBuilder> others = new ArrayList<>(allAugments);
460             others.remove(augment);
461
462             // try to resolve it
463             boolean resolved = resolveAugment(modules, augment);
464             // while not resolved
465             int j = 0;
466             while (!(resolved) && j < others.size()) {
467                 // try to resolve next augment
468                 resolveAugment(modules, others.get(j));
469                 // then try to resolve first again
470                 resolved = resolveAugment(modules, augment);
471                 j++;
472
473             }
474
475             if (!resolved) {
476                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
477                         "Error in augment parsing: failed to find augment target");
478             }
479         }
480     }
481
482     /**
483      * Search for augment target and perform augmentation.
484      *
485      * @param modules
486      *            all loaded modules
487      * @param augmentBuilder
488      *            augment to resolve
489      * @return true if target node found, false otherwise
490      */
491     private boolean resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
492             final AugmentationSchemaBuilder augmentBuilder) {
493         if (augmentBuilder.isResolved()) {
494             return true;
495         }
496
497         int line = augmentBuilder.getLine();
498         ModuleBuilder module = getParentModule(augmentBuilder);
499         List<QName> path = augmentBuilder.getTargetPath().getPath();
500         Builder augmentParent = augmentBuilder.getParent();
501
502         Builder firstNodeParent = null;
503         if (augmentParent instanceof ModuleBuilder) {
504             // if augment is defined under module, parent of first node is
505             // target module
506             final QName firstNameInPath = path.get(0);
507             String prefix = firstNameInPath.getPrefix();
508             if (prefix == null) {
509                 prefix = module.getPrefix();
510             }
511             firstNodeParent = findDependentModuleBuilder(modules, module, prefix, line);
512         } else if (augmentParent instanceof UsesNodeBuilder) {
513             firstNodeParent = augmentParent.getParent();
514         } else {
515             // augment can be defined only under module or uses
516             throw new YangParseException(augmentBuilder.getModuleName(), line,
517                     "Failed to parse augment: Unresolved parent of augment: " + augmentParent);
518         }
519
520         return processAugmentation(augmentBuilder, firstNodeParent, path);
521     }
522
523     /**
524      * Go through all augment definitions and resolve them. This method works in
525      * same way as {@link #resolveAugments(Map)} except that if target node is
526      * not found in loaded modules, it search for target node in given context.
527      *
528      * @param modules
529      *            all loaded modules
530      * @param context
531      *            SchemaContext containing already resolved modules
532      */
533     private void resolveAugmentsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
534             final SchemaContext context) {
535         // collect augments from all loaded modules
536         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
537         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
538             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
539                 allAugments.addAll(inner.getValue().getAllAugments());
540             }
541         }
542
543         for (int i = 0; i < allAugments.size(); i++) {
544             // pick augment from list
545             final AugmentationSchemaBuilder augment = allAugments.get(i);
546             // try to resolve it
547             boolean resolved = resolveAugmentWithContext(modules, augment, context);
548             // while not resolved
549             int j = i + 1;
550             while (!(resolved) && j < allAugments.size()) {
551                 // try to resolve next augment
552                 resolveAugmentWithContext(modules, allAugments.get(j), context);
553                 // then try to resolve first again
554                 resolved = resolveAugmentWithContext(modules, augment, context);
555                 j++;
556             }
557
558             if (!resolved) {
559                 throw new YangParseException(augment.getModuleName(), augment.getLine(),
560                         "Error in augment parsing: failed to find augment target");
561             }
562         }
563     }
564
565     /**
566      * Search for augment target and perform augmentation.
567      *
568      * @param modules
569      *            all loaded modules
570      * @param augmentBuilder
571      *            augment to resolve
572      * @param context
573      *            SchemaContext containing already resolved modules
574      * @return true if target node found, false otherwise
575      */
576     private boolean resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
577             final AugmentationSchemaBuilder augmentBuilder, final SchemaContext context) {
578         if (augmentBuilder.isResolved()) {
579             return true;
580         }
581         int line = augmentBuilder.getLine();
582         ModuleBuilder module = getParentModule(augmentBuilder);
583         List<QName> path = augmentBuilder.getTargetPath().getPath();
584         final QName firstNameInPath = path.get(0);
585         String prefix = firstNameInPath.getPrefix();
586         if (prefix == null) {
587             prefix = module.getPrefix();
588         }
589         Builder augmentParent = augmentBuilder.getParent();
590         Builder currentParent = null;
591
592         if (augmentParent instanceof ModuleBuilder) {
593             // if augment is defined under module, first parent is target module
594             currentParent = findDependentModuleBuilder(modules, module, prefix, line);
595         } else if (augmentParent instanceof UsesNodeBuilder) {
596             currentParent = augmentParent.getParent();
597         } else {
598             // augment can be defined only under module or uses
599             throw new YangParseException(augmentBuilder.getModuleName(), augmentBuilder.getLine(),
600                     "Error in augment parsing: Unresolved parent of augment: " + augmentParent);
601         }
602
603         if (currentParent == null) {
604             return processAugmentationOnContext(augmentBuilder, path, module, prefix, context);
605         } else {
606             return processAugmentation(augmentBuilder, currentParent, path);
607         }
608     }
609
610     /**
611      * Go through identity statements defined in current module and resolve
612      * their 'base' statement if present.
613      *
614      * @param modules
615      *            all modules
616      * @param module
617      *            module being resolved
618      */
619     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
620         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
621         for (IdentitySchemaNodeBuilder identity : identities) {
622             final String baseIdentityName = identity.getBaseIdentityName();
623             if (baseIdentityName != null) {
624                 String baseIdentityPrefix = null;
625                 String baseIdentityLocalName = null;
626                 if (baseIdentityName.contains(":")) {
627                     final String[] splitted = baseIdentityName.split(":");
628                     baseIdentityPrefix = splitted[0];
629                     baseIdentityLocalName = splitted[1];
630                 } else {
631                     baseIdentityPrefix = module.getPrefix();
632                     baseIdentityLocalName = baseIdentityName;
633                 }
634                 final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, baseIdentityPrefix,
635                         identity.getLine());
636
637                 final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule.getIdentities();
638                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
639                     if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
640                         identity.setBaseIdentity(idBuilder);
641                     }
642                 }
643             }
644         }
645     }
646
647     /**
648      * Go through identity statements defined in current module and resolve
649      * their 'base' statement. Method tries to find base identity in given
650      * modules. If base identity is not found, method will search it in context.
651      *
652      * @param modules
653      *            all loaded modules
654      * @param module
655      *            current module
656      * @param context
657      *            SchemaContext containing already resolved modules
658      */
659     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
660             final ModuleBuilder module, final SchemaContext context) {
661         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
662         for (IdentitySchemaNodeBuilder identity : identities) {
663             final String baseIdentityName = identity.getBaseIdentityName();
664             if (baseIdentityName != null) {
665                 String baseIdentityPrefix = null;
666                 String baseIdentityLocalName = null;
667                 if (baseIdentityName.contains(":")) {
668                     final String[] splitted = baseIdentityName.split(":");
669                     baseIdentityPrefix = splitted[0];
670                     baseIdentityLocalName = splitted[1];
671                 } else {
672                     baseIdentityPrefix = module.getPrefix();
673                     baseIdentityLocalName = baseIdentityName;
674                 }
675                 final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
676                         baseIdentityPrefix, identity.getLine());
677
678                 if (dependentModuleBuilder == null) {
679                     final Module dependentModule = findModuleFromContext(context, module, baseIdentityPrefix,
680                             identity.getLine());
681                     final Set<IdentitySchemaNode> dependentModuleIdentities = dependentModule.getIdentities();
682                     for (IdentitySchemaNode idNode : dependentModuleIdentities) {
683                         if (idNode.getQName().getLocalName().equals(baseIdentityLocalName)) {
684                             identity.setBaseIdentity(idNode);
685                         }
686                     }
687                 } else {
688                     final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModuleBuilder
689                             .getIdentities();
690                     for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
691                         if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
692                             identity.setBaseIdentity(idBuilder);
693                         }
694                     }
695                 }
696             }
697         }
698     }
699
700     /**
701      * Find target grouping for all uses nodes.
702      *
703      * @param modules
704      *            all loaded modules
705      */
706     private void findUsesTargets(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
707         final List<UsesNodeBuilder> allUses = new ArrayList<>();
708         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
709             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
710                 allUses.addAll(inner.getValue().getAllUsesNodes());
711             }
712         }
713         for (UsesNodeBuilder usesNode : allUses) {
714             ModuleBuilder module = ParserUtils.getParentModule(usesNode);
715             final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
716                     module);
717             if (targetGroupingBuilder == null) {
718                 if (context == null) {
719                     throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
720                             + usesNode.getGroupingName() + "' not found.");
721                 } else {
722                     GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
723                             module, context);
724                     usesNode.setGroupingDefinition(targetGroupingDefinition);
725                     usesNode.setGroupingPath(targetGroupingDefinition.getPath());
726                 }
727             } else {
728                 usesNode.setGrouping(targetGroupingBuilder);
729                 usesNode.setGroupingPath(targetGroupingBuilder.getPath());
730             }
731         }
732     }
733
734     /**
735      * Copy data from uses target, update uses parent and perform refinement.
736      * Augmentations have to be resolved already.
737      *
738      * @param modules
739      *            all loaded modules
740      */
741     private void resolveUses(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
742         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
743             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
744                 ModuleBuilder module = inner.getValue();
745                 List<UsesNodeBuilder> usesNodes = null;
746                 boolean dataCollected = module.isAllUsesDataCollected();
747
748                 while (!dataCollected) {
749                     usesNodes = new ArrayList<>(module.getAllUsesNodes());
750                     for (UsesNodeBuilder usesNode : usesNodes) {
751                         if (!usesNode.isDataCollected()) {
752                             GroupingUtils.collectUsesData(usesNode);
753                         }
754                     }
755                     dataCollected = module.isAllUsesDataCollected();
756                 }
757             }
758         }
759
760         // new cycle is must because in collecting data process new uses could
761         // be created
762         final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
763         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
764             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
765                 allModulesUses.addAll(inner.getValue().getAllUsesNodes());
766             }
767         }
768
769         for (UsesNodeBuilder usesNode : allModulesUses) {
770             GroupingUtils.updateUsesParent(usesNode);
771             GroupingUtils.performRefine(usesNode);
772         }
773         for (UsesNodeBuilder usesNode : allModulesUses) {
774             GroupingUtils.fixUsesNodesPath(usesNode);
775         }
776     }
777
778     /**
779      *
780      * @param modules
781      *            all loaded modules
782      * @param context
783      *            SchemaContext containing already resolved modules
784      */
785     private void resolveUsesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
786             final SchemaContext context) {
787         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
788             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
789                 ModuleBuilder module = inner.getValue();
790                 List<UsesNodeBuilder> usesNodes = null;
791                 boolean dataCollected = module.isAllUsesDataCollected();
792
793                 while (!dataCollected) {
794                     usesNodes = new ArrayList<>(module.getAllUsesNodes());
795                     for (UsesNodeBuilder usesNode : usesNodes) {
796                         if (!usesNode.isDataCollected()) {
797                             if (usesNode.getGroupingBuilder() == null) {
798                                 GroupingUtils.collectUsesDataFromContext(usesNode);
799                             } else {
800                                 GroupingUtils.collectUsesData(usesNode);
801                             }
802                         }
803                     }
804                     dataCollected = module.isAllUsesDataCollected();
805                 }
806             }
807         }
808
809         // new cycle is must because in collecting data process new uses could
810         // be created
811         final List<UsesNodeBuilder> allModulesUses = new ArrayList<>();
812         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
813             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
814                 allModulesUses.addAll(inner.getValue().getAllUsesNodes());
815             }
816         }
817
818         for (UsesNodeBuilder usesNode : allModulesUses) {
819             GroupingUtils.updateUsesParent(usesNode);
820             GroupingUtils.performRefine(usesNode);
821         }
822         for (UsesNodeBuilder usesNode : allModulesUses) {
823             GroupingUtils.fixUsesNodesPath(usesNode);
824         }
825     }
826
827     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
828         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
829             QName nodeType = usnb.getNodeType();
830             if (nodeType.getNamespace() == null || nodeType.getRevision() == null) {
831                 try {
832                     ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
833                             usnb.getLine());
834                     QName newNodeType = new QName(dependentModule.getNamespace(), dependentModule.getRevision(),
835                             nodeType.getPrefix(), nodeType.getLocalName());
836                     usnb.setNodeType(newNodeType);
837                 } catch (YangParseException e) {
838                     LOG.debug(module.getName(), usnb.getLine(), "Failed to find unknown node type: " + nodeType);
839                 }
840             }
841         }
842     }
843
844     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
845             final ModuleBuilder module, final SchemaContext context) {
846         for (UnknownSchemaNodeBuilder unknownNodeBuilder : module.getAllUnknownNodes()) {
847             QName nodeType = unknownNodeBuilder.getNodeType();
848             if (nodeType.getNamespace() == null || nodeType.getRevision() == null) {
849                 try {
850                     ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
851                             nodeType.getPrefix(), unknownNodeBuilder.getLine());
852
853                     QName newNodeType = null;
854                     if (dependentModuleBuilder == null) {
855                         Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
856                                 unknownNodeBuilder.getLine());
857                         newNodeType = new QName(dependentModule.getNamespace(), dependentModule.getRevision(),
858                                 nodeType.getPrefix(), nodeType.getLocalName());
859                     } else {
860                         newNodeType = new QName(dependentModuleBuilder.getNamespace(),
861                                 dependentModuleBuilder.getRevision(), nodeType.getPrefix(), nodeType.getLocalName());
862                     }
863
864                     unknownNodeBuilder.setNodeType(newNodeType);
865                 } catch (YangParseException e) {
866                     LOG.debug(module.getName(), unknownNodeBuilder.getLine(), "Failed to find unknown node type: "
867                             + nodeType);
868                 }
869             }
870         }
871     }
872
873     /**
874      * Traverse through modules and resolve their deviation statements.
875      *
876      * @param modules
877      *            all loaded modules
878      */
879     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
880         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
881             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
882                 ModuleBuilder b = inner.getValue();
883                 resolveDeviation(modules, b);
884             }
885         }
886     }
887
888     /**
889      * Traverse through module and resolve its deviation statements.
890      *
891      * @param modules
892      *            all loaded modules
893      * @param module
894      *            module in which resolve deviations
895      */
896     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
897         for (DeviationBuilder dev : module.getDeviations()) {
898             int line = dev.getLine();
899             SchemaPath targetPath = dev.getTargetPath();
900             List<QName> path = targetPath.getPath();
901             QName q0 = path.get(0);
902             String prefix = q0.getPrefix();
903             if (prefix == null) {
904                 prefix = module.getPrefix();
905             }
906
907             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
908             processDeviation(dev, dependentModuleBuilder, path, module);
909         }
910     }
911
912     /**
913      * Traverse through modules and resolve their deviation statements with
914      * given context.
915      *
916      * @param modules
917      *            all loaded modules
918      * @param context
919      *            already resolved context
920      */
921     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
922             final SchemaContext context) {
923         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
924             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
925                 ModuleBuilder b = inner.getValue();
926                 resolveDeviationWithContext(modules, b, context);
927             }
928         }
929     }
930
931     /**
932      * Traverse through module and resolve its deviation statements with given
933      * context.
934      *
935      * @param modules
936      *            all loaded modules
937      * @param module
938      *            module in which resolve deviations
939      * @param context
940      *            already resolved context
941      */
942     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
943             final ModuleBuilder module, final SchemaContext context) {
944         for (DeviationBuilder dev : module.getDeviations()) {
945             int line = dev.getLine();
946             SchemaPath targetPath = dev.getTargetPath();
947             List<QName> path = targetPath.getPath();
948             QName q0 = path.get(0);
949             String prefix = q0.getPrefix();
950             if (prefix == null) {
951                 prefix = module.getPrefix();
952             }
953             String name = null;
954
955             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
956             if (dependentModuleBuilder == null) {
957                 Module dependentModule = findModuleFromContext(context, module, prefix, line);
958                 Object currentParent = dependentModule;
959
960                 for (int i = 0; i < path.size(); i++) {
961                     if (currentParent == null) {
962                         throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
963                     }
964                     QName q = path.get(i);
965                     name = q.getLocalName();
966                     if (currentParent instanceof DataNodeContainer) {
967                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
968                     }
969                 }
970
971                 if (currentParent == null) {
972                     throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
973                 }
974                 if (currentParent instanceof SchemaNode) {
975                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
976                 }
977
978             } else {
979                 processDeviation(dev, dependentModuleBuilder, path, module);
980             }
981         }
982     }
983
984     /**
985      * Correct deviation target path in deviation builder.
986      *
987      * @param dev
988      *            deviation
989      * @param dependentModuleBuilder
990      *            module containing deviation target
991      * @param path
992      *            current deviation target path
993      * @param module
994      *            current module
995      */
996     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
997             final List<QName> path, final ModuleBuilder module) {
998         final int line = dev.getLine();
999         Builder currentParent = dependentModuleBuilder;
1000
1001         for (int i = 0; i < path.size(); i++) {
1002             if (currentParent == null) {
1003                 throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
1004             }
1005             QName q = path.get(i);
1006             String name = q.getLocalName();
1007             if (currentParent instanceof DataNodeContainerBuilder) {
1008                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1009             }
1010         }
1011
1012         if (currentParent == null || !(currentParent instanceof SchemaNodeBuilder)) {
1013             throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
1014         }
1015         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
1016     }
1017
1018 }