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.ICustomXmlUpdateSerializer; 031import microsoft.exchange.webservices.data.core.service.ServiceObject; 032import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState; 033import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace; 034import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException; 035import microsoft.exchange.webservices.data.property.definition.PropertyDefinition; 036 037import java.util.ArrayList; 038import java.util.Iterator; 039import java.util.List; 040 041/** 042 * Represents a collection of property that can be sent to and retrieved from 043 * EWS. 044 * 045 * @param <TComplexProperty> ComplexProperty type. 046 */ 047@EditorBrowsable(state = EditorBrowsableState.Never) 048public abstract class ComplexPropertyCollection 049 <TComplexProperty extends ComplexProperty> 050 extends ComplexProperty implements ICustomXmlUpdateSerializer, 051 Iterable<TComplexProperty>, IComplexPropertyChangedDelegate<TComplexProperty> { 052 053 /** 054 * The item. 055 */ 056 private final List<TComplexProperty> items = new ArrayList<TComplexProperty>(); 057 058 /** 059 * The added item. 060 */ 061 private final List<TComplexProperty> addedItems = 062 new ArrayList<TComplexProperty>(); 063 064 /** 065 * The modified item. 066 */ 067 private final List<TComplexProperty> modifiedItems = 068 new ArrayList<TComplexProperty>(); 069 070 /** 071 * The removed item. 072 */ 073 private final List<TComplexProperty> removedItems = 074 new ArrayList<TComplexProperty>(); 075 076 /** 077 * Creates the complex property. 078 * 079 * @param xmlElementName Name of the XML element. 080 * @return Complex property instance. 081 */ 082 protected abstract TComplexProperty createComplexProperty( 083 String xmlElementName); 084 085 /** 086 * Gets the name of the collection item XML element. 087 * 088 * @param complexProperty The complex property. 089 * @return XML element name. 090 */ 091 protected abstract String getCollectionItemXmlElementName( 092 TComplexProperty complexProperty); 093 094 /** 095 * Initializes a new instance of. ComplexPropertyCollection 096 */ 097 protected ComplexPropertyCollection() { 098 super(); 099 } 100 101 /** 102 * Item changed. 103 * 104 * @param property The complex property. 105 */ 106 protected void itemChanged(final TComplexProperty property) { 107 EwsUtilities.ewsAssert( 108 property != null, "ComplexPropertyCollection.ItemChanged", 109 "The complexProperty argument must be not null" 110 ); 111 112 if (!this.addedItems.contains(property)) { 113 if (!this.modifiedItems.contains(property)) { 114 this.modifiedItems.add(property); 115 this.changed(); 116 } 117 } 118 } 119 120 /** 121 * Loads from XML. 122 * 123 * @param reader The reader. 124 * @param localElementName Name of the local element. 125 */ 126 @Override public void loadFromXml(EwsServiceXmlReader reader, String localElementName) throws Exception { 127 this.loadFromXml( 128 reader, 129 XmlNamespace.Types, 130 localElementName); 131 } 132 133 /** 134 * Loads from XML. 135 * 136 * @param reader The reader. 137 * @param xmlNamespace The XML namespace. 138 * @param localElementName Name of the local element. 139 */ 140 @Override public void loadFromXml(EwsServiceXmlReader reader, XmlNamespace xmlNamespace, 141 String localElementName) throws Exception { 142 reader.ensureCurrentNodeIsStartElement(xmlNamespace, 143 localElementName); 144 if (!reader.isEmptyElement()) { 145 do { 146 reader.read(); 147 148 if (reader.isStartElement()) { 149 TComplexProperty complexProperty = this 150 .createComplexProperty(reader.getLocalName()); 151 152 if (complexProperty != null) { 153 complexProperty.loadFromXml(reader, reader 154 .getLocalName()); 155 this.internalAdd(complexProperty, true); 156 } else { 157 reader.skipCurrentElement(); 158 } 159 } 160 } while (!reader.isEndElement(xmlNamespace, localElementName)); 161 } else { 162 reader.read(); 163 } 164 } 165 166 /** 167 * Loads from XML to update itself. 168 * 169 * @param reader The reader. 170 * @param xmlNamespace The XML namespace. 171 * @param xmlElementName Name of the XML element. 172 */ 173 public void updateFromXml( 174 EwsServiceXmlReader reader, 175 XmlNamespace xmlNamespace, 176 String xmlElementName) throws Exception { 177 reader.ensureCurrentNodeIsStartElement(xmlNamespace, xmlElementName); 178 179 if (!reader.isEmptyElement()) { 180 int index = 0; 181 do { 182 reader.read(); 183 184 if (reader.isStartElement()) { 185 TComplexProperty complexProperty = this.createComplexProperty(reader.getLocalName()); 186 TComplexProperty actualComplexProperty = this.getPropertyAtIndex(index++); 187 188 if (complexProperty == null || !complexProperty.equals(actualComplexProperty)) { 189 throw new ServiceLocalException("Property type incompatible when updating collection."); 190 } 191 192 actualComplexProperty.updateFromXml(reader, xmlNamespace, reader.getLocalName()); 193 } 194 } 195 while (!reader.isEndElement(xmlNamespace, xmlElementName)); 196 } 197 } 198 199 /** 200 * Writes to XML. 201 * 202 * @param writer The writer. 203 * @param xmlNamespace The XML namespace. 204 * @param xmlElementName Name of the XML element. 205 */ 206 @Override public void writeToXml(EwsServiceXmlWriter writer, XmlNamespace xmlNamespace, 207 String xmlElementName) throws Exception { 208 if (this.shouldWriteToXml()) { 209 super.writeToXml( 210 writer, 211 xmlNamespace, 212 xmlElementName); 213 } 214 } 215 216 /** 217 * Determine whether we should write collection to XML or not. 218 * 219 * @return True if collection contains at least one element. 220 */ 221 public boolean shouldWriteToXml() { 222 //Only write collection if it has at least one element. 223 return this.getCount() > 0; 224 } 225 226 /** 227 * Writes elements to XML. 228 * 229 * @param writer The writer. 230 * @throws Exception the exception 231 */ 232 @Override 233 public void writeElementsToXml(EwsServiceXmlWriter writer) 234 throws Exception { 235 for (TComplexProperty complexProperty : this) { 236 complexProperty.writeToXml(writer, this 237 .getCollectionItemXmlElementName(complexProperty)); 238 } 239 } 240 241 /** 242 * Clears the change log. 243 */ 244 @Override public void clearChangeLog() { 245 this.removedItems.clear(); 246 this.addedItems.clear(); 247 this.modifiedItems.clear(); 248 } 249 250 /** 251 * Removes from change log. 252 * 253 * @param complexProperty The complex property. 254 */ 255 protected void removeFromChangeLog(TComplexProperty complexProperty) { 256 this.removedItems.remove(complexProperty); 257 this.modifiedItems.remove(complexProperty); 258 this.addedItems.remove(complexProperty); 259 } 260 261 /** 262 * Gets the item. 263 * 264 * @return The item. 265 */ 266 public List<TComplexProperty> getItems() { 267 return this.items; 268 } 269 270 /** 271 * Gets the added item. 272 * 273 * @return The added item. 274 */ 275 protected List<TComplexProperty> getAddedItems() { 276 return this.addedItems; 277 } 278 279 /** 280 * Gets the modified item. 281 * 282 * @return The modified item. 283 */ 284 protected List<TComplexProperty> getModifiedItems() { 285 return this.modifiedItems; 286 } 287 288 /** 289 * Gets the removed item. 290 * 291 * @return The removed item. 292 */ 293 protected List<TComplexProperty> getRemovedItems() { 294 return this.removedItems; 295 } 296 297 /** 298 * Add complex property. 299 * 300 * @param complexProperty The complex property. 301 */ 302 protected void internalAdd(TComplexProperty complexProperty) { 303 this.internalAdd(complexProperty, false); 304 } 305 306 /** 307 * Add complex property. 308 * 309 * @param complexProperty The complex property. 310 * @param loading If true, collection is being loaded. 311 */ 312 private void internalAdd(TComplexProperty complexProperty, 313 boolean loading) { 314 EwsUtilities.ewsAssert(complexProperty != null, "ComplexPropertyCollection.InternalAdd", 315 "complexProperty is null"); 316 317 if (!this.items.contains(complexProperty)) { 318 this.items.add(complexProperty); 319 if (!loading) { 320 this.removedItems.remove(complexProperty); 321 this.addedItems.add(complexProperty); 322 } 323 complexProperty.addOnChangeEvent(this); 324 this.changed(); 325 } 326 } 327 328 /** 329 * Complex property changed. 330 * 331 * @param complexProperty accepts ComplexProperty 332 */ 333 @Override 334 public void complexPropertyChanged(final TComplexProperty complexProperty) { 335 this.itemChanged(complexProperty); 336 } 337 338 /** 339 * Clear collection. 340 */ 341 protected void internalClear() { 342 while (this.getCount() > 0) { 343 this.internalRemoveAt(0); 344 } 345 } 346 347 /** 348 * Remote entry at index. 349 * 350 * @param index The index. 351 */ 352 protected void internalRemoveAt(int index) { 353 EwsUtilities.ewsAssert(index >= 0 && index < this.getCount(), 354 "ComplexPropertyCollection.InternalRemoveAt", "index is out of range."); 355 356 this.internalRemove(this.items.get(index)); 357 } 358 359 /** 360 * Remove specified complex property. 361 * 362 * @param complexProperty The complex property. 363 * @return True if the complex property was successfully removed from the 364 * collection, false otherwise. 365 */ 366 protected boolean internalRemove(TComplexProperty complexProperty) { 367 EwsUtilities.ewsAssert(complexProperty != null, "ComplexPropertyCollection.InternalRemove", 368 "complexProperty is null"); 369 370 if (this.items.remove(complexProperty)) { 371 complexProperty.removeChangeEvent(this); 372 if (!this.addedItems.contains(complexProperty)) { 373 this.removedItems.add(complexProperty); 374 } else { 375 this.addedItems.remove(complexProperty); 376 } 377 this.modifiedItems.remove(complexProperty); 378 this.changed(); 379 return true; 380 } else { 381 return false; 382 } 383 } 384 385 /** 386 * Determines whether a specific property is in the collection. 387 * 388 * @param complexProperty The property to locate in the collection. 389 * @return True if the property was found in the collection, false 390 * otherwise. 391 */ 392 public boolean contains(TComplexProperty complexProperty) { 393 return this.items.contains(complexProperty); 394 } 395 396 /** 397 * Searches for a specific property and return its zero-based index within 398 * the collection. 399 * 400 * @param complexProperty The property to locate in the collection. 401 * @return The zero-based index of the property within the collection. 402 */ 403 public int indexOf(TComplexProperty complexProperty) { 404 return this.items.indexOf(complexProperty); 405 } 406 407 /** 408 * Gets the total number of property in the collection. 409 * 410 * @return the count 411 */ 412 public int getCount() { 413 return this.items.size(); 414 } 415 416 /** 417 * Gets the property at the specified index. 418 * 419 * @param index the index 420 * @return index The property at the specified index. 421 * @throws IllegalArgumentException thrown if if index is out of range. 422 */ 423 public TComplexProperty getPropertyAtIndex(int index) 424 throws IllegalArgumentException { 425 if (index < 0 || index >= this.getCount()) { 426 throw new IllegalArgumentException( 427 String.format("index %d is out of range [0..%d[.", index, this.getCount()) 428 ); 429 } 430 return this.items.get(index); 431 } 432 433 /** 434 * Gets an enumerator that iterates through the elements of the collection. 435 * 436 * @return An Iterator for the collection. 437 */ 438 @Override 439 public Iterator<TComplexProperty> iterator() { 440 return this.items.iterator(); 441 } 442 443 /** 444 * Write set update to xml. 445 * 446 * @param writer accepts EwsServiceXmlWriter 447 * @param ewsObject accepts ServiceObject 448 * @param propertyDefinition accepts PropertyDefinition 449 * @return true 450 * @throws Exception the exception 451 */ 452 @Override 453 public boolean writeSetUpdateToXml(EwsServiceXmlWriter writer, 454 ServiceObject ewsObject, PropertyDefinition propertyDefinition) 455 throws Exception { 456 // If the collection is empty, delete the property. 457 if (this.getCount() == 0) { 458 writer.writeStartElement(XmlNamespace.Types, ewsObject 459 .getDeleteFieldXmlElementName()); 460 propertyDefinition.writeToXml(writer); 461 writer.writeEndElement(); 462 return true; 463 } 464 // Otherwise, use the default XML serializer. 465 else { 466 return false; 467 } 468 } 469 470 /** 471 * Writes the deletion update to XML. 472 * 473 * @param writer The writer. 474 * @param ewsObject The ews object. 475 * @return True if property generated serialization. 476 * @throws Exception the exception 477 */ 478 @Override 479 public boolean writeDeleteUpdateToXml(EwsServiceXmlWriter writer, 480 ServiceObject ewsObject) throws Exception { 481 // Use the default XML serializer. 482 return false; 483 } 484}