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