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.collect.ImmutableSet;
14 import com.google.common.collect.Iterables;
15 import java.math.BigDecimal;
16 import java.math.BigInteger;
17 import java.util.ArrayList;
18 import java.util.Iterator;
19 import java.util.List;
21 import org.opendaylight.yangtools.yang.common.QName;
22 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
23 import org.opendaylight.yangtools.yang.model.api.type.LengthConstraint;
24 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
25 import org.opendaylight.yangtools.yang.model.util.UnresolvedNumber;
26 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
27 import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
28 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
29 import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
30 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.LengthConstraintEffectiveImpl;
31 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.type.RangeConstraintEffectiveImpl;
34 * util class for manipulating YANG base and extended types implementation
36 public final class TypeUtils {
38 public static final String BINARY = "binary";
39 public static final String BITS = "bits";
40 public static final String BOOLEAN = "boolean";
41 public static final String DECIMAL64 = "decimal64";
42 public static final String EMPTY = "empty";
43 public static final String ENUMERATION = "enumeration";
44 public static final String IDENTITY_REF = "identityref";
45 public static final String INSTANCE_IDENTIFIER = "instance-identifier";
46 public static final String INT8 = "int8";
47 public static final String INT16 = "int16";
48 public static final String INT32 = "int32";
49 public static final String INT64 = "int64";
50 public static final String LEAF_REF = "leafref";
51 public static final String STRING = "string";
52 public static final String UINT8 = "uint8";
53 public static final String UINT16 = "uint16";
54 public static final String UINT32 = "uint32";
55 public static final String UINT64 = "uint64";
56 public static final String UNION = "union";
58 private static final Set<String> BUILT_IN_TYPES =
59 ImmutableSet.of(BINARY, BITS, BOOLEAN, DECIMAL64, EMPTY, ENUMERATION, IDENTITY_REF, INSTANCE_IDENTIFIER,
60 INT8, INT16, INT32, INT64, LEAF_REF, STRING, UINT8, UINT16, UINT32, UINT64, UNION);
62 private static final Set<String> TYPE_BODY_STMTS = ImmutableSet.of(
63 DECIMAL64, ENUMERATION, LEAF_REF, IDENTITY_REF, BITS, UNION);
64 private static final Splitter PIPE_SPLITTER = Splitter.on('|').trimResults();
65 private static final Splitter TWO_DOTS_SPLITTER = Splitter.on("..").trimResults();
70 private static BigDecimal yangConstraintToBigDecimal(final Number number) {
71 if (UnresolvedNumber.max().equals(number)) {
72 return RangeStatementImpl.YANG_MAX_NUM;
74 if (UnresolvedNumber.min().equals(number)) {
75 return RangeStatementImpl.YANG_MIN_NUM;
78 return new BigDecimal(number.toString());
81 private static int compareNumbers(final Number n1, final Number n2) {
83 final BigDecimal num1 = yangConstraintToBigDecimal(n1);
84 final BigDecimal num2 = yangConstraintToBigDecimal(n2);
86 return new BigDecimal(num1.toString()).compareTo(new BigDecimal(num2.toString()));
89 private static Number parseIntegerConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
90 if ("max".equals(value)) {
91 return UnresolvedNumber.max();
93 if ("min".equals(value)) {
94 return UnresolvedNumber.min();
98 return new BigInteger(value);
99 } catch (NumberFormatException e) {
100 throw new SourceException(String.format("Value %s is not a valid integer", value),
101 ctx.getStatementSourceReference(), e);
105 private static Number parseDecimalConstraintValue(final StmtContext<?, ?, ?> ctx, final String value) {
106 if ("max".equals(value)) {
107 return UnresolvedNumber.max();
109 if ("min".equals(value)) {
110 return UnresolvedNumber.min();
114 return value.indexOf('.') != -1 ? new BigDecimal(value) : new BigInteger(value);
115 } catch (NumberFormatException e) {
116 throw new SourceException(String.format("Value %s is not a valid decimal number", value),
117 ctx.getStatementSourceReference(), e);
121 public static List<RangeConstraint> parseRangeListFromString(final StmtContext<?, ?, ?> ctx,
122 final String rangeArgument) {
124 Optional<String> description = Optional.absent();
125 Optional<String> reference = Optional.absent();
127 List<RangeConstraint> rangeConstraints = new ArrayList<>();
129 for (final String singleRange : PIPE_SPLITTER.split(rangeArgument)) {
130 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
131 final Number min = parseDecimalConstraintValue(ctx, boundaries.next());
134 if (boundaries.hasNext()) {
135 max = parseDecimalConstraintValue(ctx, boundaries.next());
137 // if min larger than max then error
138 InferenceException.throwIf(compareNumbers(min, max) == 1, ctx.getStatementSourceReference(),
139 "Range constraint %s has descending order of boundaries; should be ascending", singleRange);
141 SourceException.throwIf(boundaries.hasNext(), ctx.getStatementSourceReference(),
142 "Wrong number of boundaries in range constraint %s", singleRange);
147 // some of intervals overlapping
148 if (rangeConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(rangeConstraints).getMax()) != 1) {
149 throw new InferenceException(ctx.getStatementSourceReference(),
150 "Some of the ranges in %s are not disjoint", rangeArgument);
153 rangeConstraints.add(new RangeConstraintEffectiveImpl(min, max, description, reference));
156 return rangeConstraints;
159 public static List<LengthConstraint> parseLengthListFromString(final StmtContext<?, ?, ?> ctx,
160 final String lengthArgument) {
161 Optional<String> description = Optional.absent();
162 Optional<String> reference = Optional.absent();
164 List<LengthConstraint> lengthConstraints = new ArrayList<>();
166 for (final String singleRange : PIPE_SPLITTER.split(lengthArgument)) {
167 final Iterator<String> boundaries = TWO_DOTS_SPLITTER.splitToList(singleRange).iterator();
168 final Number min = parseIntegerConstraintValue(ctx, boundaries.next());
171 if (boundaries.hasNext()) {
172 max = parseIntegerConstraintValue(ctx, boundaries.next());
174 // if min larger than max then error
175 Preconditions.checkArgument(compareNumbers(min, max) != 1,
176 "Length constraint %s has descending order of boundaries; should be ascending. Statement source at %s",
177 singleRange, ctx.getStatementSourceReference());
178 Preconditions.checkArgument(!boundaries.hasNext(),
179 "Wrong number of boundaries in length constraint %s. Statement source at %s", singleRange,
180 ctx.getStatementSourceReference());
185 // some of intervals overlapping
186 if (lengthConstraints.size() > 1 && compareNumbers(min, Iterables.getLast(lengthConstraints).getMax()) != 1) {
187 throw new InferenceException(ctx.getStatementSourceReference(),
188 "Some of the length ranges in %s are not disjoint", lengthArgument);
191 lengthConstraints.add(new LengthConstraintEffectiveImpl(min, max, description, reference));
194 return lengthConstraints;
197 public static boolean isYangTypeBodyStmtString(final String typeName) {
198 return TYPE_BODY_STMTS.contains(typeName);
201 public static boolean isYangBuiltInTypeString(final String typeName) {
202 return BUILT_IN_TYPES.contains(typeName);
206 public static SchemaPath typeEffectiveSchemaPath(final StmtContext<?, ?, ?> stmtCtx) {
207 final SchemaPath path = stmtCtx.getSchemaPath().get();
208 final QName qname = stmtCtx.getFromNamespace(QNameCacheNamespace.class,
209 QName.create(path.getParent().getLastComponent(), path.getLastComponent().getLocalName()));
210 return path.getParent().createChild(qname);