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.property.complex;
025    
026    import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
027    import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
028    import microsoft.exchange.webservices.data.core.XmlElementNames;
029    import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
030    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlDeserializationException;
031    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
032    
033    import javax.xml.stream.XMLStreamException;
034    
035    import java.util.ArrayList;
036    import java.util.Iterator;
037    import java.util.List;
038    
039    /**
040     * Represents a list of strings.
041     */
042    public 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    }