/* * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Nuxeo - initial API and implementation * * $Id$ */ package org.eclipse.ecr.core.api.model; import java.io.Serializable; import java.util.Collection; import java.util.Iterator; import org.eclipse.ecr.core.api.Blob; import org.eclipse.ecr.core.schema.types.ComplexType; import org.eclipse.ecr.core.schema.types.Field; import org.eclipse.ecr.core.schema.types.Schema; import org.eclipse.ecr.core.schema.types.Type; import org.nuxeo.common.utils.Path; /** * Document properties are instances of document schema fields. * <p> * You can say that a {@link Field} object is like a Java class and a Property * object like a class instance. Thus, schemas defines fields (or * elements) which have a name and a type, and each field of a document can be * instantiated (if the schema permits) as a Property object. * <p> * Properties are always bound to a schema field that provides the type and * constraints on the property values. An exception is the root property the * {@link DocumentPart} object which is not bound to a field but to a schema. * <p> * So properties are holding the actual values for each defined field. * <p> * The usual way of using properties is to get a document from the storage * server then modify document properties and send them back to the storage * server to that modifications are be stored. * <p> * Note that the storage server can be on a remote machine so when modifying * properties remotely they are serialized and sent through the network between * the two machines. This means properties must hold serializable values and * also they must store some state flags so that the storage can decide which * property was modified and how in order to correctly update the stored * versions. * <p> * As we have seen each property may hold a serializable value which we will * refer to as the <code>normalized</code> property value. For each schema * field type there is only one java serializable object representation that * will be used as the normalized value. The property API is giving you the * possibility to use different compatible objects when setting or getting * property values. Each property implementation will automatically convert the * given value into a normalized one; so internally only the normalized value is * stored. * <p> * For example, for date properties you may use either <code>Date</code> * or <code>Calendar</code> when setting or retrieving a property value, but * the normalized value will be the <code>Calendar</code> one. * <p> * As we have seen, properties keep some state flags. Property flags can be divided in * two groups: * <ul> * <li> Dirty Flags - that reflect the public status of the document * <li> Internal Flags - that reflect some internal state * </ul> * <p> * Property Types: * <p> * Before going deeper in property flags, we will talk first about property * types. There are several types of properties that are very closed on the type * of fields they are bound onto. * <ul> * <li> Root Property (or <code>DocumentPart</code>) - this is a special * property that is bound to a schema instead of a field And it is the root of * the property tree. * <li> Complex Properties - container properties that are bound to complex * field types that can be represented as java <code>Map</code> objects. These * properties contains a set of schema defined properties. You cannot add new * child properties. You can only modify existing child properties. Complex * property values are expressed as java <code>Map</code> objects. * <ul> * <li> Structured Properties - this is a special case of complex properties. * The difference is that structured property values are expressed as <i>scalar</i> * java objects instead of java maps. By scalar java objects we mean any well * structured object which is not a container like a <code>Map</code> or a * <code>Collection</code>. These objects are usually as scalar values - it * doesn't make sense for example to set only some parts of that objects without * creating the object completely. An example of usage are Blob properties that * use {@link Blob} values. * </ul> * <li> List Properties - container properties that are bound to list field * types. * <li> Scalar Properties - atomic properties that are bound to scalar field * types and that are using as values scalar or primitive java objects like * arrays, primitives, String, Date etc. * </ul> * <p> * As we've seen there are 2 categories of properties: container properties and * scalar properties Complex and list properties are container properties while * structured and scalar properties are scalar. * <p> * Dirty Flags: * <p> * Dirty flags are used to keep track of the dirty state of a property. The * following flags are supported: * <ul> * <li> <code>IS_PHANTOM</code> - whether the property is existing in the * storage (was explicitly set by the user) or it was dynamically generated * using the default value by the implementation to fulfill schema definition. * This applies to all property types * <li> <code>IS_MODIFIED</code> - whether the property value was modified. * This applies to all property types. * <li> <code>IS_NEW</code> - whether the property is a new property that was * added to a parent list property This applies only to properties that are * children of a list property. * <li> <code>IS_REMOVED</code> - whether a property was removed. A removed * property will be removed from the storage and the next time you access the * property it will be a <code>phantom</code> one. This applies only to * properties that are children of a complex property. * <li> <code>IS_MOVED</code> - whether the property was moved on another * position inside the container list. This applies only to properties that are * children of a list property. * </ul> * <p> * There are several constraints on how property flags may change. This is a * list of all changes that may occur over dirty flags: * <ul> * <li> NONE + MODIFIED => MODFIED * <li> NONE + REMOVED => REMOVED * <li> NONE + MOVED => MOVED * <li> PHANTOM + MODIFIED => MODIFIED * <li> NEW + MODIFIED => NEW | MODIFIED * <li> NEW + MOVED => NEW | MOVED * <li> MODIFIED + REMOVED => REMOVED * <li> MODIFIED + MOVED => MODIFIED | MOVED * <li> MODIFIED + MODIFIED => MODIFIED * </ul> * <p> * The combinations not listed above are not permitted. * <p> * In case of list items, the REMOVED flag is not used since the property will * be physically removed from the property tree. * <p> * Also when the dirty flag of a children property changes, its parent is * informed to update its MODIFIED flag if needed. This way a modification on a * children property is propagated to parents in the form of a MODIFIED flag. * <p> * Internal Flags: * <p> * Internal flags are used by the implementation to keep some internal state. * For these flags you should look into the implementation * <p> * Apart flags properties can also hold some random user data using * {@link Property#setData(Object)} and {@link Property#getData()} methods. This * can be used for example to keep a context attached to a property. But be * aware when using this you should provide serializable objects as the data you * are attaching otherwise if properties are serialized / unserialized this will * generate errors. The API is not forcing you to use serializable values since * you can also use this feature to store temporary context data that will not * be sent over the network. * * @see <code>TestPropertyModel</code> for usage of property API * * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a> */ public interface Property extends Cloneable, Serializable, Iterable<Property> { /** * No dirty flags set. */ int NONE = 0; /** * Flag used to mark a property as new. Property was added to a list. */ int IS_NEW = 1; /** * Flag used to mark a property as dirty. Property value was modified. */ int IS_MODIFIED = 2; /** * Flag used to mark a property as dirty. Property was removed. */ int IS_REMOVED = 4; /** * Flag used to mark a property as dirty. Property was moved to another * index. */ int IS_MOVED = 8; /** * Flag used to mark a property as phantom. */ int IS_PHANTOM = 16; /** * A mask for the first 4 flags: NEW, REMOVED, MODIFIED, MOVED. */ int IS_DIRTY = IS_NEW | IS_REMOVED | IS_MOVED | IS_MODIFIED; /** * A mask for public flags. */ int DIRTY_MASK = IS_PHANTOM | IS_DIRTY; /** * Tests if this property is new (just created but not yet stored). * <p> * A property is new when added to a collection. This is the typical state * for a new property added to a list * * @return true if this property is new, false otherwise */ boolean isNew(); /** * Tests if a property is flagged as removed. Removed properties are child * property that were removed from their container. * * @return if the property was removed, false otherwise */ boolean isRemoved(); /** * Tests if a property value was modified. * * @return if the property was removed, false otherwise */ boolean isModified(); /** * Tests if a property value was moved to another index in the parent list * if any. * * @return if the property was removed, false otherwise */ boolean isMoved(); /** * Tests if the property is a phantom. This means it doesn't exists yet in * the storage and it is not a new property. This is a placeholder for a * property that is defined by the schema but was not yet set. * * @return true if a phantom false otherwise */ boolean isPhantom(); /** * Tests whether a property is dirty. * <p> * This tests whether or not a dirty flag is set on the property. * * @return true if the property changed */ boolean isDirty(); /** * Get the dirty flags that are set on this property. * * @return the dirty flags mask */ int getDirtyFlags(); /** * Notify the property that its changes was stored so it can safely remove * dirty flags. * <p> * Dirty flags are removed according to the type of the modifications. * This way if the property was REMOVED it becomes a PHANTOM otherwise all * dirty flags are cleared. * <p> * This method should be used by storage implementors to notify the property * it should reset its dirty flags. Note that clearing dirty flags is not * propagated to the parent property or to children. You need to clear dirty * flags explicitly for each property. */ void clearDirtyFlags(); /** * Whether the property is read only. * * @return true if read only false otherwise */ boolean isReadOnly(); /** * Checks whether this property is validating values when set. * * @return true if validating false otherwise */ boolean isValidating(); /** * Sets the read only flag. * * @param value true to set this property read only false otherwise */ void setReadOnly(boolean value); /** * Sets the validating flag. * * @param value true to put validating on false otherwise */ void setValidating(boolean value); /** * Tests whether this property is of a map (complex) type. * * @return true if the property is of map type, false otherwise */ boolean isComplex(); /** * Tests whether this property is of a list type. * * @return true if the property is of list type, false otherwise */ boolean isList(); /** * Tests whether this property is of a scalar type. * * @return true if the property is of a scalar type, false otherwise */ boolean isScalar(); /** * Whether this property is a container - this means the property value is a * map or a list. * <p> * Container properties don't have a scalar values. Container values are * computed each time they are requested - by calling on of the * <code>getValue</code> methods - by collecting the values of the child * properties. * * @return true if scalar false otherwise */ boolean isContainer(); /** * Gets the property name. * * @return the property name */ String getName(); /** * Gets the path of this property relative to the owner document. * <p> * The path for top level properties is the same to the property name. * * @return the path */ String getPath(); /** * Get the type of the field corresponding to this property. * * @return the property type */ Type getType(); /** * Gets the field corresponding to this property. * <p> * The field is the object defining the property. You can see the field as a * java class and the property as a class instance * * @return */ Field getField(); /** * Gets the property parent. * * @return the property parent for sub properties or null for top level * properties */ Property getParent(); /** * Gets the document schema defining the property tree from which the * property belongs. * * @return the document schema owning the field corresponding to the * property */ Schema getSchema(); /** * Gets the root property. * * @return the root property */ DocumentPart getRoot(); /** * Initializes the property with the given normalized value. * <p> * The given value must be normalized - note that no check is done on that. * <p> * The phantom flag is unset by this operation. * <p> * This method should be used to initialize properties. * * @param value the normalized value to set * */ void init(Serializable value) throws PropertyException; /** * Sets this property value. The value will be first normalized and then * set. * <p> * For complex or list properties the value will be set recursively (as a * map or list value). * * @param value the value to set * * @throws {@link InvalidPropertyValueException} if the given value type is * not compatible with the expected value type */ void setValue(Object value) throws PropertyException; /** * Gets the property normalized value. * <p> * Normalized values are of the java type that correspond to the field type. * * @return the property value, which may be null */ Serializable getValue() throws PropertyException; /** * Gets the property normalized value for write. * <p> * Can be different fropm {@link #getValue()} in cases where the property * adapts the value it is given to store. * * @return the property value to use for write, which may be null * @since 5.2.1 */ Serializable getValueForWrite() throws PropertyException; /** * Gets the property value as the given type. * <p> * The value is converted using the registered converter to the given type. * <p> * If conversion is not supported a runtime exception will be triggered. * * @return the property value, which may be null */ <T> T getValue(Class<T> type) throws PropertyException; /** * Removes this property from the tree. * <p> * This method marks the property as dirty and sets its value to null. * * @return the old property value */ Serializable remove() throws PropertyException; /** * Gets the child property having the given name. * <p> * If the property is a scalar, this will return always null. * <p> * The given name should be the full name (i.e. prefixed name if any prefix * exists). * <p> * If a non prefixed name is given, the first child property having the * given local name will be returned. * <p> * Relative paths are not resolved. THis method is intended to lookup direct * children. For path lookups use {@link Property#resolvePath(String)} * instead. * * @param name the child property name (the full name including the prefix * if any) * @return the child property if any null if no child property with that * name is found or if the property is a scalar * @throws {@link UnsupportedOperationException} if the property is a scalar * property (doesn't have children) * @throws {@link PropertyNotFoundException} if the child property is not * found in the type definition */ Property get(String name) throws PropertyNotFoundException; /** * Get the child property given it's index. This operation is mandatory for * List properties. * <p> * If this method is not supported an {@link UnsupportedOperationException} * must be thrown * <p> * Relative paths are not resolved. THis method is intended to lookup direct * chilren. For path lookups, use {@link Property#resolvePath(String)} * instead. * * @param index * @return the child property if any null if no child property with that * name is found or if the property is a scalar * @throws {@link UnsupportedOperationException} if the property is a scalar * property (doesn't have children) * @throws {@link PropertyNotFoundException} if the child property is not * found in the type definition */ Property get(int index) throws PropertyNotFoundException; /** * Sets a child property value given its index. This method is required only * for List properties. * <p> * If this method is not supported, an {@link UnsupportedOperationException} * must be thrown. * <p> * This method will mark the child value as dirty for existing values and in * the case of map properties it will mark phantom properties as new * properties. * * @param index * @param value the new value * @throws {@link UnsupportedOperationException} if the property is a scalar * property (doesn't have children) * @throws {@link PropertyNotFoundException} if the child property is not * found in the type definition */ void setValue(int index, Object value) throws PropertyException; /** * Get a collection over the children properties. This includes all children * including phantom ones (those who are not yet set by the user). * <p> * The returned collection is ordered for list properties, and unordered for * complex properties * <p> * Be aware that this method is creating phantom child properties for all * schema fields that are not yet set. * * @return the children properties */ Collection<Property> getChildren(); /** * Get the count of the children properties. This includes phantom * properties. So the returned size will be equal to the one returned by the * property {@link ComplexType#getFieldsCount()}. * * @return the children properties count */ int size(); /** * Appends a new value to the list. A new property will be created to store * the given value and appended to the children list. * <p> * The created property will be marked as {@link Property#isNew()}. * * @param value * @return the added property */ Property addValue(Object value) throws PropertyException; /** * Inserts at the given position a new value to the list. A new property * will be created to store the given value and appended to the children * list. * <p> * The created property will be marked as {@link Property#isNew()}. * * @param value * @param index the position to insert the value * @return the added property */ Property addValue(int index, Object value) throws PropertyException; /** * Creates an empty child property and adds it as a property to the list * container. * <p> * This method is useful to construct lists. * * @return the created property * @throws PropertyException */ Property addEmpty() throws PropertyException; /** * Moves a property position into the parent container list. * <p> * This method applies only for list item properties. The given index * includes removed properties. * * @param index the position in the parent container to move this property * @throws UnsupportedOperationException if the operation is not supported * by the target property */ void moveTo(int index); /** * Same as {@link Property#resolvePath(Path)} but with a string path as * argument. This is the same as calling <code>resolvePath(new Path(path))</code>. * * @param path the string path to resolve. * @return the resolved property * @throws PropertyNotFoundException if the path cannot be resolved */ Property resolvePath(String path) throws PropertyNotFoundException; /** * Resolves the given path relative to the current property and return the * property if any is found otherwise throws an exception. * <p> * The path format is a subset of XPath. Thus, / is used as path element * separator, [n] for list element indexes. Attribute separator '@' are not * supported since all properties are assumed to be elements. Also you .. * and . can be used as element names. * <p> * Example of paths: * <ul> * <li><code>dc:title</code> * <li><code>attachments/item[2]/mimeType</code> * <li><code>../dc:title</code> * </ul> * * @param path the path to resolve. * @return the resolved property * @throws PropertyNotFoundException if the path cannot be resolved */ Property resolvePath(Path path) throws PropertyNotFoundException; /** * Gets the value of the property resolved using the given path. * <p> * This method is a shortcut for: <code>resolvePath(path).getValue()</code>. * * @param path the path to the property * @return the property value */ Serializable getValue(String path) throws PropertyException; /** * Gets the value of the property resolved using the given path. * <p> * The value will be converted to the given type if possible, otherwise an * exception will be thrown. * <p> * This method is a shortcut for: * <code>resolvePath(path).getValue(type)</code>. * * @param <T> The type of the value to return * @param type the class of the value * @param path the java path of the property value * @return the value * @throws PropertyException */ <T> T getValue(Class<T> type, String path) throws PropertyException; /** * Sets the value of the property resolved using the given path. * <p> * This method is a shortcut for: * <code>resolvePath(path).setValue(value)</code>. * * @param path the property path * @param value the value * @throws PropertyException */ void setValue(String path, Object value) throws PropertyException; /** * Normalizes the given value as dictated by the property type. * <p> * Normalized values are the ones that are used for transportation over the * net and that are given to the storage implementation to be stored in the * repository * <p> * Normalized values must be {@link Serializable} * <p> * If the given value is already normalized it will be returned back. * * @param value the value to normalize according to the property type * @return the normalized value */ Serializable normalize(Object value) throws PropertyConversionException; /** * Checks if the given value is a normalized one. This means the value has a * type that is normalized. * <p> * Null values are considered as normalized. * * @param value the value to check * @return true if the value is normalized false otherwise */ boolean isNormalized(Object value); /** * Converts the given normalized value to the given type. * <p> * If the value has already the given type it will be returned back. * * @param value the normalized value to convert * @param toType the conversion type * @return the converted value, which may be null * * @throws PropertyConversionException if the conversion cannot be made * because of type incompatibilities */ <T> T convertTo(Serializable value, Class<T> toType) throws PropertyConversionException; /** * Validates the given value type. * <p> * Tests if the given value type can be converted to a normalized type and * thus a value of this type can be set to that property. * * @param type the type to validate * @return true if the type is valid, false otherwise */ boolean validateType(Class<?> type); /** * Validates the given normalized value. * <p> * Only normalized values can be validated. * <p> * If the value is not validated, returns false. * * @param value the value to validate * @return true if the value is valid, false otherwise * * @see Property#validateType(Class) */ boolean validate(Serializable value); /** * Creates a new and empty instance of a normalized value. * <p> * Empty is used in the sense of a value that has not been initialized or * can be considered as an empty value. For example for the {@link String} * type the empty value will be the empty string "" * * @return the empty instance the empty instance, or null for some * implementations */ Object newInstance(); /** * Method that implement the visitor pattern. * <p> * The visitor must return null to stop visiting children otherwise a * context object that will be passed as the arg argument to children * * @param visitor the visitor to accept * @param arg an argument passed to the visitor. This should be used by the * visitor to carry on the visiting context. */ void accept(PropertyVisitor visitor, Object arg) throws PropertyException; /** * Compare the two properties by content. * * @param property * @return true If the properties have a similar content, otherwise false * @throws PropertyException */ boolean isSameAs(Property property) throws PropertyException; /** * Gets an iterator over the dirty children properties. * * @return the iterator */ Iterator<Property> getDirtyChildren(); /** * Sets the application-defined data to associated it with the receiver * property. * <p> * The property data is reserved for the implementation and you must not * directly set it. Data attached with properties must be * {@link Serializable} objects if you want to serialize them along with the * property * * @param value */ void setData(Object value); /** * Sets the application defined data associated with the receiver under the * given key. * <p> * The property data is reserved for the implementation and you must not * directly set it. Data attached with properties must be * {@link Serializable} objects if you want to serialize them along with the * property. * * @param key * @param value */ void setData(String key, Object value); /** * Returns the application defined data associated with the receiver, or * null if it has not been set. * <p> * The property data is reserved for the implementation and you must not * directly set it. Data attached with properties must be * {@link Serializable} objects if you want to serialize them along with the * property. */ Object getData(); /** * Returns the application defined data associated with the receiver under * the given key, or null if it has not been set. * <p> * You are free to set any data you want on properties but be aware to use * {@link Serializable} objects if you want to serialize them along with the * property. * * @param key */ Object getData(String key); }