microsoft.exchange.webservices.data.core.EwsUtilities.java Source code

Java tutorial

Introduction

Here is the source code for microsoft.exchange.webservices.data.core.EwsUtilities.java

Source

/*
 * The MIT License
 * Copyright (c) 2012 Microsoft Corporation
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package microsoft.exchange.webservices.data.core;

import microsoft.exchange.webservices.data.ISelfValidate;
import microsoft.exchange.webservices.data.attribute.EwsEnum;
import microsoft.exchange.webservices.data.attribute.RequiredServerVersion;
import microsoft.exchange.webservices.data.core.request.HttpWebRequest;
import microsoft.exchange.webservices.data.core.service.ICreateServiceObjectWithAttachmentParam;
import microsoft.exchange.webservices.data.core.service.ICreateServiceObjectWithServiceParam;
import microsoft.exchange.webservices.data.core.service.ServiceObject;
import microsoft.exchange.webservices.data.core.service.ServiceObjectInfo;
import microsoft.exchange.webservices.data.core.service.item.Item;
import microsoft.exchange.webservices.data.core.enumeration.notification.EventType;
import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
import microsoft.exchange.webservices.data.core.enumeration.service.FileAsMapping;
import microsoft.exchange.webservices.data.core.enumeration.search.ItemTraversal;
import microsoft.exchange.webservices.data.core.enumeration.property.MailboxType;
import microsoft.exchange.webservices.data.core.enumeration.service.MeetingRequestsDeliveryScope;
import microsoft.exchange.webservices.data.core.enumeration.property.RuleProperty;
import microsoft.exchange.webservices.data.core.enumeration.property.WellKnownFolderName;
import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
import microsoft.exchange.webservices.data.core.exception.misc.ArgumentException;
import microsoft.exchange.webservices.data.core.exception.misc.ArgumentNullException;
import microsoft.exchange.webservices.data.core.exception.http.EWSHttpException;
import microsoft.exchange.webservices.data.core.exception.misc.FormatException;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
import microsoft.exchange.webservices.data.core.exception.service.local.ServiceVersionException;
import microsoft.exchange.webservices.data.misc.TimeSpan;
import microsoft.exchange.webservices.data.property.complex.ItemAttachment;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.joda.time.Period;
import org.joda.time.format.ISOPeriodFormat;

import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URISyntaxException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * EWS utilities.
 */
public final class EwsUtilities {

    private static final Log LOG = LogFactory.getLog(EwsUtilities.class);

    /**
     * The Constant XSFalse.
     */
    public static final String XSFalse = "false";

    /**
     * The Constant XSTrue.
     */
    public static final String XSTrue = "true";

    /**
     * The Constant EwsTypesNamespacePrefix.
     */
    public static final String EwsTypesNamespacePrefix = "t";

    /**
     * The Constant EwsMessagesNamespacePrefix.
     */
    public static final String EwsMessagesNamespacePrefix = "m";

    /**
     * The Constant EwsErrorsNamespacePrefix.
     */
    public static final String EwsErrorsNamespacePrefix = "e";

    /**
     * The Constant EwsSoapNamespacePrefix.
     */
    public static final String EwsSoapNamespacePrefix = "soap";

    /**
     * The Constant EwsXmlSchemaInstanceNamespacePrefix.
     */
    public static final String EwsXmlSchemaInstanceNamespacePrefix = "xsi";

    /**
     * The Constant PassportSoapFaultNamespacePrefix.
     */
    public static final String PassportSoapFaultNamespacePrefix = "psf";

    /**
     * The Constant WSTrustFebruary2005NamespacePrefix.
     */
    public static final String WSTrustFebruary2005NamespacePrefix = "wst";

    /**
     * The Constant WSAddressingNamespacePrefix.
     */
    public static final String WSAddressingNamespacePrefix = "wsa";

    /**
     * The Constant AutodiscoverSoapNamespacePrefix.
     */
    public static final String AutodiscoverSoapNamespacePrefix = "a";

    /**
     * The Constant WSSecurityUtilityNamespacePrefix.
     */
    public static final String WSSecurityUtilityNamespacePrefix = "wsu";

    /**
     * The Constant WSSecuritySecExtNamespacePrefix.
     */
    public static final String WSSecuritySecExtNamespacePrefix = "wsse";

    /**
     * The Constant EwsTypesNamespace.
     */
    public static final String EwsTypesNamespace = "http://schemas.microsoft.com/exchange/services/2006/types";

    /**
     * The Constant EwsMessagesNamespace.
     */
    public static final String EwsMessagesNamespace = "http://schemas.microsoft.com/exchange/services/2006/messages";

    /**
     * The Constant EwsErrorsNamespace.
     */
    public static final String EwsErrorsNamespace = "http://schemas.microsoft.com/exchange/services/2006/errors";

    /**
     * The Constant EwsSoapNamespace.
     */
    public static final String EwsSoapNamespace = "http://schemas.xmlsoap.org/soap/envelope/";

    /**
     * The Constant EwsSoap12Namespace.
     */
    public static final String EwsSoap12Namespace = "http://www.w3.org/2003/05/soap-envelope";

    /**
     * The Constant EwsXmlSchemaInstanceNamespace.
     */
    public static final String EwsXmlSchemaInstanceNamespace = "http://www.w3.org/2001/XMLSchema-instance";

