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 xpath of this property.
324     * <p>
325     * The xpath is of the form {@code pref:foo/mylist/123/elem}, of note:
326     * <ul>
327     * <li>there is no initial {@code /}
328     * <li>the schema prefix is only present if a prefix is configured for the base property's schema
329     * <li>list elements indexes start at 0
330     * <li>list elements aren't using the old syntax {@code foo/bar[123]/baz} but the new syntax {@code foo/123/baz}
331     * </ul>
332     *
333     * @return the xpath
334     * @since 9.1
335     */
336    String getXPath();
337
338    /**
339     * Gets the path of this property relative to the owner document.
340     * <p>
341     * The path for top level properties is the same to the property name.
342     * <p>
343     * NOTE the path returned contains an initial {@code /} and expresses list indexes using an old syntax
344     *
345     * @return the path
346     * @deprecated since 9.1 as it's cumbersome to use; use {@link #getXPath} instead
347     */
348    @Deprecated
349    String getPath();
350
351    /**
352     * Get the type of the field corresponding to this property.
353     *
354     * @return the property type
355     */
356    Type getType();
357
358    /**
359     * Gets the field corresponding to this property.
360     * <p>
361     * The field is the object defining the property. You can see the field as a java class and the property as a class
362     * instance
363     *
364     * @return
365     */
366    Field getField();
367
368    /**
369     * Gets the property parent.
370     *
371     * @return the property parent for sub properties or null for top level properties
372     */
373    Property getParent();
374
375    /**
376     * Gets the document schema defining the property tree from which the property belongs.
377     *
378     * @return the document schema owning the field corresponding to the property
379     */
380    Schema getSchema();
381
382    /**
383     * Gets the root property.
384     *
385     * @return the root property
386     */
387    DocumentPart getRoot();
388
389    /**
390     * Initializes the property with the given normalized value.
391     * <p>
392     * The given value must be normalized - note that no check is done on that.
393     * <p>
394     * The phantom flag is unset by this operation.
395     * <p>
396     * This method should be used to initialize properties.
397     *
398     * @param value the normalized value to set
399     */
400    void init(Serializable value) throws PropertyException;
401
402    /**
403     * Sets this property value. The value will be first normalized and then set.
404     * <p>
405     * For complex or list properties the value will be set recursively (as a map or list value).
406     *
407     * @param value the value to set
408     * @throws {@link InvalidPropertyValueException} if the given value type is not compatible with the expected value
409     *         type
410     */
411    void setValue(Object value) throws PropertyException;
412
413    /**
414     * Gets the property normalized value.
415     * <p>
416     * Normalized values are of the java type that correspond to the field type.
417     *
418     * @return the property value, which may be null
419     */
420    Serializable getValue() throws PropertyException;
421
422    /**
423     * Gets the property normalized value for write.
424     * <p>
425     * Can be different fropm {@link #getValue()} in cases where the property adapts the value it is given to store.
426     *
427     * @return the property value to use for write, which may be null
428     * @since 5.2.1
429     */
430    Serializable getValueForWrite() throws PropertyException;
431
432    /**
433     * Gets the property value as the given type.
434     * <p>
435     * The value is converted using the registered converter to the given type.
436     * <p>
437     * If conversion is not supported a runtime exception will be triggered.
438     *
439     * @return the property value, which may be null
440     */
441    <T> T getValue(Class<T> type) throws PropertyException;
442
443    /**
444     * Removes this property from the tree.
445     * <p>
446     * This method marks the property as dirty and sets its value to null.
447     *
448     * @return the old property value
449     */
450    Serializable remove() throws PropertyException;
451
452    /**
453     * Gets the child property having the given name.
454     * <p>
455     * If the property is a scalar, this will return always null.
456     * <p>
457     * The given name should be the full name (i.e. prefixed name if any prefix exists).
458     * <p>
459     * If a non prefixed name is given, the first child property having the given local name will be returned.
460     * <p>
461     * Relative paths are not resolved. THis method is intended to lookup direct children. For path lookups use
462     * {@link Property#resolvePath(String)} instead.
463     *
464     * @param name the child property name (the full name including the prefix if any)
465     * @return the child property if any null if no child property with that name is found or if the property is a
466     *         scalar
467     * @throws {@link UnsupportedOperationException} if the property is a scalar property (doesn't have children)
468     * @throws {@link PropertyNotFoundException} if the child property is not found in the type definition
469     */
470    Property get(String name) throws PropertyNotFoundException;
471
472    /**
473     * Get the child property given it's index. This operation is mandatory for List properties.
474     * <p>
475     * If this method is not supported an {@link UnsupportedOperationException} must be thrown
476     * <p>
477     * Relative paths are not resolved. THis method is intended to lookup direct chilren. For path lookups, use
478     * {@link Property#resolvePath(String)} instead.
479     *
480     * @param index
481     * @return the child property if any null if no child property with that name is found or if the property is a
482     *         scalar
483     * @throws {@link UnsupportedOperationException} if the property is a scalar property (doesn't have children)
484     * @throws {@link PropertyNotFoundException} if the child property is not found in the type definition
485     */
486    Property get(int index) throws PropertyNotFoundException;
487
488    /**
489     * Sets a child property value given its index. This method is required only for List properties.
490     * <p>
491     * If this method is not supported, an {@link UnsupportedOperationException} must be thrown.
492     * <p>
493     * This method will mark the child value as dirty for existing values and in the case of map properties it will mark
494     * phantom properties as new properties.
495     *
496     * @param index
497     * @param value the new value
498     * @throws {@link UnsupportedOperationException} if the property is a scalar property (doesn't have children)
499     * @throws {@link PropertyNotFoundException} if the child property is not found in the type definition
500     */
501    void setValue(int index, Object value) throws PropertyException;
502
503    /**
504     * Get a collection over the children properties. This includes all children including phantom ones (those who are
505     * not yet set by the user).
506     * <p>
507     * The returned collection is ordered for list properties, and unordered for complex properties
508     * <p>
509     * Be aware that this method is creating phantom child properties for all schema fields that are not yet set.
510     *
511     * @return the children properties
512     */
513    Collection<Property> getChildren();
514
515    /**
516     * Get the count of the children properties. This includes phantom properties. So the returned size will be equal to
517     * the one returned by the property {@link ComplexType#getFieldsCount()}.
518     *
519     * @return the children properties count
520     */
521    int size();
522
523    /**
524     * Appends a new value to the list. A new property will be created to store the given value and appended to the
525     * children list.
526     * <p>
527     * The created property will be marked as {@link Property#isNew()}.
528     *
529     * @param value
530     * @return the added property
531     */
532    Property addValue(Object value) throws PropertyException;
533
534    /**
535     * Inserts at the given position a new value to the list. A new property will be created to store the given value
536     * and appended to the children list.
537     * <p>
538     * The created property will be marked as {@link Property#isNew()}.
539     *
540     * @param value
541     * @param index the position to insert the value
542     * @return the added property
543     */
544    Property addValue(int index, Object value) throws PropertyException;
545
546    /**
547     * Creates an empty child property and adds it as a property to the list container.
548     * <p>
549     * This method is useful to construct lists.
550     *
551     * @return the created property
552     * @throws PropertyException
553     */
554    Property addEmpty() throws PropertyException;
555
556    /**
557     * Moves a property position into the parent container list.
558     * <p>
559     * This method applies only for list item properties. The given index includes removed properties.
560     *
561     * @param index the position in the parent container to move this property
562     * @throws UnsupportedOperationException if the operation is not supported by the target property
563     */
564    void moveTo(int index);
565
566    /**
567     * Same as {@link Property#resolvePath(Path)} but with a string path as argument. This is the same as calling
568     * <code>resolvePath(new Path(path))</code>.
569     *
570     * @param path the string path to resolve.
571     * @return the resolved property
572     * @throws PropertyNotFoundException if the path cannot be resolved
573     */
574    Property resolvePath(String path) throws PropertyNotFoundException;
575
576    /**
577     * Resolves the given path relative to the current property and return the property if any is found otherwise throws
578     * an exception.
579     * <p>
580     * The path format is a subset of XPath. Thus, / is used as path element separator, [n] for list element indexes.
581     * Attribute separator '@' are not supported since all properties are assumed to be elements. Also you .. and . can
582     * be used as element names.
583     * <p>
584     * Example of paths:
585     * <ul>
586     * <li><code>dc:title</code>
587     * <li><code>attachments/item[2]/mimeType</code>
588     * <li><code>../dc:title</code>
589     * </ul>
590     *
591     * @param path the path to resolve.
592     * @return the resolved property
593     * @throws PropertyNotFoundException if the path cannot be resolved
594     */
595    Property resolvePath(Path path) throws PropertyNotFoundException;
596
597    /**
598     * Gets the value of the property resolved using the given path.
599     * <p>
600     * This method is a shortcut for: <code>resolvePath(path).getValue()</code>.
601     *
602     * @param path the path to the property
603     * @return the property value
604     */
605    Serializable getValue(String path) throws PropertyException;
606
607    /**
608     * Gets the value of the property resolved using the given path.
609     * <p>
610     * The value will be converted to the given type if possible, otherwise an exception will be thrown.
611     * <p>
612     * This method is a shortcut for: <code>resolvePath(path).getValue(type)</code>.
613     *
614     * @param <T> The type of the value to return
615     * @param type the class of the value
616     * @param path the java path of the property value
617     * @return the value
618     * @throws PropertyException
619     */
620    <T> T getValue(Class<T> type, String path) throws PropertyException;
621
622    /**
623     * Sets the value of the property resolved using the given path.
624     * <p>
625     * This method is a shortcut for: <code>resolvePath(path).setValue(value)</code>.
626     *
627     * @param path the property path
628     * @param value the value
629     * @throws PropertyException
630     */
631    void setValue(String path, Object value) throws PropertyException;
632
633    /**
634     * Normalizes the given value as dictated by the property type.
635     * <p>
636     * Normalized values are the ones that are used for transportation over the net and that are given to the storage
637     * implementation to be stored in the repository
638     * <p>
639     * Normalized values must be {@link Serializable}
640     * <p>
641     * If the given value is already normalized it will be returned back.
642     *
643     * @param value the value to normalize according to the property type
644     * @return the normalized value
645     */
646    Serializable normalize(Object value) throws PropertyConversionException;
647
648    /**
649     * Checks if the given value is a normalized one. This means the value has a type that is normalized.
650     * <p>
651     * Null values are considered as normalized.
652     *
653     * @param value the value to check
654     * @return true if the value is normalized false otherwise
655     */
656    boolean isNormalized(Object value);
657
658    /**
659     * Converts the given normalized value to the given type.
660     * <p>
661     * If the value has already the given type it will be returned back.
662     *
663     * @param value the normalized value to convert
664     * @param toType the conversion type
665     * @return the converted value, which may be null
666     * @throws PropertyConversionException if the conversion cannot be made because of type incompatibilities
667     */
668    <T> T convertTo(Serializable value, Class<T> toType) throws PropertyConversionException;
669
670    /**
671     * Validates the given value type.
672     * <p>
673     * Tests if the given value type can be converted to a normalized type and thus a value of this type can be set to
674     * that property.
675     *
676     * @param type the type to validate
677     * @return true if the type is valid, false otherwise
678     */
679    boolean validateType(Class<?> type);
680
681    /**
682     * Creates a new and empty instance of a normalized value.
683     * <p>
684     * Empty is used in the sense of a value that has not been initialized or can be considered as an empty value. For
685     * example for the {@link String} type the empty value will be the empty string ""
686     *
687     * @return the empty instance the empty instance, or null for some implementations
688     */
689    Object newInstance();
690
691    /**
692     * Method that implement the visitor pattern.
693     * <p>
694     * The visitor must return null to stop visiting children otherwise a context object that will be passed as the arg
695     * argument to children
696     *
697     * @param visitor the visitor to accept
698     * @param arg an argument passed to the visitor. This should be used by the visitor to carry on the visiting
699     *            context.
700     */
701    void accept(PropertyVisitor visitor, Object arg) throws PropertyException;
702
703    /**
704     * Compare the two properties by content.
705     *
706     * @param property
707     * @return true If the properties have a similar content, otherwise false
708     * @throws PropertyException
709     */
710    boolean isSameAs(Property property) throws PropertyException;
711
712    /**
713     * Gets an iterator over the dirty children properties.
714     *
715     * @return the iterator
716     */
717    Iterator<Property> getDirtyChildren();
718
719    /**
720     * @return A {@link PropertyObjectResolver} to manage this property reference to external entities, null if this
721     *         property's type has no resolver.
722     * @since 7.1
723     */
724    PropertyObjectResolver getObjectResolver();
725
726}