f30780f30ce7b30d70d730592b618cfc7def41e0
[controller.git] / opendaylight / sal / yang-prototype / code-generator / yang-model-parser-impl / src / main / java / org / opendaylight / controller / yang / model / parser / impl / YangModelParserImpl.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.model.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.Calendar;
17 import java.util.Date;
18 import java.util.HashMap;
19 import java.util.HashSet;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23 import java.util.TreeMap;
24
25 import org.antlr.v4.runtime.ANTLRInputStream;
26 import org.antlr.v4.runtime.CommonTokenStream;
27 import org.antlr.v4.runtime.tree.ParseTree;
28 import org.antlr.v4.runtime.tree.ParseTreeWalker;
29 import org.opendaylight.controller.antlrv4.code.gen.YangLexer;
30 import org.opendaylight.controller.antlrv4.code.gen.YangParser;
31 import org.opendaylight.controller.yang.common.QName;
32 import org.opendaylight.controller.yang.model.api.AugmentationSchema;
33 import org.opendaylight.controller.yang.model.api.DataSchemaNode;
34 import org.opendaylight.controller.yang.model.api.ExtensionDefinition;
35 import org.opendaylight.controller.yang.model.api.Module;
36 import org.opendaylight.controller.yang.model.api.ModuleImport;
37 import org.opendaylight.controller.yang.model.api.NotificationDefinition;
38 import org.opendaylight.controller.yang.model.api.RpcDefinition;
39 import org.opendaylight.controller.yang.model.api.SchemaContext;
40 import org.opendaylight.controller.yang.model.api.SchemaPath;
41 import org.opendaylight.controller.yang.model.api.TypeDefinition;
42 import org.opendaylight.controller.yang.model.api.type.BinaryTypeDefinition;
43 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition;
44 import org.opendaylight.controller.yang.model.api.type.BitsTypeDefinition.Bit;
45 import org.opendaylight.controller.yang.model.api.type.DecimalTypeDefinition;
46 import org.opendaylight.controller.yang.model.api.type.IntegerTypeDefinition;
47 import org.opendaylight.controller.yang.model.api.type.LengthConstraint;
48 import org.opendaylight.controller.yang.model.api.type.PatternConstraint;
49 import org.opendaylight.controller.yang.model.api.type.RangeConstraint;
50 import org.opendaylight.controller.yang.model.api.type.StringTypeDefinition;
51 import org.opendaylight.controller.yang.model.parser.api.YangModelParser;
52 import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationSchemaBuilder;
53 import org.opendaylight.controller.yang.model.parser.builder.api.AugmentationTargetBuilder;
54 import org.opendaylight.controller.yang.model.parser.builder.api.ChildNodeBuilder;
55 import org.opendaylight.controller.yang.model.parser.builder.api.DataSchemaNodeBuilder;
56 import org.opendaylight.controller.yang.model.parser.builder.api.TypeAwareBuilder;
57 import org.opendaylight.controller.yang.model.parser.builder.api.TypeDefinitionBuilder;
58 import org.opendaylight.controller.yang.model.parser.builder.impl.IdentitySchemaNodeBuilder;
59 import org.opendaylight.controller.yang.model.parser.builder.impl.ModuleBuilder;
60 import org.opendaylight.controller.yang.model.parser.builder.impl.UnionTypeBuilder;
61 import org.opendaylight.controller.yang.model.util.BaseConstraints;
62 import org.opendaylight.controller.yang.model.util.BinaryType;
63 import org.opendaylight.controller.yang.model.util.BitsType;
64 import org.opendaylight.controller.yang.model.util.StringType;
65 import org.opendaylight.controller.yang.model.util.UnknownType;
66 import org.opendaylight.controller.yang.model.util.YangTypesConverter;
67 import org.slf4j.Logger;
68 import org.slf4j.LoggerFactory;
69
70 public class YangModelParserImpl implements YangModelParser {
71
72     private static final Logger logger = LoggerFactory
73             .getLogger(YangModelParserImpl.class);
74
75     @Override
76     public Module parseYangModel(String yangFile) {
77         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFile);
78         Set<Module> result = build(modules);
79         return result.iterator().next();
80     }
81
82     @Override
83     public Set<Module> parseYangModels(String... yangFiles) {
84         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangFiles);
85         Set<Module> result = build(modules);
86         return result;
87     }
88
89     @Override
90     public Set<Module> parseYangModelsFromStreams(
91             InputStream... yangModelStreams) {
92         final Map<String, TreeMap<Date, ModuleBuilder>> modules = resolveModuleBuildersFromStreams(yangModelStreams);
93         Set<Module> result = build(modules);
94         return result;
95     }
96
97     @Override
98     public SchemaContext resolveSchemaContext(Set<Module> modules) {
99         return new SchemaContextImpl(modules);
100     }
101
102     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(
103             String... yangFiles) {
104         InputStream[] streams = new InputStream[yangFiles.length];
105         for (int i = 0; i < yangFiles.length; i++) {
106             final String yangFileName = yangFiles[i];
107             final File yangFile = new File(yangFileName);
108             FileInputStream inStream = null;
109             try {
110                 inStream = new FileInputStream(yangFile);
111             } catch (FileNotFoundException e) {
112                 logger.warn("Exception while reading yang stream: " + inStream,
113                         e);
114             }
115             streams[i] = inStream;
116         }
117         return resolveModuleBuildersFromStreams(streams);
118     }
119
120     private Map<String, TreeMap<Date, ModuleBuilder>> resolveModuleBuildersFromStreams(
121             InputStream... yangFiles) {
122         final Map<String, TreeMap<Date, ModuleBuilder>> modules = new HashMap<String, TreeMap<Date, ModuleBuilder>>();
123
124         final YangModelParserListenerImpl yangModelParser = new YangModelParserListenerImpl();
125         final ParseTreeWalker walker = new ParseTreeWalker();
126
127         List<ParseTree> trees = parseStreams(yangFiles);
128
129         ModuleBuilder[] builders = new ModuleBuilder[trees.size()];
130
131         for (int i = 0; i < trees.size(); i++) {
132             walker.walk(yangModelParser, trees.get(i));
133             builders[i] = yangModelParser.getModuleBuilder();
134         }
135
136         for (ModuleBuilder builder : builders) {
137             final String builderName = builder.getName();
138             Date builderRevision = builder.getRevision();
139             if (builderRevision == null) {
140                 builderRevision = createEpochTime();
141             }
142             TreeMap<Date, ModuleBuilder> builderByRevision = modules
143                     .get(builderName);
144             if (builderByRevision == null) {
145                 builderByRevision = new TreeMap<Date, ModuleBuilder>();
146             }
147             builderByRevision.put(builderRevision, builder);
148             modules.put(builderName, builderByRevision);
149         }
150         return modules;
151     }
152
153     private List<ParseTree> parseStreams(InputStream... yangStreams) {
154         List<ParseTree> trees = new ArrayList<ParseTree>();
155         for (InputStream yangStream : yangStreams) {
156             trees.add(parseStream(yangStream));
157         }
158         return trees;
159     }
160
161     private ParseTree parseStream(InputStream yangStream) {
162         ParseTree result = null;
163         try {
164             final ANTLRInputStream input = new ANTLRInputStream(yangStream);
165             final YangLexer lexer = new YangLexer(input);
166             final CommonTokenStream tokens = new CommonTokenStream(lexer);
167             final YangParser parser = new YangParser(tokens);
168             result = parser.yang();
169         } catch (IOException e) {
170             logger.warn("Exception while reading yang file: " + yangStream, e);
171         }
172         return result;
173     }
174
175     private Set<Module> build(Map<String, TreeMap<Date, ModuleBuilder>> modules) {
176         // first validate
177         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
178                 .entrySet()) {
179             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
180                     .entrySet()) {
181                 ModuleBuilder moduleBuilder = childEntry.getValue();
182                 validateBuilder(modules, moduleBuilder);
183             }
184         }
185         // then build
186         final Set<Module> result = new HashSet<Module>();
187         for (Map.Entry<String, TreeMap<Date, ModuleBuilder>> entry : modules
188                 .entrySet()) {
189             final Map<Date, Module> modulesByRevision = new HashMap<Date, Module>();
190             for (Map.Entry<Date, ModuleBuilder> childEntry : entry.getValue()
191                     .entrySet()) {
192                 ModuleBuilder moduleBuilder = childEntry.getValue();
193                 modulesByRevision.put(childEntry.getKey(),
194                         moduleBuilder.build());
195                 result.add(moduleBuilder.build());
196             }
197         }
198
199         return result;
200     }
201
202     private void validateBuilder(
203             Map<String, TreeMap<Date, ModuleBuilder>> modules,
204             ModuleBuilder builder) {
205         resolveTypedefs(modules, builder);
206         resolveAugments(modules, builder);
207         resolveIdentities(modules, builder);
208     }
209
210     /**
211      * Search for dirty nodes (node which contains UnknownType) and resolve
212      * unknown types.
213      *
214      * @param modules
215      *            all available modules
216      * @param module
217      *            current module
218      */
219     private void resolveTypedefs(
220             Map<String, TreeMap<Date, ModuleBuilder>> modules,
221             ModuleBuilder module) {
222         Map<List<String>, TypeAwareBuilder> dirtyNodes = module.getDirtyNodes();
223         if (dirtyNodes.size() == 0) {
224             return;
225         } else {
226             for (Map.Entry<List<String>, TypeAwareBuilder> entry : dirtyNodes
227                     .entrySet()) {
228                 TypeAwareBuilder typeToResolve = entry.getValue();
229
230                 if (typeToResolve instanceof UnionTypeBuilder) {
231                     resolveUnionTypeBuilder(modules, module,
232                             (UnionTypeBuilder) typeToResolve);
233                 } else {
234                     UnknownType ut = (UnknownType) typeToResolve.getType();
235                     TypeDefinition<?> resolvedType = findTargetType(ut,
236                             modules, module);
237                     typeToResolve.setType(resolvedType);
238                 }
239             }
240         }
241     }
242
243     private UnionTypeBuilder resolveUnionTypeBuilder(
244             Map<String, TreeMap<Date, ModuleBuilder>> modules,
245             ModuleBuilder builder, UnionTypeBuilder unionTypeBuilderToResolve) {
246         List<TypeDefinition<?>> resolvedTypes = new ArrayList<TypeDefinition<?>>();
247         List<TypeDefinition<?>> typesToRemove = new ArrayList<TypeDefinition<?>>();
248
249         for (TypeDefinition<?> td : unionTypeBuilderToResolve.getTypes()) {
250             if (td instanceof UnknownType) {
251                 TypeDefinition<?> resolvedType = findTargetType(
252                         (UnknownType) td, modules, builder);
253                 resolvedTypes.add(resolvedType);
254                 typesToRemove.add(td);
255             }
256         }
257
258         List<TypeDefinition<?>> unionTypeBuilderTypes = unionTypeBuilderToResolve
259                 .getTypes();
260         unionTypeBuilderTypes.addAll(resolvedTypes);
261         unionTypeBuilderTypes.removeAll(typesToRemove);
262
263         return unionTypeBuilderToResolve;
264     }
265
266     private TypeDefinition<?> findTargetType(UnknownType ut,
267             Map<String, TreeMap<Date, ModuleBuilder>> modules,
268             ModuleBuilder builder) {
269
270         Map<TypeDefinitionBuilder, TypeConstraints> foundedTypeDefinitionBuilder = findTypeDefinitionBuilderWithConstraints(
271                 modules, ut, builder);
272         TypeDefinitionBuilder targetType = foundedTypeDefinitionBuilder
273                 .entrySet().iterator().next().getKey();
274         TypeConstraints constraints = foundedTypeDefinitionBuilder.entrySet()
275                 .iterator().next().getValue();
276
277         TypeDefinition<?> targetTypeBaseType = targetType.getBaseType();
278
279         // RANGE
280         List<RangeConstraint> ranges = ut.getRangeStatements();
281         resolveRanges(ranges, targetType, modules, builder);
282         // LENGTH
283         List<LengthConstraint> lengths = ut.getLengthStatements();
284         resolveLengths(lengths, targetType, modules, builder);
285         // PATTERN
286         List<PatternConstraint> patterns = ut.getPatterns();
287         // Fraction Digits
288         Integer fractionDigits = ut.getFractionDigits();
289
290         targetTypeBaseType = mergeConstraints(targetTypeBaseType, constraints, ranges, lengths,
291                 patterns, fractionDigits);
292
293         return targetTypeBaseType;
294     }
295
296     /**
297      * Merge curent constraints with founded type constraints
298      *
299      * @param targetTypeBaseType
300      * @param constraints
301      * @param ranges
302      * @param lengths
303      * @param patterns
304      * @param fractionDigits
305      */
306     private TypeDefinition<?> mergeConstraints(TypeDefinition<?> targetTypeBaseType,
307             TypeConstraints constraints, List<RangeConstraint> ranges,
308             List<LengthConstraint> lengths, List<PatternConstraint> patterns,
309             Integer fractionDigits) {
310         String targetTypeBaseTypeName = targetTypeBaseType.getQName()
311                 .getLocalName();
312         // enumeration, leafref and identityref omitted because they have no
313         // restrictions
314         if (targetTypeBaseType instanceof DecimalTypeDefinition) {
315             List<RangeConstraint> fullRanges = new ArrayList<RangeConstraint>();
316             fullRanges.addAll(constraints.getRanges());
317             fullRanges.addAll(ranges);
318             Integer fd = fractionDigits == null ? constraints
319                     .getFractionDigits() : fractionDigits;
320             targetTypeBaseType = YangTypesConverter
321                     .javaTypeForBaseYangDecimal64Type(fullRanges, fd);
322         } else if (targetTypeBaseType instanceof IntegerTypeDefinition) {
323             List<RangeConstraint> fullRanges = new ArrayList<RangeConstraint>();
324             fullRanges.addAll(constraints.getRanges());
325             fullRanges.addAll(ranges);
326             if (targetTypeBaseTypeName.startsWith("int")) {
327                 targetTypeBaseType = YangTypesConverter
328                         .javaTypeForBaseYangSignedIntegerType(
329                                 targetTypeBaseTypeName, fullRanges);
330             } else {
331                 targetTypeBaseType = YangTypesConverter
332                         .javaTypeForBaseYangUnsignedIntegerType(
333                                 targetTypeBaseTypeName, fullRanges);
334             }
335         } else if (targetTypeBaseType instanceof StringTypeDefinition) {
336             List<LengthConstraint> fullLengths = new ArrayList<LengthConstraint>();
337             fullLengths.addAll(constraints.getLengths());
338             fullLengths.addAll(lengths);
339             List<PatternConstraint> fullPatterns = new ArrayList<PatternConstraint>();
340             fullPatterns.addAll(constraints.getPatterns());
341             fullPatterns.addAll(patterns);
342             targetTypeBaseType = new StringType(fullLengths, fullPatterns);
343         } else if (targetTypeBaseType instanceof BitsTypeDefinition) {
344             BitsTypeDefinition bitsType = (BitsTypeDefinition) targetTypeBaseType;
345             List<Bit> bits = bitsType.getBits();
346             targetTypeBaseType = new BitsType(bits);
347         } else if (targetTypeBaseType instanceof BinaryTypeDefinition) {
348             targetTypeBaseType = new BinaryType(null, lengths, null);
349         }
350         return targetTypeBaseType;
351     }
352
353     private TypeDefinitionBuilder findTypeDefinitionBuilder(
354             Map<String, TreeMap<Date, ModuleBuilder>> modules,
355             UnknownType unknownType, ModuleBuilder builder) {
356         Map<TypeDefinitionBuilder, TypeConstraints> result = findTypeDefinitionBuilderWithConstraints(
357                 modules, unknownType, builder);
358         return result.entrySet().iterator().next().getKey();
359     }
360
361     private Map<TypeDefinitionBuilder, TypeConstraints> findTypeDefinitionBuilderWithConstraints(
362             Map<String, TreeMap<Date, ModuleBuilder>> modules,
363             UnknownType unknownType, ModuleBuilder builder) {
364         return findTypeDefinitionBuilderWithConstraints(new TypeConstraints(),
365                 modules, unknownType, builder);
366     }
367
368     /**
369      * Traverse through all referenced types chain until base YANG type is
370      * founded.
371      *
372      * @param constraints
373      *            current type constraints
374      * @param modules
375      *            all available modules
376      * @param unknownType
377      *            unknown type
378      * @param builder
379      *            current module
380      * @return map, where key is type referenced and value is its constraints
381      */
382     private Map<TypeDefinitionBuilder, TypeConstraints> findTypeDefinitionBuilderWithConstraints(
383             TypeConstraints constraints,
384             Map<String, TreeMap<Date, ModuleBuilder>> modules,
385             UnknownType unknownType, ModuleBuilder builder) {
386         Map<TypeDefinitionBuilder, TypeConstraints> result = new HashMap<TypeDefinitionBuilder, TypeConstraints>();
387         QName unknownTypeQName = unknownType.getQName();
388         String unknownTypeName = unknownTypeQName.getLocalName();
389         String unknownTypePrefix = unknownTypeQName.getPrefix();
390
391         // search for module which contains referenced typedef
392         ModuleBuilder dependentModuleBuilder;
393         if (unknownTypePrefix.equals(builder.getPrefix())) {
394             dependentModuleBuilder = builder;
395         } else {
396             dependentModuleBuilder = findDependentModule(modules, builder,
397                     unknownTypePrefix);
398         }
399
400         // pull all typedef statements from dependent module...
401         final Set<TypeDefinitionBuilder> typedefs = dependentModuleBuilder
402                 .getModuleTypedefs();
403         // and search for referenced typedef
404         TypeDefinitionBuilder lookedUpBuilder = null;
405         for (TypeDefinitionBuilder tdb : typedefs) {
406             QName qname = tdb.getQName();
407             if (qname.getLocalName().equals(unknownTypeName)) {
408                 lookedUpBuilder = tdb;
409                 break;
410             }
411         }
412
413         // if referenced type is UnknownType again, search recursively with
414         // current constraints
415         TypeDefinition<?> referencedType = lookedUpBuilder.getBaseType();
416         if (referencedType instanceof UnknownType) {
417             UnknownType unknown = (UnknownType) lookedUpBuilder.getBaseType();
418
419             final List<RangeConstraint> ranges = unknown.getRangeStatements();
420             constraints.addRanges(ranges);
421             final List<LengthConstraint> lengths = unknown
422                     .getLengthStatements();
423             constraints.addLengths(lengths);
424             final List<PatternConstraint> patterns = unknown.getPatterns();
425             constraints.addPatterns(patterns);
426             return findTypeDefinitionBuilderWithConstraints(constraints,
427                     modules, unknown, dependentModuleBuilder);
428         } else {
429             // pull restriction from this base type and add them to
430             // 'constraints'
431             if (referencedType instanceof DecimalTypeDefinition) {
432                 constraints.addRanges(((DecimalTypeDefinition) referencedType)
433                         .getRangeStatements());
434                 constraints
435                         .setFractionDigits(((DecimalTypeDefinition) referencedType)
436                                 .getFractionDigits());
437             } else if (referencedType instanceof IntegerTypeDefinition) {
438                 constraints.addRanges(((IntegerTypeDefinition) referencedType)
439                         .getRangeStatements());
440             } else if (referencedType instanceof StringTypeDefinition) {
441                 constraints.addPatterns(((StringTypeDefinition) referencedType)
442                         .getPatterns());
443             } else if (referencedType instanceof BinaryTypeDefinition) {
444                 constraints.addLengths(((BinaryTypeDefinition) referencedType)
445                         .getLengthConstraints());
446             }
447             result.put(lookedUpBuilder, constraints);
448             return result;
449         }
450     }
451
452     /**
453      * Go through all augmentation definitions and resolve them. This means find
454      * referenced node and add child nodes to it.
455      *
456      * @param modules
457      *            all available modules
458      * @param module
459      *            current module
460      */
461     private void resolveAugments(
462             Map<String, TreeMap<Date, ModuleBuilder>> modules,
463             ModuleBuilder module) {
464         Set<AugmentationSchemaBuilder> augmentBuilders = module
465                 .getAddedAugments();
466
467         Set<AugmentationSchema> augments = new HashSet<AugmentationSchema>();
468         for (AugmentationSchemaBuilder augmentBuilder : augmentBuilders) {
469             SchemaPath augmentTargetSchemaPath = augmentBuilder.getTargetPath();
470             String prefix = null;
471             List<String> augmentTargetPath = new ArrayList<String>();
472
473             for (QName pathPart : augmentTargetSchemaPath.getPath()) {
474                 prefix = pathPart.getPrefix();
475                 augmentTargetPath.add(pathPart.getLocalName());
476             }
477             ModuleBuilder dependentModule = findDependentModule(modules,
478                     module, prefix);
479             //
480             augmentTargetPath.add(0, dependentModule.getName());
481             //
482
483
484             AugmentationTargetBuilder augmentTarget = (AugmentationTargetBuilder) dependentModule
485                     .getNode(augmentTargetPath);
486             AugmentationSchema result = augmentBuilder.build();
487             augmentTarget.addAugmentation(result);
488             fillAugmentTarget(augmentBuilder, (ChildNodeBuilder) augmentTarget);
489             augments.add(result);
490         }
491         module.setAugmentations(augments);
492     }
493
494     /**
495      * Add all augment's child nodes to given target.
496      *
497      * @param augment
498      * @param target
499      */
500     private void fillAugmentTarget(AugmentationSchemaBuilder augment,
501             ChildNodeBuilder target) {
502         for (DataSchemaNodeBuilder builder : augment.getChildNodes()) {
503             builder.setAugmenting(true);
504             target.addChildNode(builder);
505         }
506     }
507
508     /**
509      * Go through identity statements defined in current module and resolve
510      * their 'base' statement if present.
511      *
512      * @param modules
513      *            all modules
514      * @param module
515      *            module being resolved
516      */
517     private void resolveIdentities(
518             Map<String, TreeMap<Date, ModuleBuilder>> modules,
519             ModuleBuilder module) {
520         Set<IdentitySchemaNodeBuilder> identities = module.getAddedIdentities();
521         for (IdentitySchemaNodeBuilder identity : identities) {
522             String baseIdentityName = identity.getBaseIdentityName();
523             if (baseIdentityName != null) {
524                 String baseIdentityPrefix = null;
525                 String baseIdentityLocalName = null;
526                 if (baseIdentityName.contains(":")) {
527                     String[] splitted = baseIdentityName.split(":");
528                     baseIdentityPrefix = splitted[0];
529                     baseIdentityLocalName = splitted[1];
530                 } else {
531                     baseIdentityPrefix = module.getPrefix();
532                     baseIdentityLocalName = baseIdentityName;
533                 }
534                 ModuleBuilder dependentModule;
535                 if (baseIdentityPrefix.equals(module.getPrefix())) {
536                     dependentModule = module;
537                 } else {
538                     dependentModule = findDependentModule(modules, module,
539                             baseIdentityPrefix);
540                 }
541
542                 Set<IdentitySchemaNodeBuilder> dependentModuleIdentities = dependentModule
543                         .getAddedIdentities();
544                 for (IdentitySchemaNodeBuilder idBuilder : dependentModuleIdentities) {
545                     if (idBuilder.getQName().getLocalName()
546                             .equals(baseIdentityLocalName)) {
547                         identity.setBaseIdentity(idBuilder);
548                     }
549                 }
550             }
551         }
552     }
553
554     /**
555      * Find dependent module based on given prefix
556      *
557      * @param modules
558      *            all available modules
559      * @param module
560      *            current module
561      * @param prefix
562      *            target module prefix
563      * @return dependent module builder
564      */
565     private ModuleBuilder findDependentModule(
566             Map<String, TreeMap<Date, ModuleBuilder>> modules,
567             ModuleBuilder module, String prefix) {
568         ModuleImport dependentModuleImport = getModuleImport(module, prefix);
569         String dependentModuleName = dependentModuleImport.getModuleName();
570         Date dependentModuleRevision = dependentModuleImport.getRevision();
571
572         TreeMap<Date, ModuleBuilder> moduleBuildersByRevision = modules
573                 .get(dependentModuleName);
574         ModuleBuilder dependentModule;
575         if (dependentModuleRevision == null) {
576             dependentModule = moduleBuildersByRevision.lastEntry().getValue();
577         } else {
578             dependentModule = moduleBuildersByRevision
579                     .get(dependentModuleRevision);
580         }
581         return dependentModule;
582     }
583
584     /**
585      * Get module import referenced by given prefix.
586      *
587      * @param builder
588      *            module to search
589      * @param prefix
590      *            prefix associated with import
591      * @return ModuleImport based on given prefix
592      */
593     private ModuleImport getModuleImport(ModuleBuilder builder, String prefix) {
594         ModuleImport moduleImport = null;
595         for (ModuleImport mi : builder.getModuleImports()) {
596             if (mi.getPrefix().equals(prefix)) {
597                 moduleImport = mi;
598                 break;
599             }
600         }
601         return moduleImport;
602     }
603
604     /**
605      * Helper method for resolving special 'min' or 'max' values in range
606      * constraint
607      *
608      * @param ranges
609      *            ranges to resolve
610      * @param targetType
611      *            target type
612      * @param modules
613      *            all available modules
614      * @param builder
615      *            current module
616      */
617     private void resolveRanges(List<RangeConstraint> ranges,
618             TypeDefinitionBuilder targetType,
619             Map<String, TreeMap<Date, ModuleBuilder>> modules,
620             ModuleBuilder builder) {
621         if (ranges != null && ranges.size() > 0) {
622             Long min = (Long) ranges.get(0).getMin();
623             Long max = (Long) ranges.get(ranges.size() - 1).getMax();
624             // if range contains one of the special values 'min' or 'max'
625             if (min.equals(Long.MIN_VALUE) || max.equals(Long.MAX_VALUE)) {
626                 Long[] values = parseRangeConstraint(targetType, modules,
627                         builder);
628                 if (min.equals(Long.MIN_VALUE)) {
629                     min = values[0];
630                     RangeConstraint oldFirst = ranges.get(0);
631                     RangeConstraint newFirst = BaseConstraints.rangeConstraint(
632                             min, oldFirst.getMax(), oldFirst.getDescription(),
633                             oldFirst.getReference());
634                     ranges.set(0, newFirst);
635                 }
636                 if (max.equals(Long.MAX_VALUE)) {
637                     max = values[1];
638                     RangeConstraint oldLast = ranges.get(ranges.size() - 1);
639                     RangeConstraint newLast = BaseConstraints.rangeConstraint(
640                             oldLast.getMin(), max, oldLast.getDescription(),
641                             oldLast.getReference());
642                     ranges.set(ranges.size() - 1, newLast);
643                 }
644             }
645         }
646     }
647
648     /**
649      * Helper method for resolving special 'min' or 'max' values in length
650      * constraint
651      *
652      * @param lengths
653      *            lengths to resolve
654      * @param targetType
655      *            target type
656      * @param modules
657      *            all available modules
658      * @param builder
659      *            current module
660      */
661     private void resolveLengths(List<LengthConstraint> lengths,
662             TypeDefinitionBuilder targetType,
663             Map<String, TreeMap<Date, ModuleBuilder>> modules,
664             ModuleBuilder builder) {
665         if (lengths != null && lengths.size() > 0) {
666             Long min = lengths.get(0).getMin();
667             Long max = lengths.get(lengths.size() - 1).getMax();
668             // if length contains one of the special values 'min' or 'max'
669             if (min.equals(Long.MIN_VALUE) || max.equals(Long.MAX_VALUE)) {
670                 Long[] values = parseRangeConstraint(targetType, modules,
671                         builder);
672                 if (min.equals(Long.MIN_VALUE)) {
673                     min = values[0];
674                     LengthConstraint oldFirst = lengths.get(0);
675                     LengthConstraint newFirst = BaseConstraints
676                             .lengthConstraint(min, oldFirst.getMax(),
677                                     oldFirst.getDescription(),
678                                     oldFirst.getReference());
679                     lengths.set(0, newFirst);
680                 }
681                 if (max.equals(Long.MAX_VALUE)) {
682                     max = values[1];
683                     LengthConstraint oldLast = lengths.get(lengths.size() - 1);
684                     LengthConstraint newLast = BaseConstraints
685                             .lengthConstraint(oldLast.getMin(), max,
686                                     oldLast.getDescription(),
687                                     oldLast.getReference());
688                     lengths.set(lengths.size() - 1, newLast);
689                 }
690             }
691         }
692     }
693
694     private Long[] parseRangeConstraint(TypeDefinitionBuilder targetType,
695             Map<String, TreeMap<Date, ModuleBuilder>> modules,
696             ModuleBuilder builder) {
697         TypeDefinition<?> targetBaseType = targetType.getBaseType();
698
699         if (targetBaseType instanceof IntegerTypeDefinition) {
700             IntegerTypeDefinition itd = (IntegerTypeDefinition) targetBaseType;
701             List<RangeConstraint> ranges = itd.getRangeStatements();
702             Long min = (Long) ranges.get(0).getMin();
703             Long max = (Long) ranges.get(ranges.size() - 1).getMax();
704             return new Long[] { min, max };
705         } else if (targetBaseType instanceof DecimalTypeDefinition) {
706             DecimalTypeDefinition dtd = (DecimalTypeDefinition) targetBaseType;
707             List<RangeConstraint> ranges = dtd.getRangeStatements();
708             Long min = (Long) ranges.get(0).getMin();
709             Long max = (Long) ranges.get(ranges.size() - 1).getMax();
710             return new Long[] { min, max };
711         } else {
712             return parseRangeConstraint(
713                     findTypeDefinitionBuilder(modules,
714                             (UnknownType) targetBaseType, builder), modules,
715                     builder);
716         }
717     }
718
719     private Date createEpochTime() {
720         Calendar c = Calendar.getInstance();
721         c.setTimeInMillis(0);
722         return c.getTime();
723     }
724
725     private static class SchemaContextImpl implements SchemaContext {
726         private final Set<Module> modules;
727
728         private SchemaContextImpl(Set<Module> modules) {
729             this.modules = modules;
730         }
731
732         @Override
733         public Set<DataSchemaNode> getDataDefinitions() {
734             final Set<DataSchemaNode> dataDefs = new HashSet<DataSchemaNode>();
735             for (Module m : modules) {
736                 dataDefs.addAll(m.getChildNodes());
737             }
738             return dataDefs;
739         }
740
741         @Override
742         public Set<Module> getModules() {
743             return modules;
744         }
745
746         @Override
747         public Set<NotificationDefinition> getNotifications() {
748             final Set<NotificationDefinition> notifications = new HashSet<NotificationDefinition>();
749             for (Module m : modules) {
750                 notifications.addAll(m.getNotifications());
751             }
752             return notifications;
753         }
754
755         @Override
756         public Set<RpcDefinition> getOperations() {
757             final Set<RpcDefinition> rpcs = new HashSet<RpcDefinition>();
758             for (Module m : modules) {
759                 rpcs.addAll(m.getRpcs());
760             }
761             return rpcs;
762         }
763
764         @Override
765         public Set<ExtensionDefinition> getExtensions() {
766             final Set<ExtensionDefinition> extensions = new HashSet<ExtensionDefinition>();
767             for (Module m : modules) {
768                 extensions.addAll(m.getExtensionSchemaNodes());
769             }
770             return extensions;
771         }
772     }
773
774     private static class TypeConstraints {
775         private final List<RangeConstraint> ranges = new ArrayList<RangeConstraint>();
776         private final List<LengthConstraint> lengths = new ArrayList<LengthConstraint>();
777         private final List<PatternConstraint> patterns = new ArrayList<PatternConstraint>();
778         private Integer fractionDigits;
779
780         public List<RangeConstraint> getRanges() {
781             return ranges;
782         }
783
784         public void addRanges(List<RangeConstraint> ranges) {
785             this.ranges.addAll(0, ranges);
786         }
787
788         public List<LengthConstraint> getLengths() {
789             return lengths;
790         }
791
792         public void addLengths(List<LengthConstraint> lengths) {
793             this.lengths.addAll(0, lengths);
794         }
795
796         public List<PatternConstraint> getPatterns() {
797             return patterns;
798         }
799
800         public void addPatterns(List<PatternConstraint> patterns) {
801             this.patterns.addAll(0, patterns);
802         }
803
804         public Integer getFractionDigits() {
805             return fractionDigits;
806         }
807
808         public void setFractionDigits(Integer fractionDigits) {
809             if (fractionDigits != null) {
810                 this.fractionDigits = fractionDigits;
811             }
812         }
813     }
814
815 }