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.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.ImmutableMap.Builder;
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.Iterables;
18 import java.math.BigDecimal;
19 import java.math.BigInteger;
20 import java.util.ArrayList;
21 import java.util.HashSet;
22 import java.util.Iterator;
23 import java.util.List;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.common.YangVersion;
28 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
29 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
31 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
32 import org.opendaylight.yangtools.yang.model.api.stmt.TypeEffectiveStatement;
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.LengthConstraint;
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.model.util.UnresolvedNumber;
39 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
40 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
41 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
42 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
43 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthConstraintEffectiveImpl;
44 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeConstraintEffectiveImpl;
47 * util class for manipulating YANG base and extended types implementation
49 public final class TypeUtils {
51 public static final String BINARY = "binary";
52 public static final String BITS = "bits";
53 public static final String BOOLEAN = "boolean";
54 public static final String DECIMAL64 = "decimal64";
55 public static final String EMPTY = "empty";
56 public static final String ENUMERATION = "enumeration";
57 public static final String IDENTITY_REF = "identityref";
58 public static final String INSTANCE_IDENTIFIER = "instance-identifier";
59 public static final String INT8 = "int8";
60 public static final String INT16 = "int16";
61 public static final String INT32 = "int32";
62 public static final String INT64 = "int64";
63 public static final String LEAF_REF = "leafref";
64 public static final String STRING = "string";
65 public static final String UINT8 = "uint8";
66 public static final String UINT16 = "uint16";
67 public static final String UINT32 = "uint32";
68 public static final String UINT64 = "uint64";
69 public static final String UNION = "union";
71 private static final Map<String, String> BUILT_IN_TYPES;
74 final Builder<String, String> b = ImmutableMap.builder();
76 b.put(BINARY, BINARY);
78 b.put(BOOLEAN, BOOLEAN);
79 b.put(DECIMAL64, DECIMAL64);
81 b.put(ENUMERATION, ENUMERATION);
82 b.put(IDENTITY_REF,IDENTITY_REF);
83 b.put(INSTANCE_IDENTIFIER, INSTANCE_IDENTIFIER);
88 b.put(LEAF_REF, LEAF_REF);
89 b.put(STRING, STRING);
91 b.put(UINT16, UINT16);
92 b.put(UINT32, UINT32);
93 b.put(UINT64, UINT64);
96 BUILT_IN_TYPES = b.build();
99 private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
100 DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
101 private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
102 private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
104 private TypeUtils() {
107 private static BigDecimal yangConstraintToBigDecimal(final Number number) {
108 if (UnresolvedNumber.max().equals(number)) {
109 return RangeStatementImpl.YANG_MAX_NUM;
111 if (UnresolvedNumber.min().equals(number)) {
112 return RangeStatementImpl.YANG_MIN_NUM;
115 return new BigDecimal(number.toString());
118 private static int compareNumbers(final Number n1, final Number n2) {
120 final BigDecimal num1 = yangConstraintToBigDecimal(n1);
121 final BigDecimal num2 = yangConstraintToBigDecimal(n2);
123 return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
126 private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
127 if ("max".equals(value)) {
128 return UnresolvedNumber.max();
130 if ("min".equals(value)) {
131 return UnresolvedNumber.min();
135 return new BigInteger(value);
136 } catch (final NumberFormatException e) {
137 throw new SourceException(String.format("Value %s is not a valid integer", value),
138 ctx.getStatementSourceReference(), e);
142 private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
143 if ("max".equals(value)) {
144 return UnresolvedNumber.max();
146 if ("min".equals(value)) {
147 return UnresolvedNumber.min();
151 return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
152 } catch (final NumberFormatException e) {
153 throw new SourceException(String.format("Value %s is not a valid decimal number", value),
154 ctx.getStatementSourceReference(), e);
158 public static List<RangeConstraint> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
159 final String rangeArgument) {
161 final Optional<String> description = Optional.absent();
162 final Optional<String> reference = Optional.absent();
164 final List<RangeConstraint> rangeConstraints = new ArrayList<>();
166 for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
167 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
168 final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
171 if (boundaries.hasNext()) {
172 max = parseDecimalConstraintValue(ctx, boundaries.next());
174 // if min larger than max then error
175 InferenceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
176 "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
178 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
179 "Wrong number of boundaries in range constraint %s", singleRange);
184 // some of intervals overlapping
185 if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
186 throw new InferenceException(ctx.getStatementSourceReference(),
187 "Some of the ranges in %s are not disjoint", rangeArgument);
190 rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
193 return rangeConstraints;
196 public static List<LengthConstraint> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
197 final String lengthArgument) {
198 final Optional<String> description = Optional.absent();
199 final Optional<String> reference = Optional.absent();
201 final List<LengthConstraint> lengthConstraints = new ArrayList<>();
203 for (final String singleRange : PIPE_SPLITTER.split(lengthArgument)) {
204 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
205 final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
208 if (boundaries.hasNext()) {
209 max = parseIntegerConstraintValue(ctx, boundaries.next());
211 // if min larger than max then error
212 Preconditions.checkArgument(compareNumbers(min, max) != 1,
213 "Length constraint %s has descending order of boundaries; should be ascending. Statement source at %s",
214 singleRange, ctx.getStatementSourceReference());
215 Preconditions.checkArgument(!boundaries.hasNext(),
216 "Wrong number of boundaries in length constraint %s. Statement source at %s", singleRange,
217 ctx.getStatementSourceReference());
222 // some of intervals overlapping
223 if (lengthConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(lengthConstraints).getMax()) != 1) {
224 throw new InferenceException(ctx.getStatementSourceReference(),
225 "Some of the length ranges in %s are not disjoint", lengthArgument);
228 lengthConstraints.add(new LengthConstraintEffectiveImpl(min, max, description, reference));
231 return lengthConstraints;
234 public static boolean isYangTypeBodyStmtString(final String typeName) {
235 return TYPE_BODY_STMTS.contains(typeName);
238 public static boolean isYangBuiltInTypeString(final String typeName) {
239 return BUILT_IN_TYPES.containsKey(typeName);
243 public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
244 final SchemaPath path = stmtCtx.getSchemaPath().get();
245 final SchemaPath parent = path.getParent();
246 final QName parentQName = parent.getLastComponent();
247 Preconditions.checkArgument(parentQName != null, "Path %s has an empty parent", path);
249 final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
250 QName.create(parentQName, path.getLastComponent().getLocalName()));
251 return parent.createChild(qname);
255 * Checks whether supplied type has any of specified default values marked
256 * with an if-feature. This method creates mutable copy of supplied set of
262 * type statement which should be checked
263 * @param defaultValues
264 * set of default values which should be checked. The method
265 * creates mutable copy of this set
267 * @return true if any of specified default values is marked with an
268 * if-feature, otherwise false
270 * @throws IllegalStateException
271 * if any of specified default values has not been found
273 public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
274 final TypeEffectiveStatement<?> typeStmt, final Set<String> defaultValues) {
275 return !defaultValues.isEmpty() && yangVersion == YangVersion.VERSION_1_1
276 && isRelevantForIfFeatureCheck(typeStmt)
277 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, new HashSet<>(defaultValues));
281 * Checks whether supplied type has specified default value marked with an
282 * if-feature. This method creates mutable set of supplied default value.
287 * type statement which should be checked
288 * @param defaultValue
289 * default value to be checked
291 * @return true if specified default value is marked with an if-feature,
294 * @throws IllegalStateException
295 * if specified default value has not been found
297 public static boolean hasDefaultValueMarkedWithIfFeature(final YangVersion yangVersion,
298 final TypeEffectiveStatement<?> typeStmt, final String defaultValue) {
299 final HashSet<String> defaultValues = new HashSet<>();
300 defaultValues.add(defaultValue);
301 return !Strings.isNullOrEmpty(defaultValue) && yangVersion == YangVersion.VERSION_1_1
302 && isRelevantForIfFeatureCheck(typeStmt)
303 && isAnyDefaultValueMarkedWithIfFeature(typeStmt, defaultValues);
306 static String findBuiltinString(final String rawArgument) {
307 return BUILT_IN_TYPES.get(rawArgument);
310 private static boolean isRelevantForIfFeatureCheck(final TypeEffectiveStatement<?> typeStmt) {
311 final TypeDefinition<?> typeDefinition = typeStmt.getTypeDefinition();
312 return typeDefinition instanceof EnumTypeDefinition || typeDefinition instanceof BitsTypeDefinition
313 || typeDefinition instanceof UnionTypeDefinition;
316 private static boolean isAnyDefaultValueMarkedWithIfFeature(final TypeEffectiveStatement<?> typeStmt,
317 final Set<String> defaultValues) {
318 for (final EffectiveStatement<?, ?> effectiveSubstatement : typeStmt.effectiveSubstatements()) {
319 if (YangStmtMapping.BIT.equals(effectiveSubstatement.statementDefinition())) {
320 final QName bitQName = (QName) effectiveSubstatement.argument();
321 if (defaultValues.remove(bitQName.getLocalName()) && containsIfFeature(effectiveSubstatement)) {
324 } else if (YangStmtMapping.ENUM.equals(effectiveSubstatement.statementDefinition())
325 && defaultValues.remove(effectiveSubstatement.argument())
326 && containsIfFeature(effectiveSubstatement)) {
328 } else if (effectiveSubstatement instanceof TypeEffectiveStatement) {
329 return isAnyDefaultValueMarkedWithIfFeature((TypeEffectiveStatement<?>) effectiveSubstatement,
334 Preconditions.checkState(defaultValues.isEmpty(), "Unable to find following default values %s", defaultValues);
338 private static boolean containsIfFeature(final EffectiveStatement<?, ?> effectiveStatement) {
339 for (final EffectiveStatement<?, ?> effectiveSubstatement : effectiveStatement.effectiveSubstatements()) {
340 if (YangStmtMapping.IF_FEATURE.equals(effectiveSubstatement.statementDefinition())) {