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