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.ICustomXmlUpdateSerializer; 030import microsoft.exchange.webservices.data.core.XmlAttributeNames; 031import microsoft.exchange.webservices.data.core.XmlElementNames; 032import microsoft.exchange.webservices.data.core.service.ServiceObject; 033import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState; 034import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace; 035import microsoft.exchange.webservices.data.property.definition.PropertyDefinition; 036 037import java.util.ArrayList; 038import java.util.HashMap; 039import java.util.List; 040import java.util.Map; 041import java.util.Map.Entry; 042 043/** 044 * Represents a generic dictionary that can be sent to or retrieved from EWS. 045 * TKey The type of key. TEntry The type of entry. 046 * 047 * @param <TKey> the generic type 048 * @param <TEntry> the generic type 049 */ 050@EditorBrowsable(state = EditorBrowsableState.Never) 051public abstract class DictionaryProperty 052 <TKey, TEntry extends DictionaryEntryProperty<TKey>> 053 extends ComplexProperty implements ICustomXmlUpdateSerializer, IComplexPropertyChangedDelegate<TEntry> { 054 055 /** 056 * The entries. 057 */ 058 private Map<TKey, TEntry> entries = new HashMap<TKey, TEntry>(); 059 060 /** 061 * The removed entries. 062 */ 063 private Map<TKey, TEntry> removedEntries = new HashMap<TKey, TEntry>(); 064 065 /** 066 * The added entries. 067 */ 068 private List<TKey> addedEntries = new ArrayList<TKey>(); 069 070 /** 071 * The modified entries. 072 */ 073 private List<TKey> modifiedEntries = new ArrayList<TKey>(); 074 075 /** 076 * Entry was changed. 077 * 078 * @param complexProperty the complex property 079 */ 080 private void entryChanged(final TEntry complexProperty) { 081 TKey key = complexProperty.getKey(); 082 083 if (!this.addedEntries.contains(key) && !this.modifiedEntries.contains(key)) { 084 this.modifiedEntries.add(key); 085 this.changed(); 086 } 087 } 088 089 /** 090 * Writes the URI to XML. 091 * 092 * @param writer the writer 093 * @param key the key 094 * @throws Exception the exception 095 */ 096 private void writeUriToXml(EwsServiceXmlWriter writer, TKey key) 097 throws Exception { 098 writer.writeStartElement(XmlNamespace.Types, 099 XmlElementNames.IndexedFieldURI); 100 writer.writeAttributeValue(XmlAttributeNames.FieldURI, this 101 .getFieldURI()); 102 writer.writeAttributeValue(XmlAttributeNames.FieldIndex, this 103 .getFieldIndex(key)); 104 writer.writeEndElement(); 105 } 106 107 /** 108 * Gets the index of the field. 109 * 110 * @param key the key 111 * @return Key index. 112 */ 113 protected String getFieldIndex(TKey key) { 114 return key.toString(); 115 } 116 117 /** 118 * Gets the field URI. 119 * 120 * @return Field URI. 121 */ 122 protected String getFieldURI() { 123 return null; 124 } 125 126 /** 127 * Creates the entry. 128 * 129 * @param reader the reader 130 * @return Dictionary entry. 131 */ 132 protected TEntry createEntry(EwsServiceXmlReader reader) { 133 if (reader.getLocalName().equalsIgnoreCase(XmlElementNames.Entry)) { 134 return this.createEntryInstance(); 135 } else { 136 return null; 137 } 138 } 139 140 /** 141 * Creates instance of dictionary entry. 142 * 143 * @return New instance. 144 */ 145 protected abstract TEntry createEntryInstance(); 146 147 /** 148 * Gets the name of the entry XML element. 149 * 150 * @param entry the entry 151 * @return XML element name. 152 */ 153 protected String getEntryXmlElementName(TEntry entry) { 154 return XmlElementNames.Entry; 155 } 156 157 /** 158 * Clears the change log. 159 */ 160 public void clearChangeLog() { 161 this.addedEntries.clear(); 162 this.removedEntries.clear(); 163 this.modifiedEntries.clear(); 164 165 for (TEntry entry : this.entries.values()) { 166 entry.clearChangeLog(); 167 } 168 } 169 170 /** 171 * Add entry. 172 * 173 * @param entry the entry 174 */ 175 protected void internalAdd(TEntry entry) { 176 entry.addOnChangeEvent(this); 177 178 this.entries.put(entry.getKey(), entry); 179 this.addedEntries.add(entry.getKey()); 180 this.removedEntries.remove(entry.getKey()); 181 182 this.changed(); 183 } 184 185 /** 186 * Complex property changed. 187 * 188 * @param complexProperty accepts ComplexProperty 189 */ 190 @Override 191 public void complexPropertyChanged(final TEntry complexProperty) { 192 entryChanged(complexProperty); 193 } 194 195 /** 196 * Add or replace entry. 197 * 198 * @param entry the entry 199 */ 200 protected void internalAddOrReplace(TEntry entry) { 201 TEntry oldEntry; 202 if (this.entries.containsKey(entry.getKey())) { 203 oldEntry = this.entries.get(entry.getKey()); 204 oldEntry.removeChangeEvent(this); 205 206 entry.addOnChangeEvent(this); 207 208 if (!this.addedEntries.contains(entry.getKey())) { 209 if (!this.modifiedEntries.contains(entry.getKey())) { 210 this.modifiedEntries.add(entry.getKey()); 211 } 212 } 213 214 this.changed(); 215 } else { 216 this.internalAdd(entry); 217 } 218 } 219 220 /** 221 * Remove entry based on key. 222 * 223 * @param key the key 224 */ 225 protected void internalRemove(TKey key) { 226 TEntry entry; 227 if (this.entries.containsKey(key)) { 228 entry = this.entries.get(key); 229 entry.removeChangeEvent(this); 230 231 this.entries.remove(key); 232 this.removedEntries.put(key, entry); 233 234 this.changed(); 235 } 236 237 this.addedEntries.remove(key); 238 } 239 240 /** 241 * Loads from XML. 242 * 243 * @param reader the reader 244 * @param localElementName the local element name 245 * @throws Exception the exception 246 */ 247 public void loadFromXml(EwsServiceXmlReader reader, String localElementName) throws Exception { 248 reader.ensureCurrentNodeIsStartElement(XmlNamespace.Types, 249 localElementName); 250 251 if (!reader.isEmptyElement()) { 252 do { 253 reader.read(); 254 255 if (reader.isStartElement()) { 256 TEntry entry = this.createEntry(reader); 257 258 if (entry != null) { 259 entry.loadFromXml(reader, reader.getLocalName()); 260 this.internalAdd(entry); 261 } else { 262 reader.skipCurrentElement(); 263 } 264 } 265 } while (!reader.isEndElement(XmlNamespace.Types, 266 localElementName)); 267 } else { 268 reader.read(); 269 } 270 } 271 272 /** 273 * Writes to XML. 274 * 275 * @param writer The writer 276 * @param xmlNamespace The XML namespace. 277 * @param xmlElementName Name of the XML element. 278 * @throws Exception 279 */ 280 @Override public void writeToXml(EwsServiceXmlWriter writer, XmlNamespace xmlNamespace, 281 String xmlElementName) throws Exception { 282 // Only write collection if it has at least one element. 283 if (this.entries.size() > 0) { 284 super.writeToXml( 285 writer, 286 xmlNamespace, 287 xmlElementName); 288 } 289 } 290 291 /** 292 * Writes elements to XML. 293 * 294 * @param writer the writer 295 * @throws Exception the exception 296 */ 297 public void writeElementsToXml(EwsServiceXmlWriter writer) 298 throws Exception { 299 for (Entry<TKey, TEntry> keyValuePair : this.entries.entrySet()) { 300 keyValuePair.getValue().writeToXml(writer, 301 this.getEntryXmlElementName(keyValuePair.getValue())); 302 } 303 } 304 305 /** 306 * Gets the entries. 307 * 308 * @return The entries. 309 */ 310 protected Map<TKey, TEntry> getEntries() { 311 return entries; 312 } 313 314 /** 315 * Determines whether this instance contains the specified key. 316 * 317 * @param key the key 318 * @return true if this instance contains the specified key; otherwise, 319 * false. 320 */ 321 public boolean contains(TKey key) { 322 return this.entries.containsKey(key); 323 } 324 325 /** 326 * Writes updates to XML. 327 * 328 * @param writer the writer 329 * @param ewsObject the ews object 330 * @param propertyDefinition the property definition 331 * @return True if property generated serialization. 332 * @throws Exception the exception 333 */ 334 public boolean writeSetUpdateToXml(EwsServiceXmlWriter writer, 335 ServiceObject ewsObject, PropertyDefinition propertyDefinition) 336 throws Exception { 337 List<TEntry> tempEntries = new ArrayList<TEntry>(); 338 339 for (TKey key : this.addedEntries) { 340 tempEntries.add(this.entries.get(key)); 341 } 342 for (TKey key : this.modifiedEntries) { 343 tempEntries.add(this.entries.get(key)); 344 } 345 for (TEntry entry : tempEntries) { 346 347 if (!entry.writeSetUpdateToXml(writer, ewsObject, 348 propertyDefinition.getXmlElement())) { 349 writer.writeStartElement(XmlNamespace.Types, ewsObject 350 .getSetFieldXmlElementName()); 351 this.writeUriToXml(writer, entry.getKey()); 352 353 writer.writeStartElement(XmlNamespace.Types, ewsObject 354 .getXmlElementName()); 355 //writer.writeStartElement(XmlNamespace.Types, propertyDefinition.getXmlElementName()); 356 writer.writeStartElement(XmlNamespace.Types, propertyDefinition.getXmlElement()); 357 entry.writeToXml(writer, this.getEntryXmlElementName(entry)); 358 writer.writeEndElement(); 359 writer.writeEndElement(); 360 361 writer.writeEndElement(); 362 } 363 } 364 365 for (TEntry entry : this.removedEntries.values()) { 366 if (!entry.writeDeleteUpdateToXml(writer, ewsObject)) { 367 writer.writeStartElement(XmlNamespace.Types, ewsObject 368 .getDeleteFieldXmlElementName()); 369 this.writeUriToXml(writer, entry.getKey()); 370 writer.writeEndElement(); 371 } 372 } 373 374 return true; 375 } 376 377 /** 378 * Writes deletion update to XML. 379 * 380 * @param writer the writer 381 * @param ewsObject the ews object 382 * @return True if property generated serialization. 383 */ 384 public boolean writeDeleteUpdateToXml(EwsServiceXmlWriter writer, 385 ServiceObject ewsObject) { 386 return false; 387 } 388}