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.EwsServiceXmlWriter;
027import microsoft.exchange.webservices.data.core.EwsUtilities;
028import microsoft.exchange.webservices.data.core.ICustomXmlUpdateSerializer;
029import microsoft.exchange.webservices.data.core.XmlElementNames;
030import microsoft.exchange.webservices.data.core.service.ServiceObject;
031import microsoft.exchange.webservices.data.core.service.item.Contact;
032import microsoft.exchange.webservices.data.core.service.schema.ContactGroupSchema;
033import microsoft.exchange.webservices.data.core.enumeration.property.EmailAddressKey;
034import microsoft.exchange.webservices.data.core.enumeration.property.MailboxType;
035import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
036import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
037import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
038import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
039import microsoft.exchange.webservices.data.property.definition.GroupMemberPropertyDefinition;
040import microsoft.exchange.webservices.data.property.definition.PropertyDefinition;
041
042import javax.xml.stream.XMLStreamException;
043
044import java.util.Iterator;
045import java.util.List;
046
047/**
048 * Represents a collection of members of GroupMember type.
049 */
050public final class GroupMemberCollection extends ComplexPropertyCollection<GroupMember> implements
051                                                                                        ICustomXmlUpdateSerializer {
052  /**
053   * If the collection is cleared, then store PDL members collection is
054   * updated with "SetItemField". If the collection is not cleared, then store
055   * PDL members collection is updated with "AppendToItemField".
056   */
057  private boolean collectionIsCleared = false;
058
059  /**
060   * Initializes a new instance.
061   */
062  public GroupMemberCollection() {
063    super();
064  }
065
066  /**
067   * Retrieves the XML element name corresponding to the provided
068   * GroupMember object.
069   *
070   * @param member the member
071   * @return The XML element name corresponding to the provided GroupMember
072   * object
073   */
074  @Override
075  protected String getCollectionItemXmlElementName(GroupMember member) {
076    return XmlElementNames.Member;
077  }
078
079  /**
080   * * Finds the member with the specified key in the collection.Members that
081   * have not yet been saved do not have a key.
082   *
083   * @param key the key
084   * @return The member with the specified key
085   * @throws Exception the exception
086   */
087  public GroupMember find(String key) throws Exception {
088    EwsUtilities.validateParam(key, "key");
089
090    for (GroupMember item : this.getItems()) {
091      if (item.getKey().equals(key)) {
092        return item;
093      }
094    }
095
096    return null;
097  }
098
099  /**
100   * Clears the collection.
101   */
102  public void clear() {
103    // mark the whole collection for deletion
104    this.internalClear();
105    this.collectionIsCleared = true;
106  }
107
108  /**
109   * Adds a member to the collection.
110   *
111   * @param member the member
112   * @throws Exception the exception
113   */
114  public void add(GroupMember member) throws Exception {
115    EwsUtilities.validateParam(member, "member");
116    EwsUtilities.ewsAssert(member.getKey() == null, "GroupMemberCollection.Add", "member.Key is not null.");
117    EwsUtilities.ewsAssert(!this.contains(member), "GroupMemberCollection.Add",
118                           "The member is already in the collection");
119
120    this.internalAdd(member);
121  }
122
123  /**
124   * Adds multiple members to the collection.
125   *
126   * @param members the members
127   * @throws Exception the exception
128   */
129  public void addRange(Iterator<GroupMember> members) throws Exception {
130    EwsUtilities.validateParam(members, "members");
131    while (members.hasNext()) {
132      this.add(members.next());
133
134    }
135  }
136
137  /**
138   * Adds a member linked to a Contact Group.
139   *
140   * @param contactGroupId the contact group id
141   * @throws Exception the exception
142   */
143  public void addContactGroup(ItemId contactGroupId) throws Exception {
144    this.add(new GroupMember(contactGroupId));
145  }
146
147  /**
148   * Adds a member linked to a specific contact?s e-mail address.
149   *
150   * @param contactId     the contact id
151   * @param addressToLink the address to link
152   * @throws Exception the exception
153   */
154  public void addPersonalContact(ItemId contactId, String addressToLink)
155      throws Exception {
156    this.add(new GroupMember(contactId, addressToLink));
157  }
158
159  /**
160   * Adds a member linked to a contact?s first available e-mail address.
161   *
162   * @param contactId the contact id
163   * @throws Exception the exception
164   */
165  public void addPersonalContact(ItemId contactId) throws Exception {
166    this.addPersonalContact(contactId, null);
167  }
168
169  /**
170   * Adds a member linked to an Active Directory user.
171   *
172   * @param smtpAddress the smtp address
173   * @throws ServiceLocalException the service local exception
174   * @throws Exception             the exception
175   */
176  public void addDirectoryUser(String smtpAddress)
177      throws ServiceLocalException, Exception {
178    this.addDirectoryUser(smtpAddress, new EmailAddress()
179        .getSmtpRoutingType());
180  }
181
182  /**
183   * Adds a member linked to an Active Directory user.
184   *
185   * @param address     the address
186   * @param routingType the routing type
187   * @throws ServiceLocalException the service local exception
188   * @throws Exception             the exception
189   */
190  public void addDirectoryUser(String address, String routingType)
191      throws ServiceLocalException, Exception {
192    this.add(new GroupMember(address, routingType, MailboxType.Mailbox));
193  }
194
195  /**
196   * Adds a member linked to an Active Directory contact.
197   *
198   * @param smtpAddress the smtp address
199   * @throws ServiceLocalException the service local exception
200   * @throws Exception             the exception
201   */
202  public void addDirectoryContact(String smtpAddress)
203      throws ServiceLocalException, Exception {
204    this.addDirectoryContact(smtpAddress, new EmailAddress()
205        .getSmtpRoutingType());
206  }
207
208  /**
209   * Adds a member linked to an Active Directory contact.
210   *
211   * @param address     the address
212   * @param routingType the routing type
213   * @throws ServiceLocalException the service local exception
214   * @throws Exception             the exception
215   */
216  public void addDirectoryContact(String address, String routingType)
217      throws ServiceLocalException, Exception {
218    this.add(new GroupMember(address, routingType, MailboxType.Contact));
219  }
220
221  /**
222   * Adds a member linked to a Public Group.
223   *
224   * @param smtpAddress the smtp address
225   * @throws ServiceLocalException the service local exception
226   * @throws Exception             the exception
227   */
228  public void addPublicGroup(String smtpAddress)
229      throws ServiceLocalException, Exception {
230    this.add(new GroupMember(smtpAddress, new EmailAddress()
231        .getSmtpRoutingType(), MailboxType.PublicGroup));
232  }
233
234  /**
235   * Adds a member linked to a mail-enabled Public Folder.
236   *
237   * @param smtpAddress the smtp address
238   * @throws ServiceLocalException the service local exception
239   * @throws Exception             the exception
240   */
241  public void addDirectoryPublicFolder(String smtpAddress)
242      throws ServiceLocalException, Exception {
243    this.add(new GroupMember(smtpAddress, new EmailAddress()
244        .getSmtpRoutingType(), MailboxType.PublicFolder));
245  }
246
247  /**
248   * Adds a one-off member.
249   *
250   * @param displayName the display name
251   * @param address     the address
252   * @param routingType the routing type
253   * @throws Exception the exception
254   */
255  public void addOneOff(String displayName,
256      String address, String routingType)
257      throws Exception {
258    this.add(new GroupMember(displayName, address, routingType));
259  }
260
261  /**
262   * Adds a one-off member.
263   *
264   * @param displayName the display name
265   * @param smtpAddress the smtp address
266   * @throws Exception the exception
267   */
268  public void addOneOff(String displayName, String smtpAddress)
269      throws Exception {
270    this.addOneOff(displayName, smtpAddress, new EmailAddress()
271        .getSmtpRoutingType());
272  }
273
274  /**
275   * Adds a member that is linked to a specific e-mail address of a contact.
276   *
277   * @param contact         the contact
278   * @param emailAddressKey the email address key
279   * @throws Exception the exception
280   */
281  public void addContactEmailAddress(Contact contact,
282      EmailAddressKey emailAddressKey) throws Exception {
283    this.add(new GroupMember(contact, emailAddressKey));
284  }
285
286  /**
287   * Removes a member at the specified index.
288   *
289   * @param index the index
290   */
291  public void removeAt(int index) {
292    if (index < 0 || index >= this.getCount()) {
293      throw new IllegalArgumentException("index", new Throwable("index is out of range."));
294
295    }
296
297    this.internalRemoveAt(index);
298  }
299
300  /**
301   * Removes a member from the collection.
302   *
303   * @param member the member
304   * @return True if the group member was successfully removed from the
305   * collection, false otherwise.
306   */
307  public boolean remove(GroupMember member) {
308    return this.internalRemove(member);
309  }
310
311  /**
312   * Writes the update to XML.
313   *
314   * @param writer             the writer
315   * @param ownerObject        the owner object
316   * @param propertyDefinition the property definition
317   * @return True if property generated serialization.
318   * @throws Exception the exception
319   */
320  public boolean writeSetUpdateToXml(EwsServiceXmlWriter writer,
321      ServiceObject ownerObject, PropertyDefinition propertyDefinition)
322      throws Exception {
323    if (this.collectionIsCleared) {
324
325      if (!this.getAddedItems().isEmpty()) { // not visible
326
327        // Delete the whole members collection
328        this.writeDeleteMembersCollectionToXml(writer);
329      } else {
330        // The collection is cleared, so Set
331        this.writeSetOrAppendMembersToXml(writer, this.getAddedItems(),
332            true);
333      }
334    } else {
335      // The collection is not cleared, i.e. dl.Members.Clear() is not
336      // called.
337      // Append AddedItems.
338      this.writeSetOrAppendMembersToXml(writer, this.getAddedItems(),
339          false);
340
341      // Since member replacement is not supported by server
342      // Delete old ModifiedItems, then recreate new instead.
343      this.writeDeleteMembersToXml(writer, this.getModifiedItems());
344      this.writeSetOrAppendMembersToXml(writer, this.getModifiedItems(),
345          false);
346
347      // Delete RemovedItems.
348      this.writeDeleteMembersToXml(writer, this.getRemovedItems());
349    }
350
351    return true;
352  }
353
354  /**
355   * Writes the deletion update to XML.
356   *
357   * @param writer    the writer
358   * @param ewsObject the ews object
359   * @return True if property generated serialization.
360   */
361  public boolean writeDeleteUpdateToXml(EwsServiceXmlWriter writer,
362      ServiceObject ewsObject) {
363    return false;
364  }
365
366  /**
367   * Creates a GroupMember object from an XML element name.
368   *
369   * @param xmlElementName the xml element name
370   * @return An GroupMember object
371   */
372  protected GroupMember createComplexProperty(String xmlElementName) {
373    return new GroupMember();
374  }
375
376  /**
377   * Clears the change log.
378   */
379  public void clearChangeLog() {
380    super.clearChangeLog();
381    this.collectionIsCleared = false;
382  }
383
384  /**
385   * Delete the whole members collection.
386   *
387   * @param writer the writer
388   * @throws XMLStreamException the XML stream exception
389   * @throws ServiceXmlSerializationException the service xml serialization exception
390   */
391  private void writeDeleteMembersCollectionToXml(EwsServiceXmlWriter writer)
392      throws XMLStreamException, ServiceXmlSerializationException {
393    writer.writeStartElement(XmlNamespace.Types,
394        XmlElementNames.DeleteItemField);
395    ContactGroupSchema.Members.writeToXml(writer);
396    writer.writeEndElement();
397  }
398
399  /**
400   * Generate XML to delete individual members.
401   *
402   * @param writer  the writer
403   * @param members the members
404   * @throws XMLStreamException the XML stream exception
405   * @throws ServiceXmlSerializationException the service xml serialization exception
406   */
407  private void writeDeleteMembersToXml(EwsServiceXmlWriter writer,
408      List<GroupMember> members) throws XMLStreamException,
409      ServiceXmlSerializationException {
410    if (!members.isEmpty()) {
411      GroupMemberPropertyDefinition memberPropDef =
412          new GroupMemberPropertyDefinition();
413
414      for (GroupMember member : members) {
415        writer.writeStartElement(XmlNamespace.Types,
416            XmlElementNames.DeleteItemField);
417
418        memberPropDef.setKey(member.getKey());
419        memberPropDef.writeToXml(writer);
420
421        writer.writeEndElement(); // DeleteItemField
422      }
423    }
424  }
425
426  /**
427   * Write set or append members to xml.
428   *
429   * @param writer  the writer
430   * @param members the members
431   * @param setMode the set mode
432   * @throws Exception the exception
433   */
434  private void writeSetOrAppendMembersToXml(EwsServiceXmlWriter writer,
435      List<GroupMember> members, boolean setMode) throws Exception {
436    if (!members.isEmpty()) {
437      writer.writeStartElement(XmlNamespace.Types,
438          setMode ? XmlElementNames.SetItemField
439              : XmlElementNames.AppendToItemField);
440
441      ContactGroupSchema.Members.writeToXml(writer);
442
443      writer.writeStartElement(XmlNamespace.Types,
444          XmlElementNames.DistributionList);
445      writer.writeStartElement(XmlNamespace.Types,
446          XmlElementNames.Members);
447
448      for (GroupMember member : members) {
449        member.writeToXml(writer, XmlElementNames.Member);
450      }
451
452      writer.writeEndElement(); // Members
453      writer.writeEndElement(); // Group
454      writer.writeEndElement(); // setMode ? SetItemField :
455      // AppendItemField
456    }
457  }
458
459  /**
460   * Validates this instance.
461   *
462   * @throws Exception
463   */
464  @Override
465  protected void internalValidate() throws Exception {
466    super.internalValidate();
467
468    for (GroupMember groupMember : this.getModifiedItems()) {
469      if (!(groupMember.getKey() == null || groupMember.getKey().isEmpty())) {
470        throw new ServiceValidationException("The contact group's Members property must be reloaded before "
471                                             + "newly-added members can be updated.");
472      }
473    }
474  }
475}