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;
23 import java.util.Optional;
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.stmt.UnresolvedNumber;
33 import org.opendaylight.yangtools.yang.model.api.stmt.ValueRange;
34 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
35 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
36 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
37 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
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.RangeConstraintEffectiveImpl;
45 * Utility class for manipulating YANG base and extended types implementation.
47 public final class TypeUtils {
49 public static final String BINARY = "binary";
50 public static final String BITS = "bits";
51 public static final String BOOLEAN = "boolean";
52 public static final String DECIMAL64 = "decimal64";
53 public static final String EMPTY = "empty";
54 public static final String ENUMERATION = "enumeration";
55 public static final String IDENTITY_REF = "identityref";
56 public static final String INSTANCE_IDENTIFIER = "instance-identifier";
57 public static final String INT8 = "int8";
58 public static final String INT16 = "int16";
59 public static final String INT32 = "int32";
60 public static final String INT64 = "int64";
61 public static final String LEAF_REF = "leafref";
62 public static final String STRING = "string";
63 public static final String UINT8 = "uint8";
64 public static final String UINT16 = "uint16";
65 public static final String UINT32 = "uint32";
66 public static final String UINT64 = "uint64";
67 public static final String UNION = "union";
69 private static final Map<String, String> BUILT_IN_TYPES = ImmutableMap.<String, String>builder()
72 .put(BOOLEAN, BOOLEAN)
73 .put(DECIMAL64, DECIMAL64)
75 .put(ENUMERATION, ENUMERATION)
76 .put(IDENTITY_REF,IDENTITY_REF)
77 .put(INSTANCE_IDENTIFIER, INSTANCE_IDENTIFIER)
82 .put(LEAF_REF, LEAF_REF)
91 private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
92 DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
93 private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
94 private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
99 private static BigDecimal yangConstraintToBigDecimal(final Number number) {
100 if (UnresolvedNumber.max().equals(number)) {
101 return RangeStatementImpl.YANG_MAX_NUM;
103 if (UnresolvedNumber.min().equals(number)) {
104 return RangeStatementImpl.YANG_MIN_NUM;
107 return new BigDecimal(number.toString());
110 private static int compareNumbers(final Number n1, final Number n2) {
112 final BigDecimal num1 = yangConstraintToBigDecimal(n1);
113 final BigDecimal num2 = yangConstraintToBigDecimal(n2);
115 return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
118 private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
119 if ("max".equals(value)) {
120 return UnresolvedNumber.max();
122 if ("min".equals(value)) {
123 return UnresolvedNumber.min();
127 return new BigInteger(value);
128 } catch (final NumberFormatException e) {
129 throw new SourceException(ctx.getStatementSourceReference(), e, "Value %s is not a valid integer", value);
133 private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
134 if ("max".equals(value)) {
135 return UnresolvedNumber.max();
137 if ("min".equals(value)) {
138 return UnresolvedNumber.min();
142 return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
143 } catch (final NumberFormatException e) {
144 throw new SourceException(String.format("Value %s is not a valid decimal number", value),
145 ctx.getStatementSourceReference(), e);
149 public static List<RangeConstraint> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
150 final String rangeArgument) {
152 final Optional<String> description = Optional.empty();
153 final Optional<String> reference = Optional.empty();
155 final List<RangeConstraint> rangeConstraints = new ArrayList<>();
157 for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
158 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
159 final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
162 if (boundaries.hasNext()) {
163 max = parseDecimalConstraintValue(ctx, boundaries.next());
165 // if min larger than max then error
166 InferenceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
167 "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
169 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
170 "Wrong number of boundaries in range constraint %s", singleRange);
175 // some of intervals overlapping
176 if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
177 throw new InferenceException(ctx.getStatementSourceReference(),
178 "Some of the ranges in %s are not disjoint", rangeArgument);
181 rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
184 return rangeConstraints;
187 public static List<ValueRange> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
188 final String lengthArgument) {
189 final List<ValueRange> ranges = new ArrayList<>();
191 for (final String singleRange : PIPE_SPLITTER.split(lengthArgument)) {
192 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.split(singleRange).iterator();
193 final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
196 if (boundaries.hasNext()) {
197 max = parseIntegerConstraintValue(ctx, boundaries.next());
199 // if min larger than max then error
200 SourceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
201 "Length constraint %s has descending order of boundaries; should be ascending.",
203 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
204 "Wrong number of boundaries in length constraint %s.", singleRange);
209 // some of intervals overlapping
210 InferenceException.throwIf(ranges.size() > 1
211 && compareNumbers(min, Iterables.getLast(ranges).upperBound()) != 1,
212 ctx.getStatementSourceReference(), "Some of the length ranges in %s are not disjoint",
215 ranges.add(ValueRange.of(min, max));
221 public static boolean isYangTypeBodyStmtString(final String typeName) {
222 return TYPE_BODY_STMTS.contains(typeName);
225 public static boolean isYangBuiltInTypeString(final String typeName) {
226 return BUILT_IN_TYPES.containsKey(typeName);
230 public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
231 final SchemaPath path = stmtCtx.getSchemaPath().get();
232 final SchemaPath parent = path.getParent();
233 final QName parentQName = parent.getLastComponent();
234 Preconditions.checkArgument(parentQName != null, "Path %s has an empty parent", path);
236 final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
237 QName.create(parentQName, path.getLastComponent().getLocalName()));
238 return parent.createChild(qname);
242 * Checks whether supplied type has any of specified default values marked
243 * with an if-feature. This method creates mutable copy of supplied set of
249 * type statement which should be checked
250 * @param defaultValues
251 * set of default values which should be checked. The method
252 * creates mutable copy of this set
254 * @return true if any of specified default values is marked with an
255 * if-feature, otherwise false
257 public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
258 final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
259 return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
260 && isRelevantForIfFeatureCheck(typeStmt)
261 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
265 * Checks whether supplied type has specified default value marked with an
266 * if-feature. This method creates mutable set of supplied default value.
271 * type statement which should be checked
272 * @param defaultValue
273 * default value to be checked
275 * @return true if specified default value is marked with an if-feature,
278 public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
279 final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
280 final HashSet<String> defaultValues = new HashSet<>();
281 defaultValues.add(defaultValue);
282 return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
283 && isRelevantForIfFeatureCheck(typeStmt)
284 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
287 static String findBuiltinString(final String rawArgument) {
288 return BUILT_IN_TYPES.get(rawArgument);
291 private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
292 final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
293 return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
294 || typeDefinition instanceof UnionTypeDefinition;
297 private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
298 final Set<String> defaultValues) {
299 final Iterator<? extends EffectiveStatement<?, ?>> iter = typeStmt.effectiveSubstatements().iterator();
300 while (iter.hasNext() && !defaultValues.isEmpty()) {
301 final EffectiveStatement<?, ?> effectiveSubstatement = iter.next();
302 if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
303 final QName bitQName = (QName) effectiveSubstatement.argument();
304 if (defaultValues.remove(bitQName.getLocalName()) && containsIfFeature(effectiveSubstatement)) {
307 } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
308 && defaultValues.remove(effectiveSubstatement.argument())
309 && containsIfFeature(effectiveSubstatement)) {
311 } else if (effectiveSubstatement instanceof TypeEffectiveStatement && isAnyDefaultValueMarkedWithIfFeature(
312 (TypeEffectiveStatement<?>) effectiveSubstatement, defaultValues)) {
320 private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
321 for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
322 if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {