Yang parser refactoring.
[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.resolveType;
12 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveTypeUnion;
13 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveTypeUnionWithContext;
14 import static org.opendaylight.yangtools.yang.parser.util.TypeUtils.resolveTypeWithContext;
15
16 import java.io.File;
17 import java.io.FileInputStream;
18 import java.io.FileNotFoundException;
19 import java.io.IOException;
20 import java.io.InputStream;
21 import java.net.URI;
22 import java.util.*;
23 import java.util.Map.Entry;
24
25 import org.antlr.v4.runtime.ANTLRInputStream;
26 import org.antlr.v4.runtime.CommonTokenStream;
27 import org.antlr.v4.runtime.tree.ParseTree;
28 import org.antlr.v4.runtime.tree.ParseTreeWalker;
29 import org.opendaylight.yangtools.antlrv4.code.gen.YangLexer;
30 import org.opendaylight.yangtools.antlrv4.code.gen.YangParser;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.model.api.*;
33 import org.opendaylight.yangtools.yang.model.parser.api.YangModelParser;
34 import org.opendaylight.yangtools.yang.parser.builder.api.AugmentationSchemaBuilder;
35 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
36 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
37 import org.opendaylight.yangtools.yang.parser.builder.api.DataSchemaNodeBuilder;
38 import org.opendaylight.yangtools.yang.parser.builder.api.GroupingBuilder;
39 import org.opendaylight.yangtools.yang.parser.builder.api.SchemaNodeBuilder;
40 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
41 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
42 import org.opendaylight.yangtools.yang.parser.builder.api.UsesNodeBuilder;
43 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceBuilder;
44 import org.opendaylight.yangtools.yang.parser.builder.impl.ChoiceCaseBuilder;
45 import org.opendaylight.yangtools.yang.parser.builder.impl.DeviationBuilder;
46 import org.opendaylight.yangtools.yang.parser.builder.impl.ExtensionBuilder;
47 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBuilder;
48 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
49 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
50 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
51 import org.opendaylight.yangtools.yang.parser.builder.impl.UnknownSchemaNodeBuilder;
52 import org.opendaylight.yangtools.yang.parser.util.GroupingSort;
53 import org.opendaylight.yangtools.yang.parser.util.GroupingUtils;
54 import org.opendaylight.yangtools.yang.parser.util.ModuleDependencySort;
55 import org.opendaylight.yangtools.yang.parser.util.ParserUtils;
56 import org.opendaylight.yangtools.yang.parser.util.YangParseException;
57 import org.opendaylight.yangtools.yang.validator.YangModelBasicValidator;
58 import org.slf4j.Logger;
59 import org.slf4j.LoggerFactory;
60
61 import com.google.common.base.Preconditions;
62 import com.google.common.collect.Lists;
63 import com.google.common.collect.Maps;
64 import com.google.common.collect.Sets;
65
66 public final class YangParserImpl implements YangModelParser {
67     private static final Logger LOG = LoggerFactory.getLogger(YangParserImpl.class);
68
69     private static final String FAIL_DEVIATION_TARGET = "Failed to find deviation target.";
70
71     @Override
72     public Set<Module> parseYangModels(final File yangFile, final File directory) {
73         Preconditions.checkState(yangFile.exists(), yangFile + " does not exists");
74         Preconditions.checkState(directory.exists(), directory + " does not exists");
75         Preconditions.checkState(directory.isDirectory(), directory + " is not a directory");
76
77         final String yangFileName = yangFile.getName();
78         final String[] fileList = directory.list();
79         Preconditions.checkNotNull(fileList, directory + " not found");
80
81         FileInputStream yangFileStream = null;
82         LinkedHashMap<InputStream, File> streamToFileMap = new LinkedHashMap<>();
83
84         try {
85             yangFileStream = new FileInputStream(yangFile);
86             streamToFileMap.put(yangFileStream, yangFile);
87         } catch (FileNotFoundException e) {
88             LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
89         }
90
91         for (String fileName : fileList) {
92             if (fileName.equals(yangFileName)) {
93                 continue;
94             }
95             File dependency = new File(directory, fileName);
96             try {
97                 if (dependency.isFile()) {
98                     streamToFileMap.put(new FileInputStream(dependency), dependency);
99                 }
100             } catch (FileNotFoundException e) {
101                 LOG.warn("Exception while reading yang file: " + fileName, e);
102             }
103         }
104
105         Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(new ArrayList<>(streamToFileMap.keySet()),
106                 new HashMap<ModuleBuilder, InputStream>());
107         ModuleBuilder main = parsedBuilders.get(yangFileStream);
108
109         List<ModuleBuilder> moduleBuilders = new ArrayList<>();
110         moduleBuilders.add(main);
111         filterImports(main, new ArrayList<>(parsedBuilders.values()), moduleBuilders);
112
113         ModuleBuilder[] builders = new ModuleBuilder[moduleBuilders.size()];
114         moduleBuilders.toArray(builders);
115
116         // module dependency graph sorted
117         List<ModuleBuilder> sorted = ModuleDependencySort.sort(builders);
118
119         final LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> modules = orderModules(sorted);
120         return new LinkedHashSet<>(build(modules).values());
121     }
122
123     @Override
124     public Set<Module> parseYangModels(final List<File> yangFiles) {
125         return Sets.newLinkedHashSet(parseYangModelsMapped(yangFiles).values());
126     }
127
128     @Override
129     public Set<Module> parseYangModels(final List<File> yangFiles, final SchemaContext context) {
130         if (yangFiles != null) {
131             final Map<InputStream, File> inputStreams = Maps.newHashMap();
132
133             for (final File yangFile : yangFiles) {
134                 try {
135                     inputStreams.put(new FileInputStream(yangFile), yangFile);
136                 } catch (FileNotFoundException e) {
137                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
138                 }
139             }
140
141             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
142             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
143                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
144
145             for (InputStream is : inputStreams.keySet()) {
146                 try {
147                     is.close();
148                 } catch (IOException e) {
149                     LOG.debug("Failed to close stream.");
150                 }
151             }
152
153             Collection<Module> built = buildWithContext(modules, context).values();
154             for (Module m : context.getModules()) {
155                 if (!built.contains(m)) {
156                     built.add(m);
157                 }
158             }
159             return new LinkedHashSet<>(built);
160         }
161         return Collections.emptySet();
162     }
163
164     @Override
165     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams) {
166         return Sets.newHashSet(parseYangModelsFromStreamsMapped(yangModelStreams).values());
167     }
168
169     @Override
170     public Set<Module> parseYangModelsFromStreams(final List<InputStream> yangModelStreams, SchemaContext context) {
171         if (yangModelStreams != null) {
172             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
173             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersWithContext(
174                     yangModelStreams, builderToStreamMap, context);
175
176             final Set<Module> built = new LinkedHashSet<>(buildWithContext(modules, context).values());
177             for (Module m : context.getModules()) {
178                 if (!built.contains(m)) {
179                     built.add(m);
180                 }
181             }
182             return built;
183         }
184         return Collections.emptySet();
185     }
186
187     @Override
188     public Map<File, Module> parseYangModelsMapped(List<File> yangFiles) {
189         if (yangFiles != null) {
190             final Map<InputStream, File> inputStreams = Maps.newHashMap();
191
192             for (final File yangFile : yangFiles) {
193                 try {
194                     inputStreams.put(new FileInputStream(yangFile), yangFile);
195                 } catch (FileNotFoundException e) {
196                     LOG.warn("Exception while reading yang file: " + yangFile.getName(), e);
197                 }
198             }
199
200             Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
201             final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(
202                     Lists.newArrayList(inputStreams.keySet()), builderToStreamMap);
203
204             for (InputStream is : inputStreams.keySet()) {
205                 try {
206                     is.close();
207                 } catch (IOException e) {
208                     LOG.debug("Failed to close stream.");
209                 }
210             }
211
212             Map<File, Module> retVal = Maps.newLinkedHashMap();
213             Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
214
215             for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
216                 retVal.put(inputStreams.get(builderToStreamMap.get(builderToModule.getKey())),
217                         builderToModule.getValue());
218             }
219
220             return retVal;
221         }
222         return Collections.emptyMap();
223     }
224
225     @Override
226     public Map<InputStream, Module> parseYangModelsFromStreamsMapped(final List<InputStream> yangModelStreams) {
227         Map<ModuleBuilder, InputStream> builderToStreamMap = Maps.newHashMap();
228
229         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuilders(yangModelStreams,
230                 builderToStreamMap);
231         Map<InputStream, Module> retVal = Maps.newLinkedHashMap();
232         Map<ModuleBuilder, Module> builderToModuleMap = build(modules);
233
234         for (Entry<ModuleBuilder, Module> builderToModule : builderToModuleMap.entrySet()) {
235             retVal.put(builderToStreamMap.get(builderToModule.getKey()), builderToModule.getValue());
236         }
237         return retVal;
238     }
239
240     @Override
241     public SchemaContext resolveSchemaContext(final Set<Module> modules) {
242         return new SchemaContextImpl(modules);
243     }
244
245     private Map<InputStream, ModuleBuilder> parseModuleBuilders(List<InputStream> inputStreams,
246             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
247
248         final ParseTreeWalker walker = new ParseTreeWalker();
249         final Map<InputStream, ParseTree> trees = parseStreams(inputStreams);
250         final Map<InputStream, ModuleBuilder> builders = new LinkedHashMap<>();
251
252         // validate yang
253         new YangModelBasicValidator(walker).validate(new ArrayList<>(trees.values()));
254
255         YangParserListenerImpl yangModelParser;
256         for (Map.Entry<InputStream, ParseTree> entry : trees.entrySet()) {
257             yangModelParser = new YangParserListenerImpl();
258             walker.walk(yangModelParser, entry.getValue());
259             ModuleBuilder moduleBuilder = yangModelParser.getModuleBuilder();
260
261             // We expect the order of trees and streams has to be the same
262             streamToBuilderMap.put(moduleBuilder, entry.getKey());
263             builders.put(entry.getKey(), moduleBuilder);
264         }
265
266         return builders;
267     }
268
269     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuilders(final List<InputStream> yangFileStreams,
270             Map<ModuleBuilder, InputStream> streamToBuilderMap) {
271         return resolveModuleBuildersWithContext(yangFileStreams, streamToBuilderMap, null);
272     }
273
274     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersWithContext(
275             final List<InputStream> yangFileStreams, final Map<ModuleBuilder, InputStream> streamToBuilderMap,
276             final SchemaContext context) {
277         Map<InputStream, ModuleBuilder> parsedBuilders = parseModuleBuilders(yangFileStreams, streamToBuilderMap);
278         ModuleBuilder[] builders = new ModuleBuilder[parsedBuilders.size()];
279         parsedBuilders.values().toArray(builders);
280
281         // module dependency graph sorted
282         List<ModuleBuilder> sorted;
283         if (context == null) {
284             sorted = ModuleDependencySort.sort(builders);
285         } else {
286             sorted = ModuleDependencySort.sortWithContext(context, builders);
287         }
288
289         return orderModules(sorted);
290     }
291
292     /**
293      * Order modules by name and revision.
294      *
295      * @param modules
296      *            modules to order
297      * @return modules ordered by name and revision
298      */
299     private LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> orderModules(List<ModuleBuilder> modules) {
300         // LinkedHashMap must be used to preserve order
301         LinkedHashMap<String, TreeMap<Date, ModuleBuilder>> result = new LinkedHashMap<>();
302         for (final ModuleBuilder builder : modules) {
303             if (builder == null) {
304                 continue;
305             }
306             final String builderName = builder.getName();
307             Date builderRevision = builder.getRevision();
308             if (builderRevision == null) {
309                 builderRevision = new Date(0L);
310             }
311             TreeMap<Date, ModuleBuilder> builderByRevision = result.get(builderName);
312             if (builderByRevision == null) {
313                 builderByRevision = new TreeMap<>();
314             }
315             builderByRevision.put(builderRevision, builder);
316             result.put(builderName, builderByRevision);
317         }
318         return result;
319     }
320
321     private void filterImports(ModuleBuilder main, List<ModuleBuilder> other, List<ModuleBuilder> filtered) {
322         for (ModuleImport mi : main.getModuleImports()) {
323             for (ModuleBuilder builder : other) {
324                 if (mi.getModuleName().equals(builder.getModuleName())) {
325                     if (mi.getRevision() == null) {
326                         if (!filtered.contains(builder)) {
327                             filtered.add(builder);
328                             filterImports(builder, other, filtered);
329                         }
330                     } else {
331                         if (mi.getRevision().equals(builder.getRevision())) {
332                             if (!filtered.contains(builder)) {
333                                 filtered.add(builder);
334                                 filterImports(builder, other, filtered);
335                             }
336                         }
337                     }
338                 }
339             }
340         }
341     }
342
343     private Map<InputStream, ParseTree> parseStreams(final List<InputStream> yangStreams) {
344         final Map<InputStream, ParseTree> trees = new HashMap<>();
345         for (InputStream yangStream : yangStreams) {
346             trees.put(yangStream, parseStream(yangStream));
347         }
348         return trees;
349     }
350
351     private ParseTree parseStream(final InputStream yangStream) {
352         ParseTree result = null;
353         try {
354             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
355             final YangLexer lexer = new YangLexer(input);
356             final CommonTokenStream tokens = new CommonTokenStream(lexer);
357             final YangParser parser = new YangParser(tokens);
358             parser.removeErrorListeners();
359             YangErrorListener errorListener = new YangErrorListener();
360             parser.addErrorListener(errorListener);
361             result = parser.yang();
362             errorListener.validate();
363         } catch (IOException e) {
364             LOG.warn("Exception while reading yang file: " + yangStream, e);
365         }
366         return result;
367     }
368
369     private Map<ModuleBuilder, Module> build(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
370         // fix unresolved nodes
371         resolveDirtyNodes(modules);
372         resolveAugmentsTargetPath(modules, null);
373         resolveUsesTargetGrouping(modules, null);
374         resolveUsesForGroupings(modules, null);
375         resolveUsesForNodes(modules, null);
376         resolveAugments(modules, null);
377         resolveDeviations(modules);
378
379         // build
380         final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
381         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
382             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
383                 final ModuleBuilder moduleBuilder = childEntry.getValue();
384                 final Module module = moduleBuilder.build();
385                 result.put(moduleBuilder, module);
386             }
387         }
388         return result;
389     }
390
391     private Map<ModuleBuilder, Module> buildWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
392             final SchemaContext context) {
393         // fix unresolved nodes
394         resolvedDirtyNodesWithContext(modules, context);
395         resolveAugmentsTargetPath(modules, context);
396         resolveUsesTargetGrouping(modules, context);
397         resolveUsesForGroupings(modules, context);
398         resolveUsesForNodes(modules, context);
399         resolveAugments(modules, context);
400         resolveDeviationsWithContext(modules, context);
401
402         // build
403         final Map<ModuleBuilder, Module> result = new LinkedHashMap<>();
404         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
405             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
406                 final ModuleBuilder moduleBuilder = childEntry.getValue();
407                 final Module module = moduleBuilder.build();
408                 result.put(moduleBuilder, module);
409             }
410         }
411         return result;
412     }
413
414     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
415         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
416             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
417                 final ModuleBuilder module = childEntry.getValue();
418                 resolveUnknownNodes(modules, module);
419                 resolveIdentities(modules, module);
420                 resolveDirtyNodes(modules, module);
421             }
422         }
423     }
424
425     private void resolvedDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
426             final SchemaContext context) {
427         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
428             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue().entrySet()) {
429                 final ModuleBuilder module = childEntry.getValue();
430                 resolveUnknownNodesWithContext(modules, module, context);
431                 resolveIdentitiesWithContext(modules, module, context);
432                 resolveDirtyNodesWithContext(modules, module, context);
433             }
434         }
435     }
436
437     /**
438      * Search for dirty nodes (node which contains UnknownType) and resolve
439      * unknown types.
440      *
441      * @param modules
442      *            all available modules
443      * @param module
444      *            current module
445      */
446     private void resolveDirtyNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
447         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
448         if (!dirtyNodes.isEmpty()) {
449             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
450                 if (nodeToResolve instanceof UnionTypeBuilder) {
451                     // special handling for union types
452                     resolveTypeUnion((UnionTypeBuilder) nodeToResolve, modules, module);
453                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
454                     // special handling for identityref types
455                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
456                     IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
457                             idref.getLine());
458                     if (identity == null) {
459                         throw new YangParseException(module.getName(), idref.getLine(), "Failed to find base identity");
460                     }
461                     idref.setBaseIdentity(identity);
462                     nodeToResolve.setType(idref.build());
463                 } else {
464                     resolveType(nodeToResolve, modules, module);
465                 }
466             }
467         }
468     }
469
470     private void resolveDirtyNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
471             final ModuleBuilder module, SchemaContext context) {
472         final Set<TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
473         if (!dirtyNodes.isEmpty()) {
474             for (TypeAwareBuilder nodeToResolve : dirtyNodes) {
475                 if (nodeToResolve instanceof UnionTypeBuilder) {
476                     // special handling for union types
477                     resolveTypeUnionWithContext((UnionTypeBuilder) nodeToResolve, modules, module, context);
478                 } else if (nodeToResolve.getTypedef() instanceof IdentityrefTypeBuilder) {
479                     // special handling for identityref types
480                     IdentityrefTypeBuilder idref = (IdentityrefTypeBuilder) nodeToResolve.getTypedef();
481                     IdentitySchemaNodeBuilder identity = findBaseIdentity(modules, module, idref.getBaseString(),
482                             idref.getLine());
483                     idref.setBaseIdentity(identity);
484                     nodeToResolve.setType(idref.build());
485                 } else {
486                     resolveTypeWithContext(nodeToResolve, modules, module, context);
487                 }
488             }
489         }
490     }
491
492     /**
493      * Correct augment target path.
494      *
495      * @param modules
496      *            all loaded modules
497      */
498     private void resolveAugmentsTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
499             SchemaContext context) {
500         // collect augments from all loaded modules
501         final List<AugmentationSchemaBuilder> allAugments = new ArrayList<>();
502         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
503             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
504                 allAugments.addAll(inner.getValue().getAllAugments());
505             }
506         }
507
508         for (AugmentationSchemaBuilder augment : allAugments) {
509             setCorrectAugmentTargetPath(modules, augment, context);
510         }
511     }
512
513     private void setCorrectAugmentTargetPath(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
514             final AugmentationSchemaBuilder augment, final SchemaContext context) {
515         ModuleBuilder module = ParserUtils.getParentModule(augment);
516         SchemaPath oldSchemaPath = augment.getTargetPath();
517         List<QName> oldPath = oldSchemaPath.getPath();
518         List<QName> newPath = new ArrayList<>();
519
520         Builder parent = augment.getParent();
521         if (parent instanceof UsesNodeBuilder) {
522             DataNodeContainerBuilder usesParent = ((UsesNodeBuilder) parent).getParent();
523             newPath.addAll(usesParent.getPath().getPath());
524
525             URI ns;
526             Date revision;
527             String prefix;
528             QName baseQName = usesParent.getQName();
529             if (baseQName == null) {
530                 ModuleBuilder m = ParserUtils.getParentModule(usesParent);
531                 ns = m.getNamespace();
532                 revision = m.getRevision();
533                 prefix = m.getPrefix();
534             } else {
535                 ns = baseQName.getNamespace();
536                 revision = baseQName.getRevision();
537                 prefix = baseQName.getPrefix();
538             }
539
540             for (QName qn : oldSchemaPath.getPath()) {
541                 newPath.add(new QName(ns, revision, prefix, qn.getLocalName()));
542             }
543         } else {
544
545             for (QName qn : oldPath) {
546                 URI ns = module.getNamespace();
547                 Date rev = module.getRevision();
548                 String pref = module.getPrefix();
549                 String localPrefix = qn.getPrefix();
550                 if (localPrefix != null && !("".equals(localPrefix))) {
551                     ModuleBuilder currentModule = ParserUtils.findModuleFromBuilders(modules, module, localPrefix,
552                             augment.getLine());
553                     if (currentModule == null) {
554                         Module m = ParserUtils.findModuleFromContext(context, module, localPrefix, augment.getLine());
555                         if (m == null) {
556                             throw new YangParseException(module.getName(), augment.getLine(), "Module with prefix "
557                                     + localPrefix + " not found.");
558                         }
559                         ns = m.getNamespace();
560                         rev = m.getRevision();
561                         pref = m.getPrefix();
562                     } else {
563                         ns = currentModule.getNamespace();
564                         rev = currentModule.getRevision();
565                         pref = currentModule.getPrefix();
566                     }
567                 }
568                 newPath.add(new QName(ns, rev, pref, qn.getLocalName()));
569             }
570         }
571         augment.setTargetNodeSchemaPath(new SchemaPath(newPath, augment.getTargetPath().isAbsolute()));
572
573         for (DataSchemaNodeBuilder childNode : augment.getChildNodes()) {
574             correctPathForAugmentNodes(childNode, augment.getTargetNodeSchemaPath());
575         }
576     }
577
578     private void correctPathForAugmentNodes(DataSchemaNodeBuilder node, SchemaPath parentPath) {
579         SchemaPath newPath = ParserUtils.createSchemaPath(parentPath, node.getQName());
580         node.setPath(newPath);
581         if (node instanceof DataNodeContainerBuilder) {
582             for (DataSchemaNodeBuilder child : ((DataNodeContainerBuilder) node).getChildNodes()) {
583                 correctPathForAugmentNodes(child, node.getPath());
584             }
585         }
586         if (node instanceof ChoiceBuilder) {
587             for (ChoiceCaseBuilder child : ((ChoiceBuilder)node).getCases()) {
588                 correctPathForAugmentNodes(child, node.getPath());
589             }
590         }
591     }
592
593     /**
594      * Check augments for mandatory nodes. If the target node is in another
595      * module, then nodes added by the augmentation MUST NOT be mandatory nodes.
596      * If mandatory node is found, throw an exception.
597      *
598      * @param augments
599      *            augments to check
600      */
601     private void checkAugmentMandatoryNodes(Collection<AugmentationSchemaBuilder> augments) {
602         for (AugmentationSchemaBuilder augment : augments) {
603             String augmentPrefix = augment.getTargetPath().getPath().get(0).getPrefix();
604             ModuleBuilder module = ParserUtils.getParentModule(augment);
605             String modulePrefix = module.getPrefix();
606
607             if (augmentPrefix == null || augmentPrefix.isEmpty() || augmentPrefix.equals(modulePrefix)) {
608                 continue;
609             }
610
611             for (DataSchemaNodeBuilder childNode : augment.getChildNodes()) {
612                 if (childNode.getConstraints().isMandatory()) {
613                     throw new YangParseException(augment.getModuleName(), augment.getLine(),
614                             "Error in augment parsing: cannot augment mandatory node "
615                                     + childNode.getQName().getLocalName());
616                 }
617             }
618         }
619     }
620
621     /**
622      * Go through all augment definitions and resolve them. This method works in
623      * same way as {@link #resolveAugments(Map)} except that if target node is
624      * not found in loaded modules, it search for target node in given context.
625      *
626      * @param modules
627      *            all loaded modules
628      * @param context
629      *            SchemaContext containing already resolved modules
630      */
631     private void resolveAugments(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
632         List<ModuleBuilder> all = new ArrayList<>();
633         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
634             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
635                 all.add(inner.getValue());
636             }
637         }
638
639         List<ModuleBuilder> sorted;
640         if (context == null) {
641             sorted = ModuleDependencySort.sort(all.toArray(new ModuleBuilder[all.size()]));
642         } else {
643             sorted = ModuleDependencySort.sortWithContext(context, all.toArray(new ModuleBuilder[all.size()]));
644         }
645
646         // resolve other augments
647         for (ModuleBuilder mb : sorted) {
648             if (mb != null) {
649                 List<AugmentationSchemaBuilder> augments = mb.getAllAugments();
650                 checkAugmentMandatoryNodes(augments);
651                 for (AugmentationSchemaBuilder augment : augments) {
652                     if (!(augment.isResolved())) {
653                         boolean resolved = resolveAugment(augment, mb, modules, context);
654                         if (!resolved) {
655                             throw new YangParseException(augment.getModuleName(), augment.getLine(),
656                                     "Error in augment parsing: failed to find augment target: " + augment);
657
658                         }
659                     }
660                 }
661             }
662         }
663     }
664
665     private boolean resolveUsesAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
666             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
667         if (augment.isResolved()) {
668             return true;
669         }
670
671         UsesNodeBuilder usesNode = (UsesNodeBuilder) augment.getParent();
672         DataNodeContainerBuilder parentNode = usesNode.getParent();
673         SchemaNodeBuilder targetNode;
674         if (parentNode instanceof ModuleBuilder) {
675             targetNode = findSchemaNodeInModule(augment.getTargetPath().getPath(), (ModuleBuilder)parentNode);
676         } else {
677             targetNode = findSchemaNode(augment.getTargetPath().getPath(), (SchemaNodeBuilder)parentNode);
678         }
679
680         fillAugmentTarget(augment, targetNode);
681         augment.setResolved(true);
682         return true;
683     }
684
685     private boolean resolveAugment(final AugmentationSchemaBuilder augment, final ModuleBuilder module,
686             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
687         if (augment.isResolved()) {
688             return true;
689         }
690
691         List<QName> targetPath = augment.getTargetPath().getPath();
692         ModuleBuilder targetModule = findTargetModule(targetPath.get(0), module, modules, context, augment.getLine());
693         if (targetModule == null) {
694             throw new YangParseException(module.getModuleName(), augment.getLine(), "Failed to resolve augment "
695                     + augment);
696         }
697
698         return processAugmentation(augment, targetModule);
699     }
700
701     /**
702      * Find module from loaded modules or from context based on given qname. If
703      * module is found in context, create wrapper over this module and add it to
704      * collection of loaded modules.
705      *
706      * @param qname
707      * @param module
708      *            current module
709      * @param modules
710      *            all loaded modules
711      * @param context
712      *            schema context
713      * @param line
714      *            current line
715      * @return
716      */
717     private ModuleBuilder findTargetModule(final QName qname, final ModuleBuilder module,
718             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context, final int line) {
719         ModuleBuilder targetModule = null;
720
721         String prefix = qname.getPrefix();
722         if (prefix == null || prefix.equals("")) {
723             targetModule = module;
724         } else {
725             targetModule = findModuleFromBuilders(modules, module, qname.getPrefix(), line);
726         }
727
728         if (targetModule == null && context != null) {
729             Module m = findModuleFromContext(context, module, prefix, line);
730             targetModule = new ModuleBuilder(m);
731             DataSchemaNode firstNode = m.getDataChildByName(qname.getLocalName());
732             DataSchemaNodeBuilder firstNodeWrapped = wrapChildNode(targetModule.getModuleName(), line, firstNode,
733                     targetModule.getPath(), firstNode.getQName());
734             targetModule.addChildNode(firstNodeWrapped);
735
736             TreeMap<Date, ModuleBuilder> map = new TreeMap<>();
737             map.put(targetModule.getRevision(), targetModule);
738             modules.put(targetModule.getModuleName(), map);
739         }
740
741         return targetModule;
742     }
743
744     /**
745      * Go through identity statements defined in current module and resolve
746      * their 'base' statement if present.
747      *
748      * @param modules
749      *            all modules
750      * @param module
751      *            module being resolved
752      */
753     private void resolveIdentities(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
754         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
755         for (IdentitySchemaNodeBuilder identity : identities) {
756             final String baseIdentityName = identity.getBaseIdentityName();
757             final int line = identity.getLine();
758             if (baseIdentityName != null) {
759                 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
760                 if (baseIdentity == null) {
761                     throw new YangParseException(module.getName(), identity.getLine(), "Failed to find base identity");
762                 } else {
763                     identity.setBaseIdentity(baseIdentity);
764                 }
765             }
766         }
767     }
768
769     /**
770      * Go through identity statements defined in current module and resolve
771      * their 'base' statement. Method tries to find base identity in given
772      * modules. If base identity is not found, method will search it in context.
773      *
774      * @param modules
775      *            all loaded modules
776      * @param module
777      *            current module
778      * @param context
779      *            SchemaContext containing already resolved modules
780      */
781     private void resolveIdentitiesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
782             final ModuleBuilder module, final SchemaContext context) {
783         final Set<IdentitySchemaNodeBuilder> identities = module.getIdentities();
784         for (IdentitySchemaNodeBuilder identity : identities) {
785             final String baseIdentityName = identity.getBaseIdentityName();
786             final int line = identity.getLine();
787             if (baseIdentityName != null) {
788                 IdentitySchemaNodeBuilder baseIdentity = findBaseIdentity(modules, module, baseIdentityName, line);
789                 if (baseIdentity == null) {
790                     IdentitySchemaNode baseId = findBaseIdentityFromContext(modules, module, baseIdentityName, line,
791                             context);
792                     identity.setBaseIdentity(baseId);
793                 } else {
794                     identity.setBaseIdentity(baseIdentity);
795                 }
796             }
797         }
798     }
799
800     /**
801      * Find and add reference of uses target grouping.
802      *
803      * @param modules
804      *            all loaded modules
805      * @param context
806      *            SchemaContext containing already resolved modules or null if
807      *            context is not available
808      */
809     private void resolveUsesTargetGrouping(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
810             final SchemaContext context) {
811         final List<UsesNodeBuilder> allUses = new ArrayList<>();
812         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
813             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
814                 allUses.addAll(inner.getValue().getAllUsesNodes());
815             }
816         }
817         for (UsesNodeBuilder usesNode : allUses) {
818             ModuleBuilder module = ParserUtils.getParentModule(usesNode);
819             final GroupingBuilder targetGroupingBuilder = GroupingUtils.getTargetGroupingFromModules(usesNode, modules,
820                     module);
821             if (targetGroupingBuilder == null) {
822                 if (context == null) {
823                     throw new YangParseException(module.getName(), usesNode.getLine(), "Referenced grouping '"
824                             + usesNode.getGroupingPathAsString() + "' not found.");
825                 } else {
826                     GroupingDefinition targetGroupingDefinition = GroupingUtils.getTargetGroupingFromContext(usesNode,
827                             module, context);
828                     usesNode.setGroupingDefinition(targetGroupingDefinition);
829                 }
830             } else {
831                 usesNode.setGrouping(targetGroupingBuilder);
832             }
833         }
834     }
835
836     private void resolveUsesForGroupings(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
837         final Set<GroupingBuilder> allGroupings = new HashSet<>();
838         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
839             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
840                 ModuleBuilder module = inner.getValue();
841                 allGroupings.addAll(module.getAllGroupings());
842             }
843         }
844         final List<GroupingBuilder> sorted = GroupingSort.sort(allGroupings);
845         for (GroupingBuilder gb : sorted) {
846             List<UsesNodeBuilder> usesNodes = new ArrayList<>(GroupingSort.getAllUsesNodes(gb));
847             Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
848             for (UsesNodeBuilder usesNode : usesNodes) {
849                 resolveUses(usesNode, modules, context);
850             }
851         }
852     }
853
854     private void resolveUsesForNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
855         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
856             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
857                 ModuleBuilder module = inner.getValue();
858                 List<UsesNodeBuilder> usesNodes = module.getAllUsesNodes();
859                 Collections.sort(usesNodes, new GroupingUtils.UsesComparator());
860                 for (UsesNodeBuilder usesNode : usesNodes) {
861                     resolveUses(usesNode, modules, context);
862                 }
863             }
864         }
865     }
866
867     private void resolveUses(UsesNodeBuilder usesNode,
868             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final SchemaContext context) {
869         if (!usesNode.isResolved()) {
870             DataNodeContainerBuilder parent = usesNode.getParent();
871             ModuleBuilder module = ParserUtils.getParentModule(parent);
872             GroupingBuilder target = GroupingUtils.getTargetGroupingFromModules(usesNode, modules, module);
873             if (target == null) {
874                 resolveUsesWithContext(usesNode);
875                 usesNode.setResolved(true);
876                 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
877                     resolveUsesAugment(augment, module, modules, context);
878                 }
879             } else {
880                 parent.getChildNodes().addAll(target.instantiateChildNodes(parent));
881                 parent.getTypeDefinitionBuilders().addAll(target.instantiateTypedefs(parent));
882                 parent.getGroupingBuilders().addAll(target.instantiateGroupings(parent));
883                 parent.getUnknownNodes().addAll(target.instantiateUnknownNodes(parent));
884                 usesNode.setResolved(true);
885                 for (AugmentationSchemaBuilder augment : usesNode.getAugmentations()) {
886                     resolveUsesAugment(augment, module, modules, context);
887                 }
888             }
889             GroupingUtils.performRefine(usesNode);
890         }
891     }
892
893     private void resolveUsesWithContext(UsesNodeBuilder usesNode) {
894         final int line = usesNode.getLine();
895         DataNodeContainerBuilder parent = usesNode.getParent();
896         ModuleBuilder module = ParserUtils.getParentModule(parent);
897         SchemaPath parentPath;
898         URI ns = null;
899         Date rev = null;
900         String pref = null;
901         if (parent instanceof AugmentationSchemaBuilder || parent instanceof ModuleBuilder) {
902             ns = module.getNamespace();
903             rev = module.getRevision();
904             pref = module.getPrefix();
905             if (parent instanceof AugmentationSchemaBuilder) {
906                 parentPath = ((AugmentationSchemaBuilder)parent).getTargetNodeSchemaPath();
907             } else {
908                 parentPath = ((ModuleBuilder)parent).getPath();
909             }
910         } else {
911             ns = ((DataSchemaNodeBuilder) parent).getQName().getNamespace();
912             rev = ((DataSchemaNodeBuilder) parent).getQName().getRevision();
913             pref = ((DataSchemaNodeBuilder) parent).getQName().getPrefix();
914             parentPath = ((DataSchemaNodeBuilder)parent).getPath();
915         }
916
917         GroupingDefinition gd = usesNode.getGroupingDefinition();
918
919         Set<DataSchemaNodeBuilder> childNodes = wrapChildNodes(module.getModuleName(), line,
920                 gd.getChildNodes(), parentPath, ns, rev, pref);
921         parent.getChildNodes().addAll(childNodes);
922         for (DataSchemaNodeBuilder childNode : childNodes) {
923             setNodeAddedByUses(childNode);
924         }
925
926         Set<TypeDefinitionBuilder> typedefs = wrapTypedefs(module.getModuleName(), line, gd, parentPath, ns,
927                 rev, pref);
928         parent.getTypeDefinitionBuilders().addAll(typedefs);
929         for (TypeDefinitionBuilder typedef : typedefs) {
930             setNodeAddedByUses(typedef);
931         }
932
933         Set<GroupingBuilder> groupings = wrapGroupings(module.getModuleName(), line, usesNode
934                 .getGroupingDefinition().getGroupings(), parentPath, ns, rev, pref);
935         parent.getGroupingBuilders().addAll(groupings);
936         for (GroupingBuilder gb : groupings) {
937             setNodeAddedByUses(gb);
938         }
939
940         List<UnknownSchemaNodeBuilder> unknownNodes = wrapUnknownNodes(module.getModuleName(), line,
941                 gd.getUnknownSchemaNodes(), parentPath, ns, rev, pref);
942         parent.getUnknownNodes().addAll(unknownNodes);
943         for (UnknownSchemaNodeBuilder un : unknownNodes) {
944             un.setAddedByUses(true);
945         }
946     }
947
948     private void resolveUnknownNodes(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
949         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
950             QName nodeType = usnb.getNodeType();
951             try {
952                 ModuleBuilder dependentModule = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
953                         usnb.getLine());
954                 for (ExtensionBuilder extension : dependentModule.getExtensions()) {
955                     if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
956                         usnb.setNodeType(extension.getQName());
957                         usnb.setExtensionBuilder(extension);
958                         break;
959                     }
960                 }
961             } catch (YangParseException e) {
962                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
963                         + ": no such extension definition found.", e);
964             }
965         }
966     }
967
968     private void resolveUnknownNodesWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
969             final ModuleBuilder module, final SchemaContext context) {
970         for (UnknownSchemaNodeBuilder usnb : module.getAllUnknownNodes()) {
971             QName nodeType = usnb.getNodeType();
972             try {
973                 ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, nodeType.getPrefix(),
974                         usnb.getLine());
975
976                 if (dependentModuleBuilder == null) {
977                     Module dependentModule = findModuleFromContext(context, module, nodeType.getPrefix(),
978                             usnb.getLine());
979                     for (ExtensionDefinition e : dependentModule.getExtensionSchemaNodes()) {
980                         if (e.getQName().getLocalName().equals(nodeType.getLocalName())) {
981                             usnb.setNodeType(new QName(e.getQName().getNamespace(), e.getQName().getRevision(),
982                                     nodeType.getPrefix(), e.getQName().getLocalName()));
983                             usnb.setExtensionDefinition(e);
984                             break;
985                         }
986                     }
987                 } else {
988                     for (ExtensionBuilder extension : dependentModuleBuilder.getExtensions()) {
989                         if (extension.getQName().getLocalName().equals(nodeType.getLocalName())) {
990                             usnb.setExtensionBuilder(extension);
991                             break;
992                         }
993                     }
994                 }
995
996             } catch (YangParseException e) {
997                 throw new YangParseException(module.getName(), usnb.getLine(), "Failed to resolve node " + usnb
998                         + ": no such extension definition found.", e);
999             }
1000
1001         }
1002     }
1003
1004     /**
1005      * Traverse through modules and resolve their deviation statements.
1006      *
1007      * @param modules
1008      *            all loaded modules
1009      */
1010     private void resolveDeviations(final Map<String, TreeMap<Date, ModuleBuilder>> modules) {
1011         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1012             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1013                 ModuleBuilder b = inner.getValue();
1014                 resolveDeviation(modules, b);
1015             }
1016         }
1017     }
1018
1019     /**
1020      * Traverse through module and resolve its deviation statements.
1021      *
1022      * @param modules
1023      *            all loaded modules
1024      * @param module
1025      *            module in which resolve deviations
1026      */
1027     private void resolveDeviation(final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
1028         for (DeviationBuilder dev : module.getDeviations()) {
1029             int line = dev.getLine();
1030             SchemaPath targetPath = dev.getTargetPath();
1031             List<QName> path = targetPath.getPath();
1032             QName q0 = path.get(0);
1033             String prefix = q0.getPrefix();
1034             if (prefix == null) {
1035                 prefix = module.getPrefix();
1036             }
1037
1038             ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1039             processDeviation(dev, dependentModuleBuilder, path, module);
1040         }
1041     }
1042
1043     /**
1044      * Traverse through modules and resolve their deviation statements with
1045      * given context.
1046      *
1047      * @param modules
1048      *            all loaded modules
1049      * @param context
1050      *            already resolved context
1051      */
1052     private void resolveDeviationsWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1053             final SchemaContext context) {
1054         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules.entrySet()) {
1055             for (Map.Entry<Date, ModuleBuilder> inner : entry.getValue().entrySet()) {
1056                 ModuleBuilder b = inner.getValue();
1057                 resolveDeviationWithContext(modules, b, context);
1058             }
1059         }
1060     }
1061
1062     /**
1063      * Traverse through module and resolve its deviation statements with given
1064      * context.
1065      *
1066      * @param modules
1067      *            all loaded modules
1068      * @param module
1069      *            module in which resolve deviations
1070      * @param context
1071      *            already resolved context
1072      */
1073     private void resolveDeviationWithContext(final Map<String, TreeMap<Date, ModuleBuilder>> modules,
1074             final ModuleBuilder module, final SchemaContext context) {
1075         for (DeviationBuilder dev : module.getDeviations()) {
1076             int line = dev.getLine();
1077             SchemaPath targetPath = dev.getTargetPath();
1078             List<QName> path = targetPath.getPath();
1079             QName q0 = path.get(0);
1080             String prefix = q0.getPrefix();
1081             if (prefix == null) {
1082                 prefix = module.getPrefix();
1083             }
1084
1085             ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, prefix, line);
1086             if (dependentModuleBuilder == null) {
1087                 Object currentParent = findModuleFromContext(context, module, prefix, line);
1088
1089                 for (QName q : path) {
1090                     if (currentParent == null) {
1091                         throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1092                     }
1093                     String name = q.getLocalName();
1094                     if (currentParent instanceof DataNodeContainer) {
1095                         currentParent = ((DataNodeContainer) currentParent).getDataChildByName(name);
1096                     }
1097                 }
1098
1099                 if (currentParent == null) {
1100                     throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1101                 }
1102                 if (currentParent instanceof SchemaNode) {
1103                     dev.setTargetPath(((SchemaNode) currentParent).getPath());
1104                 }
1105
1106             } else {
1107                 processDeviation(dev, dependentModuleBuilder, path, module);
1108             }
1109         }
1110     }
1111
1112     /**
1113      * Correct deviation target path in deviation builder.
1114      *
1115      * @param dev
1116      *            deviation
1117      * @param dependentModuleBuilder
1118      *            module containing deviation target
1119      * @param path
1120      *            current deviation target path
1121      * @param module
1122      *            current module
1123      */
1124     private void processDeviation(final DeviationBuilder dev, final ModuleBuilder dependentModuleBuilder,
1125             final List<QName> path, final ModuleBuilder module) {
1126         final int line = dev.getLine();
1127         Builder currentParent = dependentModuleBuilder;
1128
1129         for (QName q : path) {
1130             if (currentParent == null) {
1131                 throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1132             }
1133             String name = q.getLocalName();
1134             if (currentParent instanceof DataNodeContainerBuilder) {
1135                 currentParent = ((DataNodeContainerBuilder) currentParent).getDataChildByName(name);
1136             }
1137         }
1138
1139         if (!(currentParent instanceof SchemaNodeBuilder)) {
1140             throw new YangParseException(module.getName(), line, FAIL_DEVIATION_TARGET);
1141         }
1142         dev.setTargetPath(((SchemaNodeBuilder) currentParent).getPath());
1143     }
1144
1145 }