    /**
     * The Constant PassportSoapFaultNamespace.
     */
    public static final String PassportSoapFaultNamespace = "http://schemas.microsoft.com/Passport/SoapServices/SOAPFault";

    /**
     * The Constant WSTrustFebruary2005Namespace.
     */
    public static final String WSTrustFebruary2005Namespace = "http://schemas.xmlsoap.org/ws/2005/02/trust";

    /**
     * The Constant WSAddressingNamespace.
     */
    public static final String WSAddressingNamespace = "http://www.w3.org/2005/08/addressing";
    // "http://schemas.xmlsoap.org/ws/2004/08/addressing";

    /**
     * The Constant AutodiscoverSoapNamespace.
     */
    public static final String AutodiscoverSoapNamespace = "http://schemas.microsoft.com/exchange/2010/Autodiscover";

    public static final String WSSecurityUtilityNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd";
    public static final String WSSecuritySecExtNamespace = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd";

    /**
     * The service object info.
     */
    private static final LazyMember<ServiceObjectInfo> SERVICE_OBJECT_INFO = new LazyMember<ServiceObjectInfo>(
            new ILazyMember<ServiceObjectInfo>() {
                public ServiceObjectInfo createInstance() {
                    return new ServiceObjectInfo();
                }
            });

    private static final String XML_SCHEMA_DATE_FORMAT = "yyyy-MM-dd'Z'";
    private static final String XML_SCHEMA_DATE_TIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss'Z'";

    private static final Pattern PATTERN_TIME_SPAN = Pattern.compile("-P");
    private static final Pattern PATTERN_YEAR = Pattern.compile("(\\d+)Y");
    private static final Pattern PATTERN_MONTH = Pattern.compile("(\\d+)M");
    private static final Pattern PATTERN_DAY = Pattern.compile("(\\d+)D");
    private static final Pattern PATTERN_HOUR = Pattern.compile("(\\d+)H");
    private static final Pattern PATTERN_MINUTES = Pattern.compile("(\\d+)M");
    private static final Pattern PATTERN_SECONDS = Pattern.compile("(\\d+)\\."); // Need to escape dot, otherwise it matches any char
    private static final Pattern PATTERN_MILLISECONDS = Pattern.compile("(\\d+)S");

    private EwsUtilities() {
        throw new UnsupportedOperationException();
    }

    /**
     * Gets the builds the version.
     *
     * @return the builds the version
     */
    public static String getBuildVersion() {
        return "0.0.0.0";
    }

    /**
     * The enum version dictionaries.
     */
    private static final LazyMember<Map<Class<?>, Map<String, ExchangeVersion>>> ENUM_VERSION_DICTIONARIES = new LazyMember<Map<Class<?>, Map<String, ExchangeVersion>>>(
            new ILazyMember<Map<Class<?>, Map<String, ExchangeVersion>>>() {
                @Override
                public Map<Class<?>, Map<String, ExchangeVersion>> createInstance() {
                    Map<Class<?>, Map<String, ExchangeVersion>> enumDicts = new HashMap<Class<?>, Map<String, ExchangeVersion>>();
                    enumDicts.put(WellKnownFolderName.class, buildEnumDict(WellKnownFolderName.class));
                    enumDicts.put(ItemTraversal.class, buildEnumDict(ItemTraversal.class));
                    enumDicts.put(FileAsMapping.class, buildEnumDict(FileAsMapping.class));
                    enumDicts.put(EventType.class, buildEnumDict(EventType.class));
                    enumDicts.put(MeetingRequestsDeliveryScope.class,
                            buildEnumDict(MeetingRequestsDeliveryScope.class));
                    return enumDicts;
                }
            });
    /**
     * Dictionary of enum type to schema-name-to-enum-value maps.
     */
    private static final LazyMember<Map<Class<?>, Map<String, String>>> SCHEMA_TO_ENUM_DICTIONARIES = new LazyMember<Map<Class<?>, Map<String, String>>>(
            new ILazyMember<Map<Class<?>, Map<String, String>>>() {
                @Override
                public Map<Class<?>, Map<String, String>> createInstance() {
                    Map<Class<?>, Map<String, String>> enumDicts = new HashMap<Class<?>, Map<String, String>>();
                    enumDicts.put(EventType.class, buildSchemaToEnumDict(EventType.class));
                    enumDicts.put(MailboxType.class, buildSchemaToEnumDict(MailboxType.class));
                    enumDicts.put(FileAsMapping.class, buildSchemaToEnumDict(FileAsMapping.class));
                    enumDicts.put(RuleProperty.class, buildSchemaToEnumDict(RuleProperty.class));
                    return enumDicts;

                }
            });

    /**
     * Dictionary of enum type to enum-value-to-schema-name maps.
     */
    public static final LazyMember<Map<Class<?>, Map<String, String>>> ENUM_TO_SCHEMA_DICTIONARIES = new LazyMember<Map<Class<?>, Map<String, String>>>(
            new ILazyMember<Map<Class<?>, Map<String, String>>>() {
                @Override
                public Map<Class<?>, Map<String, String>> createInstance() {
                    Map<Class<?>, Map<String, String>> enumDicts = new HashMap<Class<?>, Map<String, String>>();
                    enumDicts.put(EventType.class, buildEnumToSchemaDict(EventType.class));
                    enumDicts.put(MailboxType.class, buildEnumToSchemaDict(MailboxType.class));
                    enumDicts.put(FileAsMapping.class, buildEnumToSchemaDict(FileAsMapping.class));
                    enumDicts.put(RuleProperty.class, buildEnumToSchemaDict(RuleProperty.class));
                    return enumDicts;
                }
            });

