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