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 org.opendaylight.yangtools.yang.model.util.BaseTypes.INT16_QNAME;
11 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT32_QNAME;
12 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT64_QNAME;
13 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.INT8_QNAME;
14 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT16_QNAME;
15 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT32_QNAME;
16 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT64_QNAME;
17 import static org.opendaylight.yangtools.yang.model.util.BaseTypes.UINT8_QNAME;
19 import com.google.common.base.CharMatcher;
20 import com.google.common.base.Optional;
21 import com.google.common.collect.Range;
22 import java.util.ArrayList;
23 import java.util.Collections;
24 import java.util.List;
25 import java.util.regex.Matcher;
26 import java.util.regex.Pattern;
27 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.type.IntegerTypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.type.RangeConstraint;
30 import org.opendaylight.yangtools.yang.model.api.type.UnsignedIntegerTypeDefinition;
32 abstract class AbstractIntegerStringCodec<N extends Number & Comparable<N>, T extends TypeDefinition<T>> extends TypeDefinitionAwareCodec<N, T>{
34 private static final Pattern intPattern = Pattern.compile("[+-]?[1-9][0-9]*$");
35 private static final Pattern hexPattern = Pattern.compile("[+-]?0[xX][0-9a-fA-F]+");
36 private static final Pattern octalPattern = Pattern.compile("[+-]?0[1-7][0-7]*$");
38 // For up to two characters, this is very fast
39 private static final CharMatcher X_MATCHER = CharMatcher.anyOf("xX");
41 private static final String INCORRECT_LEXICAL_REPRESENTATION = "Incorrect lexical representation of integer value: %s."
42 + "\nAn integer value can be defined as: "
43 + "\n - a decimal number,"
44 + "\n - a hexadecimal number (prefix 0x)," + "%n - an octal number (prefix 0)."
45 + "\nSigned values are allowed. Spaces between digits are NOT allowed.";
48 private final List<Range<N>> rangeConstraints;
50 protected AbstractIntegerStringCodec(final Optional<T> typeDefinition, final List<RangeConstraint> constraints , final Class<N> outputClass) {
51 super(typeDefinition, outputClass);
52 if(constraints.isEmpty()) {
53 rangeConstraints = Collections.emptyList();
55 final ArrayList<Range<N>> builder = new ArrayList<>(constraints.size());
56 for(final RangeConstraint yangConstraint : constraints) {
57 builder.add(createRange(yangConstraint.getMin(),yangConstraint.getMax()));
59 rangeConstraints = builder;
64 static TypeDefinitionAwareCodec<?, IntegerTypeDefinition> from(final IntegerTypeDefinition type) {
65 final Optional<IntegerTypeDefinition> typeOptional = Optional.of(type);
66 IntegerTypeDefinition baseType = type;
67 while(baseType.getBaseType() != null) {
68 baseType = baseType.getBaseType();
70 if (INT8_QNAME.equals(baseType.getQName())) {
71 return new Int8StringCodec(typeOptional);
72 } else if (INT16_QNAME.equals(baseType.getQName())) {
73 return new Int16StringCodec(typeOptional);
74 } else if (INT32_QNAME.equals(baseType.getQName())) {
75 return new Int32StringCodec(typeOptional);
76 } else if (INT64_QNAME.equals(baseType.getQName())) {
77 return new Int64StringCodec(typeOptional);
79 throw new IllegalArgumentException("Unsupported base type: " + baseType.getQName());
82 static TypeDefinitionAwareCodec<?, UnsignedIntegerTypeDefinition> from(final UnsignedIntegerTypeDefinition type) {
83 final Optional<UnsignedIntegerTypeDefinition> typeOptional = Optional.of(type);
84 UnsignedIntegerTypeDefinition baseType = type;
85 while(baseType.getBaseType() != null) {
86 baseType = baseType.getBaseType();
88 if (UINT8_QNAME.equals(baseType.getQName())) {
89 return new Uint8StringCodec(typeOptional);
90 } else if (UINT16_QNAME.equals(baseType.getQName())) {
91 return new Uint16StringCodec(typeOptional);
92 } else if (UINT32_QNAME.equals(baseType.getQName())) {
93 return new Uint32StringCodec(typeOptional);
94 } else if (UINT64_QNAME.equals(baseType.getQName())) {
95 return new Uint64StringCodec(typeOptional);
97 throw new IllegalArgumentException("Unsupported base type: " + baseType.getQName());
100 private Range<N> createRange(final Number yangMin, final Number yangMax) {
101 final N min = convertValue(yangMin);
102 final N max = convertValue(yangMax);
103 return Range.closed(min, max);
107 public final N deserialize(final String stringRepresentation) {
108 final int base = provideBase(stringRepresentation);
109 final N deserialized;
111 deserialized = deserialize(normalizeHexadecimal(stringRepresentation),base);
113 deserialized = deserialize(stringRepresentation,base);
115 validate(deserialized);
120 private final void validate(final N value) {
121 if (rangeConstraints.isEmpty()) {
124 for (final Range<N> constraint : rangeConstraints) {
125 if (constraint.contains(value)) {
129 throw new IllegalArgumentException("Value '" + value + "' is not in required range " + rangeConstraints);
133 * Deserializes value from supplied string representation
136 * See {@link Integer#parseInt(String, int)} for in-depth
137 * description about string and radix relationship.
139 * @param stringRepresentation String representation
140 * @param radix numeric base.
141 * @return Deserialized value.
143 protected abstract N deserialize(String stringRepresentation, int radix);
145 protected abstract N convertValue(Number value);
148 protected static List<RangeConstraint> extractRange(final IntegerTypeDefinition type) {
150 return Collections.emptyList();
152 return type.getRangeConstraints();
155 protected static List<RangeConstraint> extractRange(final UnsignedIntegerTypeDefinition type) {
157 return Collections.emptyList();
159 return type.getRangeConstraints();
162 private static final int provideBase(final String integer) {
163 if (integer == null) {
164 throw new IllegalArgumentException("String representing integer number cannot be NULL");
167 if ((integer.length() == 1) && (integer.charAt(0) == '0')) {
171 final Matcher intMatcher = intPattern.matcher(integer);
172 if (intMatcher.matches()) {
175 final Matcher hexMatcher = hexPattern.matcher(integer);
176 if (hexMatcher.matches()) {
179 final Matcher octMatcher = octalPattern.matcher(integer);
180 if (octMatcher.matches()) {
183 final String formatedMessage =
184 String.format(INCORRECT_LEXICAL_REPRESENTATION, integer);
185 throw new NumberFormatException(formatedMessage);
188 private static String normalizeHexadecimal(final String hexInt) {
189 if (hexInt == null) {
190 throw new IllegalArgumentException(
191 "String representing integer number in Hexadecimal format cannot be NULL!");
194 return X_MATCHER.removeFrom(hexInt);