    /**
     * Regular expression for legal domain names.
     */
    public static final String DomainRegex = "^[-a-zA-Z0-9_.]+$";

    /**
     * Asserts that the specified condition if true.
     *
     * @param condition Assertion.
     * @param caller    The caller.
     * @param message   The message to use if assertion fails.
     */
    public static void ewsAssert(final boolean condition, final String caller, final String message) {
        if (!condition) {
            throw new RuntimeException(String.format("[%s] %s", caller, message));
        }
    }

    /**
     * Gets the namespace prefix from an XmlNamespace enum value.
     *
     * @param xmlNamespace The XML namespace
     * @return Namespace prefix string.
     */
    public static String getNamespacePrefix(XmlNamespace xmlNamespace) {
        return xmlNamespace.getNameSpacePrefix();
    }

    /**
     * Gets the namespace URI from an XmlNamespace enum value.
     *
     * @param xmlNamespace The XML namespace.
     * @return Uri as string
     */
    public static String getNamespaceUri(XmlNamespace xmlNamespace) {
        return xmlNamespace.getNameSpaceUri();
    }

    /**
     * Gets the namespace from uri.
     *
     * @param namespaceUri the namespace uri
     * @return the namespace from uri
     */
    public static XmlNamespace getNamespaceFromUri(String namespaceUri) {
        if (EwsErrorsNamespace.equals(namespaceUri)) {
            return XmlNamespace.Errors;
        } else if (EwsTypesNamespace.equals(namespaceUri)) {
            return XmlNamespace.Types;
        } else if (EwsMessagesNamespace.equals(namespaceUri)) {
            return XmlNamespace.Messages;
        } else if (EwsSoapNamespace.equals(namespaceUri)) {
            return XmlNamespace.Soap;
        } else if (EwsSoap12Namespace.equals(namespaceUri)) {
            return XmlNamespace.Soap12;
        } else if (EwsXmlSchemaInstanceNamespace.equals(namespaceUri)) {
            return XmlNamespace.XmlSchemaInstance;
        } else if (PassportSoapFaultNamespace.equals(namespaceUri)) {
            return XmlNamespace.PassportSoapFault;
        } else if (WSTrustFebruary2005Namespace.equals(namespaceUri)) {
            return XmlNamespace.WSTrustFebruary2005;
        } else if (WSAddressingNamespace.equals(namespaceUri)) {
            return XmlNamespace.WSAddressing;
        } else {
            return XmlNamespace.NotSpecified;
        }
    }

    /**
     * Creates the ews object from xml element name.
     *
     * @param <TServiceObject> the generic type
     * @param itemClass        the item class
     * @param service          the service
     * @param xmlElementName   the xml element name
     * @return the t service object
     * @throws Exception the exception
     */
    @SuppressWarnings("unchecked")
    public static <TServiceObject extends ServiceObject> TServiceObject createEwsObjectFromXmlElementName(
            Class<?> itemClass, ExchangeService service, String xmlElementName) throws Exception {
        final ServiceObjectInfo member = EwsUtilities.SERVICE_OBJECT_INFO.getMember();
        final Map<String, Class<?>> map = member.getXmlElementNameToServiceObjectClassMap();

        final Class<?> ic = map.get(xmlElementName);
        if (ic != null) {
            final Map<Class<?>, ICreateServiceObjectWithServiceParam> serviceParam = member
                    .getServiceObjectConstructorsWithServiceParam();
            final ICreateServiceObjectWithServiceParam creationDelegate = serviceParam.get(ic);

            if (creationDelegate != null) {
                return (TServiceObject) creationDelegate.createServiceObjectWithServiceParam(service);
            } else {
                throw new IllegalArgumentException(
                        "No appropriate constructor could be found for this item class.");
            }
        }

        return (TServiceObject) itemClass.newInstance();
    }

    /**
     * Creates the item from item class.
     *
     * @param itemAttachment the item attachment
     * @param itemClass      the item class
     * @param isNew          the is new
     * @return the item
     * @throws Exception the exception
     */
    public static Item createItemFromItemClass(ItemAttachment itemAttachment, Class<?> itemClass, boolean isNew)
            throws Exception {
        final ServiceObjectInfo member = EwsUtilities.SERVICE_OBJECT_INFO.getMember();
        final Map<Class<?>, ICreateServiceObjectWithAttachmentParam> dataMap = member
                .getServiceObjectConstructorsWithAttachmentParam();
        final ICreateServiceObjectWithAttachmentParam creationDelegate = dataMap.get(itemClass);

        if (creationDelegate != null) {
            return (Item) creationDelegate.createServiceObjectWithAttachmentParam(itemAttachment, isNew);
        }
        throw new IllegalArgumentException("No appropriate constructor could be found for this item class.");
    }

    /**
     * Creates the item from xml element name.
     *
     * @param itemAttachment the item attachment
     * @param xmlElementName the xml element name
     * @return the item
     * @throws Exception the exception
     */
    public static Item createItemFromXmlElementName(ItemAttachment itemAttachment, String xmlElementName)
            throws Exception {
        final ServiceObjectInfo member = EwsUtilities.SERVICE_OBJECT_INFO.getMember();
        final Map<String, Class<?>> map = member.getXmlElementNameToServiceObjectClassMap();

        final Class<?> itemClass = map.get(xmlElementName);
        if (itemClass != null) {
            return createItemFromItemClass(itemAttachment, itemClass, false);
        }
        return null;
    }

