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