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.core.EwsServiceXmlReader;
027import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
028import microsoft.exchange.webservices.data.core.XmlElementNames;
029import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
030import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
031import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
032
033import javax.xml.stream.XMLStreamException;
034
035import java.util.ArrayList;
036import java.util.Iterator;
037import java.util.List;
038
039/**
040 * Represents a list of strings.
041 */
042public class StringList extends ComplexProperty implements Iterable<String> {
043
044  /**
045   * The item.
046   */
047  private List<String> items = new ArrayList<String>();
048
049  /**
050   * The item xml element name.
051   */
052  private String itemXmlElementName = XmlElementNames.String;
053
054  /**
055   * Initializes a new instance of the "StringList" class.
056   */
057  public StringList() {
058  }
059
060  /**
061   * Initializes a new instance of the <see cref="StringList"/> class.
062   *
063   * @param strings The strings.
064   */
065  public StringList(Iterable<String> strings) {
066    this.addRange(strings);
067  }
068
069  /**
070   * Initializes a new instance of the "StringList" class.
071   *
072   * @param itemXmlElementName Name of the item XML element.
073   */
074  public StringList(String itemXmlElementName) {
075    this.itemXmlElementName = itemXmlElementName;
076  }
077
078  /**
079   * Tries to read element from XML.
080   *
081   * @param reader accepts EwsServiceXmlReader
082   * @return True if element was read
083   * @throws XMLStreamException the XML stream exception
084   * @throws ServiceXmlDeserializationException the service xml deserialization exception
085   */
086  @Override
087  public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
088      throws XMLStreamException, ServiceXmlDeserializationException {
089    boolean returnValue = false;
090    if (reader.getLocalName().equals(this.itemXmlElementName)) {
091      if (!reader.isEmptyElement()) {
092        this.add(reader.readValue());
093        returnValue = true;
094      } else {
095        reader.read();
096
097        returnValue = true;
098      }
099
100    }
101    return returnValue;
102  }
103
104  /**
105   * Writes elements to XML.
106   *
107   * @param writer accepts EwsServiceXmlWriter
108   * @throws ServiceXmlSerializationException the service xml serialization exception
109   * @throws XMLStreamException the XML stream exception
110   */
111  @Override
112  public void writeElementsToXml(EwsServiceXmlWriter writer)
113      throws ServiceXmlSerializationException, XMLStreamException {
114    for (String item : this.items) {
115      writer.writeStartElement(XmlNamespace.Types,
116          this.itemXmlElementName);
117      writer.writeValue(item, this.itemXmlElementName);
118      writer.writeEndElement();
119    }
120  }
121
122  /**
123   * Adds a string to the list.
124   *
125   * @param s The string to add.
126   */
127  public void add(String s) {
128    this.items.add(s);
129    this.changed();
130  }
131
132  /**
133   * Adds multiple strings to the list.
134   *
135   * @param strings The strings to add.
136   */
137  public void addRange(Iterable<String> strings) {
138    boolean changed = false;
139
140    for (String s : strings) {
141      if (!this.contains(s)) {
142        this.items.add(s);
143        changed = true;
144      }
145    }
146    if (changed) {
147      this.changed();
148    }
149  }
150
151  /**
152   * Determines whether the list contains a specific string.
153   *
154   * @param s The string to check the presence of.
155   * @return True if s is present in the list, false otherwise.
156   */
157  public boolean contains(String s) {
158    return this.items.contains(s);
159  }
160
161  /**
162   * Removes a string from the list.
163   *
164   * @param s The string to remove.
165   * @return True is s was removed, false otherwise.
166   */
167  public boolean remove(String s) {
168    boolean result = this.items.remove(s);
169    if (result) {
170      this.changed();
171    }
172    return result;
173  }
174
175  /**
176   * Removes the string at the specified position from the list.
177   *
178   * @param index The index of the string to remove.
179   */
180  public void removeAt(int index) {
181    if (index < 0 || index >= this.getSize()) {
182      throw new ArrayIndexOutOfBoundsException("index is out of range.");
183    }
184    this.items.remove(index);
185    this.changed();
186  }
187
188  /**
189   * Clears the list.
190   */
191  public void clearList() {
192    this.items.clear();
193    this.changed();
194  }
195
196  /**
197   * Returns a string representation of the object. In general, the
198   * <code>toString</code> method returns a string that "textually represents"
199   * this object. The result should be a concise but informative
200   * representation that is easy for a person to read. It is recommended that
201   * all subclasses override this method.
202   * <p/>
203   * The <code>toString</code> method for class <code>Object</code> returns a
204   * string consisting of the name of the class of which the object is an
205   * instance, the at-sign character `<code>@</code>', and the unsigned
206   * hexadecimal representation of the hash code of the object. In other
207   * words, this method returns a string equal to the value of: <blockquote>
208   * <p/>
209   * <pre>
210   * getClass().getName() + '@' + Integer.toHexString(hashCode())
211   * </pre>
212   * <p/>
213   * </blockquote>
214   *
215   * @return a string representation of the object.
216   */
217  @Override
218  public String toString() {
219    StringBuffer temp = new StringBuffer();
220    for (String str : this.items) {
221      temp.append(str.concat(","));
222    }
223    String tempString = temp.toString();
224    return tempString;
225  }
226
227  /**
228   * Gets the number of strings in the list.
229   *
230   * @return the size
231   */
232  public int getSize() {
233    return this.items.size();
234  }
235
236  /**
237   * Gets the string at the specified index.
238   *
239   * @param index The index of the string to get or set.
240   * @return The string at the specified index.
241   */
242  public String getString(int index) {
243    if (index < 0 || index >= this.getSize()) {
244      throw new ArrayIndexOutOfBoundsException("index is out of range.");
245    }
246    return this.items.get(index);
247  }
248
249  /**
250   * Sets the string at the specified index.
251   *
252   * @param index  The index
253   * @param object The object.
254   */
255  public void setString(int index, Object object) {
256    if (index < 0 || index >= this.getSize()) {
257      throw new ArrayIndexOutOfBoundsException("index is out of range.");
258    }
259
260    if (this.items.get(index) != object) {
261      this.items.set(index, (String) object);
262      this.changed();
263    }
264  }
265
266  /**
267   * Gets an iterator that iterates through the elements of the collection.
268   *
269   * @return An Iterator for the collection.
270   */
271  public Iterator<String> getIterator() {
272    return this.items.iterator();
273  }
274
275  /**
276   * Indicates whether some other object is "equal to" this one.
277   * <p/>
278   * The <code>equals</code> method implements an equivalence relation on
279   * non-null object references:
280   * <ul>
281   * <li>It is <i>reflexive</i>: for any non-null reference value
282   * <code>x</code>, <code>x.equals(x)</code> should return <code>true</code>.
283   * <li>It is <i>symmetric</i>: for any non-null reference values
284   * <code>x</code> and <code>y</code>, <code>x.equals(y)</code> should return
285   * <code>true</code> if and only if <code>y.equals(x)</code> returns
286   * <code>true</code>.
287   * <li>It is <i>transitive</i>: for any non-null reference values
288   * <code>x</code>, <code>y</code>, and <code>z</code>, if
289   * <code>x.equals(y)</code> returns <code>true</code> and
290   * <code>y.equals(z)</code> returns <code>true</code>, then
291   * <code>x.equals(z)</code> should return <code>true</code>.
292   * <li>It is <i>consistent</i>: for any non-null reference values
293   * <code>x</code> and <code>y</code>, multiple invocations of
294   * <tt>x.equals(y)</tt> consistently return <code>true</code> or
295   * consistently return <code>false</code>, provided no information used in
296   * <code>equals</code> comparisons on the objects is modified.
297   * <li>For any non-null reference value <code>x</code>,
298   * <code>x.equals(null)</code> should return <code>false</code>.
299   * </ul>
300   * <p/>
301   * The <tt>equals</tt> method for class <code>Object</code> implements the
302   * most discriminating possible equivalence relation on objects; that is,
303   * for any non-null reference values <code>x</code> and <code>y</code>, this
304   * method returns <code>true</code> if and only if <code>x</code> and
305   * <code>y</code> refer to the same object (<code>x == y</code> has the
306   * value <code>true</code>).
307   * <p/>
308   * Note that it is generally necessary to override the <tt>hashCode</tt>
309   * method whenever this method is overridden, so as to maintain the general
310   * contract for the <tt>hashCode</tt> method, which states that equal
311   * objects must have equal hash codes.
312   *
313   * @param obj the reference object with which to compare.
314   * @return if this object is the same as the obj argument; otherwise.
315   * @see #hashCode()
316   * @see java.util.Hashtable
317   */
318  @Override
319  public boolean equals(Object obj) {
320    if (obj instanceof StringList) {
321      StringList other = (StringList) obj;
322      return this.toString().equals(other.toString());
323    } else {
324      return false;
325    }
326  }
327
328  /**
329   * Serves as a hash function for a particular type.
330   *
331   * @return A hash code for the current "T:System.Object".
332   */
333  @Override
334  public int hashCode() {
335    return this.toString().hashCode();
336  }
337
338  /**
339   * Returns an iterator over a set of elements of type T.
340   *
341   * @return an Iterator.
342   */
343  @Override
344  public Iterator<String> iterator() {
345    return items.iterator();
346  }
347}