    public static Class<?> getItemTypeFromXmlElementName(String xmlElementName) {
        final ServiceObjectInfo member = EwsUtilities.SERVICE_OBJECT_INFO.getMember();
        final Map<String, Class<?>> map = member.getXmlElementNameToServiceObjectClassMap();
        return map.get(xmlElementName);
    }

    /**
     * Finds the first item of type TItem (not a descendant type) in the
     * specified collection.
     *
     * @param <TItem> TItem is the type of the item to find.
     * @param cls     the cls
     * @param items   the item
     * @return A TItem instance or null if no instance of TItem could be found.
     */
    @SuppressWarnings("unchecked")
    public static <TItem extends Item> TItem findFirstItemOfType(Class<TItem> cls, Iterable<Item> items) {
        for (Item item : items) {
            // We're looking for an exact class match here.
            final Class<? extends Item> itemClass = item.getClass();
            if (itemClass.equals(cls)) {
                return (TItem) item;
            }
        }

        return null;
    }

    /**
     * Write trace start element.
     *
     * @param writer         the writer to write the start element to
     * @param traceTag       the trace tag
     * @param includeVersion if true, include build version attribute
     * @throws XMLStreamException the XML stream exception
     */
    private static void writeTraceStartElement(XMLStreamWriter writer, String traceTag, boolean includeVersion)
            throws XMLStreamException {
        writer.writeStartElement("Trace");
        writer.writeAttribute("Tag", traceTag);
        writer.writeAttribute("Tid", Thread.currentThread().getId() + "");
        Date d = new Date();
        DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss'Z'");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));
        String formattedString = df.format(d);
        writer.writeAttribute("Time", formattedString);

        if (includeVersion) {
            writer.writeAttribute("Version", EwsUtilities.getBuildVersion());
        }
    }

    /**
     * .
     *
     * @param entryKind the entry kind
     * @param logEntry  the log entry
     * @return the string
     * @throws XMLStreamException the XML stream exception
     * @throws IOException signals that an I/O exception has occurred.
     */
    public static String formatLogMessage(String entryKind, String logEntry)
            throws XMLStreamException, IOException {
        String lineSeparator = System.getProperty("line.separator");
        ByteArrayOutputStream outStream = new ByteArrayOutputStream();
        XMLOutputFactory factory = XMLOutputFactory.newInstance();
        XMLStreamWriter writer = factory.createXMLStreamWriter(outStream);
        EwsUtilities.writeTraceStartElement(writer, entryKind, false);
        writer.writeCharacters(lineSeparator);
        writer.writeCharacters(logEntry);
        writer.writeCharacters(lineSeparator);
        writer.writeEndElement();
        writer.writeCharacters(lineSeparator);
        writer.flush();
        writer.close();
        outStream.flush();
        String formattedLogMessage = outStream.toString();
        formattedLogMessage = formattedLogMessage.replaceAll("&apos;", "'");
        formattedLogMessage = formattedLogMessage.replaceAll("&quot;", "\"");
        formattedLogMessage = formattedLogMessage.replaceAll("&gt;", ">");
        formattedLogMessage = formattedLogMessage.replaceAll("&lt;", "<");
        formattedLogMessage = formattedLogMessage.replaceAll("&amp;", "&");
        outStream.close();
        return formattedLogMessage;
    }

    /**
     * Format http response headers.
     *
     * @param response the response
     * @return the string
     * @throws EWSHttpException the EWS http exception
     */
    public static String formatHttpResponseHeaders(HttpWebRequest response) throws EWSHttpException {
        final int code = response.getResponseCode();
        final String contentType = response.getResponseContentType();
        final Map<String, String> headers = response.getResponseHeaders();

        return code + " " + contentType + "\n" + EwsUtilities.formatHttpHeaders(headers) + "\n";
    }

    /**
     * Format request HTTP headers.
     *
     * @param request The HTTP request.
     */
    public static String formatHttpRequestHeaders(HttpWebRequest request)
            throws URISyntaxException, EWSHttpException {
        final String method = request.getRequestMethod().toUpperCase();
        final String path = request.getUrl().toURI().getPath();
        final Map<String, String> property = request.getRequestProperty();
        final String headers = EwsUtilities.formatHttpHeaders(property);

        return String.format("%s %s HTTP/%s\n", method, path, "1.1") + headers + "\n";
    }

