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