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 => MODFIED 119 * <li>NONE + REMOVED => REMOVED 120 * <li>NONE + MOVED => MOVED 121 * <li>PHANTOM + MODIFIED => MODIFIED 122 * <li>NEW + MODIFIED => NEW | MODIFIED 123 * <li>NEW + MOVED => NEW | MOVED 124 * <li>MODIFIED + REMOVED => REMOVED 125 * <li>MODIFIED + MOVED => MODIFIED | MOVED 126 * <li>MODIFIED + MODIFIED => 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}