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.core.service.schema; 025 026import microsoft.exchange.webservices.data.attribute.EditorBrowsable; 027import microsoft.exchange.webservices.data.core.EwsUtilities; 028import microsoft.exchange.webservices.data.core.ILazyMember; 029import microsoft.exchange.webservices.data.core.LazyMember; 030import microsoft.exchange.webservices.data.core.XmlElementNames; 031import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState; 032import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion; 033import microsoft.exchange.webservices.data.core.enumeration.property.PropertyDefinitionFlags; 034import microsoft.exchange.webservices.data.misc.OutParam; 035import microsoft.exchange.webservices.data.property.complex.ExtendedPropertyCollection; 036import microsoft.exchange.webservices.data.property.complex.ICreateComplexPropertyDelegate; 037import microsoft.exchange.webservices.data.property.definition.ComplexPropertyDefinition; 038import microsoft.exchange.webservices.data.property.definition.IndexedPropertyDefinition; 039import microsoft.exchange.webservices.data.property.definition.PropertyDefinition; 040import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase; 041import org.apache.commons.logging.Log; 042import org.apache.commons.logging.LogFactory; 043 044import java.lang.reflect.Field; 045import java.lang.reflect.Modifier; 046import java.util.ArrayList; 047import java.util.EnumSet; 048import java.util.HashMap; 049import java.util.Iterator; 050import java.util.List; 051import java.util.Map; 052 053/** 054 * Represents the base class for all item and folder schema. 055 */ 056@EditorBrowsable(state = EditorBrowsableState.Never) 057public 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}