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