    /**
     * Formats HTTP headers.
     *
     * @param headers The headers.
     * @return Headers as a string
     */
    private static String formatHttpHeaders(Map<String, String> headers) {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, String> header : headers.entrySet()) {
            sb.append(String.format("%s : %s\n", header.getKey(), header.getValue()));
        }
        return sb.toString();
    }

    /**
     * Format XML content in a MemoryStream for message.
     *
     * @param traceTypeStr Kind of the entry.
     * @param stream       The memory stream.
     * @return XML log entry as a string.
     */
    public static String formatLogMessageWithXmlContent(String traceTypeStr, ByteArrayOutputStream stream) {
        try {
            return formatLogMessage(traceTypeStr, stream.toString());
        } catch (Exception e) {
            return stream.toString();
        }
    }

    /**
     * Convert bool to XML Schema bool.
     *
     * @param value Bool value.
     * @return String representing bool value in XML Schema.
     */
    public static String boolToXSBool(Boolean value) {
        return value ? EwsUtilities.XSTrue : EwsUtilities.XSFalse;
    }

    /**
     * Parses an enum value list.
     *
     * @param <T>        the generic type
     * @param c          the c
     * @param list       the list
     * @param value      the value
     * @param separators the separators
     */
    public static <T extends Enum<?>> void parseEnumValueList(Class<T> c, List<T> list, String value,
            char... separators) {
        EwsUtilities.ewsAssert(c.isEnum(), "EwsUtilities.ParseEnumValueList", "T is not an enum type.");

        StringBuilder regexp = new StringBuilder();
        regexp.append("[");
        for (char s : separators) {
            regexp.append("[");
            regexp.append(Pattern.quote(s + ""));
            regexp.append("]");
        }
        regexp.append("]");

        String[] enumValues = value.split(regexp.toString());

        for (String enumValue : enumValues) {
            for (T o : c.getEnumConstants()) {
                if (o.toString().equals(enumValue)) {
                    list.add(o);
                }
            }
        }
    }

    /**
     * Converts an enum to a string, using the mapping dictionaries if
     * appropriate.
     *
     * @param value The enum value to be serialized
     * @return String representation of enum to be used in the protocol
     */
    public static String serializeEnum(Object value) {
        String strValue = value.toString();
        final Map<Class<?>, Map<String, String>> member = ENUM_TO_SCHEMA_DICTIONARIES.getMember();

        final Map<String, String> enumToStringDict = member.get(value.getClass());
        if (enumToStringDict != null) {
            final Enum<?> e = (Enum<?>) value;
            final String enumStr = enumToStringDict.get(e.name());
            if (enumStr != null) {
                strValue = enumStr;
            }
        }
        return strValue;
    }

    /**
     * Parses the.
     *
     * @param <T>   the generic type
     * @param cls   the cls
     * @param value the value
     * @return the t
     * @throws java.text.ParseException the parse exception
     */
    @SuppressWarnings("unchecked")
    public static <T> T parse(Class<T> cls, String value) throws ParseException {
        if (cls.isEnum()) {
            final Map<Class<?>, Map<String, String>> member = SCHEMA_TO_ENUM_DICTIONARIES.getMember();

            String val = value;
            final Map<String, String> stringToEnumDict = member.get(cls);
            if (stringToEnumDict != null) {
                final String strEnumName = stringToEnumDict.get(value);
                if (strEnumName != null) {
                    val = strEnumName;
                }
            }
            for (T o : cls.getEnumConstants()) {
                if (o.toString().equals(val)) {
                    return o;
                }
            }
            return null;
        } else if (Number.class.isAssignableFrom(cls)) {
            if (Double.class.isAssignableFrom(cls)) {
                return (T) ((Double) Double.parseDouble(value));
            } else if (Integer.class.isAssignableFrom(cls)) {
                return (T) ((Integer) Integer.parseInt(value));
            } else if (Long.class.isAssignableFrom(cls)) {
                return (T) ((Long) Long.parseLong(value));
            } else if (Float.class.isAssignableFrom(cls)) {
                return (T) ((Float) Float.parseFloat(value));
            } else if (Byte.class.isAssignableFrom(cls)) {
                return (T) ((Byte) Byte.parseByte(value));
            } else if (Short.class.isAssignableFrom(cls)) {
                return (T) ((Short) Short.parseShort(value));
            } else if (BigInteger.class.isAssignableFrom(cls)) {
                return (T) (new BigInteger(value));
            } else if (BigDecimal.class.isAssignableFrom(cls)) {
                return (T) (new BigDecimal(value));
            }
        } else if (Date.class.isAssignableFrom(cls)) {
            DateFormat df = createDateFormat(XML_SCHEMA_DATE_TIME_FORMAT);
            return (T) df.parse(value);
        } else if (Boolean.class.isAssignableFrom(cls)) {
            return (T) ((Boolean) Boolean.parseBoolean(value));
        } else if (String.class.isAssignableFrom(cls)) {
            return (T) value;
        }
        return null;
    }

    /**
     * Builds the schema to enum mapping dictionary.
     *
     * @param <E> Type of the enum.
     * @param c   Class
     * @return The mapping from enum to schema name
     */
    private static <E extends Enum<E>> Map<String, String> buildSchemaToEnumDict(Class<E> c) {
        Map<String, String> dict = new HashMap<String, String>();

        Field[] fields = c.getDeclaredFields();
        for (Field f : fields) {
            if (f.isEnumConstant() && f.isAnnotationPresent(EwsEnum.class)) {
                EwsEnum ewsEnum = f.getAnnotation(EwsEnum.class);
                String fieldName = f.getName();
                String schemaName = ewsEnum.schemaName();
                if (!schemaName.isEmpty()) {
                    dict.put(schemaName, fieldName);
                }
            }
        }
        return dict;
    }

    /**
     * Validate param collection.
     *
     * @param eventTypes the event types
     * @param paramName  the param name
     * @throws Exception the exception
     */
    public static void validateParamCollection(EventType[] eventTypes, String paramName) throws Exception {
        validateParam(eventTypes, paramName);
        int count = 0;

        for (EventType event : eventTypes) {
            try {
                validateParam(event, String.format("collection[%d] , ", count));
            } catch (Exception e) {
                throw new IllegalArgumentException(String.format("The element at position %d is invalid", count),
                        e);
            }
            count++;
        }

        if (count == 0) {
            throw new IllegalArgumentException(String.format("The collection \"%s\" is empty.", paramName));
        }
    }

    /**
     * Convert DateTime to XML Schema date.
     *
     * @param date the date
     * @return String representation of DateTime.
     */
    public static String dateTimeToXSDate(Date date) {
        return formatDate(date, XML_SCHEMA_DATE_FORMAT);
    }

    /**
     * Dates the DateTime into an XML schema date time.
     *
     * @param date the date
     * @return String representation of DateTime.
     */
    public static String dateTimeToXSDateTime(Date date) {
        return formatDate(date, XML_SCHEMA_DATE_TIME_FORMAT);
    }

    /**
     * Takes a System.TimeSpan structure and converts it into an xs:duration
     * string as defined by the W3 Consortiums Recommendation
     * "XML Schema Part 2: Datatypes Second Edition",
     * http://www.w3.org/TR/xmlschema-2/#duration
     *
     * @param timeOffset structure to convert
     * @return xs:duration formatted string
     */
    public static String getTimeSpanToXSDuration(TimeSpan timeOffset) {
        // Optional '-' offset
        String offsetStr = (timeOffset.getTotalSeconds() < 0) ? "-" : "";
        long days = Math.abs(timeOffset.getDays());
        long hours = Math.abs(timeOffset.getHours());
        long minutes = Math.abs(timeOffset.getMinutes());
        long seconds = Math.abs(timeOffset.getSeconds());
        long milliseconds = Math.abs(timeOffset.getMilliseconds());

        // The TimeSpan structure does not have a Year or Month
        // property, therefore we wouldn't be able to return an xs:duration
        // string from a TimeSpan that included the nY or nM components.
        return offsetStr + "P" + days + "DT" + hours + "H" + minutes + "M" + seconds + "." + milliseconds + "S";
    }

    /**
     * Takes an xs:duration string as defined by the W3 Consortiums
     * Recommendation "XML Schema Part 2: Datatypes Second Edition",
     * http://www.w3.org/TR/xmlschema-2/#duration, and converts it into a
     * System.TimeSpan structure This method uses the following approximations:
     * 1 year = 365 days 1 month = 30 days Additionally, it only allows for four
     * decimal points of seconds precision.
     *
     * @param xsDuration xs:duration string to convert
     * @return System.TimeSpan structure
     */
    public static TimeSpan getXSDurationToTimeSpan(String xsDuration) {
        // TODO: Need to check whether this should be the equivalent or not
        Matcher m = PATTERN_TIME_SPAN.matcher(xsDuration);
        boolean negative = false;
        if (m.find()) {
            negative = true;
        }

        // Removing leading '-'
        if (negative) {
            xsDuration = xsDuration.replace("-P", "P");
        }

        Period period = Period.parse(xsDuration, ISOPeriodFormat.standard());

        long retval = period.toStandardDuration().getMillis();

        if (negative) {
            retval = -retval;
        }

        return new TimeSpan(retval);

    }

    /**
     * Time span to xs time.
     *
     * @param timeSpan the time span
     * @return the string
     */
    public static String timeSpanToXSTime(TimeSpan timeSpan) {
        DecimalFormat myFormatter = new DecimalFormat("00");
        return String.format("%s:%s:%s", myFormatter.format(timeSpan.getHours()),
                myFormatter.format(timeSpan.getMinutes()), myFormatter.format(timeSpan.getSeconds()));
    }

    /**
     * Gets the domain name from an email address.
     *
     * @param emailAddress The email address.
     * @return Domain name.
     * @throws FormatException the format exception
     */
    public static String domainFromEmailAddress(String emailAddress) throws FormatException {
        String[] emailAddressParts = emailAddress.split("@");

        if (emailAddressParts.length != 2 || (emailAddressParts[1] == null || emailAddressParts[1].isEmpty())) {
            throw new FormatException("The e-mail address is formed incorrectly.");
        }

        return emailAddressParts[1];
    }

    public static int getDim(Object array) {
        int dim = 0;
        Class<?> c = array.getClass();
        while (c.isArray()) {
            c = c.getComponentType();
            dim++;
        }
        return (dim);
    }

    /**
     * Validates parameter (and allows null value).
     *
     * @param param     The param.
     * @param paramName Name of the param.
     * @throws Exception the exception
     */
    public static void validateParamAllowNull(Object param, String paramName) throws Exception {
        if (param instanceof ISelfValidate) {
            ISelfValidate selfValidate = (ISelfValidate) param;
            try {
                selfValidate.validate();
            } catch (ServiceValidationException e) {
                throw new Exception(String.format("%s %s", "Validation failed.", paramName), e);
            }
        }

        if (param instanceof ServiceObject) {
            ServiceObject ewsObject = (ServiceObject) param;
            if (ewsObject.isNew()) {
                throw new Exception(String.format("%s %s", "This service object doesn't have an ID.", paramName));
            }
        }
    }

    /**
     * Validates parameter (null value not allowed).
     *
     * @param param     The param.
     * @param paramName Name of the param.
     * @throws Exception the exception
     */
    public static void validateParam(Object param, String paramName) throws Exception {
        boolean isValid;

        if (param instanceof String) {
            String strParam = (String) param;
            isValid = !strParam.isEmpty();
        } else {
            isValid = param != null;
        }

        if (!isValid) {
            throw new Exception(String.format("Argument %s not valid", paramName));
        }
        validateParamAllowNull(param, paramName);
    }

    /**
     * Validates parameter collection.
     *
     * @param <T>        the generic type
     * @param collection The collection.
     * @param paramName  Name of the param.
     * @throws Exception the exception
     */
    public static <T> void validateParamCollection(Iterator<T> collection, String paramName) throws Exception {
        validateParam(collection, paramName);
        int count = 0;

        while (collection.hasNext()) {
            T obj = collection.next();
            try {
                validateParam(obj, String.format("collection[%d],", count));
            } catch (Exception e) {
                throw new IllegalArgumentException(String.format("The element at position %d is invalid", count),
                        e);
            }
            count++;
        }

        if (count == 0) {
            throw new IllegalArgumentException(String.format("The collection \"%s\" is empty.", paramName));
        }
    }

    /**
     * Validates string parameter to be non-empty string (null value allowed).
     *
     * @param param     The string parameter.
     * @param paramName Name of the parameter.
     * @throws ArgumentException
     * @throws ServiceLocalException
     */
    public static void validateNonBlankStringParamAllowNull(String param, String paramName)
            throws ArgumentException, ServiceLocalException {
        if (param != null) {
            // Non-empty string has at least one character
            //which is *not* a whitespace character
            if (param.length() == countMatchingChars(param, new IPredicate<Character>() {
                @Override
                public boolean predicate(Character obj) {
                    return Character.isWhitespace(obj);
                }
            })) {
                throw new ArgumentException("The string argument contains only white space characters.", paramName);
            }
        }
    }

    /**
     * Validates string parameter to be
     * non-empty string (null value not allowed).
     *
     * @param param     The string parameter.
     * @param paramName Name of the parameter.
     * @throws ArgumentNullException
     * @throws ArgumentException
     * @throws ServiceLocalException
     */
    public static void validateNonBlankStringParam(String param, String paramName)
            throws ArgumentNullException, ArgumentException, ServiceLocalException {
        if (param == null) {
            throw new ArgumentNullException(paramName);
        }

        validateNonBlankStringParamAllowNull(param, paramName);
    }

    /**
     * Validate enum version value.
     *
     * @param enumValue      the enum value
     * @param requestVersion the request version
     * @throws ServiceVersionException the service version exception
     */
    public static void validateEnumVersionValue(Enum<?> enumValue, ExchangeVersion requestVersion)
            throws ServiceVersionException {
        final Map<Class<?>, Map<String, ExchangeVersion>> member = ENUM_VERSION_DICTIONARIES.getMember();
        final Map<String, ExchangeVersion> enumVersionDict = member.get(enumValue.getClass());

        final ExchangeVersion enumVersion = enumVersionDict.get(enumValue.toString());
        if (enumVersion != null) {
            final int i = requestVersion.compareTo(enumVersion);
            if (i < 0) {
                throw new ServiceVersionException(String.format(
                        "Enumeration value %s in enumeration type %s is only valid for Exchange version %s or later.",
                        enumValue.toString(), enumValue.getClass().getName(), enumVersion));
            }
        }
    }

    /**
     * Validates service object version against the request version.
     *
     * @param serviceObject  The service object.
     * @param requestVersion The request version.
     * @throws ServiceVersionException Raised if this service object type requires a later version
     *                                 of Exchange.
     */
    public static void validateServiceObjectVersion(ServiceObject serviceObject, ExchangeVersion requestVersion)
            throws ServiceVersionException {
        ExchangeVersion minimumRequiredServerVersion = serviceObject.getMinimumRequiredServerVersion();

        if (requestVersion.ordinal() < minimumRequiredServerVersion.ordinal()) {
            String msg = String.format(
                    "The object type %s is only valid for Exchange Server version %s or later versions.",
                    serviceObject.getClass().getName(), minimumRequiredServerVersion.toString());
            throw new ServiceVersionException(msg);
        }
    }

    /**
     * Validates property version against the request version.
     *
     * @param service              The Exchange service.
     * @param minimumServerVersion The minimum server version
     * @param propertyName         The property name
     * @throws ServiceVersionException The service version exception
     */
    public static void validatePropertyVersion(ExchangeService service, ExchangeVersion minimumServerVersion,
            String propertyName) throws ServiceVersionException {
        if (service.getRequestedServerVersion().ordinal() < minimumServerVersion.ordinal()) {
            throw new ServiceVersionException(
                    String.format("The property %s is valid only for Exchange %s or later versions.", propertyName,
                            minimumServerVersion));
        }
    }

    /**
     * Validate method version.
     *
     * @param service              the service
     * @param minimumServerVersion the minimum server version
     * @param methodName           the method name
     * @throws ServiceVersionException the service version exception
     */
    public static void validateMethodVersion(ExchangeService service, ExchangeVersion minimumServerVersion,
            String methodName) throws ServiceVersionException {
        if (service.getRequestedServerVersion().ordinal() < minimumServerVersion.ordinal())

        {
            throw new ServiceVersionException(
                    String.format("Method %s is only valid for Exchange Server version %s or later.", methodName,
                            minimumServerVersion));
        }
    }

    /**
     * Validates class version against the request version.
     *
     * @param service              the service
     * @param minimumServerVersion The minimum server version that supports the method.
     * @param className            Name of the class.
     * @throws ServiceVersionException
     */
    public static void validateClassVersion(ExchangeService service, ExchangeVersion minimumServerVersion,
            String className) throws ServiceVersionException {
        if (service.getRequestedServerVersion().ordinal() < minimumServerVersion.ordinal()) {
            throw new ServiceVersionException(String.format(
                    "Class %s is only valid for Exchange version %s or later.", className, minimumServerVersion));
        }
    }

    /**
     * Validates domain name (null value allowed)
     *
     * @param domainName Domain name.
     * @param paramName  Parameter name.
     * @throws ArgumentException
     */
    public static void validateDomainNameAllowNull(String domainName, String paramName) throws ArgumentException {
        if (domainName != null) {
            Pattern domainNamePattern = Pattern.compile(DomainRegex);
            Matcher domainNameMatcher = domainNamePattern.matcher(domainName);
            if (!domainNameMatcher.find()) {
                throw new ArgumentException(String.format("'%s' is not a valid domain name.", domainName),
                        paramName);
            }
        }
    }

    /**
     * Builds the enum dict.
     *
     * @param <E> the element type
     * @param c   the c
     * @return the map
     */
    private static <E extends Enum<E>> Map<String, ExchangeVersion> buildEnumDict(Class<E> c) {
        Map<String, ExchangeVersion> dict = new HashMap<String, ExchangeVersion>();
        Field[] fields = c.getDeclaredFields();
        for (Field f : fields) {
            if (f.isEnumConstant() && f.isAnnotationPresent(RequiredServerVersion.class)) {
                RequiredServerVersion ewsEnum = f.getAnnotation(RequiredServerVersion.class);
                String fieldName = f.getName();
                ExchangeVersion exchangeVersion = ewsEnum.version();
                dict.put(fieldName, exchangeVersion);
            }
        }
        return dict;
    }

    /**
     * Builds the enum to schema mapping dictionary.
     *
     * @param c class type
     * @return The mapping from enum to schema name
     */
    private static Map<String, String> buildEnumToSchemaDict(Class<?> c) {
        Map<String, String> dict = new HashMap<String, String>();
        Field[] fields = c.getFields();
        for (Field f : fields) {
            if (f.isEnumConstant() && f.isAnnotationPresent(EwsEnum.class)) {
                EwsEnum ewsEnum = f.getAnnotation(EwsEnum.class);
                String fieldName = f.getName();
                String schemaName = ewsEnum.schemaName();
                if (!schemaName.isEmpty()) {
                    dict.put(fieldName, schemaName);
                }
            }
        }
        return dict;
    }

    /**
     * Gets the enumerated object count.
     *
     * @param <T>     the generic type
     * @param objects The objects.
     * @return Count of objects in iterator.
     */
    public static <T> int getEnumeratedObjectCount(Iterator<T> objects) {
        int count = 0;
        while (objects != null && objects.hasNext()) {
            objects.next();
            count++;
        }
        return count;
    }

    /**
     * Gets the enumerated object at.
     *
     * @param <T>     the generic type
     * @param objects the objects
     * @param index   the index
     * @return the enumerated object at
     */
    public static <T> Object getEnumeratedObjectAt(Iterable<T> objects, int index) {
        int count = 0;
        for (Object obj : objects) {
            if (count == index) {
                return obj;
            }
            count++;
        }
        throw new IndexOutOfBoundsException("The IEnumerable doesn't contain that many objects.");
    }

    /**
     * Count characters in string that match a condition.
     *
     * @param str           The string.
     * @param charPredicate Predicate to evaluate for each character in the string.
     * @return Count of characters that match condition expressed by predicate.
     * @throws ServiceLocalException
     */
    public static int countMatchingChars(String str, IPredicate<Character> charPredicate)
            throws ServiceLocalException {
        int count = 0;
        for (int i = 0; i < str.length(); i++) {
            if (charPredicate.predicate(str.charAt(i))) {
                count++;
            }
        }
        return count;
    }

    /**
     * Determines whether every element in the collection
     * matches the conditions defined by the specified predicate.
     *
     * @param <T>        Entry type.
     * @param collection The collection.
     * @param predicate  Predicate that defines the conditions to check against the elements.
     * @return True if every element in the collection matches
     * the conditions defined by the specified predicate; otherwise, false.
     * @throws ServiceLocalException
     */
    public static <T> boolean trueForAll(Iterable<T> collection, IPredicate<T> predicate)
            throws ServiceLocalException {
        for (T entry : collection) {
            if (!predicate.predicate(entry)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Call an action for each member of a collection.
     *
     * @param <T>        Collection element type.
     * @param collection The collection.
     * @param action     The action to apply.
     */
    public static <T> void forEach(Iterable<T> collection, IAction<T> action) {
        for (T entry : collection) {
            action.action(entry);
        }
    }

    private static String formatDate(Date date, String format) {
        final DateFormat utcFormatter = createDateFormat(format);
        return utcFormatter.format(date);
    }

    private static DateFormat createDateFormat(String format) {
        final DateFormat utcFormatter = new SimpleDateFormat(format);
        utcFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
        return utcFormatter;
    }

}