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
024 package microsoft.exchange.webservices.data.misc;
025
026 import microsoft.exchange.webservices.data.core.EwsUtilities;
027 import microsoft.exchange.webservices.data.core.ILazyMember;
028 import microsoft.exchange.webservices.data.core.LazyMember;
029 import microsoft.exchange.webservices.data.core.enumeration.property.MapiPropertyType;
030 import microsoft.exchange.webservices.data.core.exception.misc.FormatException;
031 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
032 import org.apache.commons.logging.Log;
033 import org.apache.commons.logging.LogFactory;
034
035 import java.text.DateFormat;
036 import java.text.ParseException;
037 import java.text.SimpleDateFormat;
038 import java.util.ArrayList;
039 import java.util.Date;
040 import java.util.Iterator;
041 import java.util.List;
042 import java.util.Map;
043 import java.util.UUID;
044
045 /**
046 * Utility class to convert between MAPI Property type values and strings.
047 */
048 public 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 }