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