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