001/* 002 * The MIT License 003 * Copyright (c) 2012 Microsoft Corporation 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining a copy 006 * of this software and associated documentation files (the "Software"), to deal 007 * in the Software without restriction, including without limitation the rights 008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 009 * copies of the Software, and to permit persons to whom the Software is 010 * furnished to do so, subject to the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be included in 013 * all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 021 * THE SOFTWARE. 022 */ 023 024package microsoft.exchange.webservices.data.misc; 025 026import microsoft.exchange.webservices.data.core.EwsUtilities; 027import microsoft.exchange.webservices.data.core.ILazyMember; 028import microsoft.exchange.webservices.data.core.LazyMember; 029import microsoft.exchange.webservices.data.core.enumeration.property.MapiPropertyType; 030import microsoft.exchange.webservices.data.core.exception.misc.FormatException; 031import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException; 032import org.apache.commons.logging.Log; 033import org.apache.commons.logging.LogFactory; 034 035import java.text.DateFormat; 036import java.text.ParseException; 037import java.text.SimpleDateFormat; 038import java.util.ArrayList; 039import java.util.Date; 040import java.util.Iterator; 041import java.util.List; 042import java.util.Map; 043import java.util.UUID; 044 045/** 046 * Utility class to convert between MAPI Property type values and strings. 047 */ 048public class MapiTypeConverter { 049 050 private static final Log LOG = LogFactory.getLog(MapiTypeConverter.class); 051 052 private static final IFunction<String, Object> DATE_TIME_PARSER = new IFunction<String, Object>() { 053 public Object func(final String s) { 054 return parseDateTime(s); 055 } 056 }; 057 058 private static final IFunction<String, Object> MAPI_VALUE_PARSER = new IFunction<String, Object>() { 059 public Object func(final String s) { 060 return MapiTypeConverter.parseMapiIntegerValue(s); 061 } 062 }; 063 064 /** 065 * The mapi type converter map. 066 */ 067 private static final LazyMember<MapiTypeConverterMap> MAPI_TYPE_CONVERTER_MAP = 068 new LazyMember<MapiTypeConverterMap>(new ILazyMember<MapiTypeConverterMap>() { 069 @Override 070 public MapiTypeConverterMap createInstance() { 071 MapiTypeConverterMap map = new MapiTypeConverterMap(); 072 073 map.put(MapiPropertyType.ApplicationTime, new MapiTypeConverterMapEntry(Double.class)); 074 075 MapiTypeConverterMapEntry mapitype = new MapiTypeConverterMapEntry(Double.class); 076 mapitype.setIsArray(true); 077 map.put(MapiPropertyType.ApplicationTimeArray, mapitype); 078 079 mapitype = new MapiTypeConverterMapEntry(Byte[].class); 080 mapitype.setParse(IFunctions.Base64Decoder.INSTANCE); 081 mapitype.setConvertToString(IFunctions.Base64Encoder.INSTANCE); 082 map.put(MapiPropertyType.Binary, mapitype); 083 084 mapitype = new MapiTypeConverterMapEntry(Byte[].class); 085 mapitype.setParse(IFunctions.Base64Decoder.INSTANCE); 086 mapitype.setConvertToString(IFunctions.Base64Encoder.INSTANCE); 087 mapitype.setIsArray(true); 088 map.put(MapiPropertyType.BinaryArray, mapitype); 089 090 mapitype = new MapiTypeConverterMapEntry(Boolean.class); 091 mapitype.setParse(IFunctions.ToBoolean.INSTANCE); 092 mapitype.setConvertToString(IFunctions.ToLowerCase.INSTANCE); 093 map.put(MapiPropertyType.Boolean, mapitype); 094 095 mapitype = new MapiTypeConverterMapEntry(UUID.class); 096 mapitype.setParse(IFunctions.ToUUID.INSTANCE); 097 mapitype.setConvertToString(IFunctions.ToString.INSTANCE); 098 map.put(MapiPropertyType.CLSID, mapitype); 099 100 mapitype = new MapiTypeConverterMapEntry(UUID.class); 101 mapitype.setParse(IFunctions.ToUUID.INSTANCE); 102 mapitype.setConvertToString(IFunctions.ToString.INSTANCE); 103 mapitype.setIsArray(true); 104 map.put(MapiPropertyType.CLSIDArray, mapitype); 105 106 map.put(MapiPropertyType.Currency, new MapiTypeConverterMapEntry(Long.class)); 107 108 mapitype = new MapiTypeConverterMapEntry(Long.class); 109 mapitype.setIsArray(true); 110 map.put(MapiPropertyType.CurrencyArray, mapitype); 111 112 map.put(MapiPropertyType.Double, new MapiTypeConverterMapEntry(Double.class)); 113 114 mapitype = new MapiTypeConverterMapEntry(Double.class); 115 mapitype.setIsArray(true); 116 map.put(MapiPropertyType.DoubleArray, mapitype); 117 118 map.put(MapiPropertyType.Error, new MapiTypeConverterMapEntry(Integer.class)); 119 map.put(MapiPropertyType.Float, new MapiTypeConverterMapEntry(Float.class)); 120 121 mapitype = new MapiTypeConverterMapEntry(Float.class); 122 mapitype.setIsArray(true); 123 map.put(MapiPropertyType.FloatArray, mapitype); 124 125 mapitype = new MapiTypeConverterMapEntry(Integer.class); 126 mapitype.setParse(MAPI_VALUE_PARSER); 127 map.put(MapiPropertyType.Integer, mapitype); 128 129 mapitype = new MapiTypeConverterMapEntry(Integer.class); 130 mapitype.setIsArray(true); 131 map.put(MapiPropertyType.IntegerArray, mapitype); 132 133 map.put(MapiPropertyType.Long, new MapiTypeConverterMapEntry(Long.class)); 134 135 mapitype = new MapiTypeConverterMapEntry(Long.class); 136 mapitype.setIsArray(true); 137 map.put(MapiPropertyType.LongArray, mapitype); 138 139 mapitype = new MapiTypeConverterMapEntry(String.class); 140 mapitype.setParse(IFunctions.StringToObject.INSTANCE); 141 map.put(MapiPropertyType.Object, mapitype); 142 143 mapitype = new MapiTypeConverterMapEntry(String.class); 144 mapitype.setParse(IFunctions.StringToObject.INSTANCE); 145 mapitype.setIsArray(true); 146 map.put(MapiPropertyType.ObjectArray, mapitype); 147 148 map.put(MapiPropertyType.Short, new MapiTypeConverterMapEntry(Short.class)); 149 150 mapitype = new MapiTypeConverterMapEntry(Short.class); 151 mapitype.setIsArray(true); 152 map.put(MapiPropertyType.ShortArray, mapitype); 153 154 mapitype = new MapiTypeConverterMapEntry(String.class); 155 mapitype.setParse(IFunctions.StringToObject.INSTANCE); 156 map.put(MapiPropertyType.String, mapitype); 157 158 mapitype = new MapiTypeConverterMapEntry(String.class); 159 mapitype.setParse(IFunctions.StringToObject.INSTANCE); 160 mapitype.setIsArray(true); 161 map.put(MapiPropertyType.StringArray, mapitype); 162 163 mapitype = new MapiTypeConverterMapEntry(Date.class); 164 mapitype.setParse(DATE_TIME_PARSER); 165 mapitype.setConvertToString(IFunctions.DateTimeToXSDateTime.INSTANCE); 166 map.put(MapiPropertyType.SystemTime, mapitype); 167 168 mapitype = new MapiTypeConverterMapEntry(Date.class); 169 mapitype.setParse(DATE_TIME_PARSER); 170 mapitype.setConvertToString(IFunctions.DateTimeToXSDateTime.INSTANCE); 171 mapitype.setIsArray(true); 172 map.put(MapiPropertyType.SystemTimeArray, mapitype); 173 174 return map; 175 } 176 }); 177 178 179 /** 180 * Converts the string list to array. 181 * 182 * @param mapiPropType Type of the MAPI property. 183 * @param strings the strings 184 * @return Array of objects. 185 * @throws Exception the exception 186 */ 187 public static List<Object> convertToValue(MapiPropertyType mapiPropType, Iterator<String> strings) throws Exception { 188 EwsUtilities.validateParam(strings, "strings"); 189 190 MapiTypeConverterMapEntry typeConverter = getMapiTypeConverterMap() 191 .get(mapiPropType); 192 List<Object> array = new ArrayList<Object>(); 193 194 int index = 0; 195 196 while (strings.hasNext()) { 197 Object value = typeConverter.convertToValueOrDefault(strings.next()); 198 array.add(index, value); 199 } 200 return array; 201 } 202 203 /** 204 * Converts a string to value consistent with MAPI type. 205 * 206 * @param mapiPropType the mapi prop type 207 * @param stringValue the string value 208 * @return the object 209 * @throws ServiceXmlDeserializationException the service xml deserialization exception 210 * @throws FormatException the format exception 211 */ 212 public static Object convertToValue(MapiPropertyType mapiPropType, String stringValue) throws ServiceXmlDeserializationException, FormatException { 213 return getMapiTypeConverterMap().get(mapiPropType).convertToValue( 214 stringValue); 215 216 } 217 218 /** 219 * Converts a value to a string. 220 * 221 * @param mapiPropType the mapi prop type 222 * @param value the value 223 * @return String value. 224 */ 225 public static String convertToString(MapiPropertyType mapiPropType, Object value) { 226 /* 227 * if(! (value instanceof FuncInterface<?,?>)){ return null; } 228 */ 229 return (value == null) ? "" : getMapiTypeConverterMap().get( 230 mapiPropType).getConvertToString().func(value); 231 } 232 233 /** 234 * Change value to a value of compatible type. 235 * 236 * @param mapiType the mapi type 237 * @param value the value 238 * @return the object 239 * @throws Exception the exception 240 */ 241 public static Object changeType(MapiPropertyType mapiType, Object value) 242 throws Exception { 243 EwsUtilities.validateParam(value, "value"); 244 245 return getMapiTypeConverterMap().get(mapiType).changeType(value); 246 } 247 248 /** 249 * Converts a MAPI Integer value. 250 * Usually the value is an integer but there are cases where the value has been "schematized" to an 251 * Enumeration value (e.g. NoData) which we have no choice but to fallback and represent as a string. 252 * 253 * @param s The string value. 254 * @return Integer value or the original string if the value could not be parsed as such. 255 */ 256 protected static Object parseMapiIntegerValue(String s) { 257 int intValue; 258 try { 259 intValue = Integer.parseInt(s.trim()); 260 return Integer.valueOf(intValue); 261 } catch (NumberFormatException e) { 262 return s; 263 } 264 } 265 266 /** 267 * Determines whether MapiPropertyType is an array type. 268 * 269 * @param mapiType the mapi type 270 * @return true, if is array type 271 */ 272 public static boolean isArrayType(MapiPropertyType mapiType) { 273 return getMapiTypeConverterMap().get(mapiType).getIsArray(); 274 } 275 276 /** 277 * Gets the MAPI type converter map. 278 * 279 * @return the mapi type converter map 280 */ 281 public static Map<MapiPropertyType, MapiTypeConverterMapEntry> 282 getMapiTypeConverterMap() { 283 284 return MAPI_TYPE_CONVERTER_MAP.getMember(); 285 } 286 287 288 private static Object parseDateTime(String s) { 289 String utcPattern = "yyyy-MM-dd'T'HH:mm:ss'Z'"; 290 String errMsg = String.format("Date String %s not in " + "valid UTC/local format", s); 291 DateFormat utcFormatter = new SimpleDateFormat(utcPattern); 292 Date dt; 293 294 if (s.endsWith("Z")) { 295 try { 296 dt = utcFormatter.parse(s); 297 } catch (ParseException e) { 298 s = s.substring(0, 10) + "T12:00:00Z"; 299 try { 300 dt = utcFormatter.parse(s); 301 } catch (ParseException e1) { 302 LOG.error(e); 303 throw new IllegalArgumentException( 304 errMsg, e); 305 } 306 } 307 } else if (s.endsWith("z")) { 308 // String in UTC format yyyy-MM-ddTHH:mm:ssZ 309 utcFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'z'"); 310 try { 311 dt = utcFormatter.parse(s); 312 } catch (ParseException e) { 313 throw new IllegalArgumentException(e); 314 } 315 } else { 316 utcFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); 317 try { 318 dt = utcFormatter.parse(s); 319 } catch (ParseException e) { 320 throw new IllegalArgumentException(e); 321 } 322 } 323 return dt; 324 } 325 326}