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