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 => MODFIED 112 * <li>NONE + REMOVED => REMOVED 113 * <li>NONE + MOVED => MOVED 114 * <li>PHANTOM + MODIFIED => MODIFIED 115 * <li>NEW + MODIFIED => NEW | MODIFIED 116 * <li>NEW + MOVED => NEW | MOVED 117 * <li>MODIFIED + REMOVED => REMOVED 118 * <li>MODIFIED + MOVED => MODIFIED | MOVED 119 * <li>MODIFIED + MODIFIED => 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}