68c55c886b876ce493eae6c12adedf5bf6efb010
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / rfc6020 / TypeUtils.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.parser.stmt.rfc6020;
9
10 import com.google.common.base.Optional;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Splitter;
13 import com.google.common.base.Strings;
14 import com.google.common.collect.ImmutableMap;
15 import com.google.common.collect.ImmutableSet;
16 import com.google.common.collect.Iterables;
17 import java.math.BigDecimal;
18 import java.math.BigInteger;
19 import java.util.ArrayList;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25 import org.opendaylight.yangtools.yang.common.QName;
26 import org.opendaylight.yangtools.yang.common.YangVersion;
27 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
28 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
30 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
32 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
33 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
35 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
36 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
37 import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
41 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
42 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthConstraintEffectiveImpl;
43 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeConstraintEffectiveImpl;
44
45 /**
46 * Utility class for manipulating YANG base and extended types implementation.
47 */
48 public final class TypeUtils {
49
50     public static final String BINARY = "binary";
51     public static final String BITS = "bits";
52     public static final String BOOLEAN = "boolean";
53     public static final String DECIMAL64 = "decimal64";
54     public static final String EMPTY = "empty";
55     public static final String ENUMERATION = "enumeration";
56     public static final String IDENTITY_REF = "identityref";
57     public static final String INSTANCE_IDENTIFIER = "instance-identifier";
58     public static final String INT8 = "int8";
59     public static final String INT16 = "int16";
60     public static final String INT32 = "int32";
61     public static final String INT64 = "int64";
62     public static final String LEAF_REF = "leafref";
63     public static final String STRING = "string";
64     public static final String UINT8 = "uint8";
65     public static final String UINT16 = "uint16";
66     public static final String UINT32 = "uint32";
67     public static final String UINT64 = "uint64";
68     public static final String UNION = "union";
69
70     private static final Map<String, String> BUILT_IN_TYPES = ImmutableMap.<String, String>builder()
71         .put(BINARY, BINARY)
72         .put(BITS, BITS)
73         .put(BOOLEAN, BOOLEAN)
74         .put(DECIMAL64, DECIMAL64)
75         .put(EMPTY, EMPTY)
76         .put(ENUMERATION, ENUMERATION)
77         .put(IDENTITY_REF,IDENTITY_REF)
78         .put(INSTANCE_IDENTIFIER, INSTANCE_IDENTIFIER)
79         .put(INT8, INT8)
80         .put(INT16, INT16)
81         .put(INT32, INT32)
82         .put(INT64, INT64)
83         .put(LEAF_REF, LEAF_REF)
84         .put(STRING, STRING)
85         .put(UINT8, UINT8)
86         .put(UINT16, UINT16)
87         .put(UINT32, UINT32)
88         .put(UINT64, UINT64)
89         .put(UNION, UNION)
90         .build();
91
92     private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
93         DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
94     private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
95     private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
96
97     private TypeUtils() {
98     }
99
100     private static BigDecimal yangConstraintToBigDecimal(final Number number) {
101         if (UnresolvedNumber.max().equals(number)) {
102             return RangeStatementImpl.YANG_MAX_NUM;
103         }
104         if (UnresolvedNumber.min().equals(number)) {
105             return RangeStatementImpl.YANG_MIN_NUM;
106         }
107
108         return new BigDecimal(number.toString());
109     }
110
111     private static int compareNumbers(final Number n1, final Number n2) {
112
113         final BigDecimal num1 = yangConstraintToBigDecimal(n1);
114         final BigDecimal num2 = yangConstraintToBigDecimal(n2);
115
116         return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
117     }
118
119     private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
120         if ("max".equals(value)) {
121             return UnresolvedNumber.max();
122         }
123         if ("min".equals(value)) {
124             return UnresolvedNumber.min();
125         }
126
127         try {
128             return new BigInteger(value);
129         } catch (final NumberFormatException e) {
130             throw new SourceException(String.format("Value %s is not a valid integer", value),
131                     ctx.getStatementSourceReference(), e);
132         }
133     }
134
135     private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
136         if ("max".equals(value)) {
137             return UnresolvedNumber.max();
138         }
139         if ("min".equals(value)) {
140             return UnresolvedNumber.min();
141         }
142
143         try {
144             return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
145         } catch (final NumberFormatException e) {
146             throw new SourceException(String.format("Value %s is not a valid decimal number", value),
147                     ctx.getStatementSourceReference(), e);
148         }
149     }
150
151     public static List<RangeConstraint> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
152                                                                  final String rangeArgument) {
153
154         final Optional<String> description = Optional.absent();
155         final Optional<String> reference = Optional.absent();
156
157         final List<RangeConstraint> rangeConstraints = new ArrayList<>();
158
159         for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
160             final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
161             final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
162
163             final Number max;
164             if (boundaries.hasNext()) {
165                 max = parseDecimalConstraintValue(ctx, boundaries.next());
166
167                 // if min larger than max then error
168                 InferenceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
169                         "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
170
171                 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
172                     "Wrong number of boundaries in range constraint %s", singleRange);
173             } else {
174                 max = min;
175             }
176
177             // some of intervals overlapping
178             if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
179                 throw new InferenceException(ctx.getStatementSourceReference(),
180                     "Some of the ranges in %s are not disjoint", rangeArgument);
181             }
182
183             rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
184         }
185
186         return rangeConstraints;
187     }
188
189     public static List<LengthConstraint> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
190             final String lengthArgument) {
191         final Optional<String> description = Optional.absent();
192         final Optional<String> reference = Optional.absent();
193
194         final List<LengthConstraint> lengthConstraints = new ArrayList<>();
195
196         for (final String singleRange : PIPE_SPLITTER.split(lengthArgument)) {
197             final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
198             final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
199
200             final Number max;
201             if (boundaries.hasNext()) {
202                 max = parseIntegerConstraintValue(ctx, boundaries.next());
203
204                 // if min larger than max then error
205                 SourceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
206                         "Length constraint %s has descending order of boundaries; should be ascending.",
207                         singleRange);
208                 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
209                         "Wrong number of boundaries in length constraint %s.", singleRange);
210             } else {
211                 max = min;
212             }
213
214             // some of intervals overlapping
215             InferenceException.throwIf(lengthConstraints.size() > 1
216                 && compareNumbers(min, Iterables.getLast(lengthConstraints).getMax()) != 1,
217                         ctx.getStatementSourceReference(),  "Some of the length ranges in %s are not disjoint",
218                         lengthArgument);
219
220             lengthConstraints.add(new LengthConstraintEffectiveImpl(min, max, description, reference));
221         }
222
223         return lengthConstraints;
224     }
225
226     public static boolean isYangTypeBodyStmtString(final String typeName) {
227         return TYPE_BODY_STMTS.contains(typeName);
228     }
229
230     public static boolean isYangBuiltInTypeString(final String typeName) {
231         return BUILT_IN_TYPES.containsKey(typeName);
232     }
233
234
235     public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
236         final SchemaPath path = stmtCtx.getSchemaPath().get();
237         final SchemaPath parent = path.getParent();
238         final QName parentQName = parent.getLastComponent();
239         Preconditions.checkArgument(parentQName != null, "Path %s has an empty parent", path);
240
241         final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
242             QName.create(parentQName, path.getLastComponent().getLocalName()));
243         return parent.createChild(qname);
244     }
245
246     /**
247      * Checks whether supplied type has any of specified default values marked
248      * with an if-feature. This method creates mutable copy of supplied set of
249      * default values.
250      *
251      * @param yangVersion
252      *            yang version
253      * @param typeStmt
254      *            type statement which should be checked
255      * @param defaultValues
256      *            set of default values which should be checked. The method
257      *            creates mutable copy of this set
258      *
259      * @return true if any of specified default values is marked with an
260      *         if-feature, otherwise false
261      */
262     public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
263             final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
264         return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
265                 && isRelevantForIfFeatureCheck(typeStmt)
266                 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
267     }
268
269     /**
270      * Checks whether supplied type has specified default value marked with an
271      * if-feature. This method creates mutable set of supplied default value.
272      *
273      * @param yangVersion
274      *            yang version
275      * @param typeStmt
276      *            type statement which should be checked
277      * @param defaultValue
278      *            default value to be checked
279      *
280      * @return true if specified default value is marked with an if-feature,
281      *         otherwise false
282      */
283     public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
284             final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
285         final HashSet<String> defaultValues = new HashSet<>();
286         defaultValues.add(defaultValue);
287         return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
288                 && isRelevantForIfFeatureCheck(typeStmt)
289                 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
290     }
291
292     static String findBuiltinString(final String rawArgument) {
293         return BUILT_IN_TYPES.get(rawArgument);
294     }
295
296     private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
297         final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
298         return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
299                 || typeDefinition instanceof UnionTypeDefinition;
300     }
301
302     private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
303             final Set<String> defaultValues) {
304         final Iterator<? extends EffectiveStatement<?, ?>> iter = typeStmt.effectiveSubstatements().iterator();
305         while (iter.hasNext() && !defaultValues.isEmpty()) {
306             final EffectiveStatement<?, ?> effectiveSubstatement = iter.next();
307             if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
308                 final QName bitQName = (QName) effectiveSubstatement.argument();
309                 if (defaultValues.remove(bitQName.getLocalName()) && containsIfFeature(effectiveSubstatement)) {
310                     return true;
311                 }
312             } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
313                     && defaultValues.remove(effectiveSubstatement.argument())
314                     && containsIfFeature(effectiveSubstatement)) {
315                 return true;
316             } else if (effectiveSubstatement instanceof TypeEffectiveStatement && isAnyDefaultValueMarkedWithIfFeature(
317                     (TypeEffectiveStatement<?>) effectiveSubstatement, defaultValues)) {
318                 return true;
319             }
320         }
321
322         return false;
323     }
324
325     private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
326         for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
327             if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {
328                 return true;
329             }
330         }
331         return false;
332     }
333 }