001/* 002 * Copyright 2018 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.core.model.type; 017 018import java.text.DecimalFormat; 019import java.text.DecimalFormatSymbols; 020import java.util.List; 021import java.util.Locale; 022import java.util.Optional; 023import java.util.stream.Stream; 024 025import org.apache.commons.lang3.StringUtils; 026import org.apache.commons.lang3.tuple.Triple; 027 028import org.ametys.runtime.model.compare.DataChangeType; 029import org.ametys.runtime.model.compare.DataChangeTypeDetail; 030import org.ametys.runtime.model.exception.BadItemTypeException; 031import org.ametys.runtime.model.type.DataContext; 032 033/** 034 * Abstract class for double element types 035 */ 036public abstract class AbstractDoubleElementType extends AbstractElementType<Double> 037{ 038 private static final ThreadLocal<DecimalFormat> __FORMATTER = new ThreadLocal<>(); 039 040 @Override 041 public Double convertValue(Object value) throws BadItemTypeException 042 { 043 if (value instanceof Number) 044 { 045 return ((Number) value).doubleValue(); 046 } 047 else if (value instanceof String) 048 { 049 if (StringUtils.isEmpty((String) value)) 050 { 051 return null; 052 } 053 054 try 055 { 056 return Double.valueOf((String) value); 057 } 058 catch (NumberFormatException e) 059 { 060 throw new BadItemTypeException("Unable to cast '" + value + "' to a double", e); 061 } 062 } 063 064 return super.convertValue(value); 065 } 066 067 public Object fromJSONForClient(Object json, DataContext context) 068 { 069 if (json == null) 070 { 071 return null; 072 } 073 else if (json instanceof Number || json instanceof String) 074 { 075 return convertValue(json); 076 } 077 else if (json instanceof List) 078 { 079 @SuppressWarnings("unchecked") 080 List<Object> jsonList = (List<Object>) json; 081 Double[] result = new Double[jsonList.size()]; 082 for (int i = 0; i < jsonList.size(); i++) 083 { 084 Object singleJSON = jsonList.get(i); 085 if (singleJSON instanceof Number || singleJSON instanceof String) 086 { 087 result[i] = convertValue(singleJSON); 088 } 089 else 090 { 091 throw new IllegalArgumentException("Try to convert the non Double JSON object '" + json + "' into a Double"); 092 } 093 } 094 return result; 095 } 096 else 097 { 098 throw new IllegalArgumentException("Try to convert the non Double JSON object '" + json + "' into a Double"); 099 } 100 } 101 102 @Override 103 protected Stream<Triple<DataChangeType, DataChangeTypeDetail, String>> _compareSingleValues(Double value1, Double value2) 104 { 105 return Stream.of(ModelItemTypeHelper.compareSingleNumbers(value1, value2, StringUtils.EMPTY)) 106 .filter(Optional::isPresent) 107 .map(Optional::get); 108 } 109 110 @Override 111 public boolean isCompatible(Object value) 112 { 113 return super.isCompatible(value) || value instanceof double[] || value instanceof Float || value instanceof Float[] || value instanceof float[]; 114 } 115 116 public boolean isSimple() 117 { 118 return true; 119 } 120 121 @Override 122 public String toString(Double value) 123 { 124 DecimalFormat df = __FORMATTER.get(); 125 if (df == null) 126 { 127 df = new DecimalFormat("0.0", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); // English to have . as fraction separator 128 df.setMaximumFractionDigits(340); // 340 is the max supporter by the implementation (DecimalFormat.DOUBLE_FRACTION_DIGITS) 129 130 __FORMATTER.set(df); 131 } 132 return df.format(value); 133 } 134}