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.data.impl.codec;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT16_QNAME;
12 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT32_QNAME;
13 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT64_QNAME;
14 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT8_QNAME;
15 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT16_QNAME;
16 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT32_QNAME;
17 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT64_QNAME;
18 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT8_QNAME;
20 import com.google.common.annotations.Beta;
21 import com.google.common.base.CharMatcher;
22 import com.google.common.collect.Range;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.List;
26 import java.util.Optional;
27 import java.util.regex.Pattern;
28 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
31 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
34 * Do not use this class outside of yangtools, its presence does not fall into the API stability contract.
37 public abstract class AbstractIntegerStringCodec<N extends Number & Comparable<N>, T extends TypeDefinition<T>>
38 extends TypeDefinitionAwareCodec<N, T> {
40 private static final Pattern INT_PATTERN = Pattern.compile("[+-]?[1-9][0-9]*$");
41 private static final Pattern HEX_PATTERN = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+");
42 private static final Pattern OCT_PATTERN = Pattern.compile("[+-]?0[1-7][0-7]*$");
44 // For up to two characters, this is very fast
45 private static final CharMatcher X_MATCHER = CharMatcher.anyOf("xX");
47 private static final String INCORRECT_LEXICAL_REPRESENTATION =
48 "Incorrect lexical representation of integer value: %s."
49 + "\nAn integer value can be defined as: "
50 + "\n - a decimal number,"
51 + "\n - a hexadecimal number (prefix 0x)," + "%n - an octal number (prefix 0)."
52 + "\nSigned values are allowed. Spaces between digits are NOT allowed.";
54 private final List<Range<N>> rangeConstraints;
56 AbstractIntegerStringCodec(final Optional<T> typeDefinition, final List<RangeConstraint> constraints,
57 final Class<N> outputClass) {
58 super(typeDefinition, outputClass);
59 if (constraints.isEmpty()) {
60 rangeConstraints = Collections.emptyList();
62 final List<Range<N>> builder = new ArrayList<>(constraints.size());
63 for (final RangeConstraint yangConstraint : constraints) {
64 builder.add(createRange(yangConstraint.getMin(), yangConstraint.getMax()));
66 rangeConstraints = builder;
70 public static AbstractIntegerStringCodec<?, IntegerTypeDefinition> from(final IntegerTypeDefinition type) {
71 // FIXME: this is not necessary with yang.model.util.type
72 IntegerTypeDefinition baseType = type;
73 while (baseType.getBaseType() != null) {
74 baseType = baseType.getBaseType();
77 final Optional<IntegerTypeDefinition> typeOptional = Optional.of(type);
79 // FIXME: use DerivedTypes#isInt8() and friends
80 if (INT8_QNAME.equals(baseType.getQName())) {
81 return new Int8StringCodec(typeOptional);
82 } else if (INT16_QNAME.equals(baseType.getQName())) {
83 return new Int16StringCodec(typeOptional);
84 } else if (INT32_QNAME.equals(baseType.getQName())) {
85 return new Int32StringCodec(typeOptional);
86 } else if (INT64_QNAME.equals(baseType.getQName())) {
87 return new Int64StringCodec(typeOptional);
89 throw new IllegalArgumentException("Unsupported base type: " + baseType.getQName());
93 public static AbstractIntegerStringCodec<?, UnsignedIntegerTypeDefinition> from(
94 final UnsignedIntegerTypeDefinition type) {
95 // FIXME: this is not necessary with yang.model.util.type
96 UnsignedIntegerTypeDefinition baseType = type;
97 while (baseType.getBaseType() != null) {
98 baseType = baseType.getBaseType();
101 final Optional<UnsignedIntegerTypeDefinition> typeOptional = Optional.of(type);
103 // FIXME: use DerivedTypes#isUint8() and friends
104 if (UINT8_QNAME.equals(baseType.getQName())) {
105 return new Uint8StringCodec(typeOptional);
106 } else if (UINT16_QNAME.equals(baseType.getQName())) {
107 return new Uint16StringCodec(typeOptional);
108 } else if (UINT32_QNAME.equals(baseType.getQName())) {
109 return new Uint32StringCodec(typeOptional);
110 } else if (UINT64_QNAME.equals(baseType.getQName())) {
111 return new Uint64StringCodec(typeOptional);
113 throw new IllegalArgumentException("Unsupported base type: " + baseType.getQName());
117 private Range<N> createRange(final Number yangMin, final Number yangMax) {
118 final N min = convertValue(yangMin);
119 final N max = convertValue(yangMax);
120 return Range.closed(min, max);
124 public final N deserialize(final String stringRepresentation) {
125 final int base = provideBase(stringRepresentation);
126 final N deserialized;
128 deserialized = deserialize(normalizeHexadecimal(stringRepresentation),base);
130 deserialized = deserialize(stringRepresentation,base);
132 validate(deserialized);
137 * Deserializes value from supplied string representation is supplied radix. See
138 * {@link Integer#parseInt(String, int)} for in-depth description about string and radix relationship.
140 * @param stringRepresentation String representation
141 * @param radix numeric base.
142 * @return Deserialized value.
144 abstract N deserialize(String stringRepresentation, int radix);
146 abstract N convertValue(Number value);
148 private void validate(final N value) {
149 if (rangeConstraints.isEmpty()) {
152 for (final Range<N> constraint : rangeConstraints) {
153 if (constraint.contains(value)) {
157 throw new IllegalArgumentException("Value '" + value + "' is not in required range " + rangeConstraints);
160 protected static List<RangeConstraint> extractRange(final IntegerTypeDefinition type) {
162 return Collections.emptyList();
164 return type.getRangeConstraints();
167 protected static List<RangeConstraint> extractRange(final UnsignedIntegerTypeDefinition type) {
169 return Collections.emptyList();
171 return type.getRangeConstraints();
174 private static int provideBase(final String integer) {
175 checkArgument(integer != null, "String representing integer number cannot be NULL");
177 if (integer.length() == 1 && integer.charAt(0) == '0') {
179 } else if (INT_PATTERN.matcher(integer).matches()) {
181 } else if (HEX_PATTERN.matcher(integer).matches()) {
183 } else if (OCT_PATTERN.matcher(integer).matches()) {
186 throw new NumberFormatException(String.format(INCORRECT_LEXICAL_REPRESENTATION, integer));
190 private static String normalizeHexadecimal(final String hexInt) {
191 checkArgument(hexInt != null, "String representing integer number in Hexadecimal format cannot be NULL!");
192 return X_MATCHER.removeFrom(hexInt);