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.property.complex;
025
026import microsoft.exchange.webservices.data.attribute.EditorBrowsable;
027import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
028import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
029import microsoft.exchange.webservices.data.core.EwsUtilities;
030import microsoft.exchange.webservices.data.core.XmlAttributeNames;
031import microsoft.exchange.webservices.data.core.XmlElementNames;
032import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState;
033import microsoft.exchange.webservices.data.core.enumeration.property.UserConfigurationDictionaryObjectType;
034import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
035import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
036import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
037import microsoft.exchange.webservices.data.misc.OutParam;
038import microsoft.exchange.webservices.data.util.DateTimeUtils;
039import org.apache.commons.codec.binary.Base64;
040
041import javax.xml.stream.XMLStreamException;
042
043import java.lang.reflect.Array;
044import java.util.ArrayList;
045import java.util.Date;
046import java.util.HashMap;
047import java.util.Iterator;
048import java.util.List;
049import java.util.Map;
050import java.util.Map.Entry;
051
052/**
053 * Represents a user configuration's Dictionary property.
054 */
055@EditorBrowsable(state = EditorBrowsableState.Never)
056public final class UserConfigurationDictionary extends ComplexProperty
057    implements Iterable<Object> {
058
059  // TODO: Consider implementing IsDirty mechanism in ComplexProperty.
060
061  /**
062   * The dictionary.
063   */
064  private Map<Object, Object> dictionary;
065
066  /**
067   * The is dirty.
068   */
069  private boolean isDirty = false;
070
071  /**
072   * Initializes a new instance of "UserConfigurationDictionary" class.
073   */
074  public UserConfigurationDictionary() {
075    super();
076    this.dictionary = new HashMap<Object, Object>();
077  }
078
079  /**
080   * Gets the element with the specified key.
081   *
082   * @param key The key of the element to get or set.
083   * @return The element with the specified key.
084   */
085  public Object getElements(Object key) {
086    return this.dictionary.get(key);
087  }
088
089  /**
090   * Sets the element with the specified key.
091   *
092   * @param key   The key of the element to get or set
093   * @param value the value
094   * @throws Exception the exception
095   */
096  public void setElements(Object key, Object value) throws Exception {
097    this.validateEntry(key, value);
098    this.dictionary.put(key, value);
099    this.changed();
100  }
101
102  /**
103   * Adds an element with the provided key and value to the user configuration
104   * dictionary.
105   *
106   * @param key   The object to use as the key of the element to add.
107   * @param value The object to use as the value of the element to add.
108   * @throws Exception the exception
109   */
110  public void addElement(Object key, Object value) throws Exception {
111    this.validateEntry(key, value);
112    this.dictionary.put(key, value);
113    this.changed();
114  }
115
116  /**
117   * Determines whether the user configuration dictionary contains an element
118   * with the specified key.
119   *
120   * @param key The key to locate in the user configuration dictionary.
121   * @return true if the user configuration dictionary contains an element
122   * with the key; otherwise false.
123   */
124  public boolean containsKey(Object key) {
125    return this.dictionary.containsKey(key);
126  }
127
128  /**
129   * Removes the element with the specified key from the user configuration
130   * dictionary.
131   *
132   * @param key The key of the element to remove.
133   * @return true if the element is successfully removed; otherwise false.
134   */
135  public boolean remove(Object key) {
136    boolean isRemoved = false;
137    if (key != null) {
138      this.dictionary.remove(key);
139      isRemoved = true;
140    }
141
142    if (isRemoved) {
143      this.changed();
144    }
145
146    return isRemoved;
147  }
148
149  /**
150   * Gets the value associated with the specified key.
151   *
152   * @param key   The key whose value to get.
153   * @param value When this method returns, the value associated with the
154   *              specified key, if the key is found; otherwise, null.
155   * @return true if the user configuration dictionary contains the key;
156   * otherwise false.
157   */
158  public boolean tryGetValue(Object key, OutParam<Object> value) {
159    if (this.dictionary.containsKey(key)) {
160      value.setParam(this.dictionary.get(key));
161      return true;
162    } else {
163      value.setParam(null);
164      return false;
165    }
166
167  }
168
169  /**
170   * Gets the number of elements in the user configuration dictionary.
171   *
172   * @return the count
173   */
174  public int getCount() {
175    return this.dictionary.size();
176  }
177
178  /**
179   * Removes all item from the user configuration dictionary.
180   */
181  public void clear() {
182    if (this.dictionary.size() != 0) {
183      this.dictionary.clear();
184
185      this.changed();
186    }
187  }
188
189  /**
190   * Gets the enumerator.
191   *
192   * @return the enumerator
193   */
194
195  /**
196   * Returns an enumerator that iterates through
197   * the user configuration dictionary.
198   *
199   * @return An IEnumerator that can be used
200   * to iterate through the user configuration dictionary.
201   */
202  public Iterator<Object> getEnumerator() {
203    return (this.dictionary.values().iterator());
204  }
205
206  /**
207   * Gets the isDirty flag.
208   *
209   * @return the checks if is dirty
210   */
211  public boolean getIsDirty() {
212    return this.isDirty;
213  }
214
215  /**
216   * Sets the isDirty flag.
217   *
218   * @param value the new checks if is dirty
219   */
220  public void setIsDirty(boolean value) {
221    this.isDirty = value;
222  }
223
224  /**
225   * Instance was changed.
226   */
227  @Override public void changed() {
228    super.changed();
229    this.isDirty = true;
230  }
231
232  /**
233   * Writes elements to XML.
234   *
235   * @param writer accepts EwsServiceXmlWriter
236   * @throws XMLStreamException the XML stream exception
237   * @throws ServiceXmlSerializationException the service xml serialization exception
238   */
239  @Override
240  public void writeElementsToXml(EwsServiceXmlWriter writer)
241      throws XMLStreamException, ServiceXmlSerializationException {
242    EwsUtilities.ewsAssert(writer != null, "UserConfigurationDictionary.WriteElementsToXml", "writer is null");
243    Iterator<Entry<Object, Object>> it = this.dictionary.entrySet()
244        .iterator();
245    while (it.hasNext()) {
246      Entry<Object, Object> dictionaryEntry = it.next();
247      writer.writeStartElement(XmlNamespace.Types,
248          XmlElementNames.DictionaryEntry);
249      this.writeObjectToXml(writer, XmlElementNames.DictionaryKey,
250          dictionaryEntry.getKey());
251      this.writeObjectToXml(writer, XmlElementNames.DictionaryValue,
252          dictionaryEntry.getValue());
253      writer.writeEndElement();
254    }
255  }
256
257  /**
258   * Writes a dictionary object (key or value) to Xml.
259   *
260   * @param writer           the writer
261   * @param xmlElementName   the Xml element name
262   * @param dictionaryObject the object to write
263   * @throws XMLStreamException the XML stream exception
264   * @throws ServiceXmlSerializationException the service xml serialization exception
265   */
266  private void writeObjectToXml(EwsServiceXmlWriter writer,
267      String xmlElementName, Object dictionaryObject)
268      throws XMLStreamException, ServiceXmlSerializationException {
269    EwsUtilities.ewsAssert(writer != null, "UserConfigurationDictionary.WriteObjectToXml", "writer is null");
270    EwsUtilities.ewsAssert(xmlElementName != null, "UserConfigurationDictionary.WriteObjectToXml",
271                           "xmlElementName is null");
272    writer.writeStartElement(XmlNamespace.Types, xmlElementName);
273
274    if (dictionaryObject == null) {
275      EwsUtilities.ewsAssert((!xmlElementName.equals(XmlElementNames.DictionaryKey)),
276                             "UserConfigurationDictionary.WriteObjectToXml", "Key is null");
277
278      writer.writeAttributeValue(
279          EwsUtilities.EwsXmlSchemaInstanceNamespacePrefix,
280          XmlAttributeNames.Nil, EwsUtilities.XSTrue);
281    } else {
282      this.writeObjectValueToXml(writer, dictionaryObject);
283    }
284
285    writer.writeEndElement();
286  }
287
288  /**
289   * Writes a dictionary Object's value to Xml.
290   *
291   * @param writer           The writer.
292   * @param dictionaryObject The dictionary object to write. <br />
293   *                         Object values are either:  <br />
294   *                         an array of strings, an array of bytes (which will be encoded into base64) <br />
295   *                         or a single value. Single values can be: <br />
296   *                         - datetime, boolean, byte, int, long, string
297   * @throws XMLStreamException the XML stream exception
298   * @throws ServiceXmlSerializationException the service xml serialization exception
299   */
300  private void writeObjectValueToXml(final EwsServiceXmlWriter writer,
301      final Object dictionaryObject) throws XMLStreamException,
302      ServiceXmlSerializationException {
303    // Preconditions
304    if (dictionaryObject == null) {
305      throw new NullPointerException("DictionaryObject must not be null");
306    }
307    if (writer == null) {
308      throw new NullPointerException(
309          "EwsServiceXmlWriter must not be null");
310    }
311
312    // Processing
313    final UserConfigurationDictionaryObjectType dictionaryObjectType;
314    if (dictionaryObject instanceof String[]) {
315      dictionaryObjectType = UserConfigurationDictionaryObjectType.StringArray;
316      this.writeEntryTypeToXml(writer, dictionaryObjectType);
317
318      for (String arrayElement : (String[]) dictionaryObject) {
319        this.writeEntryValueToXml(writer, arrayElement);
320      }
321    } else {
322      final String valueAsString;
323      if (dictionaryObject instanceof String) {
324        dictionaryObjectType = UserConfigurationDictionaryObjectType.String;
325        valueAsString = String.valueOf(dictionaryObject);
326      } else if (dictionaryObject instanceof Boolean) {
327        dictionaryObjectType = UserConfigurationDictionaryObjectType.Boolean;
328        valueAsString = EwsUtilities
329            .boolToXSBool((Boolean) dictionaryObject);
330      } else if (dictionaryObject instanceof Byte) {
331        dictionaryObjectType = UserConfigurationDictionaryObjectType.Byte;
332        valueAsString = String.valueOf(dictionaryObject);
333      } else if (dictionaryObject instanceof Date) {
334        dictionaryObjectType = UserConfigurationDictionaryObjectType.DateTime;
335        valueAsString = writer.getService()
336            .convertDateTimeToUniversalDateTimeString(
337                (Date) dictionaryObject);
338      } else if (dictionaryObject instanceof Integer) {
339        // removed unsigned integer because in Java, all types are
340        // signed, there are no unsigned versions
341        dictionaryObjectType = UserConfigurationDictionaryObjectType.Integer32;
342        valueAsString = String.valueOf(dictionaryObject);
343      } else if (dictionaryObject instanceof Long) {
344        // removed unsigned integer because in Java, all types are
345        // signed, there are no unsigned versions
346        dictionaryObjectType = UserConfigurationDictionaryObjectType.Integer64;
347        valueAsString = String.valueOf(dictionaryObject);
348      } else if (dictionaryObject instanceof byte[]) {
349        dictionaryObjectType = UserConfigurationDictionaryObjectType.ByteArray;
350        valueAsString = Base64.encodeBase64String((byte[]) dictionaryObject);
351      } else if (dictionaryObject instanceof Byte[]) {
352        dictionaryObjectType = UserConfigurationDictionaryObjectType.ByteArray;
353
354        // cast Byte[] to byte[]
355        Byte[] from = (Byte[]) dictionaryObject;
356        byte[] to = new byte[from.length];
357        for (int currentIndex = 0; currentIndex < from.length; currentIndex++) {
358          to[currentIndex] = (byte) from[currentIndex];
359        }
360
361        valueAsString = Base64.encodeBase64String(to);
362      } else {
363        throw new IllegalArgumentException(String.format(
364            "Unsupported type: %s", dictionaryObject.getClass()
365                .toString()));
366      }
367      this.writeEntryTypeToXml(writer, dictionaryObjectType);
368      this.writeEntryValueToXml(writer, valueAsString);
369    }
370  }
371
372
373  /**
374   * Writes a dictionary entry type to Xml.
375   *
376   * @param writer               the writer
377   * @param dictionaryObjectType type to write
378   * @throws XMLStreamException the XML stream exception
379   * @throws ServiceXmlSerializationException the service xml serialization exception
380   */
381  private void writeEntryTypeToXml(EwsServiceXmlWriter writer,
382      UserConfigurationDictionaryObjectType dictionaryObjectType)
383      throws XMLStreamException, ServiceXmlSerializationException {
384    writer.writeStartElement(XmlNamespace.Types, XmlElementNames.Type);
385    writer
386        .writeValue(dictionaryObjectType.toString(),
387            XmlElementNames.Type);
388    writer.writeEndElement();
389  }
390
391  /**
392   * Writes a dictionary entry value to Xml.
393   *
394   * @param writer the writer
395   * @param value  value to write
396   * @throws XMLStreamException the XML stream exception
397   * @throws ServiceXmlSerializationException the service xml serialization exception
398   */
399  private void writeEntryValueToXml(EwsServiceXmlWriter writer, String value)
400      throws XMLStreamException, ServiceXmlSerializationException {
401    writer.writeStartElement(XmlNamespace.Types, XmlElementNames.Value);
402
403    // While an entry value can't be null, if the entry is an array, an
404    // element of the array can be null.
405    if (value != null) {
406      writer.writeValue(value, XmlElementNames.Value);
407    }
408
409    writer.writeEndElement();
410  }
411
412  /*
413   * (non-Javadoc)
414   *
415   * @see
416   * microsoft.exchange.webservices.ComplexProperty#loadFromXml(microsoft.
417   * exchange.webservices.EwsServiceXmlReader,
418   * microsoft.exchange.webservices.XmlNamespace, java.lang.String)
419   */
420  @Override
421  /**
422   * Loads this dictionary from the specified reader.
423   * @param reader The reader.
424   * @param xmlNamespace The dictionary's XML namespace.
425   * @param xmlElementName Name of the XML element
426   * representing the dictionary.
427   */ public void loadFromXml(EwsServiceXmlReader reader, XmlNamespace xmlNamespace, String xmlElementName) throws Exception {
428    super.loadFromXml(reader, xmlNamespace, xmlElementName);
429
430    this.isDirty = false;
431  }
432
433  /*
434   * (non-Javadoc)
435   *
436   * @see
437   * microsoft.exchange.webservices.ComplexProperty#tryReadElementFromXml(
438   * microsoft.exchange.webservices.EwsServiceXmlReader)
439   */
440  @Override
441  /**
442   * Tries to read element from XML.
443   * @param reader The reader.
444   * @return True if element was read.
445   */
446  public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
447      throws Exception {
448    reader.ensureCurrentNodeIsStartElement(this.getNamespace(),
449        XmlElementNames.DictionaryEntry);
450    this.loadEntry(reader);
451    return true;
452  }
453
454  /**
455   * Loads an entry, consisting of a key value pair, into this dictionary from
456   * the specified reader.
457   *
458   * @param reader The reader.
459   * @throws Exception the exception
460   */
461  private void loadEntry(EwsServiceXmlReader reader) throws Exception {
462    EwsUtilities.ewsAssert(reader != null, "UserConfigurationDictionary.LoadEntry", "reader is null");
463
464    Object key;
465    Object value = null;
466
467    // Position at DictionaryKey
468    reader.readStartElement(this.getNamespace(),
469        XmlElementNames.DictionaryKey);
470
471    key = this.getDictionaryObject(reader);
472
473    // Position at DictionaryValue
474    reader.readStartElement(this.getNamespace(),
475        XmlElementNames.DictionaryValue);
476
477    String nil = reader.readAttributeValue(XmlNamespace.XmlSchemaInstance,
478        XmlAttributeNames.Nil);
479    boolean hasValue = (nil == null)
480        || (!nil.getClass().equals(Boolean.TYPE));
481    if (hasValue) {
482      value = this.getDictionaryObject(reader);
483    }
484    this.dictionary.put(key, value);
485  }
486
487  /**
488   * Extracts a dictionary object (key or entry value) from the specified
489   * reader.
490   *
491   * @param reader The reader.
492   * @return Dictionary object.
493   * @throws Exception the exception
494   */
495  private Object getDictionaryObject(EwsServiceXmlReader reader)
496      throws Exception {
497    EwsUtilities.ewsAssert(reader != null, "UserConfigurationDictionary.loadFromXml", "reader is null");
498    UserConfigurationDictionaryObjectType type = this.getObjectType(reader);
499    List<String> values = this.getObjectValue(reader, type);
500    return this.constructObject(type, values, reader);
501  }
502
503  /**
504   * Extracts a dictionary object (key or entry value) as a string list from
505   * the specified reader.
506   *
507   * @param reader The reader.
508   * @param type   The object type.
509   * @return String list representing a dictionary object.
510   * @throws Exception the exception
511   */
512  private List<String> getObjectValue(EwsServiceXmlReader reader,
513      UserConfigurationDictionaryObjectType type) throws Exception {
514    EwsUtilities.ewsAssert(reader != null, "UserConfigurationDictionary.loadFromXml", "reader is null");
515
516    List<String> values = new ArrayList<String>();
517
518    reader.readStartElement(this.getNamespace(), XmlElementNames.Value);
519
520    do {
521      String value = null;
522
523      if (reader.isEmptyElement()) {
524        // Only string types can be represented with empty values.
525        if (type.equals(UserConfigurationDictionaryObjectType.String)
526            || type
527            .equals(UserConfigurationDictionaryObjectType.
528                StringArray)) {
529          value = "";
530        } else {
531          EwsUtilities
532              .ewsAssert(false, "UserConfigurationDictionary." + "GetObjectValue",
533                         "Empty element passed for type: " + type.toString());
534
535        }
536
537      } else {
538        value = reader.readElementValue();
539      }
540
541      values.add(value);
542      reader.read(); // Position at next element or
543      // DictionaryKey/DictionaryValue end element
544    } while (reader.isStartElement(this.getNamespace(),
545        XmlElementNames.Value));
546    return values;
547  }
548
549  /**
550   * Extracts the dictionary object (key or entry value) type from the
551   * specified reader.
552   *
553   * @param reader The reader.
554   * @return Dictionary object type.
555   * @throws Exception the exception
556   */
557  private UserConfigurationDictionaryObjectType getObjectType(
558      EwsServiceXmlReader reader) throws Exception {
559    EwsUtilities.ewsAssert(reader != null, "UserConfigurationDictionary.loadFromXml", "reader is null");
560
561    reader.readStartElement(this.getNamespace(), XmlElementNames.Type);
562
563    String type = reader.readElementValue();
564    return UserConfigurationDictionaryObjectType.valueOf(type);
565  }
566
567  /**
568   * Constructs a dictionary object (key or entry value) from the specified
569   * type and string list.
570   *
571   * @param type   Object type to construct.
572   * @param value  Value of the dictionary object as a string list
573   * @param reader The reader.
574   * @return Dictionary object.
575   */
576  private Object constructObject(UserConfigurationDictionaryObjectType type,
577      List<String> value, EwsServiceXmlReader reader) {
578    EwsUtilities.ewsAssert(value != null, "UserConfigurationDictionary.ConstructObject", "value is null");
579    EwsUtilities
580        .ewsAssert((value.size() == 1 || type == UserConfigurationDictionaryObjectType.StringArray),
581
582                   "UserConfigurationDictionary.ConstructObject",
583                   "value is array but type is not StringArray");
584    EwsUtilities
585        .ewsAssert(reader != null, "UserConfigurationDictionary.ConstructObject", "reader is null");
586
587    Object dictionaryObject = null;
588    if (type.equals(UserConfigurationDictionaryObjectType.Boolean)) {
589      dictionaryObject = Boolean.parseBoolean(value.get(0));
590    } else if (type.equals(UserConfigurationDictionaryObjectType.Byte)) {
591      dictionaryObject = Byte.parseByte(value.get(0));
592    } else if (type.equals(UserConfigurationDictionaryObjectType.ByteArray)) {
593      dictionaryObject = Base64.decodeBase64(value.get(0));
594    } else if (type.equals(UserConfigurationDictionaryObjectType.DateTime)) {
595      Date dateTime = DateTimeUtils.convertDateTimeStringToDate(value.get(0));
596      if (dateTime != null) {
597        dictionaryObject = dateTime;
598      } else {
599        EwsUtilities.ewsAssert(false, "UserConfigurationDictionary.ConstructObject", "DateTime is null");
600      }
601    } else if (type.equals(UserConfigurationDictionaryObjectType.Integer32)) {
602      dictionaryObject = Integer.parseInt(value.get(0));
603    } else if (type.equals(UserConfigurationDictionaryObjectType.Integer64)) {
604      dictionaryObject = Long.parseLong(value.get(0));
605    } else if (type.equals(UserConfigurationDictionaryObjectType.String)) {
606      dictionaryObject = String.valueOf(value.get(0));
607    } else if (type
608        .equals(UserConfigurationDictionaryObjectType.StringArray)) {
609      dictionaryObject = value.toArray();
610    } else if (type
611        .equals(UserConfigurationDictionaryObjectType.
612            UnsignedInteger32)) {
613      dictionaryObject = Integer.parseInt(value.get(0));
614    } else if (type
615        .equals(UserConfigurationDictionaryObjectType.
616            UnsignedInteger64)) {
617      dictionaryObject = Long.parseLong(value.get(0));
618    } else {
619      EwsUtilities.ewsAssert(false, "UserConfigurationDictionary.ConstructObject",
620                             "Type not recognized: " + type.toString());
621    }
622
623    return dictionaryObject;
624  }
625
626  /**
627   * Validates the specified key and value.
628   *
629   * @param key   The key.
630   * @param value The diction dictionary entry key.ary entry value.
631   * @throws Exception the exception
632   */
633  private void validateEntry(Object key, Object value) throws Exception {
634    this.validateObject(key);
635    this.validateObject(value);
636
637  }
638
639  /**
640   * Validates the dictionary object (key or entry value).
641   *
642   * @param dictionaryObject Object to validate.
643   * @throws Exception the exception
644   */
645  private void validateObject(Object dictionaryObject) throws Exception {
646    // Keys may not be null but we rely on the internal dictionary to throw
647    // if the key is null.
648    if (dictionaryObject != null) {
649      if (dictionaryObject.getClass().isArray()) {
650        int length = Array.getLength(dictionaryObject);
651        Class<?> wrapperType = Array.get(dictionaryObject, 0).getClass();
652        Object[] newArray = (Object[]) Array.
653            newInstance(wrapperType, length);
654        for (int i = 0; i < length; i++) {
655          newArray[i] = Array.get(dictionaryObject, i);
656        }
657        this.validateArrayObject(newArray);
658      } else {
659        this.validateObjectType(dictionaryObject);
660
661      }
662
663    } else {
664      throw new NullPointerException();
665    }
666  }
667
668  /**
669   * Validate the array object.
670   *
671   * @param dictionaryObjectAsArray Object to validate
672   * @throws ServiceLocalException the service local exception
673   */
674  private void validateArrayObject(Object[] dictionaryObjectAsArray)
675      throws ServiceLocalException {
676    // This logic is based on
677    // Microsoft.Exchange.Data.Storage.ConfigurationDictionary.
678    // CheckElementSupportedType().
679    // if (dictionaryObjectAsArray is string[])
680
681    if (dictionaryObjectAsArray instanceof String[]) {
682      if (dictionaryObjectAsArray.length > 0) {
683        for (Object arrayElement : dictionaryObjectAsArray) {
684          if (arrayElement == null) {
685            throw new ServiceLocalException("The array contains at least one null element.");
686          }
687        }
688      } else {
689        throw new ServiceLocalException("The array must contain at least one element.");
690      }
691    } else if (dictionaryObjectAsArray instanceof Byte[]) {
692      if (dictionaryObjectAsArray.length <= 0) {
693        throw new ServiceLocalException("The array must contain at least one element.");
694      }
695    } else {
696      throw new ServiceLocalException(String.format(
697          "Objects of type %s can't be added to the dictionary. The following types are supported: string array, byte array, boolean, byte, DateTime, integer, long, string, unsigned integer, and unsigned long.", dictionaryObjectAsArray
698              .getClass()));
699    }
700  }
701
702  /**
703   * Validates the dictionary object type.
704   *
705   * @param theObject Object to validate.
706   * @throws ServiceLocalException the service local exception
707   */
708  private void validateObjectType(Object theObject) throws ServiceLocalException {
709    // This logic is based on
710    // Microsoft.Exchange.Data.Storage.ConfigurationDictionary.
711    // CheckElementSupportedType().
712    boolean isValidType = false;
713    if (theObject != null) {
714      if (theObject instanceof String ||
715          theObject instanceof Boolean ||
716          theObject instanceof Byte ||
717          theObject instanceof Long ||
718          theObject instanceof Date ||
719          theObject instanceof Integer) {
720        isValidType = true;
721      }
722    }
723
724    if (!isValidType) {
725      throw new ServiceLocalException(
726          String.format(
727              "Objects of type %s can't be added to the dictionary. The following types are supported: string array, byte array, boolean, byte, DateTime, integer, long, string, unsigned integer, and unsigned long.", (theObject != null ?
728              theObject.getClass().toString() : "null")));
729    }
730  }
731
732  /*
733   * (non-Javadoc)
734   *
735   * @see java.lang.Iterable#iterator()
736   */
737  @Override
738  public Iterator<Object> iterator() {
739    return this.dictionary.values().iterator();
740
741  }
742
743}