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