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