/* * The MIT License * Copyright (c) 2012 Microsoft Corporation * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ package microsoft.exchange.webservices.data.core.service.schema; import microsoft.exchange.webservices.data.attribute.EditorBrowsable; import microsoft.exchange.webservices.data.core.EwsUtilities; import microsoft.exchange.webservices.data.core.ILazyMember; import microsoft.exchange.webservices.data.core.LazyMember; import microsoft.exchange.webservices.data.core.XmlElementNames; import microsoft.exchange.webservices.data.core.enumeration.attribute.EditorBrowsableState; import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion; import microsoft.exchange.webservices.data.core.enumeration.property.PropertyDefinitionFlags; import microsoft.exchange.webservices.data.misc.OutParam; import microsoft.exchange.webservices.data.property.complex.ExtendedPropertyCollection; import microsoft.exchange.webservices.data.property.complex.ICreateComplexPropertyDelegate; import microsoft.exchange.webservices.data.property.definition.ComplexPropertyDefinition; import microsoft.exchange.webservices.data.property.definition.IndexedPropertyDefinition; import microsoft.exchange.webservices.data.property.definition.PropertyDefinition; import microsoft.exchange.webservices.data.property.definition.PropertyDefinitionBase; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Represents the base class for all item and folder schema. */ @EditorBrowsable(state = EditorBrowsableState.Never) public abstract class ServiceObjectSchema implements Iterable<PropertyDefinition> { private static final Log LOG = LogFactory.getLog(ServiceObjectSchema.class); /** * The lock object. */ private static final Object lockObject = new Object(); /** * List of all schema types. If you add a new ServiceObject subclass that * has an associated schema, add the schema type to the list below. */ private static LazyMember<List<Class<?>>> allSchemaTypes = new LazyMember<List<Class<?>>>(new ILazyMember<List<Class<?>>>() { public List<Class<?>> createInstance() { List<Class<?>> typeList = new ArrayList<Class<?>>(); // typeList.add() /* * typeList.add(AppointmentSchema.class); * typeList.add(CalendarResponseObjectSchema.class); * typeList.add(CancelMeetingMessageSchema.class); * typeList.add(ContactGroupSchema.class); * typeList.add(ContactSchema.class); * typeList.add(EmailMessageSchema.class); * typeList.add(FolderSchema.class); * typeList.add(ItemSchema.class); * typeList.add(MeetingMessageSchema.class); * typeList.add(MeetingRequestSchema.class); * typeList.add(PostItemSchema.class); * typeList.add(PostReplySchema.class); * typeList.add(ResponseMessageSchema.class); * typeList.add(ResponseObjectSchema.class); * typeList.add(ServiceObjectSchema.class); * typeList.add(SearchFolderSchema.class); * typeList.add(TaskSchema.class); */ // Verify that all Schema types in the Managed API assembly // have been included. /* * var missingTypes = from type in * Assembly.GetExecutingAssembly().GetTypes() where * type.IsSubclassOf(typeof(ServiceObjectSchema)) && * !typeList.Contains(type) select type; if * (missingTypes.Count() > 0) { throw new * ServiceLocalException * ("SchemaTypeList does not include all * defined schema types." * ); } */ return typeList; } }); /** * Dictionary of all property definitions. */ private static LazyMember<Map<String, PropertyDefinitionBase>> allSchemaProperties = new LazyMember<Map<String, PropertyDefinitionBase>>( new ILazyMember<Map<String, PropertyDefinitionBase>>() { public Map<String, PropertyDefinitionBase> createInstance() { Map<String, PropertyDefinitionBase> propDefDictionary = new HashMap<String, PropertyDefinitionBase>(); for (Class<?> c : ServiceObjectSchema.allSchemaTypes .getMember()) { ServiceObjectSchema.addSchemaPropertiesToDictionary(c, propDefDictionary); } return propDefDictionary; } }); /** * Adds schema property to dictionary. * * @param type Schema type. * @param propDefDictionary The property definition dictionary. */ protected static void addSchemaPropertiesToDictionary(Class<?> type, Map<String, PropertyDefinitionBase> propDefDictionary) { Field[] fields = type.getDeclaredFields(); for (Field field : fields) { int modifier = field.getModifiers(); if (Modifier.isPublic(modifier) && Modifier.isStatic(modifier)) { Object o; try { o = field.get(null); if (o instanceof PropertyDefinition) { PropertyDefinition propertyDefinition = (PropertyDefinition) o; // Some property definitions descend from // ServiceObjectPropertyDefinition but don't have // a Uri, like ExtendedProperties. Ignore them. if (null != propertyDefinition.getUri() && !propertyDefinition.getUri().isEmpty()) { PropertyDefinitionBase existingPropertyDefinition; if (propDefDictionary .containsKey(propertyDefinition.getUri())) { existingPropertyDefinition = propDefDictionary .get(propertyDefinition.getUri()); EwsUtilities .ewsAssert(existingPropertyDefinition == propertyDefinition, "Schema.allSchemaProperties." + "delegate", String.format("There are at least " + "two distinct property " + "definitions with the" + " following URI: %s", propertyDefinition.getUri())); } else { propDefDictionary.put(propertyDefinition .getUri(), propertyDefinition); // The following is a "generic hack" to register // property that are not public and // thus not returned by the above GetFields // call. It is currently solely used to register // the MeetingTimeZone property. List<PropertyDefinition> associatedInternalProperties = propertyDefinition.getAssociatedInternalProperties(); for (PropertyDefinition associatedInternalProperty : associatedInternalProperties) { propDefDictionary .put(associatedInternalProperty .getUri(), associatedInternalProperty); } } } } } catch (IllegalArgumentException e) { LOG.error(e); // Skip the field } catch (IllegalAccessException e) { LOG.error(e); // Skip the field } } } } /** * Adds the schema property names to dictionary. * * @param type The type. * @param propertyNameDictionary The property name dictionary. */ protected static void addSchemaPropertyNamesToDictionary(Class<?> type, Map<PropertyDefinition, String> propertyNameDictionary) { Field[] fields = type.getDeclaredFields(); for (Field field : fields) { int modifier = field.getModifiers(); if (Modifier.isPublic(modifier) && Modifier.isStatic(modifier)) { Object o; try { o = field.get(null); if (o instanceof PropertyDefinition) { PropertyDefinition propertyDefinition = (PropertyDefinition) o; propertyNameDictionary.put(propertyDefinition, field .getName()); } } catch (IllegalArgumentException e) { LOG.error(e); // Skip the field } catch (IllegalAccessException e) { LOG.error(e); // Skip the field } } } } /** * Initializes a new instance. */ protected ServiceObjectSchema() { this.registerProperties(); } /** * Finds the property definition. * * @param uri The URI. * @return Property definition. */ public static PropertyDefinitionBase findPropertyDefinition(String uri) { return ServiceObjectSchema.allSchemaProperties.getMember().get(uri); } /** * Initialize schema property names. */ public static void initializeSchemaPropertyNames() { synchronized (lockObject) { for (Class<?> type : ServiceObjectSchema.allSchemaTypes.getMember()) { Field[] fields = type.getDeclaredFields(); for (Field field : fields) { int modifier = field.getModifiers(); if (Modifier.isPublic(modifier) && Modifier.isStatic(modifier)) { Object o; try { o = field.get(null); if (o instanceof PropertyDefinition) { PropertyDefinition propertyDefinition = (PropertyDefinition) o; propertyDefinition.setName(field.getName()); } } catch (IllegalArgumentException e) { LOG.error(e); // Skip the field } catch (IllegalAccessException e) { LOG.error(e); // Skip the field } } } } } } /** * Defines the ExtendedProperties property. */ public static final PropertyDefinition extendedProperties = new ComplexPropertyDefinition<ExtendedPropertyCollection>( ExtendedPropertyCollection.class, XmlElementNames.ExtendedProperty, EnumSet.of(PropertyDefinitionFlags.AutoInstantiateOnRead, PropertyDefinitionFlags.ReuseInstance, PropertyDefinitionFlags.CanSet, PropertyDefinitionFlags.CanUpdate), ExchangeVersion.Exchange2007_SP1, new ICreateComplexPropertyDelegate<ExtendedPropertyCollection>() { public ExtendedPropertyCollection createComplexProperty() { return new ExtendedPropertyCollection(); } }); /** * The property. */ private Map<String, PropertyDefinition> properties = new HashMap<String, PropertyDefinition>(); /** * The visible property. */ private List<PropertyDefinition> visibleProperties = new ArrayList<PropertyDefinition>(); /** * The first class property. */ private List<PropertyDefinition> firstClassProperties = new ArrayList<PropertyDefinition>(); /** * The first class summary property. */ private List<PropertyDefinition> firstClassSummaryProperties = new ArrayList<PropertyDefinition>(); private List<IndexedPropertyDefinition> indexedProperties = new ArrayList<IndexedPropertyDefinition>(); /** * Registers a schema property. * * @param property The property to register. * @param isInternal Indicates whether the property is internal or should be * visible to developers. */ private void registerProperty(PropertyDefinition property, boolean isInternal) { this.properties.put(property.getXmlElement(), property); if (!isInternal) { this.visibleProperties.add(property); } // If this property does not have to be requested explicitly, add // it to the list of firstClassProperties. if (!property.hasFlag(PropertyDefinitionFlags.MustBeExplicitlyLoaded)) { this.firstClassProperties.add(property); } // If this property can be found, add it to the list of // firstClassSummaryProperties if (property.hasFlag(PropertyDefinitionFlags.CanFind)) { this.firstClassSummaryProperties.add(property); } } /** * Registers a schema property that will be visible to developers. * * @param property The property to register. */ protected void registerProperty(PropertyDefinition property) { this.registerProperty(property, false); } /** * Registers an internal schema property. * * @param property The property to register. */ protected void registerInternalProperty(PropertyDefinition property) { this.registerProperty(property, true); } /** * Registers an indexed property. * * @param indexedProperty The indexed property to register. */ protected void registerIndexedProperty(IndexedPropertyDefinition indexedProperty) { this.indexedProperties.add(indexedProperty); } /** * Registers property. */ protected void registerProperties() { } /** * Gets the list of first class property for this service object type. * * @return the first class property */ public List<PropertyDefinition> getFirstClassProperties() { return this.firstClassProperties; } /** * Gets the list of first class summary property for this service object * type. * * @return the first class summary property */ public List<PropertyDefinition> getFirstClassSummaryProperties() { return this.firstClassSummaryProperties; } /** * Tries to get property definition. * * @param xmlElementName Name of the XML element. * @param propertyDefinitionOutParam The property definition. * @return True if property definition exists. */ public boolean tryGetPropertyDefinition(String xmlElementName, OutParam<PropertyDefinition> propertyDefinitionOutParam) { if (this.properties.containsKey(xmlElementName)) { propertyDefinitionOutParam.setParam(this.properties .get(xmlElementName)); return true; } else { return false; } } /** * Returns an iterator over a set of elements of type T. * * @return an Iterator. */ @Override public Iterator<PropertyDefinition> iterator() { return this.visibleProperties.iterator(); } }