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