Removed duplicate code in TypeUtils class.
[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 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 void mergeConstraints(final TypeDefinition<?> type, final TypeConstraints constraints) {
308         if (type instanceof DecimalTypeDefinition) {
309             constraints.addRanges(((DecimalTypeDefinition) type).getRangeStatements());
310             constraints.addFractionDigits(((DecimalTypeDefinition) type).getFractionDigits());
311         } else if (type instanceof IntegerTypeDefinition) {
312             constraints.addRanges(((IntegerTypeDefinition) type).getRangeStatements());
313         } else if (type instanceof StringTypeDefinition) {
314             constraints.addPatterns(((StringTypeDefinition) type).getPatterns());
315             constraints.addLengths(((StringTypeDefinition) type).getLengthStatements());
316         } else if (type instanceof BinaryTypeDefinition) {
317             constraints.addLengths(((BinaryTypeDefinition) type).getLengthConstraints());
318         }
319     }
320
321     /**
322      * Create new type builder based on old type with new base type. Note: only
323      * one of newBaseTypeBuilder or newBaseType can be specified.
324      *
325      * @param newBaseTypeBuilder
326      *            new base type builder or null
327      * @param newBaseType
328      *            new base type or null
329      * @param oldExtendedType
330      *            old type
331      * @param modules
332      *            all loaded modules
333      * @param module
334      *            current module
335      * @param line
336      *            current line in module
337      * @return new type builder based on old type with new base type
338      */
339     private static TypeDefinitionBuilder extendedTypeWithNewBase(final TypeDefinitionBuilder newBaseTypeBuilder,
340             final TypeDefinition<?> newBaseType, final ExtendedType oldExtendedType,
341             final Map<String, TreeMap<Date, ModuleBuilder>> modules, final ModuleBuilder module, final int line) {
342         if ((newBaseTypeBuilder == null && newBaseType == null) || (newBaseTypeBuilder != null && newBaseType != null)) {
343             throw new YangParseException(module.getName(), line,
344                     "only one of newBaseTypeBuilder or newBaseType can be specified");
345         }
346
347         final TypeDefinitionBuilderImpl newType = new TypeDefinitionBuilderImpl(module.getModuleName(), line,
348                 oldExtendedType.getQName());
349         final TypeConstraints tc = new TypeConstraints(module.getName(), line);
350         TypeConstraints constraints = null;
351         if (newBaseType == null) {
352             tc.addFractionDigits(oldExtendedType.getFractionDigits());
353             tc.addLengths(oldExtendedType.getLengths());
354             tc.addPatterns(oldExtendedType.getPatterns());
355             tc.addRanges(oldExtendedType.getRanges());
356             constraints = findConstraintsFromTypeBuilder(newBaseTypeBuilder, tc, modules, module, null);
357             newType.setTypedef(newBaseTypeBuilder);
358         } else {
359             constraints = findConstraintsFromTypeDefinition(newBaseType, tc);
360             newType.setType(newBaseType);
361         }
362
363         newType.setPath(oldExtendedType.getPath());
364         newType.setDescription(oldExtendedType.getDescription());
365         newType.setReference(oldExtendedType.getReference());
366         newType.setStatus(oldExtendedType.getStatus());
367         newType.setLengths(constraints.getLength());
368         newType.setPatterns(constraints.getPatterns());
369         newType.setRanges(constraints.getRange());
370         newType.setFractionDigits(constraints.getFractionDigits());
371         newType.setUnits(oldExtendedType.getUnits());
372         newType.setDefaultValue(oldExtendedType.getDefaultValue());
373         newType.setUnknownNodes(oldExtendedType.getUnknownSchemaNodes());
374         return newType;
375     }
376
377     /**
378      * Pull restrictions from type and add them to constraints.
379      *
380      * @param typeToResolve
381      *            type from which constraints will be read
382      * @param constraints
383      *            constraints object to which constraints will be added
384      * @return constraints contstraints object containing constraints from given
385      *         type
386      */
387     private static TypeConstraints findConstraintsFromTypeDefinition(final TypeDefinition<?> typeToResolve,
388             final TypeConstraints constraints) {
389         // union type cannot be restricted
390         if (typeToResolve instanceof UnionTypeDefinition) {
391             return constraints;
392         }
393         if (typeToResolve instanceof ExtendedType) {
394             ExtendedType extType = (ExtendedType) typeToResolve;
395             constraints.addFractionDigits(extType.getFractionDigits());
396             constraints.addLengths(extType.getLengths());
397             constraints.addPatterns(extType.getPatterns());
398             constraints.addRanges(extType.getRanges());
399             return findConstraintsFromTypeDefinition(extType.getBaseType(), constraints);
400         } else {
401             mergeConstraints(typeToResolve, constraints);
402             return constraints;
403         }
404     }
405
406     private static TypeConstraints findConstraintsFromTypeBuilder(final TypeAwareBuilder nodeToResolve,
407             final TypeConstraints constraints, final Map<String, TreeMap<Date, ModuleBuilder>> modules,
408             final ModuleBuilder builder, final SchemaContext context) {
409
410         // union and identityref types cannot be restricted
411         if (nodeToResolve instanceof UnionTypeBuilder || nodeToResolve instanceof IdentityrefTypeBuilder) {
412             return constraints;
413         }
414
415         if (nodeToResolve instanceof TypeDefinitionBuilder) {
416             TypeDefinitionBuilder typedefToResolve = (TypeDefinitionBuilder) nodeToResolve;
417             constraints.addFractionDigits(typedefToResolve.getFractionDigits());
418             constraints.addLengths(typedefToResolve.getLengths());
419             constraints.addPatterns(typedefToResolve.getPatterns());
420             constraints.addRanges(typedefToResolve.getRanges());
421         }
422
423         TypeDefinition<?> type = nodeToResolve.getType();
424         if (type == null) {
425             return findConstraintsFromTypeBuilder(nodeToResolve.getTypedef(), constraints, modules, builder, context);
426         } else {
427             QName qname = type.getQName();
428             if (type instanceof UnknownType) {
429                 ModuleBuilder dependentModuleBuilder = ParserUtils.findDependentModuleBuilder(modules, builder,
430                         qname.getPrefix(), nodeToResolve.getLine());
431                 if (dependentModuleBuilder == null) {
432                     if (context == null) {
433                         throw new YangParseException(builder.getName(), nodeToResolve.getLine(),
434                                 "Failed to resolved type constraints.");
435                     }
436                     Module dm = ParserUtils.findModuleFromContext(context, builder, qname.getPrefix(),
437                             nodeToResolve.getLine());
438                     TypeDefinition<?> t = findTypeByName(dm.getTypeDefinitions(), qname.getLocalName());
439                     if (t instanceof ExtendedType) {
440                         ExtendedType extType = (ExtendedType) t;
441                         constraints.addFractionDigits(extType.getFractionDigits());
442                         constraints.addLengths(extType.getLengths());
443                         constraints.addPatterns(extType.getPatterns());
444                         constraints.addRanges(extType.getRanges());
445                         return constraints;
446                     } else {
447                         mergeConstraints(t, constraints);
448                         return constraints;
449                     }
450                 } else {
451                     TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(nodeToResolve, dependentModuleBuilder,
452                             qname.getLocalName(), builder.getName(), nodeToResolve.getLine());
453                     return findConstraintsFromTypeBuilder(tdb, constraints, modules, dependentModuleBuilder, context);
454                 }
455             } else if (type instanceof ExtendedType) {
456                 ExtendedType extType = (ExtendedType) type;
457                 constraints.addFractionDigits(extType.getFractionDigits());
458                 constraints.addLengths(extType.getLengths());
459                 constraints.addPatterns(extType.getPatterns());
460                 constraints.addRanges(extType.getRanges());
461
462                 TypeDefinition<?> base = extType.getBaseType();
463                 if (base instanceof UnknownType) {
464                     ModuleBuilder dependentModule = ParserUtils.findDependentModuleBuilder(modules, builder, base
465                             .getQName().getPrefix(), nodeToResolve.getLine());
466                     TypeDefinitionBuilder tdb = findTypeDefinitionBuilder(nodeToResolve, dependentModule, base
467                             .getQName().getLocalName(), builder.getName(), nodeToResolve.getLine());
468                     return findConstraintsFromTypeBuilder(tdb, constraints, modules, dependentModule, context);
469                 } else {
470                     // it has to be base yang type
471                     mergeConstraints(type, constraints);
472                     return constraints;
473                 }
474             } else {
475                 // it is base yang type
476                 mergeConstraints(type, constraints);
477                 return constraints;
478             }
479         }
480     }
481
482     /**
483      * Search for type definition builder by name.
484      *
485      * @param dirtyNodeSchemaPath
486      *            schema path of node which contains unresolved type
487      * @param dependentModule
488      *            module which should contains referenced type
489      * @param typeName
490      *            name of type definition
491      * @param currentModuleName
492      *            name of current module
493      * @param line
494      *            current line in module
495      * @return
496      */
497     private static TypeDefinitionBuilder findTypeDefinitionBuilder(final TypeAwareBuilder nodeToResolve,
498             final ModuleBuilder dependentModule, final String typeName, final String currentModuleName, final int line) {
499
500         TypeDefinitionBuilder result = null;
501
502         Set<TypeDefinitionBuilder> typedefs = dependentModule.getTypeDefinitionBuilders();
503         result = findTypedefBuilderByName(typedefs, typeName);
504         if (result != null) {
505             return result;
506         }
507
508         Builder parent = nodeToResolve.getParent();
509         while (parent != null) {
510             if (parent instanceof DataNodeContainerBuilder) {
511                 typedefs = ((DataNodeContainerBuilder) parent).getTypeDefinitionBuilders();
512             } else if (parent instanceof RpcDefinitionBuilder) {
513                 typedefs = ((RpcDefinitionBuilder) parent).getTypeDefinitions();
514             }
515             result = findTypedefBuilderByName(typedefs, typeName);
516             if (result == null) {
517                 parent = parent.getParent();
518             } else {
519                 break;
520             }
521         }
522
523         if (result == null) {
524             throw new YangParseException(currentModuleName, line, "Referenced type '" + typeName + "' not found.");
525         }
526         return result;
527     }
528
529 }