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.EwsServiceXmlWriter;
027    import microsoft.exchange.webservices.data.core.EwsUtilities;
028    import microsoft.exchange.webservices.data.core.ICustomXmlUpdateSerializer;
029    import microsoft.exchange.webservices.data.core.XmlElementNames;
030    import microsoft.exchange.webservices.data.core.service.ServiceObject;
031    import microsoft.exchange.webservices.data.core.service.item.Contact;
032    import microsoft.exchange.webservices.data.core.service.schema.ContactGroupSchema;
033    import microsoft.exchange.webservices.data.core.enumeration.property.EmailAddressKey;
034    import microsoft.exchange.webservices.data.core.enumeration.property.MailboxType;
035    import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
036    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
037    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
038    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceXmlSerializationException;
039    import microsoft.exchange.webservices.data.property.definition.GroupMemberPropertyDefinition;
040    import microsoft.exchange.webservices.data.property.definition.PropertyDefinition;
041    
042    import javax.xml.stream.XMLStreamException;
043    
044    import java.util.Iterator;
045    import java.util.List;
046    
047    /**
048     * Represents a collection of members of GroupMember type.
049     */
050    public 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    }