| /******************************************************************************* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| ******************************************************************************/ |
| package org.apache.olingo.odata2.core.edm; |
| |
| import java.math.BigDecimal; |
| import java.util.Locale; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.apache.olingo.odata2.api.edm.EdmFacets; |
| import org.apache.olingo.odata2.api.edm.EdmLiteralKind; |
| import org.apache.olingo.odata2.api.edm.EdmSimpleType; |
| import org.apache.olingo.odata2.api.edm.EdmSimpleTypeException; |
| |
| /** |
| * Implementation of the EDM simple type Double. |
| * |
| */ |
| public class EdmDouble extends AbstractSimpleType { |
| |
| // value-range limitations according to the CSDL document |
| private static final int MAX_PRECISION = 15; |
| private static final int MAX_SCALE = 308; |
| |
| private static final Pattern PATTERN = Pattern.compile( |
| "(?:\\+|-)?\\p{Digit}+(?:\\.\\p{Digit}+)?(?:(?:E|e)(?:\\+|-)?\\p{Digit}{1,3})?(D|d)?"); |
| private static final EdmDouble instance = new EdmDouble(); |
| |
| public static EdmDouble getInstance() { |
| return instance; |
| } |
| |
| @Override |
| public boolean isCompatible(final EdmSimpleType simpleType) { |
| return simpleType instanceof Bit |
| || simpleType instanceof Uint7 |
| || simpleType instanceof EdmByte |
| || simpleType instanceof EdmSByte |
| || simpleType instanceof EdmInt16 |
| || simpleType instanceof EdmInt32 |
| || simpleType instanceof EdmInt64 |
| || simpleType instanceof EdmSingle |
| || simpleType instanceof EdmDouble; |
| } |
| |
| @Override |
| public Class<?> getDefaultType() { |
| return Double.class; |
| } |
| |
| @Override |
| protected <T> T internalValueOfString(final String value, final EdmLiteralKind literalKind, final EdmFacets facets, |
| final Class<T> returnType) throws EdmSimpleTypeException { |
| String valueString = value; |
| Double result = null; |
| // Handle special values first. |
| if ("-INF".equals(value)) { |
| result = Double.NEGATIVE_INFINITY; |
| } else if ("INF".equals(value)) { |
| result = Double.POSITIVE_INFINITY; |
| } else if ("NaN".equals(value)) { |
| result = Double.NaN; |
| } else { |
| // Now only "normal" numbers remain. |
| final Matcher matcher = PATTERN.matcher(value); |
| if (!matcher.matches() |
| || (literalKind == EdmLiteralKind.URI) == (matcher.group(1) == null)) { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)); |
| } |
| |
| if (literalKind == EdmLiteralKind.URI) { |
| valueString = value.substring(0, value.length() - 1); |
| } |
| |
| // The number format is checked above, so we don't have to catch NumberFormatException. |
| result = Double.valueOf(valueString); |
| |
| // "Real" infinite values have been treated already above, so we can throw an exception |
| // if the conversion to a float results in an infinite value. |
| if (result.isInfinite()) { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_ILLEGAL_CONTENT.addContent(value)); |
| } |
| } |
| |
| if (returnType.isAssignableFrom(Double.class)) { |
| return returnType.cast(result); |
| } else if (returnType.isAssignableFrom(Float.class)) { |
| if (result.floatValue() == result) { |
| return returnType.cast(result.floatValue()); |
| } else { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_UNCONVERTIBLE_TO_VALUE_TYPE.addContent(value, |
| returnType)); |
| } |
| } else if (result.isInfinite() || result.isNaN()) { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_UNCONVERTIBLE_TO_VALUE_TYPE.addContent(value, |
| returnType)); |
| } else { |
| try { |
| final BigDecimal valueBigDecimal = new BigDecimal(valueString); |
| if (returnType.isAssignableFrom(BigDecimal.class)) { |
| return returnType.cast(valueBigDecimal); |
| } else if (returnType.isAssignableFrom(Long.class)) { |
| return returnType.cast(valueBigDecimal.longValueExact()); |
| } else if (returnType.isAssignableFrom(Integer.class)) { |
| return returnType.cast(valueBigDecimal.intValueExact()); |
| } else if (returnType.isAssignableFrom(Short.class)) { |
| return returnType.cast(valueBigDecimal.shortValueExact()); |
| } else if (returnType.isAssignableFrom(Byte.class)) { |
| return returnType.cast(valueBigDecimal.byteValueExact()); |
| } else { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(returnType)); |
| } |
| |
| } catch (final ArithmeticException e) { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.LITERAL_UNCONVERTIBLE_TO_VALUE_TYPE.addContent(value, |
| returnType), e); |
| } |
| } |
| } |
| |
| @Override |
| protected <T> String internalValueToString(final T value, final EdmLiteralKind literalKind, final EdmFacets facets) |
| throws EdmSimpleTypeException { |
| if (value instanceof Long) { |
| if (Math.abs((Long) value) < Math.pow(10, MAX_PRECISION)) { |
| return value.toString(); |
| } else { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_ILLEGAL_CONTENT.addContent(value)); |
| } |
| } else if (value instanceof Integer || value instanceof Short || value instanceof Byte) { |
| return value.toString(); |
| } else if (value instanceof Double) { |
| final String result = value.toString(); |
| return ((Double) value).isInfinite() ? result.toUpperCase(Locale.ROOT) |
| .substring(0, value.toString().length() - 5) : result; |
| } else if (value instanceof Float) { |
| final String result = value.toString(); |
| return ((Float) value).isInfinite() ? result.toUpperCase(Locale.ROOT).substring(0, value.toString().length() - 5) |
| : result; |
| } else if (value instanceof BigDecimal) { |
| if (((BigDecimal) value).precision() <= MAX_PRECISION && Math.abs(((BigDecimal) value).scale()) <= MAX_SCALE) { |
| return ((BigDecimal) value).toString(); |
| } else { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_ILLEGAL_CONTENT.addContent(value)); |
| } |
| } else { |
| throw new EdmSimpleTypeException(EdmSimpleTypeException.VALUE_TYPE_NOT_SUPPORTED.addContent(value.getClass())); |
| } |
| } |
| |
| @Override |
| public String toUriLiteral(final String literal) { |
| return "-INF".equals(literal) || "INF".equals(literal) || "NaN".equals(literal) ? |
| literal : literal + "D"; |
| } |
| } |