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.schema;
025
026 import microsoft.exchange.webservices.data.attribute.EditorBrowsable;
027 import microsoft.exchange.webservices.data.core.EwsUtilities;
028 import microsoft.exchange.webservices.data.core.ILazyMember;
029 import microsoft.exchange.webservices.data.core.LazyMember;
030 import microsoft.exchange.webservices.data.core.XmlElementNames;
031 import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState;
032 import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
033 import microsoft.exchange.webservices.data.core.enumeration.property.PropertyDefinitionFlags;
034 import microsoft.exchange.webservices.data.misc.OutParam;
035 import microsoft.exchange.webservices.data.property.complex.ExtendedPropertyCollection;
036 import microsoft.exchange.webservices.data.property.complex.ICreateComplexPropertyDelegate;
037 import microsoft.exchange.webservices.data.property.definition.ComplexPropertyDefinition;
038 import microsoft.exchange.webservices.data.property.definition.IndexedPropertyDefinition;
039 import microsoft.exchange.webservices.data.property.definition.PropertyDefinition;
040 import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase;
041 import org.apache.commons.logging.Log;
042 import org.apache.commons.logging.LogFactory;
043
044 import java.lang.reflect.Field;
045 import java.lang.reflect.Modifier;
046 import java.util.ArrayList;
047 import java.util.EnumSet;
048 import java.util.HashMap;
049 import java.util.Iterator;
050 import java.util.List;
051 import java.util.Map;
052
053 /**
054 * Represents the base class for all item and folder schema.
055 */
056 @EditorBrowsable(state = EditorBrowsableState.Never)
057 public abstract class ServiceObjectSchema implements
058 Iterable<PropertyDefinition> {
059
060 private static final Log LOG = LogFactory.getLog(ServiceObjectSchema.class);
061
062 /**
063 * The lock object.
064 */
065 private static final Object lockObject = new Object();
066
067 /**
068 * List of all schema types. If you add a new ServiceObject subclass that
069 * has an associated schema, add the schema type to the list below.
070 */
071 private static LazyMember<List<Class<?>>> allSchemaTypes = new
072 LazyMember<List<Class<?>>>(new
073 ILazyMember<List<Class<?>>>() {
074 public List<Class<?>> createInstance() {
075 List<Class<?>> typeList = new ArrayList<Class<?>>();
076 // typeList.add()
077 /*
078 * typeList.add(AppointmentSchema.class);
079 * typeList.add(CalendarResponseObjectSchema.class);
080 * typeList.add(CancelMeetingMessageSchema.class);
081 * typeList.add(ContactGroupSchema.class);
082 * typeList.add(ContactSchema.class);
083 * typeList.add(EmailMessageSchema.class);
084 * typeList.add(FolderSchema.class);
085 * typeList.add(ItemSchema.class);
086 * typeList.add(MeetingMessageSchema.class);
087 * typeList.add(MeetingRequestSchema.class);
088 * typeList.add(PostItemSchema.class);
089 * typeList.add(PostReplySchema.class);
090 * typeList.add(ResponseMessageSchema.class);
091 * typeList.add(ResponseObjectSchema.class);
092 * typeList.add(ServiceObjectSchema.class);
093 * typeList.add(SearchFolderSchema.class);
094 * typeList.add(TaskSchema.class);
095 */
096 // Verify that all Schema types in the Managed API assembly
097 // have been included.
098 /*
099 * var missingTypes = from type in
100 * Assembly.GetExecutingAssembly().GetTypes() where
101 * type.IsSubclassOf(typeof(ServiceObjectSchema)) &&
102 * !typeList.Contains(type) select type; if
103 * (missingTypes.Count() > 0) { throw new
104 * ServiceLocalException
105 * ("SchemaTypeList does not include all
106 * defined schema types."
107 * ); }
108 */
109 return typeList;
110 }
111 });
112
113 /**
114 * Dictionary of all property definitions.
115 */
116 private static LazyMember<Map<String, PropertyDefinitionBase>>
117 allSchemaProperties = new
118 LazyMember<Map<String, PropertyDefinitionBase>>(
119 new ILazyMember<Map<String, PropertyDefinitionBase>>() {
120 public Map<String, PropertyDefinitionBase> createInstance() {
121 Map<String, PropertyDefinitionBase> propDefDictionary =
122 new HashMap<String, PropertyDefinitionBase>();
123 for (Class<?> c : ServiceObjectSchema.allSchemaTypes
124 .getMember()) {
125 ServiceObjectSchema.addSchemaPropertiesToDictionary(c,
126 propDefDictionary);
127 }
128 return propDefDictionary;
129 }
130 });
131
132 /**
133 * Adds schema property to dictionary.
134 *
135 * @param type Schema type.
136 * @param propDefDictionary The property definition dictionary.
137 */
138 protected static void addSchemaPropertiesToDictionary(Class<?> type,
139 Map<String, PropertyDefinitionBase> propDefDictionary) {
140 Field[] fields = type.getDeclaredFields();
141 for (Field field : fields) {
142 int modifier = field.getModifiers();
143 if (Modifier.isPublic(modifier) && Modifier.isStatic(modifier)) {
144 Object o;
145 try {
146 o = field.get(null);
147 if (o instanceof PropertyDefinition) {
148 PropertyDefinition propertyDefinition =
149 (PropertyDefinition) o;
150 // Some property definitions descend from
151 // ServiceObjectPropertyDefinition but don't have
152 // a Uri, like ExtendedProperties. Ignore them.
153 if (null != propertyDefinition.getUri() &&
154 !propertyDefinition.getUri().isEmpty()) {
155 PropertyDefinitionBase existingPropertyDefinition;
156 if (propDefDictionary
157 .containsKey(propertyDefinition.getUri())) {
158 existingPropertyDefinition = propDefDictionary
159 .get(propertyDefinition.getUri());
160 EwsUtilities
161 .ewsAssert(existingPropertyDefinition == propertyDefinition,
162 "Schema.allSchemaProperties." + "delegate",
163 String.format("There are at least " +
164 "two distinct property " +
165 "definitions with the" +
166 " following URI: %s", propertyDefinition.getUri()));
167 } else {
168 propDefDictionary.put(propertyDefinition
169 .getUri(), propertyDefinition);
170 // The following is a "generic hack" to register
171 // property that are not public and
172 // thus not returned by the above GetFields
173 // call. It is currently solely used to register
174 // the MeetingTimeZone property.
175 List<PropertyDefinition> associatedInternalProperties =
176 propertyDefinition.getAssociatedInternalProperties();
177 for (PropertyDefinition associatedInternalProperty : associatedInternalProperties) {
178 propDefDictionary
179 .put(associatedInternalProperty
180 .getUri(),
181 associatedInternalProperty);
182 }
183
184 }
185 }
186 }
187 } catch (IllegalArgumentException e) {
188 LOG.error(e);
189
190 // Skip the field
191 } catch (IllegalAccessException e) {
192 LOG.error(e);
193
194 // Skip the field
195 }
196
197 }
198 }
199 }
200
201 /**
202 * Adds the schema property names to dictionary.
203 *
204 * @param type The type.
205 * @param propertyNameDictionary The property name dictionary.
206 */
207 protected static void addSchemaPropertyNamesToDictionary(Class<?> type,
208 Map<PropertyDefinition, String> propertyNameDictionary) {
209
210 Field[] fields = type.getDeclaredFields();
211 for (Field field : fields) {
212 int modifier = field.getModifiers();
213 if (Modifier.isPublic(modifier) && Modifier.isStatic(modifier)) {
214 Object o;
215 try {
216 o = field.get(null);
217 if (o instanceof PropertyDefinition) {
218 PropertyDefinition propertyDefinition =
219 (PropertyDefinition) o;
220 propertyNameDictionary.put(propertyDefinition, field
221 .getName());
222 }
223 } catch (IllegalArgumentException e) {
224 LOG.error(e);
225
226 // Skip the field
227 } catch (IllegalAccessException e) {
228 LOG.error(e);
229
230 // Skip the field
231 }
232 }
233 }
234 }
235
236 /**
237 * Initializes a new instance.
238 */
239 protected ServiceObjectSchema() {
240 this.registerProperties();
241 }
242
243 /**
244 * Finds the property definition.
245 *
246 * @param uri The URI.
247 * @return Property definition.
248 */
249 public static PropertyDefinitionBase findPropertyDefinition(String uri) {
250 return ServiceObjectSchema.allSchemaProperties.getMember().get(uri);
251 }
252
253 /**
254 * Initialize schema property names.
255 */
256 public static void initializeSchemaPropertyNames() {
257 synchronized (lockObject) {
258 for (Class<?> type : ServiceObjectSchema.allSchemaTypes.getMember()) {
259 Field[] fields = type.getDeclaredFields();
260 for (Field field : fields) {
261 int modifier = field.getModifiers();
262 if (Modifier.isPublic(modifier) &&
263 Modifier.isStatic(modifier)) {
264 Object o;
265 try {
266 o = field.get(null);
267 if (o instanceof PropertyDefinition) {
268 PropertyDefinition propertyDefinition =
269 (PropertyDefinition) o;
270 propertyDefinition.setName(field.getName());
271 }
272 } catch (IllegalArgumentException e) {
273 LOG.error(e);
274
275 // Skip the field
276 } catch (IllegalAccessException e) {
277 LOG.error(e);
278
279 // Skip the field
280 }
281 }
282 }
283 }
284 }
285 }
286
287 /**
288 * Defines the ExtendedProperties property.
289 */
290 public static final PropertyDefinition extendedProperties =
291 new ComplexPropertyDefinition<ExtendedPropertyCollection>(
292 ExtendedPropertyCollection.class,
293 XmlElementNames.ExtendedProperty,
294 EnumSet.of(PropertyDefinitionFlags.AutoInstantiateOnRead,
295 PropertyDefinitionFlags.ReuseInstance,
296 PropertyDefinitionFlags.CanSet,
297 PropertyDefinitionFlags.CanUpdate),
298 ExchangeVersion.Exchange2007_SP1,
299 new ICreateComplexPropertyDelegate<ExtendedPropertyCollection>() {
300 public ExtendedPropertyCollection createComplexProperty() {
301 return new ExtendedPropertyCollection();
302 }
303 });
304
305 /**
306 * The property.
307 */
308 private Map<String, PropertyDefinition> properties =
309 new HashMap<String, PropertyDefinition>();
310
311 /**
312 * The visible property.
313 */
314 private List<PropertyDefinition> visibleProperties =
315 new ArrayList<PropertyDefinition>();
316
317 /**
318 * The first class property.
319 */
320 private List<PropertyDefinition> firstClassProperties =
321 new ArrayList<PropertyDefinition>();
322
323 /**
324 * The first class summary property.
325 */
326 private List<PropertyDefinition> firstClassSummaryProperties =
327 new ArrayList<PropertyDefinition>();
328
329 private List<IndexedPropertyDefinition> indexedProperties =
330 new ArrayList<IndexedPropertyDefinition>();
331
332 /**
333 * Registers a schema property.
334 *
335 * @param property The property to register.
336 * @param isInternal Indicates whether the property is internal or should be
337 * visible to developers.
338 */
339 private void registerProperty(PropertyDefinition property,
340 boolean isInternal) {
341 this.properties.put(property.getXmlElement(), property);
342
343 if (!isInternal) {
344 this.visibleProperties.add(property);
345 }
346
347 // If this property does not have to be requested explicitly, add
348 // it to the list of firstClassProperties.
349 if (!property.hasFlag(PropertyDefinitionFlags.MustBeExplicitlyLoaded)) {
350 this.firstClassProperties.add(property);
351 }
352
353 // If this property can be found, add it to the list of
354 // firstClassSummaryProperties
355 if (property.hasFlag(PropertyDefinitionFlags.CanFind)) {
356 this.firstClassSummaryProperties.add(property);
357 }
358 }
359
360 /**
361 * Registers a schema property that will be visible to developers.
362 *
363 * @param property The property to register.
364 */
365 protected void registerProperty(PropertyDefinition property) {
366 this.registerProperty(property, false);
367 }
368
369 /**
370 * Registers an internal schema property.
371 *
372 * @param property The property to register.
373 */
374 protected void registerInternalProperty(PropertyDefinition property) {
375 this.registerProperty(property, true);
376 }
377
378 /**
379 * Registers an indexed property.
380 *
381 * @param indexedProperty The indexed property to register.
382 */
383 protected void registerIndexedProperty(IndexedPropertyDefinition
384 indexedProperty) {
385 this.indexedProperties.add(indexedProperty);
386 }
387
388
389 /**
390 * Registers property.
391 */
392 protected void registerProperties() {
393 }
394
395 /**
396 * Gets the list of first class property for this service object type.
397 *
398 * @return the first class property
399 */
400 public List<PropertyDefinition> getFirstClassProperties() {
401 return this.firstClassProperties;
402 }
403
404 /**
405 * Gets the list of first class summary property for this service object
406 * type.
407 *
408 * @return the first class summary property
409 */
410 public List<PropertyDefinition> getFirstClassSummaryProperties() {
411 return this.firstClassSummaryProperties;
412 }
413
414 /**
415 * Tries to get property definition.
416 *
417 * @param xmlElementName Name of the XML element.
418 * @param propertyDefinitionOutParam The property definition.
419 * @return True if property definition exists.
420 */
421 public boolean tryGetPropertyDefinition(String xmlElementName,
422 OutParam<PropertyDefinition> propertyDefinitionOutParam) {
423 if (this.properties.containsKey(xmlElementName)) {
424 propertyDefinitionOutParam.setParam(this.properties
425 .get(xmlElementName));
426 return true;
427 } else {
428 return false;
429 }
430 }
431
432 /**
433 * Returns an iterator over a set of elements of type T.
434 *
435 * @return an Iterator.
436 */
437 @Override
438 public Iterator<PropertyDefinition> iterator() {
439 return this.visibleProperties.iterator();
440 }
441 }