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