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