BUG-4638: introduce CompatUtils
[yangtools.git] / yang / yang-model-util / src / main / java / org / opendaylight / yangtools / yang / model / util / type / CompatUtils.java
1 /*
2  * Copyright (c) 2015 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.model.util.type;
9
10 import com.google.common.base.Preconditions;
11 import java.util.List;
12 import javax.annotation.Nonnull;
13 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
14 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
15 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
16 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
17 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
18 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
19 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
20 import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint;
21 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
22 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
23 import org.opendaylight.yangtools.yang.model.util.ExtendedType;
24
25 /**
26  * Compatibility utilities for dealing with differences between the {@link ExtendedType}-driven type representation
27  * versus the representation this package models.
28  *
29  * @deprecated This class is provided strictly for compatibility only. No new users should be introduced, as this class
30  *             is scheduled for removal when its two OpenDaylight users, Java Binding v1 and YANG JMX Bindings are
31  *             removed.
32  */
33 @Deprecated
34 public final class CompatUtils {
35     private CompatUtils() {
36         throw new UnsupportedOperationException();
37     }
38
39     /**
40      * This package's type hierarchy model generates a type which encapsulates the default value and units for leaves.
41      * Java Binding specification is implemented in a way, where it needs to revert this process if the internal
42      * declaration has not restricted the type further -- which is not something available via
43      * {@link TypeDefinition#getBaseType()}.
44      *
45      * Here are the possible scenarios:
46      *
47      * <pre>
48      * leaf foo {
49      *     type uint8 {
50      *         range 1..2;
51      *     }
52      * }
53      * </pre>
54      * The leaf type's schema path does not match the schema path of the leaf. We do NOT want to strip it, as
55      * we need to generate an inner class to hold the restrictions.
56      *
57      * <pre>
58      * leaf foo {
59      *     type uint8 {
60      *         range 1..2;
61      *     }
62      *     default 1;
63      * }
64      * </pre>
65      * The leaf type's schema path will match the schema path of the leaf. We do NOT want to strip it, as we need
66      * to generate an inner class to hold the restrictions.
67      *
68      * <pre>
69      * leaf foo {
70      *     type uint8;
71      *     default 1;
72      * }
73      * </pre>
74      * The leaf type's schema path will match the schema path of the leaf. We DO want to strip it, as we will deal
75      * with the default value ourselves.
76      *
77      * <pre>
78      * leaf foo {
79      *     type uint8;
80      * }
81      * </pre>
82      * The leaf type's schema path will not match the schema path of the leaf. We do NOT want to strip it.
83      *
84      * The situation is different for types which do not have a default instantiation in YANG: leafref, enumeration,
85      * identityref, decimal64, bits and union. If these types are defined within this leaf's statement, a base type
86      * will be instantiated. If the leaf defines a default statement, this base type will be visible via getBaseType().
87      *
88      * <pre>
89      * leaf foo {
90      *     type decimal64 {
91      *         fraction-digits 2;
92      *     }
93      * }
94      * </pre>
95      * The leaf type's schema path will not match the schema path of the leaf, and we do not want to strip it, as it
96      * needs to be generated.
97      *
98      * <pre>
99      * leaf foo {
100      *     type decimal64 {
101      *         fraction-digits 2;
102      *     }
103      *     default 1;
104      * }
105      * </pre>
106      * The leaf type's schema path will match the schema path of the leaf, and we DO want to strip it.
107      *
108      * @param leaf Leaf for which we are acquiring the type
109      * @return Potentially base type of the leaf type.
110      */
111     @Nonnull public static TypeDefinition<?> compatLeafType(@Nonnull final LeafSchemaNode leaf) {
112         final TypeDefinition<?> leafType = leaf.getType();
113         Preconditions.checkNotNull(leafType);
114
115         if (leafType instanceof ExtendedType) {
116             // Old parser referring to a typedef
117             return leafType;
118         }
119
120         if (!leaf.getPath().equals(leafType.getPath())) {
121             // Old parser semantics, or no new default/units defined for this leaf
122             return leafType;
123         }
124
125         // We are dealing with a type generated for the leaf itself
126         final TypeDefinition<?> baseType = leafType.getBaseType();
127         Preconditions.checkArgument(baseType != null, "Leaf %s has type for leaf, but no base type", leaf);
128
129         if (leaf.getPath().equals(baseType.getPath().getParent())) {
130             // Internal instantiation of a base YANG type (decimal64 and similar)
131             return baseType;
132         }
133
134         // At this point we have dealt with the easy cases. Now we need to perform per-type checking if there are no
135         // new constraints introduced by this type. If there were not, we will return the base type.
136         if (leafType instanceof BinaryTypeDefinition) {
137             return baseTypeIfNotConstrained((BinaryTypeDefinition) leafType);
138         } else if (leafType instanceof DecimalTypeDefinition) {
139             return baseTypeIfNotConstrained((DecimalTypeDefinition) leafType);
140         } else if (leafType instanceof InstanceIdentifierTypeDefinition) {
141             return baseTypeIfNotConstrained((InstanceIdentifierTypeDefinition) leafType);
142         } else if (leafType instanceof IntegerTypeDefinition) {
143             return baseTypeIfNotConstrained((IntegerTypeDefinition) leafType);
144         } else if (leafType instanceof StringTypeDefinition) {
145             return baseTypeIfNotConstrained((StringTypeDefinition) leafType);
146         } else if (leafType instanceof UnsignedIntegerTypeDefinition) {
147             return baseTypeIfNotConstrained((UnsignedIntegerTypeDefinition) leafType);
148         } else {
149             // Other types cannot be constrained, return the base type
150             return baseType;
151         }
152     }
153
154     private static TypeDefinition<?> baseTypeIfNotConstrained(final BinaryTypeDefinition type) {
155         final BinaryTypeDefinition base = type.getBaseType();
156         return baseTypeIfNotConstrained(type, type.getLengthConstraints(), base, base.getLengthConstraints());
157     }
158
159     private static TypeDefinition<?> baseTypeIfNotConstrained(final DecimalTypeDefinition type) {
160         final DecimalTypeDefinition base = type.getBaseType();
161         return baseTypeIfNotConstrained(type, type.getRangeConstraints(), base, base.getRangeConstraints());
162     }
163
164     private static TypeDefinition<?> baseTypeIfNotConstrained(final InstanceIdentifierTypeDefinition type) {
165         final InstanceIdentifierTypeDefinition base = type.getBaseType();
166         return type.requireInstance() == base.requireInstance() ? base : type;
167     }
168
169     private static TypeDefinition<?> baseTypeIfNotConstrained(final IntegerTypeDefinition type) {
170         final IntegerTypeDefinition base = type.getBaseType();
171         return baseTypeIfNotConstrained(type, type.getRangeConstraints(), base, base.getRangeConstraints());
172     }
173
174     private static TypeDefinition<?> baseTypeIfNotConstrained(final StringTypeDefinition type) {
175         final StringTypeDefinition base = type.getBaseType();
176         final List<PatternConstraint> patterns = type.getPatternConstraints();
177         final List<LengthConstraint> lengths = type.getLengthConstraints();
178
179         if ((patterns.isEmpty() || patterns.equals(base.getPatternConstraints())) &&
180                 (lengths.isEmpty() || lengths.equals(base.getLengthConstraints()))) {
181             return base;
182         }
183
184         return type;
185     }
186
187     private static TypeDefinition<?> baseTypeIfNotConstrained(final UnsignedIntegerTypeDefinition type) {
188         final UnsignedIntegerTypeDefinition base = type.getBaseType();
189         return baseTypeIfNotConstrained(type, type.getRangeConstraints(), base, base.getRangeConstraints());
190     }
191
192     private static TypeDefinition<?> baseTypeIfNotConstrained(final TypeDefinition<?> type,
193             final List<?> typeConstraints, final TypeDefinition<?> base, final List<?> baseConstraints) {
194         if (typeConstraints.isEmpty() || typeConstraints.equals(baseConstraints)) {
195             return base;
196         }
197         return type;
198     }
199 }