001package com.hfg.util; 002 003 004import java.util.Collection; 005import java.util.Date; 006import java.util.HashMap; 007import java.util.HashSet; 008import java.util.Map; 009import java.util.Set; 010 011import com.hfg.util.collection.CollectionUtil; 012 013//------------------------------------------------------------------------------ 014/** 015 Enumeration of basic data types. 016 017 @author J. Alex Taylor, hairyfatguy.com 018 */ 019//------------------------------------------------------------------------------ 020// com.hfg XML/HTML Coding Library 021// 022// This library is free software; you can redistribute it and/or 023// modify it under the terms of the GNU Lesser General Public 024// License as published by the Free Software Foundation; either 025// version 2.1 of the License, or (at your option) any later version. 026// 027// This library is distributed in the hope that it will be useful, 028// but WITHOUT ANY WARRANTY; without even the implied warranty of 029// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 030// Lesser General Public License for more details. 031// 032// You should have received a copy of the GNU Lesser General Public 033// License along with this library; if not, write to the Free Software 034// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 035// 036// J. Alex Taylor, President, Founder, CEO, COO, CFO, OOPS hairyfatguy.com 037// jataylor@hairyfatguy.com 038//------------------------------------------------------------------------------ 039 040public enum DataType 041{ 042 BIGINT, 043 BOOLEAN, 044 CHARACTER, 045 DATE, 046 DOUBLE, 047 FLOAT, 048 INTEGER, 049 NUMBER, 050 STRING; 051 052 private static Map<String, DataType> sLookupMap; 053 static 054 { 055 BIGINT.addAltName("long"); 056 BOOLEAN.addAltName("bool"); 057 CHARACTER.addAltName("char"); 058 DATE.addAltName("datetime"); 059 DOUBLE.addAltName("real"); 060 INTEGER.addAltName("int"); 061 NUMBER.addAltName("num"); 062 STRING.addAltName("varchar"); 063 } 064 065 private Set<String> mAltNames; 066 067 068 //--------------------------------------------------------------------------- 069 /** 070 * Since an Enum's valueOf() can't be overridden, this is our more flexible lookup. 071 * @param inClass the Class value for which the corresponding DataType will be returned 072 * @return the DataType corresponding to the specified Class value 073 */ 074 public static DataType fromClass(Class inClass) 075 { 076 DataType value = null; 077 078 if (inClass != null) 079 { 080 if (String.class.isAssignableFrom(inClass)) 081 { 082 value = DataType.STRING; 083 } 084 else if (Double.class.isAssignableFrom(inClass)) 085 { 086 value = DataType.DOUBLE; 087 } 088 else if (Float.class.isAssignableFrom(inClass)) 089 { 090 value = DataType.FLOAT; 091 } 092 else if (Integer.class.isAssignableFrom(inClass)) 093 { 094 value = DataType.INTEGER; 095 } 096 else if (Long.class.isAssignableFrom(inClass)) 097 { 098 value = DataType.BIGINT; 099 } 100 else if (Boolean.class.isAssignableFrom(inClass)) 101 { 102 value = DataType.BOOLEAN; 103 } 104 else if (Date.class.isAssignableFrom(inClass)) 105 { 106 value = DataType.DATE; 107 } 108 else if (Character.class.isAssignableFrom(inClass)) 109 { 110 value = DataType.CHARACTER; 111 } 112 } 113 114 return value; 115 } 116 117 //--------------------------------------------------------------------------- 118 /** 119 * Since an Enum's valueOf() can't be overridden, this is our more flexible lookup. 120 * @param inStringValue the String value for which the corresponding DataType will be returned 121 * @return the DataType corresponding to the specified String value 122 */ 123 public static DataType fromString(String inStringValue) 124 { 125 if (null == sLookupMap) 126 { 127 initLookupMap(); 128 } 129 130 return sLookupMap.get(inStringValue != null ? inStringValue.toLowerCase() : null); 131 } 132 133 //--------------------------------------------------------------------------- 134 private static synchronized void initLookupMap() 135 { 136 sLookupMap = new HashMap<>(20); 137 for (DataType value : values()) 138 { 139 sLookupMap.put(value.name().toLowerCase(), value); 140 if (CollectionUtil.hasValues(value.mAltNames)) 141 { 142 for (String altName : value.mAltNames) 143 { 144 sLookupMap.put(altName.toLowerCase(), value); 145 } 146 } 147 } 148 } 149 150 //--------------------------------------------------------------------------- 151 private DataType addAltName(String inValue) 152 { 153 if (null == mAltNames) 154 { 155 mAltNames = new HashSet<>(2); 156 } 157 158 mAltNames.add(inValue.toLowerCase()); 159 160 return this; 161 } 162 163 //--------------------------------------------------------------------------- 164 public boolean isNumeric() 165 { 166 return (this == BIGINT || this == DOUBLE || this == FLOAT || this == INTEGER || this == NUMBER); 167 } 168 169 //--------------------------------------------------------------------------- 170 public static DataType guessDataTypeFromValues(Collection<? extends Object> inDataValues) 171 { 172 DataType returnDataType = DataType.STRING; 173 174 175 if (CollectionUtil.hasValues(inDataValues)) 176 { 177 // Extract the unique set of Classes represented by the specified data values 178 Set<Class> dataTypeClasses = new HashSet<>(4); 179 for (Object value : inDataValues) 180 { 181 if (value != null) 182 { 183 dataTypeClasses.add(value.getClass()); 184 } 185 } 186 187 // Convert the Set of Classes to a Set of DataTypes 188 Set<DataType> dataTypes = new HashSet<>(4); 189 for (Class clazz : dataTypeClasses) 190 { 191 DataType dataType = DataType.fromClass(clazz); 192 if (dataType != null) 193 { 194 dataTypes.add(dataType); 195 } 196 } 197 198 if (1 == dataTypes.size()) 199 { 200 returnDataType = dataTypes.iterator().next(); 201 } 202 else 203 { 204 // There are a mixture of DataTypes. 205 // Just return STRING unless all the types are numeric. 206 boolean allNumeric = true; 207 for (DataType dataType : dataTypes) 208 { 209 if (! dataType.isNumeric()) 210 { 211 allNumeric = false; 212 break; 213 } 214 } 215 216 if (allNumeric) 217 { 218 // All DataTypes present are numeric - return the broadest type. 219 // For example if the DataTypes present are DOUBLE and INTEGER, return DOUBLE. 220 if (dataTypes.contains(DataType.DOUBLE)) 221 { 222 returnDataType = DataType.DOUBLE; 223 } 224 else if (dataTypes.contains(DataType.FLOAT)) 225 { 226 returnDataType = DataType.FLOAT; 227 } 228 else if (dataTypes.contains(DataType.BIGINT)) 229 { 230 returnDataType = DataType.BIGINT; 231 } 232 } 233 } 234 } 235 236 return returnDataType; 237 } 238}