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