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.core.service;
025
026 import microsoft.exchange.webservices.data.attribute.ServiceObjectDefinition;
027 import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
028 import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
029 import microsoft.exchange.webservices.data.core.EwsUtilities;
030 import microsoft.exchange.webservices.data.core.ExchangeService;
031 import microsoft.exchange.webservices.data.core.PropertyBag;
032 import microsoft.exchange.webservices.data.core.PropertySet;
033 import microsoft.exchange.webservices.data.core.XmlElementNames;
034 import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
035 import microsoft.exchange.webservices.data.core.enumeration.service.DeleteMode;
036 import microsoft.exchange.webservices.data.core.enumeration.service.SendCancellationsMode;
037 import microsoft.exchange.webservices.data.core.enumeration.service.calendar.AffectedTaskOccurrence;
038 import microsoft.exchange.webservices.data.core.exception.misc.InvalidOperationException;
039 import microsoft.exchange.webservices.data.core.exception.service.local.ServiceLocalException;
040 import microsoft.exchange.webservices.data.core.service.schema.ServiceObjectSchema;
041 import microsoft.exchange.webservices.data.misc.OutParam;
042 import microsoft.exchange.webservices.data.property.complex.ExtendedProperty;
043 import microsoft.exchange.webservices.data.property.complex.ExtendedPropertyCollection;
044 import microsoft.exchange.webservices.data.property.complex.IServiceObjectChangedDelegate;
045 import microsoft.exchange.webservices.data.property.complex.ServiceId;
046 import microsoft.exchange.webservices.data.property.definition.ExtendedPropertyDefinition;
047 import microsoft.exchange.webservices.data.property.definition.PropertyDefinition;
048 import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase;
049
050 import java.util.ArrayList;
051 import java.util.Collection;
052 import java.util.List;
053
054 /**
055 * Represents the base abstract class for all item and folder types.
056 */
057 public abstract class ServiceObject {
058
059 /**
060 * The lock object.
061 */
062 private Object lockObject = new Object();
063
064 /**
065 * The service.
066 */
067 private ExchangeService service;
068
069 /**
070 * The property bag.
071 */
072 private PropertyBag propertyBag;
073
074 /**
075 * The xml element name.
076 */
077 private String xmlElementName;
078
079 /**
080 * Triggers dispatch of the change event.
081 */
082 public void changed() {
083
084 for (IServiceObjectChangedDelegate change : this.onChange) {
085 change.serviceObjectChanged(this);
086 }
087 }
088
089 /**
090 * Throws exception if this is a new service object.
091 *
092 * @throws InvalidOperationException the invalid operation exception
093 * @throws ServiceLocalException the service local exception
094 */
095 public void throwIfThisIsNew() throws InvalidOperationException,
096 ServiceLocalException {
097 if (this.isNew()) {
098 throw new InvalidOperationException(
099 "This operation can't be performed because this service object doesn't have an Id.");
100 }
101 }
102
103 /**
104 * Throws exception if this is not a new service object.
105 *
106 * @throws InvalidOperationException the invalid operation exception
107 * @throws ServiceLocalException the service local exception
108 */
109 protected void throwIfThisIsNotNew() throws InvalidOperationException,
110 ServiceLocalException {
111 if (!this.isNew()) {
112 throw new InvalidOperationException(
113 "This operation can't be performed because this service object already has an ID. To update this service object, use the Update() method instead.");
114 }
115 }
116
117 // / This methods lets subclasses of ServiceObject override the default
118 // mechanism
119 // / by which the XML element name associated with their type is retrieved.
120
121 /**
122 * This methods lets subclasses of ServiceObject override the default
123 * mechanism by which the XML element name associated with their type is
124 * retrieved.
125 *
126 * @return String
127 */
128 protected String getXmlElementNameOverride() {
129 return null;
130 }
131
132 /**
133 * GetXmlElementName retrieves the XmlElementName of this type based on the
134 * EwsObjectDefinition attribute that decorates it, if present.
135 *
136 * @return The XML element name associated with this type.
137 */
138 public String getXmlElementName() {
139 if (this.isNullOrEmpty(this.xmlElementName)) {
140 this.xmlElementName = this.getXmlElementNameOverride();
141 if (this.isNullOrEmpty(this.xmlElementName)) {
142 synchronized (this.lockObject) {
143
144 ServiceObjectDefinition annotation = this.getClass()
145 .getAnnotation(ServiceObjectDefinition.class);
146 if (null != annotation) {
147 this.xmlElementName = annotation.xmlElementName();
148 }
149 }
150 }
151 }
152 EwsUtilities
153 .ewsAssert(!isNullOrEmpty(this.xmlElementName), "EwsObject.GetXmlElementName", String
154 .format("The class %s does not have an " + "associated XML element name.",
155 this.getClass().getName()));
156
157 return this.xmlElementName;
158 }
159
160 /**
161 * Gets the name of the change XML element.
162 *
163 * @return the change xml element name
164 */
165 public String getChangeXmlElementName() {
166 return XmlElementNames.ItemChange;
167 }
168
169 /**
170 * Gets the name of the set field XML element.
171 *
172 * @return String
173 */
174 public String getSetFieldXmlElementName() {
175 return XmlElementNames.SetItemField;
176 }
177
178 /**
179 * Gets the name of the delete field XML element.
180 *
181 * @return String
182 */
183 public String getDeleteFieldXmlElementName() {
184 return XmlElementNames.DeleteItemField;
185 }
186
187 /**
188 * Gets a value indicating whether a time zone SOAP header should be emitted
189 * in a CreateItem or UpdateItem request so this item can be property saved
190 * or updated.
191 *
192 * @param isUpdateOperation the is update operation
193 * @return boolean
194 * @throws ServiceLocalException
195 * @throws Exception
196 */
197 protected boolean getIsTimeZoneHeaderRequired(boolean isUpdateOperation)
198 throws ServiceLocalException, Exception {
199 return false;
200 }
201
202 /**
203 * Determines whether property defined with
204 * ScopedDateTimePropertyDefinition require custom time zone scoping.
205 *
206 * @return boolean
207 */
208 protected boolean getIsCustomDateTimeScopingRequired() {
209 return false;
210 }
211
212 /**
213 * The property bag holding property values for this object.
214 *
215 * @return the property bag
216 */
217 public PropertyBag getPropertyBag() {
218 return this.propertyBag;
219 }
220
221 /**
222 * Internal constructor.
223 *
224 * @param service the service
225 * @throws Exception the exception
226 */
227 protected ServiceObject(ExchangeService service) throws Exception {
228 EwsUtilities.validateParam(service, "service");
229 EwsUtilities.validateServiceObjectVersion(this, service
230 .getRequestedServerVersion());
231 this.service = service;
232 this.propertyBag = new PropertyBag(this);
233 }
234
235 /**
236 * Gets the schema associated with this type of object.
237 *
238 * @return ServiceObjectSchema
239 */
240 public ServiceObjectSchema schema() {
241 return this.getSchema();
242 }
243
244 /**
245 * Internal method to return the schema associated with this type of object.
246 *
247 * @return the schema
248 */
249 public abstract ServiceObjectSchema getSchema();
250
251 /**
252 * Gets the minimum required server version.
253 *
254 * @return the minimum required server version
255 */
256 public abstract ExchangeVersion getMinimumRequiredServerVersion();
257
258 /**
259 * Loads service object from XML.
260 *
261 * @param reader the reader
262 * @param clearPropertyBag the clear property bag
263 * @throws Exception the exception
264 */
265 public void loadFromXml(EwsServiceXmlReader reader, boolean clearPropertyBag) throws Exception {
266
267 this.getPropertyBag().loadFromXml(reader, clearPropertyBag,
268 null, // propertySet
269 false); // summaryPropertiesOnly
270
271 }
272
273 // / Validates this instance.
274
275 /**
276 * Validate.
277 *
278 * @throws Exception the exception
279 */
280 protected void validate() throws Exception {
281 this.getPropertyBag().validate();
282 }
283
284 // / Loads service object from XML.
285
286 /**
287 * Load from xml.
288 *
289 * @param reader the reader
290 * @param clearPropertyBag the clear property bag
291 * @param requestedPropertySet the requested property set
292 * @param summaryPropertiesOnly the summary property only
293 * @throws Exception the exception
294 */
295 public void loadFromXml(EwsServiceXmlReader reader, boolean clearPropertyBag,
296 PropertySet requestedPropertySet, boolean summaryPropertiesOnly) throws Exception {
297
298 this.getPropertyBag().loadFromXml(reader, clearPropertyBag,
299 requestedPropertySet, summaryPropertiesOnly);
300
301 }
302
303 // Clears the object's change log.
304
305 /**
306 * Clear change log.
307 */
308 public void clearChangeLog() {
309 this.getPropertyBag().clearChangeLog();
310 }
311
312 // / Writes service object as XML.
313
314 /**
315 * Write to xml.
316 *
317 * @param writer the writer
318 * @throws Exception the exception
319 */
320 public void writeToXml(EwsServiceXmlWriter writer) throws Exception {
321 this.getPropertyBag().writeToXml(writer);
322 }
323
324 // Writes service object for update as XML.
325
326 /**
327 * Write to xml for update.
328 *
329 * @param writer the writer
330 * @throws Exception the exception
331 */
332 public void writeToXmlForUpdate(EwsServiceXmlWriter writer)
333 throws Exception {
334 this.getPropertyBag().writeToXmlForUpdate(writer);
335 }
336
337 // / Loads the specified set of property on the object.
338
339 /**
340 * Internal load.
341 *
342 * @param propertySet the property set
343 * @throws Exception the exception
344 */
345 protected abstract void internalLoad(PropertySet propertySet)
346 throws Exception;
347
348 // / Deletes the object.
349
350 /**
351 * Internal delete.
352 *
353 * @param deleteMode the delete mode
354 * @param sendCancellationsMode the send cancellations mode
355 * @param affectedTaskOccurrences the affected task occurrences
356 * @throws Exception the exception
357 */
358 protected abstract void internalDelete(DeleteMode deleteMode,
359 SendCancellationsMode sendCancellationsMode,
360 AffectedTaskOccurrence affectedTaskOccurrences) throws Exception;
361
362 // / Loads the specified set of property. Calling this method results in a
363 // call to EWS.
364
365 /**
366 * Load.
367 *
368 * @param propertySet the property set
369 * @throws Exception the exception
370 */
371 public void load(PropertySet propertySet) throws Exception {
372 this.internalLoad(propertySet);
373 }
374
375 // Loads the first class property. Calling this method results in a call
376 // to EWS.
377
378 /**
379 * Load.
380 *
381 * @throws Exception the exception
382 */
383 public void load() throws Exception {
384 this.internalLoad(PropertySet.getFirstClassProperties());
385 }
386
387 /**
388 * Gets the value of specified property in this instance.
389 *
390 * @param propertyDefinition Definition of the property to get.
391 * @return The value of specified property in this instance.
392 * @throws Exception the exception
393 */
394 public Object getObjectFromPropertyDefinition(
395 PropertyDefinitionBase propertyDefinition) throws Exception {
396 PropertyDefinition propDef = (PropertyDefinition) propertyDefinition;
397
398 if (propDef != null) {
399 return this.getPropertyBag().getObjectFromPropertyDefinition(propDef);
400 } else {
401 // E14:226103 -- Other subclasses of PropertyDefinitionBase are not supported.
402 throw new UnsupportedOperationException(String.format(
403 "This operation isn't supported for property definition type %s.",
404 propertyDefinition.getType().getName()));
405 }
406 }
407
408 /**
409 * Try to get the value of a specified extended property in this instance.
410 *
411 * @param propertyDefinition the property definition
412 * @param propertyValue the property value
413 * @return true, if successful
414 * @throws Exception the exception
415 */
416 protected <T> boolean tryGetExtendedProperty(Class<T> cls,
417 ExtendedPropertyDefinition propertyDefinition,
418 OutParam<T> propertyValue) throws Exception {
419 ExtendedPropertyCollection propertyCollection = this
420 .getExtendedProperties();
421
422 if ((propertyCollection != null) &&
423 propertyCollection.tryGetValue(cls, propertyDefinition, propertyValue)) {
424 return true;
425 } else {
426 propertyValue.setParam(null);
427 return false;
428 }
429 }
430
431 /**
432 * Try to get the value of a specified property in this instance.
433 *
434 * @param propertyDefinition The property definition.
435 * @param propertyValue The property value
436 * @return True if property retrieved, false otherwise.
437 * @throws Exception
438 */
439 public boolean tryGetProperty(PropertyDefinitionBase propertyDefinition, OutParam<Object> propertyValue)
440 throws Exception {
441 return this.tryGetProperty(Object.class, propertyDefinition, propertyValue);
442 }
443
444 /**
445 * Try to get the value of a specified property in this instance.
446 *
447 * @param propertyDefinition the property definition
448 * @param propertyValue the property value
449 * @return true, if successful
450 * @throws Exception the exception
451 */
452 public <T> boolean tryGetProperty(Class<T> cls, PropertyDefinitionBase propertyDefinition,
453 OutParam<T> propertyValue) throws Exception {
454
455 PropertyDefinition propDef = (PropertyDefinition) propertyDefinition;
456 if (propDef != null) {
457 return this.getPropertyBag().tryGetPropertyType(cls, propDef, propertyValue);
458 } else {
459 // E14:226103 -- Other subclasses of PropertyDefinitionBase are not supported.
460 throw new UnsupportedOperationException(String.format(
461 "This operation isn't supported for property definition type %s.",
462 propertyDefinition.getType().getName()));
463 }
464 }
465
466 /**
467 * Gets the collection of loaded property definitions.
468 *
469 * @return the loaded property definitions
470 * @throws Exception the exception
471 */
472 public Collection<PropertyDefinitionBase> getLoadedPropertyDefinitions()
473 throws Exception {
474
475 Collection<PropertyDefinitionBase> propDefs =
476 new ArrayList<PropertyDefinitionBase>();
477 for (PropertyDefinition propDef : this.getPropertyBag().getProperties()
478 .keySet()) {
479 propDefs.add(propDef);
480 }
481
482 if (this.getExtendedProperties() != null) {
483 for (ExtendedProperty extProp : getExtendedProperties()) {
484 propDefs.add(extProp.getPropertyDefinition());
485 }
486 }
487
488 return propDefs;
489 }
490
491 /**
492 * Gets the service.
493 *
494 * @return the service
495 */
496 public ExchangeService getService() {
497 return service;
498 }
499
500 /**
501 * Sets the service.
502 *
503 * @param service the new service
504 */
505 protected void setService(ExchangeService service) {
506 this.service = service;
507 }
508
509 // / The property definition for the Id of this object.
510
511 /**
512 * Gets the id property definition.
513 *
514 * @return the id property definition
515 */
516 public PropertyDefinition getIdPropertyDefinition() {
517 return null;
518 }
519
520 // / The unique Id of this object.
521
522 /**
523 * Gets the id.
524 *
525 * @return the id
526 * @throws ServiceLocalException the service local exception
527 */
528 public ServiceId getId() throws ServiceLocalException {
529 PropertyDefinition idPropertyDefinition = this
530 .getIdPropertyDefinition();
531
532 OutParam<Object> serviceId = new OutParam<Object>();
533
534 if (idPropertyDefinition != null) {
535 this.getPropertyBag().tryGetValue(idPropertyDefinition, serviceId);
536 }
537
538 return (ServiceId) serviceId.getParam();
539 }
540
541 // / Indicates whether this object is a real store item, or if it's a local
542 // object
543 // / that has yet to be saved.
544
545 /**
546 * Checks if is new.
547 *
548 * @return true, if is new
549 * @throws ServiceLocalException the service local exception
550 */
551 public boolean isNew() throws ServiceLocalException {
552
553 ServiceId id = this.getId();
554
555 return id == null ? true : !id.isValid();
556
557 }
558
559 // / Gets a value indicating whether the object has been modified and should
560 // be saved.
561
562 /**
563 * Checks if is dirty.
564 *
565 * @return true, if is dirty
566 */
567 public boolean isDirty() {
568 return this.getPropertyBag().getIsDirty();
569
570 }
571
572 // Gets the extended property collection.
573
574 /**
575 * Gets the extended property.
576 *
577 * @return the extended property
578 * @throws Exception the exception
579 */
580 protected ExtendedPropertyCollection getExtendedProperties()
581 throws Exception {
582 return null;
583 }
584
585 /**
586 * Checks is the string is null or empty.
587 *
588 * @param namespacePrefix the namespace prefix
589 * @return true, if is null or empty
590 */
591 private boolean isNullOrEmpty(String namespacePrefix) {
592 return (namespacePrefix == null || namespacePrefix.isEmpty());
593
594 }
595
596 /**
597 * The on change.
598 */
599 private List<IServiceObjectChangedDelegate> onChange =
600 new ArrayList<IServiceObjectChangedDelegate>();
601
602 /**
603 * Adds the service object changed event.
604 *
605 * @param change the change
606 */
607 public void addServiceObjectChangedEvent(
608 IServiceObjectChangedDelegate change) {
609 this.onChange.add(change);
610 }
611
612 /**
613 * Removes the service object changed event.
614 *
615 * @param change the change
616 */
617 public void removeServiceObjectChangedEvent(
618 IServiceObjectChangedDelegate change) {
619 this.onChange.remove(change);
620 }
621
622 /**
623 * Clear service object changed event.
624 */
625 public void clearServiceObjectChangedEvent() {
626 this.onChange.clear();
627 }
628
629 }