Refactored parsing of yang uses statement.
[yangtools.git] / 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
12 import java.io.File;
13 import java.io.FileInputStream;
14 import java.io.FileNotFoundException;
15 import java.io.IOException;
16 import java.io.InputStream;
17 import java.net.URI;
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.Iterator;
24 import java.util.LinkedHashMap;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.NoSuchElementException;
30 import java.util.Set;
31 import java.util.TreeMap;
32
33 import org.antlr.v4.runtime.ANTLRInputStream;
34 import org.antlr.v4.runtime.CommonTokenStream;
35 import org.antlr.v4.runtime.tree.ParseTree;
36 import org.antlr.v4.runtime.tree.ParseTreeWalker;
37 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
38 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
39 import org.opendaylight.yangtools.yang.common.QName;
40 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
41 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
42 import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
43 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
44 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
45 import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
46 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
47 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
48 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
49 import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
50 import org.opendaylight.yangtools.yang.model.api.Module;
51 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
52 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
53 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
54 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
55 import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
56 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
57 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
58 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
59 import org.opendaylight.yangtools.yang.model.util.UnknownType;
60 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
61 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
62 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
63 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
64 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
65 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingMember;
66 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
67 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
68 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
69 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
70 import org.opendaylight.yangtools.yang.parser.builder.impl.AnyXmlBuilder;
71 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
72 import org.opendaylight.yangtools.yang.parser.builder.impl.ContainerSchemaNodeBuilder;
73 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
74 import org.opendaylight.yangtools.yang.parser.builder.impl.GroupingBuilderImpl;
75 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
76 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
77 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafListSchemaNodeBuilder;
78 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
79 import org.opendaylight.yangtools.yang.parser.builder.impl.ListSchemaNodeBuilder;
80 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
81 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder;
82 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl;
83 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
84 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
85 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
86 import org.opendaylight.yangtools.yang.parser.util.RefineHolder;
87 import org.opendaylight.yangtools.yang.parser.util.RefineUtils;
88 import org.opendaylight.yangtools.yang.parser.util.TypeConstraints;
89 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
90 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
91 import org.slf4j.Logger;
92 import org.slf4j.LoggerFactory;
93
94 import com.google.common.collect.Lists;
95 import com.google.common.collect.Maps;
96 import com.google.common.collect.Sets;
97
98 public final class YangParserImpl implements YangModelParser {
99     private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
100
101     @Override
102     public Set<Module> parseYangModels(final List<File> yangFiles) {
103         return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
104     }
105
106     @Override
107     public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
108         if (yangFiles != null) {
109             final Map<InputStream, File> inputStreams = Maps.newHashMap();
110
111             for (final File yangFile : yangFiles) {
112                 try {
113                     inputStreams.put(new FileInputStream(yangFile), yangFile);
114                 } catch (FileNotFoundException e) {
115                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
116                 }
117             }
118
119             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
120
121             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
122                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
123
124             for (InputStream is : inputStreams.keySet()) {
125                 try {
126                     is.close();
127                 } catch (IOException e) {
128                     LOG.debug("Failed to close stream.");
129                 }
130             }
131
132             return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
133         }
134         return Collections.emptySet();
135     }
136
137     @Override
138     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
139         return Sets.newHashSet(parseYangModelsFromStreamsMapped(yangModelStreams).values());
140     }
141
142     @Override
143     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, SchemaContext context) {
144         if (yangModelStreams != null) {
145             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
146             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersWithContext(
147                     yangModelStreams, builderToStreamMap, context);
148             return new LinkedHashSet<Module>(buildWithContext(modules, context).values());
149         }
150         return Collections.emptySet();
151     }
152
153     @Override
154     public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
155         if (yangFiles != null) {
156             final Map<InputStream, File> inputStreams = Maps.newHashMap();
157
158             for (final File yangFile : yangFiles) {
159                 try {
160                     inputStreams.put(new FileInputStream(yangFile), yangFile);
161                 } catch (FileNotFoundException e) {
162                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
163                 }
164             }
165
166             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
167             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
168                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
169
170             for (InputStream is : inputStreams.keySet()) {
171                 try {
172                     is.close();
173                 } catch (IOException e) {
174                     LOG.debug("Failed to close stream.");
175                 }
176             }
177
178             Map<File, Module> retVal = Maps.newLinkedHashMap();
179             Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
180
181             for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
182                 retVal.put(inputStreams.get(builderToStreamMap.get(builderToModule.getKey())),
183                         builderToModule.getValue());
184             }
185
186             return retVal;
187         }
188         return Collections.emptyMap();
189     }
190
191     @Override
192     public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
193         Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
194
195         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
196                 builderToStreamMap);
197         Map<InputStream, Module> retVal = Maps.newLinkedHashMap();
198         Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
199
200         for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
201             retVal.put(builderToStreamMap.get(builderToModule.getKey()), builderToModule.getValue());
202         }
203         return retVal;
204     }
205
206     @Override
207     public SchemaContext resolveSchemaContext(final Set<Module> modules) {
208         return new SchemaContextImpl(modules);
209     }
210
211     private ModuleBuilder[] parseModuleBuilders(List<InputStream> inputStreams,
212             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
213
214         final ParseTreeWalker walker = new ParseTreeWalker();
215         final List<ParseTree> trees = parseStreams(inputStreams);
216         final ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
217
218         // validate yang
219         new YangModelBasicValidator(walker).validate(trees);
220
221         YangParserListenerImpl yangModelParser = null;
222         for (int i = 0; i < trees.size(); i++) {
223             yangModelParser = new YangParserListenerImpl();
224             walker.walk(yangModelParser, trees.get(i));
225             ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
226
227             // We expect the order of trees and streams has to be the same
228             streamToBuilderMap.put(moduleBuilder, inputStreams.get(i));
229             builders[i] = moduleBuilder;
230         }
231         return builders;
232     }
233
234     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
235             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
236         return resolveModuleBuildersWithContext(yangFileStreams, streamToBuilderMap, null);
237     }
238
239     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersWithContext(
240             final List<InputStream> yangFileStreams, final Map<ModuleBuilder, InputStream> streamToBuilderMap,
241             final SchemaContext context) {
242         final ModuleBuilder[] builders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
243
244         // Linked Hash Map MUST be used because Linked Hash Map preserves ORDER
245         // of items stored in map.
246         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = new LinkedHashMap<String, TreeMap<Date, ModuleBuilder>>();
247
248         // module dependency graph sorted
249         List<ModuleBuilder> sorted = null;
250         if (context == null) {
251             sorted = ModuleDependencySort.sort(builders);
252         } else {
253             sorted = ModuleDependencySort.sortWithContext(context, builders);
254         }
255
256         for (final ModuleBuilder builder : sorted) {
257             if (builder == null) {
258                 continue;
259             }
260             final String builderName = builder.getName();
261             Date builderRevision = builder.getRevision();
262             if (builderRevision == null) {
263                 builderRevision = new Date(0L);
264             }
265             TreeMap<Date, ModuleBuilder> builderByRevision = modules.get(builderName);
266             if (builderByRevision == null) {
267                 builderByRevision = new TreeMap<Date, ModuleBuilder>();
268             }
269             builderByRevision.put(builderRevision, builder);
270             modules.put(builderName, builderByRevision);
271         }
272         return modules;
273     }
274
275     private List<ParseTree> parseStreams(final List<InputStream> yangStreams) {
276         final List<ParseTree> trees = new ArrayList<ParseTree>();
277         for (InputStream yangStream : yangStreams) {
278             trees.add(parseStream(yangStream));
279         }
280         return trees;
281     }
282
283     private ParseTree parseStream(final InputStream yangStream) {
284         ParseTree result = null;
285         try {
286             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
287             final YangLexer lexer = new YangLexer(input);
288             final CommonTokenStream tokens = new CommonTokenStream(lexer);
289             final YangParser parser = new YangParser(tokens);
290             parser.removeErrorListeners();
291             parser.addErrorListener(new YangErrorListener());
292
293             result = parser.yang();
294         } catch (IOException e) {
295             LOG.warn("Exception while reading yang file: " + yangStream, e);
296         }
297         return result;
298     }
299
300     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
301         // fix unresolved nodes
302         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
303             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
304                 final ModuleBuilder moduleBuilder = childEntry.getValue();
305                 fixUnresolvedNodes(modules, moduleBuilder);
306             }
307         }
308         resolveAugments(modules);
309         resolveDeviations(modules);
310
311         // build
312         // LinkedHashMap MUST be used otherwise the values will not maintain
313         // order!
314         // http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html
315         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
316         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
317             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
318             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
319                 final ModuleBuilder moduleBuilder = childEntry.getValue();
320                 final Module module = moduleBuilder.build();
321                 modulesByRevision.put(childEntry.getKey(), module);
322                 result.put(moduleBuilder, module);
323             }
324         }
325         return result;
326     }
327
328     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
329             SchemaContext context) {
330         // fix unresolved nodes
331         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
332             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
333                 final ModuleBuilder moduleBuilder = childEntry.getValue();
334                 fixUnresolvedNodesWithContext(modules, moduleBuilder, context);
335             }
336         }
337         resolveAugmentsWithContext(modules, context);
338         resolveDeviationsWithContext(modules, context);
339
340         // build
341         // LinkedHashMap MUST be used otherwise the values will not maintain
342         // order!
343         // http://docs.oracle.com/javase/6/docs/api/java/util/LinkedHashMap.html
344         final Map<ModuleBuilder, Module> result = new LinkedHashMap<ModuleBuilder, Module>();
345         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
346             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
347             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
348                 final ModuleBuilder moduleBuilder = childEntry.getValue();
349                 final Module module = moduleBuilder.build();
350                 modulesByRevision.put(childEntry.getKey(), module);
351                 result.put(moduleBuilder, module);
352             }
353         }
354         return result;
355     }
356
357     private void fixUnresolvedNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder) {
358         resolveDirtyNodes(modules, builder);
359         resolveIdentities(modules, builder);
360         resolveUsesNodes(modules, builder);
361         resolveUnknownNodes(modules, builder);
362     }
363
364     private void fixUnresolvedNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
365             final ModuleBuilder builder, final SchemaContext context) {
366         resolveDirtyNodesWithContext(modules, builder, context);
367         resolveIdentitiesWithContext(modules, builder, context);
368         resolveUsesNodesWithContext(modules, builder, context);
369         resolveUnknownNodesWithContext(modules, builder, context);
370     }
371
372     /**
373      * Search for dirty nodes (node which contains UnknownType) and resolve
374      * unknown types.
375      *
376      * @param modules
377      *            all available modules
378      * @param module
379      *            current module
380      */
381     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
382         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
383         if (!dirtyNodes.isEmpty()) {
384             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
385                 if (nodeToResolve instanceof UnionTypeBuilder) {
386                     // special handling for union types
387                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
388                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
389                     // special handling for identityref types
390                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
391                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
392                 } else {
393                     resolveType(nodeToResolve, modules, module);
394                 }
395             }
396         }
397     }
398
399     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
400             final ModuleBuilder module, SchemaContext context) {
401         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
402         if (!dirtyNodes.isEmpty()) {
403             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
404                 if (nodeToResolve instanceof UnionTypeBuilder) {
405                     // special handling for union types
406                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
407                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
408                     // special handling for identityref types
409                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
410                     nodeToResolve.setType(new IdentityrefType(findFullQName(modules, module, idref), idref.getPath()));
411                 } else {
412                     resolveTypeWithContext(nodeToResolve, modules, module, context);
413                 }
414             }
415         }
416     }
417
418     /**
419      * Resolve unknown type of node. It is assumed that type of node is either
420      * UnknownType or ExtendedType with UnknownType as base type.
421      *
422      * @param nodeToResolve
423      *            node with type to resolve
424      * @param modules
425      *            all loaded modules
426      * @param module
427      *            current module
428      */
429     private void resolveType(final TypeAwareBuilder nodeToResolve,
430             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
431         TypeDefinitionBuilder resolvedType = null;
432         final int line = nodeToResolve.getLine();
433         final TypeDefinition<?> nodeToResolveType = nodeToResolve.getType();
434         final QName unknownTypeQName = nodeToResolveType.getBaseType().getQName();
435         final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, unknownTypeQName.getPrefix(),
436                 line);
437
438         final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(nodeToResolve, dependentModule,
439                 unknownTypeQName.getLocalName(), module.getName(), line);
440
441         if (nodeToResolveType instanceof ExtendedType) {
442             final ExtendedType extType = (ExtendedType) nodeToResolveType;
443             final TypeDefinitionBuilder newType = extendedTypeWithNewBaseTypeBuilder(targetTypeBuilder, extType,
444                     modules, module, nodeToResolve.getLine());
445             resolvedType = newType;
446         } else {
447             resolvedType = targetTypeBuilder;
448         }
449
450         // validate constraints
451         final TypeConstraints constraints = findConstraintsFromTypeBuilder(nodeToResolve,
452                 new TypeConstraints(module.getName(), nodeToResolve.getLine()), modules, module, null);
453         constraints.validateConstraints();
454
455         nodeToResolve.setTypedef(resolvedType);
456     }
457
458     /**
459      * Resolve unknown type of node. It is assumed that type of node is either
460      * UnknownType or ExtendedType with UnknownType as base type.
461      *
462      * @param nodeToResolve
463      *            node with type to resolve
464      * @param modules
465      *            all loaded modules
466      * @param module
467      *            current module
468      * @param context
469      *            SchemaContext containing already resolved modules
470      */
471     private void resolveTypeWithContext(final TypeAwareBuilder nodeToResolve,
472             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
473             final SchemaContext context) {
474         TypeDefinitionBuilder resolvedType = null;
475         final int line = nodeToResolve.getLine();
476         final TypeDefinition<?> nodeToResolveType = nodeToResolve.getType();
477         final QName unknownTypeQName = nodeToResolveType.getBaseType().getQName();
478         final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
479                 unknownTypeQName.getPrefix(), line);
480
481         if (dependentModuleBuilder == null) {
482             final Module dependentModule = findModuleFromContext(context, module, unknownTypeQName.getPrefix(), line);
483             final Set<TypeDefinition<?>> types = dependentModule.getTypeDefinitions();
484             final TypeDefinition<?> type = findTypeByName(types, unknownTypeQName.getLocalName());
485
486             if (nodeToResolveType instanceof ExtendedType) {
487                 final ExtendedType extType = (ExtendedType) nodeToResolveType;
488                 final TypeDefinitionBuilder newType = extendedTypeWithNewBaseType(type, extType, module,
489                         nodeToResolve.getLine());
490
491                 nodeToResolve.setTypedef(newType);
492             } else {
493                 if (nodeToResolve instanceof TypeDefinitionBuilder) {
494                     TypeDefinitionBuilder tdb = (TypeDefinitionBuilder) nodeToResolve;
495                     TypeConstraints tc = findConstraintsFromTypeBuilder(nodeToResolve,
496                             new TypeConstraints(module.getName(), nodeToResolve.getLine()), modules, module, context);
497                     tdb.setLengths(tc.getLength());
498                     tdb.setPatterns(tc.getPatterns());
499                     tdb.setRanges(tc.getRange());
500                     tdb.setFractionDigits(tc.getFractionDigits());
501                 }
502                 nodeToResolve.setType(type);
503             }
504
505         } else {
506             final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(nodeToResolve,
507                     dependentModuleBuilder, unknownTypeQName.getLocalName(), module.getName(), line);
508
509             if (nodeToResolveType instanceof ExtendedType) {
510                 final ExtendedType extType = (ExtendedType) nodeToResolveType;
511                 final TypeDefinitionBuilder newType = extendedTypeWithNewBaseTypeBuilder(targetTypeBuilder, extType,
512                         modules, module, nodeToResolve.getLine());
513                 resolvedType = newType;
514             } else {
515                 resolvedType = targetTypeBuilder;
516             }
517
518             // validate constraints
519             final TypeConstraints constraints = findConstraintsFromTypeBuilder(nodeToResolve, new TypeConstraints(
520                     module.getName(), nodeToResolve.getLine()), modules, module, context);
521             constraints.validateConstraints();
522
523             nodeToResolve.setTypedef(resolvedType);
524         }
525     }
526
527     private void resolveTypeUnion(final UnionTypeBuilder union,
528             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder) {
529
530         final List<TypeDefinition<?>> unionTypes = union.getTypes();
531         final List<TypeDefinition<?>> toRemove = new ArrayList<TypeDefinition<?>>();
532         for (TypeDefinition<?> unionType : unionTypes) {
533             if (unionType instanceof UnknownType) {
534                 final UnknownType ut = (UnknownType) unionType;
535                 final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, builder, ut.getQName()
536                         .getPrefix(), union.getLine());
537                 final TypeDefinitionBuilder resolvedType = findTypeDefinitionBuilder(union, dependentModule, ut
538                         .getQName().getLocalName(), builder.getName(), union.getLine());
539                 union.setTypedef(resolvedType);
540                 toRemove.add(ut);
541             } else if (unionType instanceof ExtendedType) {
542                 final ExtendedType extType = (ExtendedType) unionType;
543                 final TypeDefinition<?> extTypeBase = extType.getBaseType();
544                 if (extTypeBase instanceof UnknownType) {
545                     final UnknownType ut = (UnknownType) extTypeBase;
546                     final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, builder, ut.getQName()
547                             .getPrefix(), union.getLine());
548                     final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(union, dependentModule,
549                             ut.getQName().getLocalName(), builder.getName(), union.getLine());
550
551                     final TypeDefinitionBuilder newType = extendedTypeWithNewBaseTypeBuilder(targetTypeBuilder,
552                             extType, modules, builder, union.getLine());
553
554                     union.setTypedef(newType);
555                     toRemove.add(extType);
556                 }
557             }
558         }
559         unionTypes.removeAll(toRemove);
560     }
561
562     private void resolveTypeUnionWithContext(final UnionTypeBuilder union,
563             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder,
564             final SchemaContext context) {
565
566         final List<TypeDefinition<?>> unionTypes = union.getTypes();
567         final List<TypeDefinition<?>> toRemove = new ArrayList<TypeDefinition<?>>();
568         for (TypeDefinition<?> unionType : unionTypes) {
569             if (unionType instanceof UnknownType) {
570                 final UnknownType ut = (UnknownType) unionType;
571                 final QName utQName = ut.getQName();
572                 final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, builder,
573                         utQName.getPrefix(), union.getLine());
574
575                 if (dependentModuleBuilder == null) {
576                     Module dependentModule = findModuleFromContext(context, builder, utQName.getPrefix(),
577                             union.getLine());
578                     Set<TypeDefinition<?>> types = dependentModule.getTypeDefinitions();
579                     TypeDefinition<?> type = findTypeByName(types, utQName.getLocalName());
580                     union.setType(type);
581                     toRemove.add(ut);
582                 } else {
583                     final TypeDefinitionBuilder resolvedType = findTypeDefinitionBuilder(union, dependentModuleBuilder,
584                             utQName.getLocalName(), builder.getName(), union.getLine());
585                     union.setTypedef(resolvedType);
586                     toRemove.add(ut);
587                 }
588
589             } else if (unionType instanceof ExtendedType) {
590                 final ExtendedType extType = (ExtendedType) unionType;
591                 TypeDefinition<?> extTypeBase = extType.getBaseType();
592                 if (extTypeBase instanceof UnknownType) {
593                     final UnknownType ut = (UnknownType) extTypeBase;
594                     final QName utQName = ut.getQName();
595                     final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, builder,
596                             utQName.getPrefix(), union.getLine());
597
598                     if (dependentModuleBuilder == null) {
599                         final Module dependentModule = findModuleFromContext(context, builder, utQName.getPrefix(),
600                                 union.getLine());
601                         Set<TypeDefinition<?>> types = dependentModule.getTypeDefinitions();
602                         TypeDefinition<?> type = findTypeByName(types, utQName.getLocalName());
603                         final TypeDefinitionBuilder newType = extendedTypeWithNewBaseType(type, extType, builder, 0);
604
605                         union.setTypedef(newType);
606                         toRemove.add(extType);
607                     } else {
608                         final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(union,
609                                 dependentModuleBuilder, utQName.getLocalName(), builder.getName(), union.getLine());
610
611                         final TypeDefinitionBuilder newType = extendedTypeWithNewBaseTypeBuilder(targetTypeBuilder,
612                                 extType, modules, builder, union.getLine());
613
614                         union.setTypedef(newType);
615                         toRemove.add(extType);
616                     }
617                 }
618             }
619         }
620         unionTypes.removeAll(toRemove);
621     }
622
623     /**
624      * Go through all augment definitions and resolve them. It is expected that
625      * modules are already sorted by their dependencies. This method also finds
626      * augment target node and add child nodes to it.
627      *
628      * @param modules
629      *            all available modules
630      */
631     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
632         final List<ModuleBuilder> allModulesList = new ArrayList<ModuleBuilder>();
633         final Set<ModuleBuilder> allModulesSet = new HashSet<ModuleBuilder>();
634         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
635             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
636                 allModulesList.add(inner.getValue());
637                 allModulesSet.add(inner.getValue());
638             }
639         }
640
641         for (int i = 0; i < allModulesList.size(); i++) {
642             final ModuleBuilder module = allModulesList.get(i);
643             // try to resolve augments in module
644             resolveAugment(modules, module);
645             // while all augments are not resolved
646             final Iterator<ModuleBuilder> allModulesIterator = allModulesSet.iterator();
647             while (!(module.getAugmentsResolved() == module.getAllAugments().size())) {
648                 ModuleBuilder nextModule = null;
649                 // try resolve other module augments
650                 try {
651                     nextModule = allModulesIterator.next();
652                     resolveAugment(modules, nextModule);
653                 } catch (NoSuchElementException e) {
654                     throw new YangParseException("Failed to resolve augments in module '" + module.getName() + "'.", e);
655                 }
656                 // then try to resolve first module again
657                 resolveAugment(modules, module);
658             }
659         }
660     }
661
662     /**
663      * Tries to resolve augments in given module. If augment target node is not
664      * found, do nothing.
665      *
666      * @param modules
667      *            all available modules
668      * @param module
669      *            current module
670      */
671     private void resolveAugment(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
672         if (module.getAugmentsResolved() < module.getAllAugments().size()) {
673             for (AugmentationSchemaBuilder augmentBuilder : module.getAllAugments()) {
674
675                 if (!augmentBuilder.isResolved()) {
676                     final SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath();
677                     final List<QName> path = augmentTargetSchemaPath.getPath();
678
679                     final QName qname = path.get(0);
680                     String prefix = qname.getPrefix();
681                     if (prefix == null) {
682                         prefix = module.getPrefix();
683                     }
684
685                     final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, prefix,
686                             augmentBuilder.getLine());
687                     processAugmentation(augmentBuilder, path, module, dependentModule);
688                 }
689
690             }
691         }
692     }
693
694     /**
695      * Go through all augment definitions and resolve them. This method works in
696      * same way as {@link #resolveAugments(Map)} except that if target node is
697      * not found in loaded modules, it search for target node in given context.
698      *
699      * @param modules
700      *            all loaded modules
701      * @param context
702      *            SchemaContext containing already resolved modules
703      */
704     private void resolveAugmentsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
705             final SchemaContext context) {
706         final List<ModuleBuilder> allModulesList = new ArrayList<ModuleBuilder>();
707         final Set<ModuleBuilder> allModulesSet = new HashSet<ModuleBuilder>();
708         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
709             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
710                 allModulesList.add(inner.getValue());
711                 allModulesSet.add(inner.getValue());
712             }
713         }
714
715         for (int i = 0; i < allModulesList.size(); i++) {
716             final ModuleBuilder module = allModulesList.get(i);
717             // try to resolve augments in module
718             resolveAugmentWithContext(modules, module, context);
719             // while all augments are not resolved
720             final Iterator<ModuleBuilder> allModulesIterator = allModulesSet.iterator();
721             while (!(module.getAugmentsResolved() == module.getAllAugments().size())) {
722                 ModuleBuilder nextModule = null;
723                 // try resolve other module augments
724                 try {
725                     nextModule = allModulesIterator.next();
726                     resolveAugmentWithContext(modules, nextModule, context);
727                 } catch (NoSuchElementException e) {
728                     throw new YangParseException("Failed to resolve augments in module '" + module.getName() + "'.", e);
729                 }
730                 // then try to resolve first module again
731                 resolveAugmentWithContext(modules, module, context);
732             }
733         }
734     }
735
736     /**
737      * Tries to resolve augments in given module. If augment target node is not
738      * found, do nothing.
739      *
740      * @param modules
741      *            all available modules
742      * @param module
743      *            current module
744      */
745     private void resolveAugmentWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
746             final ModuleBuilder module, final SchemaContext context) {
747         if (module.getAugmentsResolved() < module.getAllAugments().size()) {
748
749             for (AugmentationSchemaBuilder augmentBuilder : module.getAllAugments()) {
750                 final int line = augmentBuilder.getLine();
751
752                 if (!augmentBuilder.isResolved()) {
753                     final List<QName> path = augmentBuilder.getTargetPath().getPath();
754                     final QName qname = path.get(0);
755                     String prefix = qname.getPrefix();
756                     if (prefix == null) {
757                         prefix = module.getPrefix();
758                     }
759
760                     // try to find augment target module in loaded modules...
761                     final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix,
762                             line);
763                     if (dependentModuleBuilder == null) {
764                         // perform augmentation on module from context and
765                         // continue to next augment
766                         processAugmentationOnContext(augmentBuilder, path, module, prefix, line, context);
767                         continue;
768                     } else {
769                         processAugmentation(augmentBuilder, path, module, dependentModuleBuilder);
770                     }
771                 }
772
773             }
774         }
775     }
776
777     /**
778      * Go through identity statements defined in current module and resolve
779      * their 'base' statement if present.
780      *
781      * @param modules
782      *            all modules
783      * @param module
784      *            module being resolved
785      */
786     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
787         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
788         for (IdentitySchemaNodeBuilder identity : identities) {
789             final String baseIdentityName = identity.getBaseIdentityName();
790             if (baseIdentityName != null) {
791                 String baseIdentityPrefix = null;
792                 String baseIdentityLocalName = null;
793                 if (baseIdentityName.contains(":")) {
794                     final String[] splitted = baseIdentityName.split(":");
795                     baseIdentityPrefix = splitted[0];
796                     baseIdentityLocalName = splitted[1];
797                 } else {
798                     baseIdentityPrefix = module.getPrefix();
799                     baseIdentityLocalName = baseIdentityName;
800                 }
801                 final ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, baseIdentityPrefix,
802                         identity.getLine());
803
804                 final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule.getIdentities();
805                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
806                     if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
807                         identity.setBaseIdentity(idBuilder);
808                     }
809                 }
810             }
811         }
812     }
813
814     /**
815      * Go through identity statements defined in current module and resolve
816      * their 'base' statement. Method tries to find base identity in given
817      * modules. If base identity is not found, method will search it in context.
818      *
819      * @param modules
820      *            all loaded modules
821      * @param module
822      *            current module
823      * @param context
824      *            SchemaContext containing already resolved modules
825      */
826     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
827             final ModuleBuilder module, final SchemaContext context) {
828         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
829         for (IdentitySchemaNodeBuilder identity : identities) {
830             final String baseIdentityName = identity.getBaseIdentityName();
831             if (baseIdentityName != null) {
832                 String baseIdentityPrefix = null;
833                 String baseIdentityLocalName = null;
834                 if (baseIdentityName.contains(":")) {
835                     final String[] splitted = baseIdentityName.split(":");
836                     baseIdentityPrefix = splitted[0];
837                     baseIdentityLocalName = splitted[1];
838                 } else {
839                     baseIdentityPrefix = module.getPrefix();
840                     baseIdentityLocalName = baseIdentityName;
841                 }
842                 final ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
843                         baseIdentityPrefix, identity.getLine());
844
845                 if (dependentModuleBuilder == null) {
846                     final Module dependentModule = findModuleFromContext(context, module, baseIdentityPrefix,
847                             identity.getLine());
848                     final Set<IdentitySchemaNode> dependentModuleIdentities = dependentModule.getIdentities();
849                     for (IdentitySchemaNode idNode : dependentModuleIdentities) {
850                         if (idNode.getQName().getLocalName().equals(baseIdentityLocalName)) {
851                             identity.setBaseIdentity(idNode);
852                         }
853                     }
854                 } else {
855                     final Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModuleBuilder
856                             .getIdentities();
857                     for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
858                         if (idBuilder.getQName().getLocalName().equals(baseIdentityLocalName)) {
859                             identity.setBaseIdentity(idBuilder);
860                         }
861                     }
862                 }
863             }
864         }
865     }
866
867     /**
868      * Go through uses statements defined in current module and resolve their
869      * refine statements.
870      *
871      * @param modules
872      *            all modules
873      * @param module
874      *            module being resolved
875      */
876     private void resolveUsesNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
877         final List<UsesNodeBuilder> allModuleUses = module.getAllUsesNodes();
878         for (UsesNodeBuilder usesNode : allModuleUses) {
879             // perform uses
880             final int line = usesNode.getLine();
881             final GroupingBuilder targetGrouping = getTargetGroupingFromModules(usesNode, modules, module);
882             usesNode.setGroupingPath(targetGrouping.getPath());
883             processUsesNode(module, usesNode, targetGrouping);
884             // refine
885             for (RefineHolder refine : usesNode.getRefines()) {
886                 DataSchemaNodeBuilder nodeToRefine = null;
887                 for (DataSchemaNodeBuilder dsnb : usesNode.getTargetChildren()) {
888                     if (refine.getName().equals(dsnb.getQName().getLocalName())) {
889                         nodeToRefine = dsnb;
890                         break;
891                     }
892                 }
893                 if (nodeToRefine == null) {
894                     throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
895                             + refine.getName() + "' not found");
896                 }
897                 if (nodeToRefine instanceof GroupingMember) {
898                     ((GroupingMember) nodeToRefine).setAddedByUses(true);
899                 }
900                 RefineUtils.performRefine(nodeToRefine, refine, line);
901                 usesNode.addRefineNode(nodeToRefine);
902             }
903         }
904         for (UsesNodeBuilder usesNode : allModuleUses) {
905             final GroupingBuilder targetGrouping = getTargetGroupingFromModules(usesNode, modules, module);
906             processUsesTarget(module, usesNode, targetGrouping);
907         }
908     }
909
910     /**
911      * Tries to search target grouping in given modules and resolve refine
912      * nodes. If grouping is not found in modules, method tries to find it in
913      * modules from context.
914      *
915      * @param modules
916      *            all loaded modules
917      * @param module
918      *            current module
919      * @param context
920      *            SchemaContext containing already resolved modules
921      */
922     private void resolveUsesNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
923             final ModuleBuilder module, final SchemaContext context) {
924         final List<UsesNodeBuilder> moduleUses = module.getAllUsesNodes();
925         for (UsesNodeBuilder usesNode : moduleUses) {
926             final int line = usesNode.getLine();
927
928             final GroupingBuilder targetGroupingBuilder = getTargetGroupingFromModules(usesNode, modules, module);
929             if (targetGroupingBuilder == null) {
930                 final GroupingDefinition targetGrouping = getTargetGroupingFromContext(usesNode, module, context);
931                 usesNode.setGroupingPath(targetGrouping.getPath());
932                 processUsesNode(usesNode, targetGrouping);
933                 for (RefineHolder refine : usesNode.getRefines()) {
934                     DataSchemaNodeBuilder nodeToRefine = null;
935                     for (DataSchemaNodeBuilder dsnb : usesNode.getTargetChildren()) {
936                         if (refine.getName().equals(dsnb.getQName().getLocalName())) {
937                             nodeToRefine = dsnb;
938                             break;
939                         }
940                     }
941                     if (nodeToRefine == null) {
942                         throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
943                                 + refine.getName() + "' not found");
944                     }
945                     if (nodeToRefine instanceof GroupingMember) {
946                         ((GroupingMember) nodeToRefine).setAddedByUses(true);
947                     }
948                     RefineUtils.performRefine(nodeToRefine, refine, line);
949                     usesNode.addRefineNode(nodeToRefine);
950                 }
951             } else {
952                 usesNode.setGroupingPath(targetGroupingBuilder.getPath());
953                 processUsesNode(module, usesNode, targetGroupingBuilder);
954                 for (RefineHolder refine : usesNode.getRefines()) {
955                     DataSchemaNodeBuilder nodeToRefine = null;
956                     for (DataSchemaNodeBuilder dsnb : usesNode.getTargetChildren()) {
957                         if (refine.getName().equals(dsnb.getQName().getLocalName())) {
958                             nodeToRefine = dsnb;
959                             break;
960                         }
961                     }
962                     if (nodeToRefine == null) {
963                         throw new YangParseException(refine.getModuleName(), refine.getLine(), "Refine target node '"
964                                 + refine.getName() + "' not found");
965                     }
966                     if (nodeToRefine instanceof GroupingMember) {
967                         ((GroupingMember) nodeToRefine).setAddedByUses(true);
968                     }
969                     RefineUtils.performRefine(nodeToRefine, refine, line);
970                     usesNode.addRefineNode(nodeToRefine);
971                 }
972             }
973         }
974     }
975
976     /**
977      * Search given modules for grouping by name defined in uses node.
978      *
979      * @param usesBuilder
980      *            builder of uses statement
981      * @param modules
982      *            all loaded modules
983      * @param module
984      *            current module
985      * @return grouping with given name if found, null otherwise
986      */
987     private GroupingBuilder getTargetGroupingFromModules(final UsesNodeBuilder usesBuilder,
988             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
989         final int line = usesBuilder.getLine();
990         final String groupingString = usesBuilder.getGroupingName();
991         String groupingPrefix;
992         String groupingName;
993
994         if (groupingString.contains(":")) {
995             String[] splitted = groupingString.split(":");
996             if (splitted.length != 2 || groupingString.contains("/")) {
997                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
998             }
999             groupingPrefix = splitted[0];
1000             groupingName = splitted[1];
1001         } else {
1002             groupingPrefix = module.getPrefix();
1003             groupingName = groupingString;
1004         }
1005
1006         ModuleBuilder dependentModule = null;
1007         if (groupingPrefix.equals(module.getPrefix())) {
1008             dependentModule = module;
1009         } else {
1010             dependentModule = findDependentModuleBuilder(modules, module, groupingPrefix, line);
1011         }
1012
1013         if (dependentModule == null) {
1014             return null;
1015         }
1016
1017         GroupingBuilder result = null;
1018         Set<GroupingBuilder> groupings = dependentModule.getGroupingBuilders();
1019         result = findGroupingBuilder(groupings, groupingName);
1020         if (result != null) {
1021             return result;
1022         }
1023
1024         Builder parent = usesBuilder.getParent();
1025
1026         while (parent != null) {
1027             if (parent instanceof DataNodeContainerBuilder) {
1028                 groupings = ((DataNodeContainerBuilder) parent).getGroupingBuilders();
1029             } else if (parent instanceof RpcDefinitionBuilder) {
1030                 groupings = ((RpcDefinitionBuilder) parent).getGroupings();
1031             }
1032             result = findGroupingBuilder(groupings, groupingName);
1033             if (result == null) {
1034                 parent = parent.getParent();
1035             } else {
1036                 break;
1037             }
1038         }
1039
1040         if (result == null) {
1041             throw new YangParseException(module.getName(), line, "Referenced grouping '" + groupingName
1042                     + "' not found.");
1043         }
1044         return result;
1045     }
1046
1047     /**
1048      * Search context for grouping by name defined in uses node.
1049      *
1050      * @param usesBuilder
1051      *            builder of uses statement
1052      * @param module
1053      *            current module
1054      * @param context
1055      *            SchemaContext containing already resolved modules
1056      * @return grouping with given name if found, null otherwise
1057      */
1058     private GroupingDefinition getTargetGroupingFromContext(final UsesNodeBuilder usesBuilder,
1059             final ModuleBuilder module, final SchemaContext context) {
1060         final int line = usesBuilder.getLine();
1061         String groupingString = usesBuilder.getGroupingName();
1062         String groupingPrefix;
1063         String groupingName;
1064
1065         if (groupingString.contains(":")) {
1066             String[] splitted = groupingString.split(":");
1067             if (splitted.length != 2 || groupingString.contains("/")) {
1068                 throw new YangParseException(module.getName(), line, "Invalid name of target grouping");
1069             }
1070             groupingPrefix = splitted[0];
1071             groupingName = splitted[1];
1072         } else {
1073             groupingPrefix = module.getPrefix();
1074             groupingName = groupingString;
1075         }
1076
1077         Module dependentModule = findModuleFromContext(context, module, groupingPrefix, line);
1078         return findGroupingDefinition(dependentModule.getGroupings(), groupingName);
1079     }
1080
1081     /**
1082      * Add nodes defined in target grouping to current context.
1083      *
1084      * @param module
1085      *            current module
1086      * @param usesNode
1087      * @param targetGrouping
1088      */
1089     private void processUsesNode(final ModuleBuilder module, final UsesNodeBuilder usesNode,
1090             final GroupingBuilder targetGrouping) {
1091         DataNodeContainerBuilder parent = usesNode.getParent();
1092         URI namespace = null;
1093         Date revision = null;
1094         String prefix = null;
1095         if (parent instanceof ModuleBuilder || parent instanceof AugmentationSchemaBuilder) {
1096             namespace = module.getNamespace();
1097             revision = module.getRevision();
1098             prefix = module.getPrefix();
1099         } else {
1100             QName parentQName = parent.getQName();
1101             namespace = parentQName.getNamespace();
1102             revision = parentQName.getRevision();
1103             prefix = parentQName.getPrefix();
1104         }
1105         SchemaPath parentPath = parent.getPath();
1106
1107         Set<DataSchemaNodeBuilder> newChildren = new HashSet<>();
1108         for (DataSchemaNodeBuilder child : targetGrouping.getChildNodeBuilders()) {
1109             if (child != null) {
1110                 DataSchemaNodeBuilder newChild = null;
1111                 if (child instanceof AnyXmlBuilder) {
1112                     newChild = new AnyXmlBuilder((AnyXmlBuilder) child);
1113                 } else if (child instanceof ChoiceBuilder) {
1114                     newChild = new ChoiceBuilder((ChoiceBuilder) child);
1115                 } else if (child instanceof ContainerSchemaNodeBuilder) {
1116                     newChild = new ContainerSchemaNodeBuilder((ContainerSchemaNodeBuilder) child);
1117                 } else if (child instanceof LeafListSchemaNodeBuilder) {
1118                     newChild = new LeafListSchemaNodeBuilder((LeafListSchemaNodeBuilder) child);
1119                 } else if (child instanceof LeafSchemaNodeBuilder) {
1120                     newChild = new LeafSchemaNodeBuilder((LeafSchemaNodeBuilder) child);
1121                 } else if (child instanceof ListSchemaNodeBuilder) {
1122                     newChild = new ListSchemaNodeBuilder((ListSchemaNodeBuilder) child);
1123                 }
1124
1125                 if (newChild == null) {
1126                     throw new YangParseException(usesNode.getModuleName(), usesNode.getLine(),
1127                             "Unknown member of target grouping while resolving uses node.");
1128                 }
1129
1130                 if (newChild instanceof GroupingMember) {
1131                     ((GroupingMember) newChild).setAddedByUses(true);
1132                 }
1133
1134                 newChild.setPath(createSchemaPath(parentPath, newChild.getQName().getLocalName(), namespace, revision,
1135                         prefix));
1136                 newChildren.add(newChild);
1137             }
1138         }
1139         usesNode.getTargetChildren().addAll(newChildren);
1140
1141         Set<GroupingBuilder> newGroupings = new HashSet<>();
1142         for (GroupingBuilder g : targetGrouping.getGroupingBuilders()) {
1143             GroupingBuilder newGrouping = new GroupingBuilderImpl(g);
1144             newGrouping.setAddedByUses(true);
1145             newGrouping.setPath(createSchemaPath(parentPath, newGrouping.getQName().getLocalName(), namespace,
1146                     revision, prefix));
1147             newGroupings.add(newGrouping);
1148         }
1149         usesNode.getTargetGroupings().addAll(newGroupings);
1150
1151         Set<TypeDefinitionBuilder> newTypedefs = new HashSet<>();
1152         for (TypeDefinitionBuilder td : targetGrouping.getTypeDefinitionBuilders()) {
1153             TypeDefinitionBuilder newType = new TypeDefinitionBuilderImpl(td);
1154             newType.setAddedByUses(true);
1155             newType.setPath(createSchemaPath(parentPath, newType.getQName().getLocalName(), namespace, revision, prefix));
1156             newTypedefs.add(newType);
1157         }
1158         usesNode.getTargetTypedefs().addAll(newTypedefs);
1159
1160         List<UnknownSchemaNodeBuilder> newUnknownNodes = new ArrayList<>();
1161         for (UnknownSchemaNodeBuilder un : targetGrouping.getUnknownNodeBuilders()) {
1162             UnknownSchemaNodeBuilder newUn = new UnknownSchemaNodeBuilder(un);
1163             newUn.setAddedByUses(true);
1164             newUn.setPath(createSchemaPath(parentPath, un.getQName().getLocalName(), namespace, revision, prefix));
1165             newUnknownNodes.add(newUn);
1166         }
1167         usesNode.getTargetUnknownNodes().addAll(newUnknownNodes);
1168     }
1169
1170     /**
1171      * Check if target grouping contains uses nodes and if it does, merge
1172      * current uses with them.
1173      *
1174      * @param module
1175      * @param usesNode
1176      * @param targetGrouping
1177      */
1178     private void processUsesTarget(final ModuleBuilder module, final UsesNodeBuilder usesNode,
1179             final GroupingBuilder targetGrouping) {
1180         for (UsesNodeBuilder unb : targetGrouping.getUses()) {
1181             usesNode.getTargetChildren().addAll(unb.getTargetChildren());
1182             usesNode.getTargetGroupings().addAll(unb.getTargetGroupings());
1183             usesNode.getTargetTypedefs().addAll(unb.getTargetTypedefs());
1184             usesNode.getTargetUnknownNodes().addAll(unb.getTargetUnknownNodes());
1185         }
1186     }
1187
1188     private void processUsesNode(final UsesNodeBuilder usesNode, final GroupingDefinition targetGrouping) {
1189         final String moduleName = usesNode.getModuleName();
1190         final int line = usesNode.getLine();
1191         DataNodeContainerBuilder parent = usesNode.getParent();
1192         URI namespace = null;
1193         Date revision = null;
1194         String prefix = null;
1195         if (parent instanceof ModuleBuilder) {
1196             ModuleBuilder module = (ModuleBuilder) parent;
1197             namespace = module.getNamespace();
1198             revision = module.getRevision();
1199             prefix = module.getPrefix();
1200         } else {
1201             QName parentQName = parent.getQName();
1202             namespace = parentQName.getNamespace();
1203             revision = parentQName.getRevision();
1204             prefix = parentQName.getPrefix();
1205         }
1206         SchemaPath parentPath = parent.getPath();
1207
1208         final Set<DataSchemaNodeBuilder> newChildren = new HashSet<>();
1209         for (DataSchemaNode child : targetGrouping.getChildNodes()) {
1210             if (child != null) {
1211                 DataSchemaNodeBuilder newChild = null;
1212                 if (child instanceof AnyXmlSchemaNode) {
1213                     newChild = createAnyXml((AnyXmlSchemaNode) child, moduleName, line);
1214                 } else if (child instanceof ChoiceNode) {
1215                     newChild = createChoice((ChoiceNode) child, moduleName, line);
1216                 } else if (child instanceof ContainerSchemaNode) {
1217                     newChild = createContainer((ContainerSchemaNode) child, moduleName, line);
1218                 } else if (child instanceof LeafListSchemaNode) {
1219                     newChild = createLeafList((LeafListSchemaNode) child, moduleName, line);
1220                 } else if (child instanceof LeafSchemaNode) {
1221                     newChild = createLeafBuilder((LeafSchemaNode) child, moduleName, line);
1222                 } else if (child instanceof ListSchemaNode) {
1223                     newChild = createList((ListSchemaNode) child, moduleName, line);
1224                 }
1225
1226                 if (newChild == null) {
1227                     throw new YangParseException(moduleName, line,
1228                             "Unknown member of target grouping while resolving uses node.");
1229                 }
1230
1231                 if (newChild instanceof GroupingMember) {
1232                     ((GroupingMember) newChild).setAddedByUses(true);
1233                 }
1234                 newChild.setPath(createSchemaPath(parentPath, newChild.getQName().getLocalName(), namespace, revision,
1235                         prefix));
1236                 newChildren.add(newChild);
1237             }
1238         }
1239         usesNode.getTargetChildren().addAll(newChildren);
1240
1241         final Set<GroupingBuilder> newGroupings = new HashSet<>();
1242         for (GroupingDefinition g : targetGrouping.getGroupings()) {
1243             GroupingBuilder newGrouping = createGrouping(g, moduleName, line);
1244             newGrouping.setAddedByUses(true);
1245             newGrouping.setPath(createSchemaPath(parentPath, newGrouping.getQName().getLocalName(), namespace,
1246                     revision, prefix));
1247             newGroupings.add(newGrouping);
1248         }
1249         usesNode.getTargetGroupings().addAll(newGroupings);
1250
1251         final Set<TypeDefinitionBuilder> newTypedefs = new HashSet<>();
1252         for (TypeDefinition<?> td : targetGrouping.getTypeDefinitions()) {
1253             TypeDefinitionBuilder newType = createTypedef((ExtendedType) td, moduleName, line);
1254             newType.setAddedByUses(true);
1255             newType.setPath(createSchemaPath(parentPath, newType.getQName().getLocalName(), namespace, revision, prefix));
1256             newTypedefs.add(newType);
1257         }
1258         usesNode.getTargetTypedefs().addAll(newTypedefs);
1259
1260         final List<UnknownSchemaNodeBuilder> newUnknownNodes = new ArrayList<>();
1261         for (UnknownSchemaNode un : targetGrouping.getUnknownSchemaNodes()) {
1262             UnknownSchemaNodeBuilder newNode = createUnknownSchemaNode(un, moduleName, line);
1263             newNode.setAddedByUses(true);
1264             newNode.setPath(createSchemaPath(parentPath, un.getQName().getLocalName(), namespace, revision, prefix));
1265             newUnknownNodes.add(newNode);
1266         }
1267         usesNode.getTargetUnknownNodes().addAll(newUnknownNodes);
1268     }
1269
1270     private QName findFullQName(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
1271             final IdentityrefTypeBuilder idref) {
1272         QName result = null;
1273         String baseString = idref.getBaseString();
1274         if (baseString.contains(":")) {
1275             String[] splittedBase = baseString.split(":");
1276             if (splittedBase.length > 2) {
1277                 throw new YangParseException(module.getName(), idref.getLine(), "Failed to parse identityref base: "
1278                         + baseString);
1279             }
1280             String prefix = splittedBase[0];
1281             String name = splittedBase[1];
1282             ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, prefix, idref.getLine());
1283             result = new QName(dependentModule.getNamespace(), dependentModule.getRevision(), prefix, name);
1284         } else {
1285             result = new QName(module.getNamespace(), module.getRevision(), module.getPrefix(), baseString);
1286         }
1287         return result;
1288     }
1289
1290     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1291         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
1292             QName nodeType = usnb.getNodeType();
1293             if (nodeType.getNamespace() == null || nodeType.getRevision() == null) {
1294                 try {
1295                     ModuleBuilder dependentModule = findDependentModuleBuilder(modules, module, nodeType.getPrefix(),
1296                             usnb.getLine());
1297                     QName newNodeType = new QName(dependentModule.getNamespace(), dependentModule.getRevision(),
1298                             nodeType.getPrefix(), nodeType.getLocalName());
1299                     usnb.setNodeType(newNodeType);
1300                 } catch (YangParseException e) {
1301                     LOG.debug(module.getName(), usnb.getLine(), "Failed to find unknown node type: " + nodeType);
1302                 }
1303             }
1304         }
1305     }
1306
1307     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1308             final ModuleBuilder module, final SchemaContext context) {
1309         for (UnknownSchemaNodeBuilder unknownNodeBuilder : module.getAllUnknownNodes()) {
1310             QName nodeType = unknownNodeBuilder.getNodeType();
1311             if (nodeType.getNamespace() == null || nodeType.getRevision() == null) {
1312                 try {
1313                     ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module,
1314                             nodeType.getPrefix(), unknownNodeBuilder.getLine());
1315
1316                     QName newNodeType = null;
1317                     if (dependentModuleBuilder == null) {
1318                         Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
1319                                 unknownNodeBuilder.getLine());
1320                         newNodeType = new QName(dependentModule.getNamespace(), dependentModule.getRevision(),
1321                                 nodeType.getPrefix(), nodeType.getLocalName());
1322                     } else {
1323                         newNodeType = new QName(dependentModuleBuilder.getNamespace(),
1324                                 dependentModuleBuilder.getRevision(), nodeType.getPrefix(), nodeType.getLocalName());
1325                     }
1326
1327                     unknownNodeBuilder.setNodeType(newNodeType);
1328                 } catch (YangParseException e) {
1329                     LOG.debug(module.getName(), unknownNodeBuilder.getLine(), "Failed to find unknown node type: "
1330                             + nodeType);
1331                 }
1332             }
1333         }
1334     }
1335
1336     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1337         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1338             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1339                 ModuleBuilder b = inner.getValue();
1340                 resolveDeviation(modules, b);
1341             }
1342         }
1343     }
1344
1345     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1346         for (DeviationBuilder dev : module.getDeviations()) {
1347             int line = dev.getLine();
1348             SchemaPath targetPath = dev.getTargetPath();
1349             List<QName> path = targetPath.getPath();
1350             QName q0 = path.get(0);
1351             String prefix = q0.getPrefix();
1352             if (prefix == null) {
1353                 prefix = module.getPrefix();
1354             }
1355
1356             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
1357             processDeviation(dev, dependentModuleBuilder, path, module);
1358         }
1359     }
1360
1361     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1362             final SchemaContext context) {
1363         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1364             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1365                 ModuleBuilder b = inner.getValue();
1366                 resolveDeviationWithContext(modules, b, context);
1367             }
1368         }
1369     }
1370
1371     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1372             final ModuleBuilder module, final SchemaContext context) {
1373         for (DeviationBuilder dev : module.getDeviations()) {
1374             int line = dev.getLine();
1375             SchemaPath targetPath = dev.getTargetPath();
1376             List<QName> path = targetPath.getPath();
1377             QName q0 = path.get(0);
1378             String prefix = q0.getPrefix();
1379             if (prefix == null) {
1380                 prefix = module.getPrefix();
1381             }
1382             String name = null;
1383
1384             ModuleBuilder dependentModuleBuilder = findDependentModuleBuilder(modules, module, prefix, line);
1385             if (dependentModuleBuilder == null) {
1386                 Module dependentModule = findModuleFromContext(context, module, prefix, line);
1387                 Object currentParent = dependentModule;
1388
1389                 for (int i = 0; i < path.size(); i++) {
1390                     if (currentParent == null) {
1391                         throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
1392                     }
1393                     QName q = path.get(i);
1394                     name = q.getLocalName();
1395                     if (currentParent instanceof DataNodeContainer) {
1396                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
1397                     }
1398                 }
1399
1400                 if (currentParent == null) {
1401                     throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
1402                 }
1403                 if (currentParent instanceof SchemaNode) {
1404                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
1405                 }
1406
1407             } else {
1408                 processDeviation(dev, dependentModuleBuilder, path, module);
1409             }
1410         }
1411     }
1412
1413     /**
1414      * Correct deviation target path in deviation builder.
1415      *
1416      * @param dev
1417      *            deviation
1418      * @param dependentModuleBuilder
1419      *            module containing deviation target
1420      * @param path
1421      *            current deviation target path
1422      * @param module
1423      *            current module
1424      */
1425     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
1426             final List<QName> path, final ModuleBuilder module) {
1427         final int line = dev.getLine();
1428         Builder currentParent = dependentModuleBuilder;
1429
1430         for (int i = 0; i < path.size(); i++) {
1431             if (currentParent == null) {
1432                 throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
1433             }
1434             QName q = path.get(i);
1435             String name = q.getLocalName();
1436             if (currentParent instanceof DataNodeContainerBuilder) {
1437                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1438             }
1439         }
1440
1441         if (currentParent == null || !(currentParent instanceof SchemaNodeBuilder)) {
1442             throw new YangParseException(module.getName(), line, "Failed to find deviation target.");
1443         }
1444         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
1445     }
1446
1447 }