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.attribute.EditorBrowsable;
027 import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
028 import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
029 import microsoft.exchange.webservices.data.core.ICustomXmlUpdateSerializer;
030 import microsoft.exchange.webservices.data.core.XmlAttributeNames;
031 import microsoft.exchange.webservices.data.core.XmlElementNames;
032 import microsoft.exchange.webservices.data.core.service.ServiceObject;
033 import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState;
034 import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
035 import microsoft.exchange.webservices.data.property.definition.PropertyDefinition;
036
037 import java.util.ArrayList;
038 import java.util.HashMap;
039 import java.util.List;
040 import java.util.Map;
041 import 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)
051 public 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 }