001/*
002 * Copyright (c) 2006-2011 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the Eclipse Public License v1.0
006 * which accompanies this distribution, and is available at
007 * http://www.eclipse.org/legal/epl-v10.html
008 *
009 * Contributors:
010 *     Nuxeo - initial API and implementation
011 *
012 * $Id$
013 */
014
015package org.nuxeo.ecm.core.api.model;
016
017import java.io.Serializable;
018import java.util.Collection;
019import java.util.Iterator;
020
021import org.nuxeo.common.utils.Path;
022import org.nuxeo.ecm.core.api.Blob;
023import org.nuxeo.ecm.core.api.PropertyException;
024import org.nuxeo.ecm.core.api.model.resolver.PropertyObjectResolver;
025import org.nuxeo.ecm.core.schema.types.ComplexType;
026import org.nuxeo.ecm.core.schema.types.Field;
027import org.nuxeo.ecm.core.schema.types.Schema;
028import org.nuxeo.ecm.core.schema.types.Type;
029
030/**
031 * Document properties are instances of document schema fields.
032 * <p>
033 * You can say that a {@link Field} object is like a Java class and a Property object like a class instance. Thus,
034 * schemas defines fields (or elements) which have a name and a type, and each field of a document can be instantiated
035 * (if the schema permits) as a Property object.
036 * <p>
037 * Properties are always bound to a schema field that provides the type and constraints on the property values. An
038 * exception is the root property the {@link DocumentPart} object which is not bound to a field but to a schema.
039 * <p>
040 * So properties are holding the actual values for each defined field.
041 * <p>
042 * The usual way of using properties is to get a document from the storage server then modify document properties and
043 * send them back to the storage server to that modifications are be stored.
044 * <p>
045 * Note that the storage server can be on a remote machine so when modifying properties remotely they are serialized and
046 * sent through the network between the two machines. This means properties must hold serializable values and also they
047 * must store some state flags so that the storage can decide which property was modified and how in order to correctly
048 * update the stored versions.
049 * <p>
050 * As we have seen each property may hold a serializable value which we will refer to as the <code>normalized</code>
051 * property value. For each schema field type there is only one java serializable object representation that will be
052 * used as the normalized value. The property API is giving you the possibility to use different compatible objects when
053 * setting or getting property values. Each property implementation will automatically convert the given value into a
054 * normalized one; so internally only the normalized value is stored.
055 * <p>
056 * For example, for date properties you may use either <code>Date</code> or <code>Calendar</code> when setting or
057 * retrieving a property value, but the normalized value will be the <code>Calendar</code> one.
058 * <p>
059 * As we have seen, properties keep some state flags. Property flags can be divided in two groups:
060 * <ul>
061 * <li>Dirty Flags - that reflect the public status of the document
062 * <li>Internal Flags - that reflect some internal state
063 * </ul>
064 * <p>
065 * Property Types:
066 * <p>
067 * Before going deeper in property flags, we will talk first about property types. There are several types of properties
068 * that are very closed on the type of fields they are bound onto.
069 * <ul>
070 * <li>Root Property (or <code>DocumentPart</code>) - this is a special property that is bound to a schema instead of a
071 * field And it is the root of the property tree.
072 * <li>Complex Properties - container properties that are bound to complex field types that can be represented as java
073 * <code>Map</code> objects. These properties contains a set of schema defined properties. You cannot add new child
074 * properties. You can only modify existing child properties. Complex property values are expressed as java
075 * <code>Map</code> objects.
076 * <ul>
077 * <li>Structured Properties - this is a special case of complex properties. The difference is that structured property
078 * values are expressed as <i>scalar</i> java objects instead of java maps. By scalar java objects we mean any well
079 * structured object which is not a container like a <code>Map</code> or a <code>Collection</code>. These objects are
080 * usually as scalar values - it doesn't make sense for example to set only some parts of that objects without creating
081 * the object completely. An example of usage are Blob properties that use {@link Blob} values.
082 * </ul>
083 * <li>List Properties - container properties that are bound to list field types.
084 * <li>Scalar Properties - atomic properties that are bound to scalar field types and that are using as values scalar or
085 * primitive java objects like arrays, primitives, String, Date etc.
086 * </ul>
087 * <p>
088 * As we've seen there are 2 categories of properties: container properties and scalar properties Complex and list
089 * properties are container properties while structured and scalar properties are scalar.
090 * <p>
091 * Dirty Flags:
092 * <p>
093 * Dirty flags are used to keep track of the dirty state of a property. The following flags are supported:
094 * <ul>
095 * <li> <code>IS_PHANTOM</code> - whether the property is existing in the storage (was explicitly set by the user) or it
096 * was dynamically generated using the default value by the implementation to fulfill schema definition. This applies to
097 * all property types
098 * <li> <code>IS_MODIFIED</code> - whether the property value was modified. This applies to all property types.
099 * <li> <code>IS_NEW</code> - whether the property is a new property that was added to a parent list property This
100 * applies only to properties that are children of a list property.
101 * <li> <code>IS_REMOVED</code> - whether a property was removed. A removed property will be removed from the storage and
102 * the next time you access the property it will be a <code>phantom</code> one. This applies only to properties that are
103 * children of a complex property.
104 * <li> <code>IS_MOVED</code> - whether the property was moved on another position inside the container list. This
105 * applies only to properties that are children of a list property.
106 * </ul>
107 * <p>
108 * There are several constraints on how property flags may change. This is a list of all changes that may occur over
109 * dirty flags:
110 * <ul>
111 * <li>NONE + MODIFIED =&gt; MODFIED
112 * <li>NONE + REMOVED =&gt; REMOVED
113 * <li>NONE + MOVED =&gt; MOVED
114 * <li>PHANTOM + MODIFIED =&gt; MODIFIED
115 * <li>NEW + MODIFIED =&gt; NEW | MODIFIED
116 * <li>NEW + MOVED =&gt; NEW | MOVED
117 * <li>MODIFIED + REMOVED =&gt; REMOVED
118 * <li>MODIFIED + MOVED =&gt; MODIFIED | MOVED
119 * <li>MODIFIED + MODIFIED =&gt; MODIFIED
120 * </ul>
121 * <p>
122 * The combinations not listed above are not permitted.
123 * <p>
124 * In case of list items, the REMOVED flag is not used since the property will be physically removed from the property
125 * tree.
126 * <p>
127 * Also when the dirty flag of a children property changes, its parent is informed to update its MODIFIED flag if
128 * needed. This way a modification on a children property is propagated to parents in the form of a MODIFIED flag.
129 * <p>
130 * Internal Flags:
131 * <p>
132 * Internal flags are used by the implementation to keep some internal state. For these flags you should look into the
133 * implementation
134 * <p>
135 * Apart flags properties can also hold some random user data using {@link Property#setData(Object)} and
136 * {@link Property#getData()} methods. This can be used for example to keep a context attached to a property. But be
137 * aware when using this you should provide serializable objects as the data you are attaching otherwise if properties
138 * are serialized / unserialized this will generate errors. The API is not forcing you to use serializable values since
139 * you can also use this feature to store temporary context data that will not be sent over the network.
140 *
141 * @see <code>TestPropertyModel</code> for usage of property API
142 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
143 */
144public interface Property extends Cloneable, Serializable, Iterable<Property> {
145
146    /**
147     * No dirty flags set.
148     */
149    int NONE = 0;
150
151    /**
152     * Flag used to mark a property as new. Property was added to a list.
153     */
154    int IS_NEW = 1;
155
156    /**
157     * Flag used to mark a property as dirty. Property value was modified.
158     */
159    int IS_MODIFIED = 2;
160
161    /**
162     * Flag used to mark a property as dirty. Property was removed.
163     */
164    int IS_REMOVED = 4;
165
166    /**
167     * Flag used to mark a property as dirty. Property was moved to another index.
168     */
169    int IS_MOVED = 8;
170
171    /**
172     * Flag used to mark a property as phantom.
173     */
174    int IS_PHANTOM = 16;
175
176    /**
177     * A mask for the first 4 flags: NEW, REMOVED, MODIFIED, MOVED.
178     */
179    int IS_DIRTY = IS_NEW | IS_REMOVED | IS_MOVED | IS_MODIFIED;
180
181    /**
182     * A mask for public flags.
183     */
184    int DIRTY_MASK = IS_PHANTOM | IS_DIRTY;
185
186    /**
187     * Tests if this property is new (just created but not yet stored).
188     * <p>
189     * A property is new when added to a collection. This is the typical state for a new property added to a list
190     *
191     * @return true if this property is new, false otherwise
192     */
193    boolean isNew();
194
195    /**
196     * Tests if a property is flagged as removed. Removed properties are child property that were removed from their
197     * container.
198     *
199     * @return if the property was removed, false otherwise
200     */
201    boolean isRemoved();
202
203    /**
204     * Tests if a property value was modified.
205     *
206     * @return if the property was removed, false otherwise
207     */
208    boolean isModified();
209
210    /**
211     * Tests if a property value was moved to another index in the parent list if any.
212     *
213     * @return if the property was removed, false otherwise
214     */
215    boolean isMoved();
216
217    /**
218     * Tests if the property is a phantom. This means it doesn't exists yet in the storage and it is not a new property.
219     * This is a placeholder for a property that is defined by the schema but was not yet set.
220     *
221     * @return true if a phantom false otherwise
222     */
223    boolean isPhantom();
224
225    /**
226     * Tests whether a property is dirty.
227     * <p>
228     * This tests whether or not a dirty flag is set on the property.
229     *
230     * @return true if the property changed or is new
231     */
232    boolean isDirty();
233
234    /**
235     * only for SimpleDocumentModel
236     */
237    boolean isForceDirty();
238
239    /**
240     * only for SimpleDocumentModel
241     */
242    void setForceDirty(boolean forceDirty);
243
244    /**
245     * Get the dirty flags that are set on this property.
246     *
247     * @return the dirty flags mask
248     */
249    int getDirtyFlags();
250
251    /**
252     * Notify the property that its changes was stored so it can safely remove dirty flags.
253     * <p>
254     * Dirty flags are removed according to the type of the modifications. This way if the property was REMOVED it
255     * becomes a PHANTOM otherwise all dirty flags are cleared.
256     * <p>
257     * This method should be used by storage implementors to notify the property it should reset its dirty flags. Note
258     * that clearing dirty flags is not propagated to the parent property or to children. You need to clear dirty flags
259     * explicitly for each property.
260     */
261    void clearDirtyFlags();
262
263    /**
264     * Whether the property is read only.
265     *
266     * @return true if read only false otherwise
267     */
268    boolean isReadOnly();
269
270    /**
271     * Sets the read only flag.
272     *
273     * @param value true to set this property read only false otherwise
274     */
275    void setReadOnly(boolean value);
276
277    /**
278     * Tests whether this property is of a map (complex) type.
279     *
280     * @return true if the property is of map type, false otherwise
281     */
282    boolean isComplex();
283
284    /**
285     * Tests whether this property is of a list type.
286     *
287     * @return true if the property is of list type, false otherwise
288     */
289    boolean isList();
290
291    /**
292     * Tests whether this property is of a scalar type.
293     *
294     * @return true if the property is of a scalar type, false otherwise
295     */
296    boolean isScalar();
297
298    /**
299     * Whether this property is a container - this means the property value is a map or a list.
300     * <p>
301     * Container properties don't have a scalar values. Container values are computed each time they are requested - by
302     * calling on of the <code>getValue</code> methods - by collecting the values of the child properties.
303     *
304     * @return true if scalar false otherwise
305     */
306    boolean isContainer();
307
308    /**
309     * Gets the property name.
310     *
311     * @return the property name
312     */
313    String getName();
314
315    /**
316     * Gets the path of this property relative to the owner document.
317     * <p>
318     * The path for top level properties is the same to the property name.
319     *
320     * @return the path
321     */
322    String getPath();
323
324    /**
325     * Get the type of the field corresponding to this property.
326     *
327     * @return the property type
328     */
329    Type getType();
330
331    /**
332     * Gets the field corresponding to this property.
333     * <p>
334     * The field is the object defining the property. You can see the field as a java class and the property as a class
335     * instance
336     *
337     * @return
338     */
339    Field getField();
340
341    /**
342     * Gets the property parent.
343     *
344     * @return the property parent for sub properties or null for top level properties
345     */
346    Property getParent();
347
348    /**
349     * Gets the document schema defining the property tree from which the property belongs.
350     *
351     * @return the document schema owning the field corresponding to the property
352     */
353    Schema getSchema();
354
355    /**
356     * Gets the root property.
357     *
358     * @return the root property
359     */
360    DocumentPart getRoot();
361
362    /**
363     * Initializes the property with the given normalized value.
364     * <p>
365     * The given value must be normalized - note that no check is done on that.
366     * <p>
367     * The phantom flag is unset by this operation.
368     * <p>
369     * This method should be used to initialize properties.
370     *
371     * @param value the normalized value to set
372     */
373    void init(Serializable value) throws PropertyException;
374
375    /**
376     * Sets this property value. The value will be first normalized and then set.
377     * <p>
378     * For complex or list properties the value will be set recursively (as a map or list value).
379     *
380     * @param value the value to set
381     * @throws {@link InvalidPropertyValueException} if the given value type is not compatible with the expected value
382     *         type
383     */
384    void setValue(Object value) throws PropertyException;
385
386    /**
387     * Gets the property normalized value.
388     * <p>
389     * Normalized values are of the java type that correspond to the field type.
390     *
391     * @return the property value, which may be null
392     */
393    Serializable getValue() throws PropertyException;
394
395    /**
396     * Gets the property normalized value for write.
397     * <p>
398     * Can be different fropm {@link #getValue()} in cases where the property adapts the value it is given to store.
399     *
400     * @return the property value to use for write, which may be null
401     * @since 5.2.1
402     */
403    Serializable getValueForWrite() throws PropertyException;
404
405    /**
406     * Gets the property value as the given type.
407     * <p>
408     * The value is converted using the registered converter to the given type.
409     * <p>
410     * If conversion is not supported a runtime exception will be triggered.
411     *
412     * @return the property value, which may be null
413     */
414    <T> T getValue(Class<T> type) throws PropertyException;
415
416    /**
417     * Removes this property from the tree.
418     * <p>
419     * This method marks the property as dirty and sets its value to null.
420     *
421     * @return the old property value
422     */
423    Serializable remove() throws PropertyException;
424
425    /**
426     * Gets the child property having the given name.
427     * <p>
428     * If the property is a scalar, this will return always null.
429     * <p>
430     * The given name should be the full name (i.e. prefixed name if any prefix exists).
431     * <p>
432     * If a non prefixed name is given, the first child property having the given local name will be returned.
433     * <p>
434     * Relative paths are not resolved. THis method is intended to lookup direct children. For path lookups use
435     * {@link Property#resolvePath(String)} instead.
436     *
437     * @param name the child property name (the full name including the prefix if any)
438     * @return the child property if any null if no child property with that name is found or if the property is a
439     *         scalar
440     * @throws {@link UnsupportedOperationException} if the property is a scalar property (doesn't have children)
441     * @throws {@link PropertyNotFoundException} if the child property is not found in the type definition
442     */
443    Property get(String name) throws PropertyNotFoundException;
444
445    /**
446     * Get the child property given it's index. This operation is mandatory for List properties.
447     * <p>
448     * If this method is not supported an {@link UnsupportedOperationException} must be thrown
449     * <p>
450     * Relative paths are not resolved. THis method is intended to lookup direct chilren. For path lookups, use
451     * {@link Property#resolvePath(String)} instead.
452     *
453     * @param index
454     * @return the child property if any null if no child property with that name is found or if the property is a
455     *         scalar
456     * @throws {@link UnsupportedOperationException} if the property is a scalar property (doesn't have children)
457     * @throws {@link PropertyNotFoundException} if the child property is not found in the type definition
458     */
459    Property get(int index) throws PropertyNotFoundException;
460
461    /**
462     * Sets a child property value given its index. This method is required only for List properties.
463     * <p>
464     * If this method is not supported, an {@link UnsupportedOperationException} must be thrown.
465     * <p>
466     * This method will mark the child value as dirty for existing values and in the case of map properties it will mark
467     * phantom properties as new properties.
468     *
469     * @param index
470     * @param value the new value
471     * @throws {@link UnsupportedOperationException} if the property is a scalar property (doesn't have children)
472     * @throws {@link PropertyNotFoundException} if the child property is not found in the type definition
473     */
474    void setValue(int index, Object value) throws PropertyException;
475
476    /**
477     * Get a collection over the children properties. This includes all children including phantom ones (those who are
478     * not yet set by the user).
479     * <p>
480     * The returned collection is ordered for list properties, and unordered for complex properties
481     * <p>
482     * Be aware that this method is creating phantom child properties for all schema fields that are not yet set.
483     *
484     * @return the children properties
485     */
486    Collection<Property> getChildren();
487
488    /**
489     * Get the count of the children properties. This includes phantom properties. So the returned size will be equal to
490     * the one returned by the property {@link ComplexType#getFieldsCount()}.
491     *
492     * @return the children properties count
493     */
494    int size();
495
496    /**
497     * Appends a new value to the list. A new property will be created to store the given value and appended to the
498     * children list.
499     * <p>
500     * The created property will be marked as {@link Property#isNew()}.
501     *
502     * @param value
503     * @return the added property
504     */
505    Property addValue(Object value) throws PropertyException;
506
507    /**
508     * Inserts at the given position a new value to the list. A new property will be created to store the given value
509     * and appended to the children list.
510     * <p>
511     * The created property will be marked as {@link Property#isNew()}.
512     *
513     * @param value
514     * @param index the position to insert the value
515     * @return the added property
516     */
517    Property addValue(int index, Object value) throws PropertyException;
518
519    /**
520     * Creates an empty child property and adds it as a property to the list container.
521     * <p>
522     * This method is useful to construct lists.
523     *
524     * @return the created property
525     * @throws PropertyException
526     */
527    Property addEmpty() throws PropertyException;
528
529    /**
530     * Moves a property position into the parent container list.
531     * <p>
532     * This method applies only for list item properties. The given index includes removed properties.
533     *
534     * @param index the position in the parent container to move this property
535     * @throws UnsupportedOperationException if the operation is not supported by the target property
536     */
537    void moveTo(int index);
538
539    /**
540     * Same as {@link Property#resolvePath(Path)} but with a string path as argument. This is the same as calling
541     * <code>resolvePath(new Path(path))</code>.
542     *
543     * @param path the string path to resolve.
544     * @return the resolved property
545     * @throws PropertyNotFoundException if the path cannot be resolved
546     */
547    Property resolvePath(String path) throws PropertyNotFoundException;
548
549    /**
550     * Resolves the given path relative to the current property and return the property if any is found otherwise throws
551     * an exception.
552     * <p>
553     * The path format is a subset of XPath. Thus, / is used as path element separator, [n] for list element indexes.
554     * Attribute separator '@' are not supported since all properties are assumed to be elements. Also you .. and . can
555     * be used as element names.
556     * <p>
557     * Example of paths:
558     * <ul>
559     * <li><code>dc:title</code>
560     * <li><code>attachments/item[2]/mimeType</code>
561     * <li><code>../dc:title</code>
562     * </ul>
563     *
564     * @param path the path to resolve.
565     * @return the resolved property
566     * @throws PropertyNotFoundException if the path cannot be resolved
567     */
568    Property resolvePath(Path path) throws PropertyNotFoundException;
569
570    /**
571     * Gets the value of the property resolved using the given path.
572     * <p>
573     * This method is a shortcut for: <code>resolvePath(path).getValue()</code>.
574     *
575     * @param path the path to the property
576     * @return the property value
577     */
578    Serializable getValue(String path) throws PropertyException;
579
580    /**
581     * Gets the value of the property resolved using the given path.
582     * <p>
583     * The value will be converted to the given type if possible, otherwise an exception will be thrown.
584     * <p>
585     * This method is a shortcut for: <code>resolvePath(path).getValue(type)</code>.
586     *
587     * @param <T> The type of the value to return
588     * @param type the class of the value
589     * @param path the java path of the property value
590     * @return the value
591     * @throws PropertyException
592     */
593    <T> T getValue(Class<T> type, String path) throws PropertyException;
594
595    /**
596     * Sets the value of the property resolved using the given path.
597     * <p>
598     * This method is a shortcut for: <code>resolvePath(path).setValue(value)</code>.
599     *
600     * @param path the property path
601     * @param value the value
602     * @throws PropertyException
603     */
604    void setValue(String path, Object value) throws PropertyException;
605
606    /**
607     * Normalizes the given value as dictated by the property type.
608     * <p>
609     * Normalized values are the ones that are used for transportation over the net and that are given to the storage
610     * implementation to be stored in the repository
611     * <p>
612     * Normalized values must be {@link Serializable}
613     * <p>
614     * If the given value is already normalized it will be returned back.
615     *
616     * @param value the value to normalize according to the property type
617     * @return the normalized value
618     */
619    Serializable normalize(Object value) throws PropertyConversionException;
620
621    /**
622     * Checks if the given value is a normalized one. This means the value has a type that is normalized.
623     * <p>
624     * Null values are considered as normalized.
625     *
626     * @param value the value to check
627     * @return true if the value is normalized false otherwise
628     */
629    boolean isNormalized(Object value);
630
631    /**
632     * Converts the given normalized value to the given type.
633     * <p>
634     * If the value has already the given type it will be returned back.
635     *
636     * @param value the normalized value to convert
637     * @param toType the conversion type
638     * @return the converted value, which may be null
639     * @throws PropertyConversionException if the conversion cannot be made because of type incompatibilities
640     */
641    <T> T convertTo(Serializable value, Class<T> toType) throws PropertyConversionException;
642
643    /**
644     * Validates the given value type.
645     * <p>
646     * Tests if the given value type can be converted to a normalized type and thus a value of this type can be set to
647     * that property.
648     *
649     * @param type the type to validate
650     * @return true if the type is valid, false otherwise
651     */
652    boolean validateType(Class<?> type);
653
654    /**
655     * Creates a new and empty instance of a normalized value.
656     * <p>
657     * Empty is used in the sense of a value that has not been initialized or can be considered as an empty value. For
658     * example for the {@link String} type the empty value will be the empty string ""
659     *
660     * @return the empty instance the empty instance, or null for some implementations
661     */
662    Object newInstance();
663
664    /**
665     * Method that implement the visitor pattern.
666     * <p>
667     * The visitor must return null to stop visiting children otherwise a context object that will be passed as the arg
668     * argument to children
669     *
670     * @param visitor the visitor to accept
671     * @param arg an argument passed to the visitor. This should be used by the visitor to carry on the visiting
672     *            context.
673     */
674    void accept(PropertyVisitor visitor, Object arg) throws PropertyException;
675
676    /**
677     * Compare the two properties by content.
678     *
679     * @param property
680     * @return true If the properties have a similar content, otherwise false
681     * @throws PropertyException
682     */
683    boolean isSameAs(Property property) throws PropertyException;
684
685    /**
686     * Gets an iterator over the dirty children properties.
687     *
688     * @return the iterator
689     */
690    Iterator<Property> getDirtyChildren();
691
692    /**
693     * @return A {@link PropertyObjectResolver} to manage this property reference to external entities, null if this
694     *         property's type has no resolver.
695     * @since 7.1
696     */
697    PropertyObjectResolver getObjectResolver();
698
699}