2839779a451fc125044430719dc3de025f60260b
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / util / TypeUtils.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.yangtools.yang.parser.util;
9
10 import static org.opendaylight.yangtools.yang.parser.util.ParserUtils.*;
11
12 import java.util.ArrayList;
13 import java.util.Date;
14 import java.util.List;
15 import java.util.Map;
16 import java.util.Set;
17 import java.util.TreeMap;
18
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.model.api.Module;
21 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
22 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
23 import org.opendaylight.yangtools.yang.model.api.type.*;
24 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
25 import org.opendaylight.yangtools.yang.model.util.UnknownType;
26 import org.opendaylight.yangtools.yang.parser.builder.api.Builder;
27 import org.opendaylight.yangtools.yang.parser.builder.api.DataNodeContainerBuilder;
28 import org.opendaylight.yangtools.yang.parser.builder.api.TypeAwareBuilder;
29 import org.opendaylight.yangtools.yang.parser.builder.api.TypeDefinitionBuilder;
30 import org.opendaylight.yangtools.yang.parser.builder.impl.IdentityrefTypeBuilder;
31 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
32 import org.opendaylight.yangtools.yang.parser.builder.impl.RpcDefinitionBuilder;
33 import org.opendaylight.yangtools.yang.parser.builder.impl.TypeDefinitionBuilderImpl;
34 import org.opendaylight.yangtools.yang.parser.builder.impl.UnionTypeBuilder;
35
36 /**
37  * Utility class which contains helper methods for dealing with type operations.
38  */
39 public final class TypeUtils {
40
41     private TypeUtils() {
42     }
43
44     /**
45      * Resolve unknown type of node. It is assumed that type of node is either
46      * UnknownType or ExtendedType with UnknownType as base type.
47      * 
48      * @param nodeToResolve
49      *            node with type to resolve
50      * @param modules
51      *            all loaded modules
52      * @param module
53      *            current module
54      */
55     public static void resolveType(final TypeAwareBuilder nodeToResolve,
56             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module) {
57         final int line = nodeToResolve.getLine();
58         final TypeDefinition<?> nodeToResolveType = nodeToResolve.getType();
59         final QName unknownTypeQName = nodeToResolveType.getBaseType().getQName();
60         final ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module,
61                 unknownTypeQName.getPrefix(), line);
62
63         TypeDefinitionBuilder resolvedType = findUnknownTypeDefinition(nodeToResolve, dependentModuleBuilder, modules,
64                 module);
65         nodeToResolve.setTypedef(resolvedType);
66     }
67
68     /**
69      * Resolve unknown type of node. It is assumed that type of node is either
70      * UnknownType or ExtendedType with UnknownType as base type.
71      * 
72      * @param nodeToResolve
73      *            node with type to resolve
74      * @param modules
75      *            all loaded modules
76      * @param module
77      *            current module
78      * @param context
79      *            SchemaContext containing already resolved modules
80      */
81     public static void resolveTypeWithContext(final TypeAwareBuilder nodeToResolve,
82             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
83             final SchemaContext context) {
84         final int line = nodeToResolve.getLine();
85         final TypeDefinition<?> nodeToResolveType = nodeToResolve.getType();
86         final QName unknownTypeQName = nodeToResolveType.getBaseType().getQName();
87         final ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module,
88                 unknownTypeQName.getPrefix(), line);
89
90         if (dependentModuleBuilder == null) {
91             final Module dependentModule = findModuleFromContext(context, module, unknownTypeQName.getPrefix(), line);
92             final Set<TypeDefinition<?>> types = dependentModule.getTypeDefinitions();
93             final TypeDefinition<?> type = findTypeByName(types, unknownTypeQName.getLocalName());
94
95             if (nodeToResolveType instanceof ExtendedType) {
96                 final ExtendedType extType = (ExtendedType) nodeToResolveType;
97                 final TypeDefinitionBuilder newType = extendedTypeWithNewBase(null, type, extType, modules, module,
98                         nodeToResolve.getLine());
99
100                 nodeToResolve.setTypedef(newType);
101             } else {
102                 if (nodeToResolve instanceof TypeDefinitionBuilder) {
103                     TypeDefinitionBuilder tdb = (TypeDefinitionBuilder) nodeToResolve;
104                     TypeConstraints tc = findConstraintsFromTypeBuilder(nodeToResolve,
105                             new TypeConstraints(module.getName(), nodeToResolve.getLine()), modules, module, context);
106                     tdb.setLengths(tc.getLength());
107                     tdb.setPatterns(tc.getPatterns());
108                     tdb.setRanges(tc.getRange());
109                     tdb.setFractionDigits(tc.getFractionDigits());
110                 }
111                 nodeToResolve.setType(type);
112             }
113
114         } else {
115             TypeDefinitionBuilder resolvedType = findUnknownTypeDefinition(nodeToResolve, dependentModuleBuilder,
116                     modules, module);
117             nodeToResolve.setTypedef(resolvedType);
118         }
119     }
120
121     public static void resolveTypeUnion(final UnionTypeBuilder union,
122             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder builder) {
123         final List<TypeDefinition<?>> unionTypes = union.getTypes();
124         final List<TypeDefinition<?>> toRemove = new ArrayList<TypeDefinition<?>>();
125         for (TypeDefinition<?> unionType : unionTypes) {
126             if (unionType instanceof UnknownType) {
127                 final ModuleBuilder dependentModule = findModuleFromBuilders(modules, builder, unionType.getQName()
128                         .getPrefix(), union.getLine());
129                 final TypeDefinitionBuilder resolvedType = findTypeDefinitionBuilder(union, dependentModule, unionType
130                         .getQName().getLocalName(), builder.getName(), union.getLine());
131                 union.setTypedef(resolvedType);
132                 toRemove.add(unionType);
133             } else if (unionType instanceof ExtendedType && unionType.getBaseType() instanceof UnknownType) {
134                 final UnknownType ut = (UnknownType) unionType.getBaseType();
135                 final ModuleBuilder dependentModule = findModuleFromBuilders(modules, builder, ut.getQName()
136                         .getPrefix(), union.getLine());
137                 final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(union, dependentModule, ut
138                         .getQName().getLocalName(), builder.getName(), union.getLine());
139                 final TypeDefinitionBuilder newType = extendedTypeWithNewBase(targetTypeBuilder, null,
140                         (ExtendedType) unionType, modules, builder, union.getLine());
141                 union.setTypedef(newType);
142                 toRemove.add(unionType);
143             }
144         }
145         unionTypes.removeAll(toRemove);
146     }
147
148     /**
149      * Resolve union type which contains one or more unresolved types.
150      *
151      * @param union
152      *            union type builder to resolve
153      * @param modules
154      *            all loaded modules
155      * @param module
156      *            current module
157      * @param context
158      *            SchemaContext containing already resolved modules
159      */
160     public static void resolveTypeUnionWithContext(final UnionTypeBuilder union,
161             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
162             final SchemaContext context) {
163         final List<TypeDefinition<?>> unionTypes = union.getTypes();
164         final List<TypeDefinition<?>> toRemove = new ArrayList<TypeDefinition<?>>();
165         for (TypeDefinition<?> unionType : unionTypes) {
166             if (unionType instanceof UnknownType) {
167                 resolveUnionUnknownType(union, (UnknownType) unionType, modules, module, context);
168                 toRemove.add(unionType);
169             } else if (unionType instanceof ExtendedType && unionType.getBaseType() instanceof UnknownType) {
170                 resolveUnionUnknownType(union, (ExtendedType) unionType, modules, module, context);
171                 toRemove.add(unionType);
172             }
173         }
174         unionTypes.removeAll(toRemove);
175     }
176
177     private static void resolveUnionUnknownType(final UnionTypeBuilder union, final UnknownType ut,
178             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
179             final SchemaContext context) {
180         final int line = union.getLine();
181         final QName utQName = ut.getQName();
182         final ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, utQName.getPrefix(), line);
183
184         if (dependentModuleBuilder == null) {
185             Module dependentModule = findModuleFromContext(context, module, utQName.getPrefix(), line);
186             Set<TypeDefinition<?>> types = dependentModule.getTypeDefinitions();
187             TypeDefinition<?> type = findTypeByName(types, utQName.getLocalName());
188             union.setType(type);
189         } else {
190             final TypeDefinitionBuilder resolvedType = findTypeDefinitionBuilder(union, dependentModuleBuilder,
191                     utQName.getLocalName(), module.getName(), union.getLine());
192             union.setTypedef(resolvedType);
193         }
194     }
195
196     private static void resolveUnionUnknownType(final UnionTypeBuilder union, final ExtendedType extType,
197             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module,
198             final SchemaContext context) {
199         final int line = union.getLine();
200         final TypeDefinition<?> extTypeBase = extType.getBaseType();
201         final UnknownType ut = (UnknownType) extTypeBase;
202         final QName utQName = ut.getQName();
203         final ModuleBuilder dependentModuleBuilder = findModuleFromBuilders(modules, module, utQName.getPrefix(), line);
204
205         if (dependentModuleBuilder == null) {
206             final Module dependentModule = findModuleFromContext(context, module, utQName.getPrefix(), line);
207             Set<TypeDefinition<?>> types = dependentModule.getTypeDefinitions();
208             TypeDefinition<?> type = findTypeByName(types, utQName.getLocalName());
209             final TypeDefinitionBuilder newType = extendedTypeWithNewBase(null, type, extType, modules, module, 0);
210             union.setTypedef(newType);
211         } else {
212             final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(union, dependentModuleBuilder,
213                     utQName.getLocalName(), module.getName(), line);
214             final TypeDefinitionBuilder newType = extendedTypeWithNewBase(targetTypeBuilder, null, extType, modules,
215                     module, line);
216             union.setTypedef(newType);
217         }
218     }
219
220     /**
221      * Find type definition of type of unresolved node.
222      * 
223      * @param nodeToResolve
224      *            node with unresolved type
225      * @param dependentModuleBuilder
226      *            module in which type definition is present
227      * @param modules
228      *            all loaded modules
229      * @param module
230      *            current module
231      * @return TypeDefinitionBuilder of node type
232      */
233     private static TypeDefinitionBuilder findUnknownTypeDefinition(final TypeAwareBuilder nodeToResolve,
234             final ModuleBuilder dependentModuleBuilder, final Map<String, TreeMap<Date, ModuleBuilder>> modules,
235             final ModuleBuilder module) {
236         final int line = nodeToResolve.getLine();
237         final TypeDefinition<?> nodeToResolveType = nodeToResolve.getType();
238         final QName unknownTypeQName = nodeToResolveType.getBaseType().getQName();
239         final TypeDefinitionBuilder targetTypeBuilder = findTypeDefinitionBuilder(nodeToResolve,
240                 dependentModuleBuilder, unknownTypeQName.getLocalName(), module.getName(), line);
241
242         TypeDefinitionBuilder resolvedType;
243         if (nodeToResolveType instanceof ExtendedType) {
244             final ExtendedType extType = (ExtendedType) nodeToResolveType;
245             resolvedType = extendedTypeWithNewBase(targetTypeBuilder, null, extType, modules, module,
246                     nodeToResolve.getLine());
247         } else {
248             resolvedType = targetTypeBuilder;
249         }
250
251         // validate constraints
252         final TypeConstraints constraints = findConstraintsFromTypeBuilder(nodeToResolve,
253                 new TypeConstraints(module.getName(), nodeToResolve.getLine()), modules, module, null);
254         constraints.validateConstraints();
255
256         return resolvedType;
257     }
258
259     /**
260      * Search types for type with given name.
261      * 
262      * @param types
263      *            types to search
264      * @param name
265      *            name of type
266      * @return type with given name if present in collection, null otherwise
267      */
268     private static TypeDefinitionBuilder findTypedefBuilderByName(Set<TypeDefinitionBuilder> types, String name) {
269         for (TypeDefinitionBuilder td : types) {
270             if (td.getQName().getLocalName().equals(name)) {
271                 return td;
272             }
273         }
274         return null;
275     }
276
277     /**
278      * Find type by name.
279      * 
280      * @param types
281      *            collection of types
282      * @param typeName
283      *            type name
284      * @return type with given name if it is present in collection, null
285      *         otherwise
286      */
287     private static TypeDefinition<?> findTypeByName(Set<TypeDefinition<?>> types, String typeName) {
288         for (TypeDefinition<?> type : types) {
289             if (type.getQName().getLocalName().equals(typeName)) {
290                 return type;
291             }
292         }
293         return null;
294     }
295
296     /**
297      * Pull restriction from type and add them to constraints.
298      * 
299      * @param type
300      *            type from which constraints will be read
301      * @param constraints
302      *            constraints object to which constraints will be added
303      */
304     private static TypeConstraints mergeConstraints(final TypeDefinition<?> type, final TypeConstraints constraints) {
305         if (type instanceof DecimalTypeDefinition) {
306             constraints.addRanges(((DecimalTypeDefinition) type).getRangeConstraints());
307             constraints.addFractionDigits(((DecimalTypeDefinition) type).getFractionDigits());
308         } else if (type instanceof IntegerTypeDefinition) {
309             constraints.addRanges(((IntegerTypeDefinition) type).getRangeConstraints());
310         } else if (type instanceof UnsignedIntegerTypeDefinition) {
311             constraints.addRanges(((UnsignedIntegerTypeDefinition) type).getRangeConstraints());
312         } else if (type instanceof StringTypeDefinition) {
313             constraints.addPatterns(((StringTypeDefinition) type).getPatternConstraints());
314             constraints.addLengths(((StringTypeDefinition) type).getLengthConstraints());
315         } else if (type instanceof BinaryTypeDefinition) {
316             constraints.addLengths(((BinaryTypeDefinition) type).getLengthConstraints());
317         } else if (type instanceof ExtendedType) {
318             constraints.addFractionDigits(((ExtendedType) type).getFractionDigits());
319             constraints.addLengths(((ExtendedType) type).getLengthConstraints());
320             constraints.addPatterns(((ExtendedType) type).getPatternConstraints());
321             constraints.addRanges(((ExtendedType) type).getRangeConstraints());
322         }
323         return constraints;
324     }
325
326     /**
327      * Create new type builder based on old type with new base type. Note: only
328      * one of newBaseTypeBuilder or newBaseType can be specified.
329      * 
330      * @param newBaseTypeBuilder
331      *            new base type builder or null
332      * @param newBaseType
333      *            new base type or null
334      * @param oldExtendedType
335      *            old type
336      * @param modules
337      *            all loaded modules
338      * @param module
339      *            current module
340      * @param line
341      *            current line in module
342      * @return new type builder based on old type with new base type
343      */
344     private static TypeDefinitionBuilder extendedTypeWithNewBase(final TypeDefinitionBuilder newBaseTypeBuilder,
345             final TypeDefinition<?> newBaseType, final ExtendedType oldExtendedType,
346             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module, final int line) {
347         if ((newBaseTypeBuilder == null && newBaseType == null) || (newBaseTypeBuilder != null && newBaseType != null)) {
348             throw new YangParseException(module.getName(), line,
349                     "only one of newBaseTypeBuilder or newBaseType can be specified");
350         }
351
352         final TypeDefinitionBuilderImpl newType = new TypeDefinitionBuilderImpl(module.getModuleName(), line,
353                 oldExtendedType.getQName());
354         final TypeConstraints tc = new TypeConstraints(module.getName(), line);
355         TypeConstraints constraints;
356         if (newBaseType == null) {
357             tc.addFractionDigits(oldExtendedType.getFractionDigits());
358             tc.addLengths(oldExtendedType.getLengthConstraints());
359             tc.addPatterns(oldExtendedType.getPatternConstraints());
360             tc.addRanges(oldExtendedType.getRangeConstraints());
361             constraints = findConstraintsFromTypeBuilder(newBaseTypeBuilder, tc, modules, module, null);
362             newType.setTypedef(newBaseTypeBuilder);
363         } else {
364             constraints = findConstraintsFromTypeDefinition(newBaseType, tc);
365             newType.setType(newBaseType);
366         }
367
368         newType.setPath(oldExtendedType.getPath());
369         newType.setDescription(oldExtendedType.getDescription());
370         newType.setReference(oldExtendedType.getReference());
371         newType.setStatus(oldExtendedType.getStatus());
372         newType.setLengths(constraints.getLength());
373         newType.setPatterns(constraints.getPatterns());
374         newType.setRanges(constraints.getRange());
375         newType.setFractionDigits(constraints.getFractionDigits());
376         newType.setUnits(oldExtendedType.getUnits());
377         newType.setDefaultValue(oldExtendedType.getDefaultValue());
378         newType.setUnknownNodes(oldExtendedType.getUnknownSchemaNodes());
379         return newType;
380     }
381
382     /**
383      * Pull restrictions from type and add them to constraints.
384      * 
385      * @param typeToResolve
386      *            type from which constraints will be read
387      * @param constraints
388      *            constraints object to which constraints will be added
389      * @return constraints contstraints object containing constraints from given
390      *         type
391      */
392     private static TypeConstraints findConstraintsFromTypeDefinition(final TypeDefinition<?> typeToResolve,
393             final TypeConstraints constraints) {
394         // union type cannot be restricted
395         if (typeToResolve instanceof UnionTypeDefinition) {
396             return constraints;
397         }
398         if (typeToResolve instanceof ExtendedType) {
399             ExtendedType extType = (ExtendedType) typeToResolve;
400             constraints.addFractionDigits(extType.getFractionDigits());
401             constraints.addLengths(extType.getLengthConstraints());
402             constraints.addPatterns(extType.getPatternConstraints());
403             constraints.addRanges(extType.getRangeConstraints());
404             return findConstraintsFromTypeDefinition(extType.getBaseType(), constraints);
405         } else {
406             mergeConstraints(typeToResolve, constraints);
407             return constraints;
408         }
409     }
410
411     private static TypeConstraints findConstraintsFromTypeBuilder(final TypeAwareBuilder nodeToResolve,
412             final TypeConstraints constraints, final Map<String, TreeMap<Date, ModuleBuilder>> modules,
413             final ModuleBuilder builder, final SchemaContext context) {
414
415         // union and identityref types cannot be restricted
416         if (nodeToResolve instanceof UnionTypeBuilder || nodeToResolve instanceof IdentityrefTypeBuilder) {
417             return constraints;
418         }
419
420         if (nodeToResolve instanceof TypeDefinitionBuilder) {
421             TypeDefinitionBuilder typedefToResolve = (TypeDefinitionBuilder) nodeToResolve;
422             constraints.addFractionDigits(typedefToResolve.getFractionDigits());
423             constraints.addLengths(typedefToResolve.getLengths());
424             constraints.addPatterns(typedefToResolve.getPatterns());
425             constraints.addRanges(typedefToResolve.getRanges());
426         }
427
428         TypeDefinition<?> type = nodeToResolve.getType();
429         if (type == null) {
430             return findConstraintsFromTypeBuilder(nodeToResolve.getTypedef(), constraints, modules, builder, context);
431         } else {
432             QName qname = type.getQName();
433             if (type instanceof UnknownType) {
434                 ModuleBuilder dependentModuleBuilder = ParserUtils.findModuleFromBuilders(modules, builder,
435                         qname.getPrefix(), nodeToResolve.getLine());
436                 if (dependentModuleBuilder == null) {
437                     if (context == null) {
438                         throw new YangParseException(builder.getName(), nodeToResolve.getLine(),
439                                 "Failed to resolved type constraints.");
440                     }
441                     Module dm = ParserUtils.findModuleFromContext(context, builder, qname.getPrefix(),
442                             nodeToResolve.getLine());
443                     TypeDefinition<?> t = findTypeByName(dm.getTypeDefinitions(), qname.getLocalName());
444                     return mergeConstraints(t, constraints);
445
446                 } else {
447                     TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(nodeToResolve, dependentModuleBuilder,
448                             qname.getLocalName(), builder.getName(), nodeToResolve.getLine());
449                     return findConstraintsFromTypeBuilder(tdb, constraints, modules, dependentModuleBuilder, context);
450                 }
451             } else if (type instanceof ExtendedType) {
452                 mergeConstraints(type, constraints);
453
454                 TypeDefinition<?> base = ((ExtendedType) type).getBaseType();
455                 if (base instanceof UnknownType) {
456                     ModuleBuilder dependentModule = ParserUtils.findModuleFromBuilders(modules, builder, base
457                             .getQName().getPrefix(), nodeToResolve.getLine());
458                     TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(nodeToResolve, dependentModule, base
459                             .getQName().getLocalName(), builder.getName(), nodeToResolve.getLine());
460                     return findConstraintsFromTypeBuilder(tdb, constraints, modules, dependentModule, context);
461                 } else {
462                     // it has to be base yang type
463                     return mergeConstraints(type, constraints);
464                 }
465             } else {
466                 // it is base yang type
467                 return mergeConstraints(type, constraints);
468             }
469         }
470     }
471
472     /**
473      * Search for type definition builder by name.
474      * 
475      * @param nodeToResolve
476      *            node which contains unresolved type
477      * @param dependentModule
478      *            module which should contains referenced type
479      * @param typeName
480      *            name of type definition
481      * @param currentModuleName
482      *            name of current module
483      * @param line
484      *            current line in module
485      * @return typeDefinitionBuilder
486      */
487     private static TypeDefinitionBuilder findTypeDefinitionBuilder(final TypeAwareBuilder nodeToResolve,
488             final ModuleBuilder dependentModule, final String typeName, final String currentModuleName, final int line) {
489         Set<TypeDefinitionBuilder> typedefs = dependentModule.getTypeDefinitionBuilders();
490         TypeDefinitionBuilder result = findTypedefBuilderByName(typedefs, typeName);
491         if (result != null) {
492             return result;
493         }
494
495         Builder parent = nodeToResolve.getParent();
496         while (parent != null) {
497             if (parent instanceof DataNodeContainerBuilder) {
498                 typedefs = ((DataNodeContainerBuilder) parent).getTypeDefinitionBuilders();
499             } else if (parent instanceof RpcDefinitionBuilder) {
500                 typedefs = ((RpcDefinitionBuilder) parent).getTypeDefinitions();
501             }
502             result = findTypedefBuilderByName(typedefs, typeName);
503             if (result == null) {
504                 parent = parent.getParent();
505             } else {
506                 break;
507             }
508         }
509
510         if (result == null) {
511             throw new YangParseException(currentModuleName, line, "Referenced type '" + typeName + "' not found.");
512         }
513         return result;
514     }
515
516 }