Class PropertyAccessor

java.lang.Object
org.apache.sis.metadata.PropertyAccessor
Direct Known Subclasses:
SpecialCases

class PropertyAccessor extends Object
The getter methods declared in a GeoAPI interface, together with setter methods (if any) declared in the SIS implementation. An instance of PropertyAccessor gives access to all public properties of an instance of a metadata object. It uses reflection for this purpose, a little bit like the Java Beans framework.

This accessor groups the properties in two categories:

  • The standard properties defined by the GeoAPI (or other standard) interfaces. Those properties are the only ones accessible by most methods in this class, except equals(Object, Object, ComparisonMode) and walkWritable(MetadataVisitor, Object, Object).
  • Extra properties defined by the IdentifiedObject interface. Those properties invisible in the ISO 19115-1 model, but appears in ISO 19115-3 XML marshalling. So we do the same in the SIS implementation: invisible in map and tree view, but visible in XML marshalling.

Thread safety

The same PropertyAccessor instance can be safely used by many threads without synchronization on the part of the caller. Subclasses shall make sure that any overridden methods remain safe to call from multiple threads, because the same PropertyAccessor instances are typically used by many ModifiableMetadata instances.
Since:
0.3
Version:
1.3
  • Field Details

    • COUNT_FIRST

      static final int COUNT_FIRST
      Enumeration constants for the mode argument in the count(Object, ValueExistencePolicy, int) method.
      See Also:
    • COUNT_SHALLOW

      static final int COUNT_SHALLOW
      Enumeration constants for the mode argument in the count(Object, ValueExistencePolicy, int) method.
      See Also:
    • COUNT_DEEP

      static final int COUNT_DEEP
      Enumeration constants for the mode argument in the count(Object, ValueExistencePolicy, int) method.
      See Also:
    • RETURN_NULL

      static final int RETURN_NULL
      Enumeration constants for the mode argument in the set(int, Object, Object, int) method.
      See Also:
    • RETURN_PREVIOUS

      static final int RETURN_PREVIOUS
      Enumeration constants for the mode argument in the set(int, Object, Object, int) method.
      See Also:
    • APPEND

      static final int APPEND
      Enumeration constants for the mode argument in the set(int, Object, Object, int) method.
      See Also:
    • IGNORE_READ_ONLY

      static final int IGNORE_READ_ONLY
      Enumeration constants for the mode argument in the set(int, Object, Object, int) method.
      See Also:
    • EXTRA_GETTER

      private static final Method EXTRA_GETTER
      Additional getter to declare in every list of getter methods that do not already provide their own getIdentifiers() method. We handle this method specially because it is needed for XML marshalling in ISO 19115-3 compliant document, while not part of abstract ISO 19115-1 specification.
      See Also:
    • type

      final Class<?> type
      The implemented metadata interface.
    • implementation

      final Class<?> implementation
      The implementation class, or type if none. The following condition must hold:
      Design note: We could enforce the above-cited restriction with type parameter: if the type field is declared as Class<T>, then this implementation field would be declared as Class<? extends T>. However, this is not useful for this internal class because the <T> type is never known; we have the <?> type everywhere except in tests, which result in compiler warnings at PropertyAccessor construction.
    • allCount

      private final int allCount
      Number of getters methods that can be used, regardless of whether the methods are visible or hidden to the user. This is either getters.length or getters.length-1, depending on whether the EXTRA_GETTER method needs to be skipped or not.
    • standardCount

      private final int standardCount
      Numbers of methods to show to the user. This is always equal or lower than allCount. This count may be lower than allCount for two reasons:
      • The EXTRA_GETTER method is not part of the international standard.
      • The interface contains deprecated methods from an older international standard. Example: changes caused by the upgrade from ISO 19115:2003 to ISO 19115:2014.
    • getters

      private final Method[] getters
      The public getter methods. This array should not contain any null element. They are the methods defined in the interface, not the implementation class.

      This array shall not contains any null elements.

    • setters

      private final Method[] setters
      The corresponding setter methods, or null if none. This array must have the same length than getters. For every getters[i] element, setters[i] is the corresponding setter or null if there is none.
    • names

      private final String[] names
      The JavaBeans property names. They are computed at construction time, interned then cached. Those names are often the same than field names (at least in SIS implementation), so it is reasonable to intern them in order to share String instances.

      This array shall not contains any null elements.

      See Also:
    • elementTypes

      private final Class<?>[] elementTypes
      The types of elements for the corresponding getter and setter methods. If a getter method returns a collection, then this is the type of elements in that collection. Otherwise this is the type of the returned value itself.

      Notes:

      • Element type of Map collection is Map.Entry.
      • Primitive types like double or int are converted to their wrapper types.
      • This array may contain null values if the type of elements in a collection is unknown (i.e. the collection is not parameterized).
    • mapping

      private final Map<String,Integer> mapping
      Index of getter or setter for a given name. Original names are duplicated with the same name converted to lower cases according Locale.ROOT conventions, for case-insensitive searches. This map shall be considered as immutable after construction.

      The keys in this map are both inferred from the method names and fetched from the UML annotations. Consequently, the map may contain many entries for the same value if some method names are different than the UML identifiers.

      See Also:
    • lastConverter

      private transient volatile ObjectConverter<?,?> lastConverter
      The last converter used. This is remembered on the assumption that the same converter will often be reused for the same property. This optimization can reduce the cost of looking for a converter, and also reduce thread contention since it reduces the number of calls to the synchronized ObjectConverters.find(Class, Class) method.
      See Also:
    • informations

      private transient org.opengis.metadata.ExtendedElementInformation[] informations
      The property information, including the name and restrictions on valid values. The array will be created when first needed. A null element means that the information at that index has not yet been computed.
      See Also:
  • Constructor Details

    • PropertyAccessor

      PropertyAccessor(Class<?> type, Class<?> implementation, Class<?> standardImpl)
      Creates a new property accessor for the specified metadata implementation.
      Parameters:
      type - the interface implemented by the metadata class.
      implementation - the class of metadata implementations, or type if none.
      standardImpl - the implementation specified by the MetadataStandard, or null if none. This is the same than implementation unless a custom implementation is used.
  • Method Details

    • addMapping

      private void addMapping(String name, Integer index)
      Adds the given (name, index) pair to mapping, making sure we don't overwrite an existing entry with different value.
    • addMappingWithLowerCase

      private void addMappingWithLowerCase(String name, Integer index)
      Adds the given (name, index) pair and its lower-case variant.
    • getGetters

      private static Method[] getGetters(Class<?> type, Class<?> implementation, Class<?> standardImpl)
      Returns the getters. The returned array should never be modified, since it may be shared among many instances of PropertyAccessor.
      Parameters:
      type - the metadata interface.
      implementation - the class of metadata implementations, or type if none.
      standardImpl - the implementation specified by the MetadataStandard, or null if none.
      Returns:
      the getters declared in the given interface (never null).
    • count

      final int count()
      Returns the number of properties that can be read. This is the properties to show in map or tree, not including hidden properties like deprecated methods or EXTRA_GETTER method.
      See Also:
    • indexOf

      final int indexOf(String name, boolean mandatory)
      Returns the index of the specified property, or -1 if none. The search is case-insensitive.
      Parameters:
      name - the name of the property to search.
      mandatory - whether this method shall throw an exception or return -1 if the given name is not found.
      Returns:
      the index of the given name, or -1 if none and mandatory is false.
      Throws:
      IllegalArgumentException - if the name is not found and mandatory is true.
    • name

      @Workaround(library="JDK", version="10") final String name(int index, KeyNamePolicy keyPolicy)
      Returns the name of the property at the given index, or null if none.
      Parameters:
      index - the index of the property for which to get the name.
      keyPolicy - the kind of name to return.
      Returns:
      the name of the given kind at the given index, or null if the index is out of bounds.
    • type

      Class<?> type(int index, TypeValuePolicy policy)
      Returns the type of the property at the given index. The returned type is usually a GeoAPI interface (at least in the case of SIS implementation).

      If the given policy is ELEMENT_TYPE, then:

      • Primitive types like double or int are converted to their wrapper types.
      • If the property is a collection, then returns the type of collection elements.
      Parameters:
      index - the index of the property.
      policy - the kind of type to return.
      Returns:
      the type of property values, or null if unknown.
    • isCollectionOrMap

      final boolean isCollectionOrMap(int index)
      Returns true if the type at the given index is Collection or Map.
    • isMap

      final boolean isMap(int index)
      Returns true if the type at the given index is Map.
    • isDeprecated

      private boolean isDeprecated(int index)
      Returns true if the property at the given index is deprecated, either in the interface that declare the method or in the implementation class. A method may be deprecated in the implementation but not in the interface when the implementation has been updated for a new standard while the interface is still reflecting the old standard.
    • information

      final org.opengis.metadata.ExtendedElementInformation information(org.opengis.metadata.citation.Citation standard, int index)
      Returns the information for the property at the given index. The information are created when first needed.
      Parameters:
      standard - the standard which define the type interface.
      index - the index of the property for which to get the information.
      Returns:
      the information for the property at the given index, or null if the index is out of bounds.
      See Also:
    • remarks

      CharSequence remarks(int index, Object metadata)
      Returns a remark or warning to format with the value at the given index, or null if none. This is provided when the value may look surprising, for example the longitude values in a geographic bounding box crossing the anti-meridian.
    • isWritable

      final boolean isWritable()
      Returns true if the implementation class has at least one setter method.
    • isWritable

      final boolean isWritable(int index)
      Returns true if the property at the given index is writable.
    • get

      Object get(int index, Object metadata) throws BackingStoreException
      Returns the value for the specified metadata, or null if none. If the given index is out of bounds, then this method returns null, so it is safe to invoke this method even if indexOf(String, boolean) returned -1.
      Parameters:
      index - the index of the property for which to get a value.
      metadata - the metadata object to query.
      Returns:
      the value, or null if none or if the given is out of bounds.
      Throws:
      BackingStoreException - if the implementation threw a checked exception.
    • get

      private static Object get(Method method, Object metadata) throws BackingStoreException
      Gets a value from the specified metadata. We do not expect any checked exception to be thrown, since classes in the org.opengis.metadata packages do not declare any. However if a checked exception is throw anyway (maybe in user defined "standard"), it will be wrapped in a BackingStoreException. Unchecked exceptions are propagated.
      Parameters:
      method - the method to use for the query.
      metadata - the metadata object to query.
      Throws:
      BackingStoreException - if the implementation threw a checked exception.
      See Also:
    • set

      Object set(int index, Object metadata, Object value, int mode) throws UnmodifiableMetadataException, ClassCastException, BackingStoreException
      Sets a value for the specified metadata and returns the old value if mode is RETURN_PREVIOUS. The mode argument can be one of the following:
      • RETURN_NULL: Set the value and returns null.
      • RETURN_PREVIOUS: Set the value and returns the previous value. If the previous value was a collection or a map, then that value is copied in a new collection or map before the new value is set because the setter methods typically copy the new collection in their existing instance.
      • APPEND: Set the value only if it does not overwrite an existing value, then returns Boolean.TRUE if the metadata changed as a result of this method call, Boolean.FALSE if the metadata didn't changed or null if the value cannot be set because another value already exists.
      • IGNORE_READ_ONLY: Set the value and returns null on success. If the property is read-only, do not throw an exception; returns exception class instead.

      The APPEND mode has an additional side effect: it sets the append argument to true in the call to the convert(Method, Object, Object, Object[], Class, boolean) method. See the convert javadoc for more information.

      If the given index is out of bounds, then this method does nothing and return null. We do that because the ValueMap.remove(Object) method may invoke this method with an index of -1 if the indexOf(String, boolean) method didn't found the property name. However, the given value will be silently discarded, so index out-of-bounds shall be used only in the context of remove operations (this is not verified).

      Parameters:
      index - the index of the property to set.
      metadata - the metadata object on which to set the value.
      value - the new value.
      mode - whether this method should first fetches the old value, as one of the constants listed in this method javadoc.
      Returns:
      the old value, or null if mode was RETURN_NULL or IGNORE_READ_ONLY.
      Throws:
      UnmodifiableMetadataException - if the property for the given key is read-only.
      ClassCastException - if the given value is not of the expected type.
      BackingStoreException - if the implementation threw a checked exception.
    • set

      private static void set(Method setter, Object metadata, Object[] newValues) throws BackingStoreException
      Sets a value for the specified metadata. This method does not attempt any conversion of argument values. Conversion of type, if needed, must have been applied before to call this method.

      We do not expect any checked exception to be thrown, since classes in the org.opengis.metadata packages do not declare any. However if a checked exception is throw anyway, then it will be wrapped in a BackingStoreException. Unchecked exceptions are propagated.

      Parameters:
      setter - the method to use for setting the new value.
      metadata - the metadata object to query.
      newValues - the argument to give to the method to be invoked.
      Throws:
      BackingStoreException - if the implementation threw a checked exception.
      See Also:
    • convert

      private Boolean convert(Method getter, Object metadata, Object oldValue, Object[] newValues, Class<?> elementType, boolean append) throws ClassCastException, BackingStoreException
      Converts a value to the type required by a setter method. The values are converted in-place in the newValues array. We use an array instead of a single argument and return value because an array will be needed anyway for invoking the convert(Object[], Class) and Method.invoke(Object, Object[]) methods.

      The collection special case

      If the metadata property is a collection, then there is a choice:
      • If append is true, then the new value (which may itself be a collection) is unconditionally added to the previous collection.
      • If append is false and the new value is not a collection, then the new value is added to the existing collection. In other words, we behave as a multi-values map for the properties that allow multi-values.
      • Otherwise the new collection replaces the previous collection. All previous values are discarded.
      Adding new values to the previous collection may or may not change the original metadata depending on whether those collections are live or not. In Apache SIS implementation, those collections are live. However, this method can be though as if the collections were not live, since the caller will invoke the setter method with the collection anyway.
      Parameters:
      getter - the method to use for fetching the previous value.
      metadata - the metadata object to query and modify.
      oldValue - the value returned by get(getter, metadata), or null if unknown. This parameter is only an optimization for avoiding to invoke the getter method twice if the value is already known.
      newValues - the argument to convert. The content of this array will be modified in-place. Current implementation requires an array of length 1, however this restriction may be relaxed in a future SIS version if needed.
      elementType - the target type (if singleton) or the type of elements in the collection.
      append - if true and the value is a collection, then that collection will be added to any previously existing collection instead of replacing it.
      Returns:
      if the given value has been added to an existing collection, then whether that existing collection has been modified as a result of this method call. Otherwise null.
      Throws:
      ClassCastException - if the element of the arguments array is not of the expected type.
      BackingStoreException - if the implementation threw a checked exception.
    • convert

      private void convert(Object[] elements, Class<?> targetType) throws ClassCastException
      Converts values in the specified array to the given type. The array content is modified in-place. This method accepts an array instead of a single value because the values to convert may be the content of a collection.
      Parameters:
      elements - the array which contains element to convert.
      targetType - the base type of target elements.
      Throws:
      ClassCastException - if an element cannot be converted.
    • count

      final int count(Object metadata, ValueExistencePolicy valuePolicy, int mode) throws BackingStoreException
      Counts the number of non-null or non-empty properties. The mode argument can be one of the following:
      • COUNT_FIRST: stop at the first property found. This mode is used for testing if a metadata is empty or not, without the need to known the exact count.
      • COUNT_SHALLOW: count all properties, counting collections as one property.
      • COUNT_DEEP: count all properties, counting collections as the number of properties returned by Collection.size().
      Parameters:
      mode - kinds of count, as described above.
      valuePolicy - the behavior of the count toward null or empty values.
      Throws:
      BackingStoreException - if the implementation threw a checked exception.
      See Also:
    • equals

      public boolean equals(Object metadata1, Object metadata2, ComparisonMode mode) throws BackingStoreException
      Compares the two specified metadata objects. This method implements a shallow comparison, i.e. all metadata properties are compared using their properties.equals(…) method without explicit calls to this accessor.equals(…) method for children. However, the final result may still be a deep comparison.
      Parameters:
      metadata1 - the first metadata object to compare. This object determines the accessor.
      metadata2 - the second metadata object to compare.
      mode - the strictness level of the comparison.
      Throws:
      BackingStoreException - if the implementation threw a checked exception.
      See Also:
    • walkReadable

      final void walkReadable(MetadataVisitor<?> visitor, Object metadata) throws Exception
      Invokes MetadataVisitor.visit(Class, Object) for all non-null properties in the given metadata. This method is not recursive, i.e. it does not traverse the children of the elements in the given metadata.
      Parameters:
      visitor - the object on which to invoke MetadataVisitor.visit(Class, Object).
      metadata - the metadata instance for which to visit the non-null properties.
      Throws:
      Exception - if an error occurred while visiting a property.
    • walkWritable

      final void walkWritable(MetadataVisitor<?> visitor, Object source, Object target) throws Exception
      Invokes MetadataVisitor.visit(Class, Object) for all writable properties in the given metadata. This method is not recursive, i.e. it does not traverse the children of the elements in the given metadata.

      Constraint

      In current implementation, if source and target are not the same, then target is assumed empty. The intent is to skip easily null or empty properties.
      Parameters:
      visitor - the object on which to invoke MetadataVisitor.visit(Class, Object).
      source - the metadata from which to read properties. May be the same than target.
      target - the metadata instance where to write properties.
      Throws:
      Exception - if an error occurred while visiting a property.
    • toString

      public String toString()
      Returns a string representation of this accessor for debugging purpose. Output example:
      Overrides:
      toString in class Object