2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.parser.stmt.rfc6020;
10 import com.google.common.base.Preconditions;
11 import com.google.common.base.Splitter;
12 import com.google.common.base.Strings;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.collect.Iterables;
16 import java.math.BigDecimal;
17 import java.math.BigInteger;
18 import java.util.ArrayList;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
24 import org.opendaylight.yangtools.yang.common.QName;
25 import org.opendaylight.yangtools.yang.common.YangVersion;
26 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
27 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
29 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
30 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
31 import org.opendaylight.yangtools.yang.model.api.stmt.UnresolvedNumber;
32 import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
33 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
35 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
36 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
37 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
38 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
39 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
42 * Utility class for manipulating YANG base and extended types implementation.
44 public final class TypeUtils {
46 public static final String BINARY = "binary";
47 public static final String BITS = "bits";
48 public static final String BOOLEAN = "boolean";
49 public static final String DECIMAL64 = "decimal64";
50 public static final String EMPTY = "empty";
51 public static final String ENUMERATION = "enumeration";
52 public static final String IDENTITY_REF = "identityref";
53 public static final String INSTANCE_IDENTIFIER = "instance-identifier";
54 public static final String INT8 = "int8";
55 public static final String INT16 = "int16";
56 public static final String INT32 = "int32";
57 public static final String INT64 = "int64";
58 public static final String LEAF_REF = "leafref";
59 public static final String STRING = "string";
60 public static final String UINT8 = "uint8";
61 public static final String UINT16 = "uint16";
62 public static final String UINT32 = "uint32";
63 public static final String UINT64 = "uint64";
64 public static final String UNION = "union";
66 private static final Map<String, String> BUILT_IN_TYPES = ImmutableMap.<String, String>builder()
69 .put(BOOLEAN, BOOLEAN)
70 .put(DECIMAL64, DECIMAL64)
72 .put(ENUMERATION, ENUMERATION)
73 .put(IDENTITY_REF,IDENTITY_REF)
74 .put(INSTANCE_IDENTIFIER, INSTANCE_IDENTIFIER)
79 .put(LEAF_REF, LEAF_REF)
88 private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
89 DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
90 private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
91 private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
93 // these objects are to compare whether range has MAX or MIN value
94 // none of these values should appear as Yang number according to spec so they are safe to use
95 private static final BigDecimal YANG_MIN_NUM = BigDecimal.valueOf(-Double.MAX_VALUE);
96 private static final BigDecimal YANG_MAX_NUM = BigDecimal.valueOf(Double.MAX_VALUE);
101 private static BigDecimal yangConstraintToBigDecimal(final Number number) {
102 if (UnresolvedNumber.max().equals(number)) {
105 if (UnresolvedNumber.min().equals(number)) {
109 return new BigDecimal(number.toString());
112 private static int compareNumbers(final Number n1, final Number n2) {
114 final BigDecimal num1 = yangConstraintToBigDecimal(n1);
115 final BigDecimal num2 = yangConstraintToBigDecimal(n2);
117 return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
120 private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
121 if ("max".equals(value)) {
122 return UnresolvedNumber.max();
124 if ("min".equals(value)) {
125 return UnresolvedNumber.min();
129 return new BigInteger(value);
130 } catch (final NumberFormatException e) {
131 throw new SourceException(ctx.getStatementSourceReference(), e, "Value %s is not a valid integer", value);
135 private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
136 if ("max".equals(value)) {
137 return UnresolvedNumber.max();
139 if ("min".equals(value)) {
140 return UnresolvedNumber.min();
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);
151 public static List<ValueRange> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
152 final String rangeArgument) {
153 final List<ValueRange> ranges = new ArrayList<>();
155 for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
156 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.split(singleRange).iterator();
157 final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
160 if (boundaries.hasNext()) {
161 max = parseDecimalConstraintValue(ctx, boundaries.next());
163 // if min larger than max then error
164 SourceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
165 "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
166 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
167 "Wrong number of boundaries in range constraint %s", singleRange);
172 // some of intervals overlapping
173 InferenceException.throwIf(ranges.size() > 1
174 && compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
175 ctx.getStatementSourceReference(), "Some of the value ranges in %s are not disjoint",
177 ranges.add(ValueRange.of(min, max));
183 public static List<ValueRange> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
184 final String lengthArgument) {
185 final List<ValueRange> ranges = new ArrayList<>();
187 for (final String singleRange : PIPE_SPLITTER.split(lengthArgument)) {
188 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.split(singleRange).iterator();
189 final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
192 if (boundaries.hasNext()) {
193 max = parseIntegerConstraintValue(ctx, boundaries.next());
195 // if min larger than max then error
196 SourceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
197 "Length constraint %s has descending order of boundaries; should be ascending.", singleRange);
198 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
199 "Wrong number of boundaries in length constraint %s.", singleRange);
204 // some of intervals overlapping
205 InferenceException.throwIf(ranges.size() > 1
206 && compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
207 ctx.getStatementSourceReference(), "Some of the length ranges in %s are not disjoint",
209 ranges.add(ValueRange.of(min, max));
217 public static boolean isYangTypeBodyStmtString(final String typeName) {
218 return TYPE_BODY_STMTS.contains(typeName);
221 public static boolean isYangBuiltInTypeString(final String typeName) {
222 return BUILT_IN_TYPES.containsKey(typeName);
226 public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
227 final SchemaPath path = stmtCtx.getSchemaPath().get();
228 final SchemaPath parent = path.getParent();
229 final QName parentQName = parent.getLastComponent();
230 Preconditions.checkArgument(parentQName != null, "Path %s has an empty parent", path);
232 final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
233 QName.create(parentQName, path.getLastComponent().getLocalName()));
234 return parent.createChild(qname);
238 * Checks whether supplied type has any of specified default values marked
239 * with an if-feature. This method creates mutable copy of supplied set of
245 * type statement which should be checked
246 * @param defaultValues
247 * set of default values which should be checked. The method
248 * creates mutable copy of this set
250 * @return true if any of specified default values is marked with an
251 * if-feature, otherwise false
253 public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
254 final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
255 return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
256 && isRelevantForIfFeatureCheck(typeStmt)
257 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
261 * Checks whether supplied type has specified default value marked with an
262 * if-feature. This method creates mutable set of supplied default value.
267 * type statement which should be checked
268 * @param defaultValue
269 * default value to be checked
271 * @return true if specified default value is marked with an if-feature,
274 public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
275 final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
276 final HashSet<String> defaultValues = new HashSet<>();
277 defaultValues.add(defaultValue);
278 return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
279 && isRelevantForIfFeatureCheck(typeStmt)
280 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
283 public static String findBuiltinString(final String rawArgument) {
284 return BUILT_IN_TYPES.get(rawArgument);
287 private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
288 final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
289 return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
290 || typeDefinition instanceof UnionTypeDefinition;
293 private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
294 final Set<String> defaultValues) {
295 final Iterator<? extends EffectiveStatement<?, ?>> iter = typeStmt.effectiveSubstatements().iterator();
296 while (iter.hasNext() && !defaultValues.isEmpty()) {
297 final EffectiveStatement<?, ?> effectiveSubstatement = iter.next();
298 if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
299 final QName bitQName = (QName) effectiveSubstatement.argument();
300 if (defaultValues.remove(bitQName.getLocalName()) && containsIfFeature(effectiveSubstatement)) {
303 } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
304 && defaultValues.remove(effectiveSubstatement.argument())
305 && containsIfFeature(effectiveSubstatement)) {
307 } else if (effectiveSubstatement instanceof TypeEffectiveStatement && isAnyDefaultValueMarkedWithIfFeature(
308 (TypeEffectiveStatement<?>) effectiveSubstatement, defaultValues)) {
316 private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
317 for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
318 if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {