This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
commit 69716c5e8fdb09281b3f99f62417deb5e71f9b47 Author: Martin Desruisseaux <[email protected]> AuthorDate: Thu May 28 13:02:02 2026 +0200 Support transitive search of metadata title when metadata are represented as a tree. --- .../org/apache/sis/metadata/MetadataStandard.java | 88 ++++++++++++++++++---- .../apache/sis/metadata/ModifiableMetadata.java | 8 +- .../org/apache/sis/metadata/PropertyAccessor.java | 2 +- .../org/apache/sis/metadata/TitleProperty.java | 11 +-- .../main/org/apache/sis/metadata/TreeNode.java | 35 +++++---- .../org/apache/sis/metadata/TreeNodeChildren.java | 74 ++++++++++++------ .../apache/sis/metadata/ValueExistencePolicy.java | 1 + .../apache/sis/metadata/sql/MetadataSource.java | 14 ++-- .../apache/sis/metadata/sql/MetadataWriter.java | 30 +++++--- .../sis/metadata/ModifiableMetadataTest.java | 1 + .../sis/metadata/PropertyConsistencyCheck.java | 8 +- 11 files changed, 186 insertions(+), 86 deletions(-) diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java index f485016d8f..5746d44b2f 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/MetadataStandard.java @@ -20,8 +20,10 @@ import java.util.Set; import java.util.Map; import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentHashMap; import java.io.IOException; @@ -359,9 +361,7 @@ public class MetadataStandard implements Serializable { /** * Returns the accessor for the specified metadata instance. - * The {@code propertyType.isInstance(metadata)} condition should always be {@code true}, - * but this is not verified by this constructor. Instead, the validity can be verified - * after constructor with {@link CacheKey#isValid()}. + * The {@code propertyType.isInstance(metadata)} condition should always be {@code true}. * * <p>A null value for {@code propertyType} is not equivalent to {@code Object.class}. * If the value is null, this constructor tries to detect the interface automatically. @@ -720,27 +720,83 @@ public class MetadataStandard implements Serializable { } /** - * Returns a value of the "title" property of the given metadata object. - * The title property is defined by {@link TitleProperty} annotation on the implementation class. + * Returns the value of the property that summarizes the given metadata object. + * The property is specified by the {@link TitleProperty} annotation on the implementation class. + * This method first searches for {@code TitleProperty} in the implementation class of the given + * {@code metadata}. If the implementation class is not annotated, then this method searches in + * the {@linkplain #getImplementation(Class) implementation class managed by this standard}. + * + * <p>If the property value is itself another metadata annotated with {@code TitleProperty}, + * then the search for a title continues recursively in the other metadata. + * If a cyclic graph is detected, this method returns the last value before the cycle.</p> * * @param metadata the metadata for which to get the title property, or {@code null}. - * @return the title property value of the given metadata, or {@code null} if none. + * @return the title property value of the given metadata, or empty if none. * * @see TitleProperty * @see ValueExistencePolicy#TITLED + * + * @since 1.7 */ - final Object getTitle(final Object metadata) { - if (metadata != null) { - final Class<?> type = metadata.getClass(); - final PropertyAccessor accessor = getTypeAccessor(type, false); - if (accessor != null) { - TitleProperty an = type.getAnnotation(TitleProperty.class); - if (an != null || (an = accessor.implementation.getAnnotation(TitleProperty.class)) != null) { - return accessor.get(accessor.indexOf(an.name(), false), metadata); + public Optional<Object> getTitle(final Object metadata) { + return Optional.ofNullable(getTitle(metadata, null, false, false, false)); + } + + /** + * Implementation of {@link #getTitle(Object)} with the possibility to specify the base interface. + * The {@code metadata} argument should be an instance of {@code propertyType}. + * + * <p>Note that a return value of {@code null} may be either because the metadata has no + * {@link TitleProperty} property, or because the property exists but has a {@code null} value. + * The {@code isStarted} parameter is required for callers that need to distinguish these two cases.</p> + * + * @param metadata the metadata for which to get the title property, or {@code null}. + * @param propertyType the interface which should be implemented to the metadata. + * @param isTypeOnly {@code false} if {@code metadata} is an instance, or {@code true} if a {@code Class}. + * @param wantTypeOnly whether to return the property type (as a {@link Class}) rather than the value. + * @param isStarted if this method is invoked for a search that already started on the first annotated property. + * @return the title property value or type of the given metadata, or {@code null} if none. + */ + final Object getTitle(Object metadata, + Class<?> propertyType, + boolean isTypeOnly, + final boolean wantTypeOnly, + final boolean isStarted) + { + final var done = new IdentityHashMap<Object, Boolean>(); + while (metadata != null) { + final Class<?> type; + final PropertyAccessor accessor; + if (isTypeOnly) { + type = (Class<?>) metadata; + accessor = getTypeAccessor(type, false); + } else { + type = metadata.getClass(); + accessor = getInstanceAccessor(metadata, propertyType, false); + } + if (accessor == null) break; // Not a metadata. + TitleProperty a = type.getAnnotation(TitleProperty.class); + if (a == null) { + if (accessor.implementation == type) break; + a = accessor.implementation.getAnnotation(TitleProperty.class); + if (a == null) break; // No `TitleProperty` annotation. + } + final int index = accessor.indexOf(a.name(), false); + if (index < 0 || accessor.isCollectionOrMap(index)) break; // Illegal property in the annotation. + if (done.put(metadata, Boolean.TRUE) != null) break; // Safety against never-ending loop. + propertyType = accessor.type(index, TypeValuePolicy.ELEMENT_TYPE); + if (isTypeOnly) { + metadata = propertyType; + } else { + metadata = accessor.get(index, metadata); + if (metadata == null && wantTypeOnly) { + metadata = propertyType; + isTypeOnly = true; } } + // Check recursively if the property value is another metadata. } - return null; + return (!isStarted && done.isEmpty()) ? null : wantTypeOnly ? propertyType : metadata; } /** @@ -790,7 +846,7 @@ public class MetadataStandard implements Serializable { * the following code prints the {@link org.opengis.util.InternationalString} class name: * * {@snippet lang="java" : - * MetadataStandard standard = MetadataStandard.ISO_19115; + * MetadataStandard standard = MetadataStandard.ISO_19115; * Map<String, Class<?>> types = standard.asTypeMap(Citation.class, UML_IDENTIFIER, ELEMENT_TYPE); * Class<?> value = types.get("alternateTitle"); * System.out.println(value); // class org.opengis.util.InternationalString diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ModifiableMetadata.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ModifiableMetadata.java index b21e83387b..f91f1d7001 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ModifiableMetadata.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ModifiableMetadata.java @@ -371,7 +371,7 @@ public abstract class ModifiableMetadata extends AbstractMetadata { * * @since 1.0 */ - protected void checkWritePermission(Object current) throws UnmodifiableMetadataException { + protected void checkWritePermission(final Object current) throws UnmodifiableMetadataException { if (state != COMPLETABLE) { if (state == FINAL) { throw new UnmodifiableMetadataException(Resources.format(Resources.Keys.UnmodifiableMetadata)); @@ -383,9 +383,9 @@ public abstract class ModifiableMetadata extends AbstractMetadata { } else { standard = getStandard(); } - final Object c = standard.getTitle(current); - if (c != null) current = c; - throw new UnmodifiableMetadataException(Resources.format(Resources.Keys.ElementAlreadyInitialized_1, current)); + throw new UnmodifiableMetadataException(Resources.format( + Resources.Keys.ElementAlreadyInitialized_1, + standard.getTitle(current).orElse(current))); } } diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java index b9f4278176..09d9eba38f 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/PropertyAccessor.java @@ -621,7 +621,7 @@ class PropertyAccessor { } /** - * Returns {@code true} if the type at the given index is {@link Collection} or {@link Map}. + * Returns {@code true} if the type at the given index is assignable to {@link Collection} or {@link Map}. */ final boolean isCollectionOrMap(final int index) { if (index >= 0 && index < allCount) { diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java index 42e15f4433..ff7c2d301b 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TitleProperty.java @@ -32,7 +32,7 @@ import java.lang.annotation.Documented; * especially when there is redundant node names. * * <h2>Example</h2> - * the {@code Citation} type contains a {@linkplain org.apache.sis.metadata.iso.citation.DefaultCitation#getDates() date} + * The {@code Citation} type contains a {@linkplain org.apache.sis.metadata.iso.citation.DefaultCitation#getDates() date} * property which itself contains another {@linkplain org.apache.sis.metadata.iso.citation.DefaultCitationDate#getDate() * date} property. They form a tree like below: * @@ -45,7 +45,7 @@ import java.lang.annotation.Documented; * * With <code>@TitleProperty(name="title")</code> on {@code DefaultCitation} implementation class and * <code>@TitleProperty(name="date")</code> on {@code DefaultCitationDate} class, - * Apache SIS can produce a more compact tree table view should be as below: + * Apache <abbr>SIS</abbr> can produce a more compact tree table view should be as below: * * <pre class="text"> * Citation……………………… My document @@ -53,15 +53,16 @@ import java.lang.annotation.Documented; * └─Date type…… Creation</pre> * * <h2>Condition</h2> - * The property referenced by this annotation should be the main property if possible, but not necessarily - * since it may be only a label. However, the property shall be a singleton ([0…1] or [1…1] multiplicity) - * and cannot be another metadata object. + * The property referenced by this annotation should be a title, a label or a property which is considered + * as the main property of the metadata object. The property shall be a singleton ([0…1] or [1…1] multiplicity). + * If the property is another metadata object, then that other object shall itself have this annotation. * * @author Martin Desruisseaux (Geomatys) * @version 0.8 * @since 0.8 * * @see ValueExistencePolicy#TITLED + * @see MetadataStandard#getTitle(Object) */ @Documented @Inherited diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java index c92448097d..ef951e6654 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNode.java @@ -207,6 +207,13 @@ class TreeNode implements Node { return table.standard.isMetadata(type); } + /** + * Returns the metadata interface for the object represented by this node. + */ + private Class<?> getInterface() { + return table.standard.getInterface(key(), null); + } + /** * Returns the key to use for calls to {@link MetadataStandard} methods. * This key is used only for some default method implementations in the root node. @@ -236,7 +243,7 @@ class TreeNode implements Node { * order to return the property identifier instead. */ String getIdentifier() { - final Class<?> type = table.standard.getInterface(key(), null); + final Class<?> type = getInterface(); final String id = Types.getStandardName(type); return (id != null) ? id : Classes.getShortName(type); } @@ -258,8 +265,7 @@ class TreeNode implements Node { * <p>The default implementation is suitable only for the root node - subclasses must override.</p> */ CharSequence getName() { - return CharSequences.camelCaseToSentence(Classes.getShortName( - table.standard.getInterface(key(), null))).toString(); + return CharSequences.camelCaseToSentence(Classes.getShortName(getInterface())).toString(); } /** @@ -348,10 +354,13 @@ class TreeNode implements Node { * this condition should ensure that two equal nodes have the same values and children. */ @Override - public boolean equals(final Object other) { - return (other != null) && other.getClass() == getClass() - && ((TreeNode) other).metadata == metadata - && ((TreeNode) other).baseType == baseType; + public boolean equals(final Object obj) { + if (obj != null && obj.getClass() == getClass()) { + final var other = (TreeNode) obj; + return other.metadata == metadata && + other.baseType == baseType; + } + return false; } /** @@ -1028,7 +1037,7 @@ class TreeNode implements Node { /** * Returns the children if the value policy puts a title on metadata objects, or {@code null} otherwise. * The callers are not interested in the collection of children, but rather in specialized methods such - * as {@link TreeNodeChildren#getParentTitle()}. + * as {@link TreeNodeChildren#getParentTitle(boolean)}. */ private TreeNodeChildren getCompactChildren() { if (table.valuePolicy.isTitled()) { @@ -1042,9 +1051,9 @@ class TreeNode implements Node { } /** - * Returns the value of this node in the given column, or {@code null} if none. This method verifies - * the {@code column} argument, then delegates to {@link #getName()}, {@link #getUserObject()} or - * other properties. + * Returns the value of this node in the given column, or {@code null} if none. + * This method verifies the {@code column} argument, then delegates to {@link #getName()}, + * {@link #getUserObject()} or other properties. */ @Override public final <V> V getValue(final TableColumn<V> column) { @@ -1062,7 +1071,7 @@ class TreeNode implements Node { } else if (column == TableColumn.TYPE) { @SuppressWarnings("LocalVariableHidesMemberVariable") final TreeNodeChildren children = getCompactChildren(); - if (children == null || (value = children.getParentType()) == null) { + if (children == null || (value = (Class<?>) children.getParentTitle(true)) == null) { value = baseType; } } else if (column == TableColumn.VALUE) { @@ -1072,7 +1081,7 @@ class TreeNode implements Node { @SuppressWarnings("LocalVariableHidesMemberVariable") final TreeNodeChildren children = getCompactChildren(); if (children != null) { - value = children.getParentTitle(); + value = children.getParentTitle(false); } } } else if (column == MetadataColumn.OBLIGATION) { diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java index 04538753ac..677735f593 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/TreeNodeChildren.java @@ -146,23 +146,31 @@ final class TreeNodeChildren extends AbstractCollection<TreeTable.Node> { /* * Search for something that looks like the main property, to be associated with the parent node * instead of provided as a child. The intent is to make trees more compact and easier to read. - * That property shall be a singleton for a simple value (not another metadata object). + * That property shall be a singleton for a simple value. */ - if (parent.table.valuePolicy.isTitled()) { + final TreeTableView table = parent.table; + if (table.valuePolicy.isTitled()) { TitleProperty an = accessor.implementation.getAnnotation(TitleProperty.class); if (an == null) { - Class<?> implementation = parent.table.standard.getImplementation(accessor.type); - if (implementation != null) { + Class<?> implementation = table.standard.getImplementation(accessor.type); + if (implementation != null && accessor.implementation != implementation) { an = implementation.getAnnotation(TitleProperty.class); } } if (an != null) { final int index = accessor.indexOf(an.name(), false); final Class<?> type = accessor.type(index, TypeValuePolicy.ELEMENT_TYPE); - if (type != null && !parent.isMetadata(type) && type == accessor.type(index, TypeValuePolicy.PROPERTY_TYPE)) { - hiddenProperty = (parent.table.valuePolicy == ValueExistencePolicy.COMPACT) ? index : -1; - titleProperty = index; - return; + if (type != null && type == accessor.type(index, TypeValuePolicy.PROPERTY_TYPE)) { + /* + * In compact mode, do not use metadata object as title (with recursive search of title) + * because we don't want to hide the whole metadata object from the tree. + */ + final boolean isCompact = (table.valuePolicy == ValueExistencePolicy.COMPACT); + if (!(isCompact && parent.isMetadata(type))) { + hiddenProperty = isCompact ? index : -1; + titleProperty = index; + return; + } } } } @@ -178,19 +186,34 @@ final class TreeNodeChildren extends AbstractCollection<TreeTable.Node> { } /** - * If a simple value should be associated to the parent node, returns the type of that value. - * Otherwise returns {@code null}. - */ - final Class<?> getParentType() { - return (titleProperty >= 0) ? accessor.type(titleProperty, TypeValuePolicy.ELEMENT_TYPE) : null; - } - - /** - * If a simple value should be associated to the parent node, returns that value. - * Otherwise returns {@code null}. + * If a simple value should be associated to the parent node, returns that value or the type of that value. + * Otherwise returns {@code null}. The title is identified by {@link TitleProperty} annotation on the class. + * If {@code wantTypeOnly} is {@code true}, then the returned object can be safety cast to {@link Class}. + * + * @param wantTypeOnly whether to return the property type (as a {@link Class}) instead of the value. + * @return the value or type of the title property of the given metadata, or {@code null} if none. */ - final Object getParentTitle() { - return (titleProperty >= 0) ? valueAt(titleProperty) : null; + final Object getParentTitle(final boolean wantTypeOnly) { + if (isNodeTitle()) { + final boolean isCompact = (hiddenProperty >= 0); + if (isCompact && !wantTypeOnly) { + return valueAt(titleProperty); // Skip the recursive search in metadata objects. + } + final Class<?> type = accessor.type(titleProperty, TypeValuePolicy.ELEMENT_TYPE); + if (isCompact) { + return type; // Skip the recursive search in metadata objects. + } + final Object value = valueAt(titleProperty); + if (value != null || wantTypeOnly) { + return parent.table.standard.getTitle( + (value != null) ? value : type, // Value or type to check for `TitleProperty` annotation. + type, // For disambiguation if `value` implements many interfaces. + value == null, // Whether the first argument is a class rather than instance. + wantTypeOnly, // Whether the caller wants only a `Class`. + true); // Tells that the first argument is already a title. + } + } + return null; } /** @@ -198,11 +221,12 @@ final class TreeNodeChildren extends AbstractCollection<TreeTable.Node> { * The returned Boolean tells whether the value has been written. */ final boolean setParentTitle(final Object value) { - if (titleProperty < 0) { - return false; + if (hiddenProperty >= 0) { + // Accept only the hidden property because it requires no recursive search of title. + accessor.set(hiddenProperty, metadata, value, PropertyAccessor.RETURN_NULL); + return true; } - accessor.set(titleProperty, metadata, value, PropertyAccessor.RETURN_NULL); - return true; + return false; } /** @@ -273,7 +297,7 @@ final class TreeNodeChildren extends AbstractCollection<TreeTable.Node> { * @param index the index in the accessor (<em>not</em> the index in this collection). * @param subIndex if the property at {@code index} is a collection, the index in that * collection (<em>not</em> the index in <em>this</em> collection). Otherwise -1. - * @return the node to be returned by public API. + * @return the node to be returned by public <abbr>API</abbr>. */ final TreeNode.Element childAt(final int index, final int subIndex) { TreeNode.Element node = children[index]; diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java index c283cef3c3..8ec800c108 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/ValueExistencePolicy.java @@ -141,6 +141,7 @@ public enum ValueExistencePolicy { * See {@link #COMPACT} Javadoc for an example.</p> * * @see TitleProperty + * @see MetadataStandard#getTitle(Object) * * @since 1.7 */ diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java index a9c7527db7..183769612e 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataSource.java @@ -367,8 +367,10 @@ public class MetadataSource implements AutoCloseable { * @param schema the database schema were metadata tables are stored, or {@code null} if none. * @param properties additional options, or {@code null} if none. See class javadoc for a description. */ - public MetadataSource(final MetadataStandard standard, final DataSource dataSource, - final String schema, final Map<String,?> properties) + public MetadataSource(final MetadataStandard standard, + final DataSource dataSource, + final String schema, + final Map<String, ?> properties) { ArgumentChecks.ensureNonNull("standard", standard); ArgumentChecks.ensureNonNull("dataSource", dataSource); @@ -935,10 +937,10 @@ public class MetadataSource implements AutoCloseable { final Dispatcher toSearch = new Dispatcher(identifier, this); try { value = subType.getConstructor().newInstance(); - final LookupInfo info = getLookupInfo(subType); - final Map<String,Object> map = asValueMap(value); - final Map<String,String> methods = standard.asNameMap(subType, NAME_POLICY, KeyNamePolicy.METHOD_NAME); - for (final Map.Entry<String,Object> entry : map.entrySet()) { + final LookupInfo info = getLookupInfo(subType); + final Map<String, Object> map = asValueMap(value); + final Map<String, String> methods = standard.asNameMap(subType, NAME_POLICY, KeyNamePolicy.METHOD_NAME); + for (final Map.Entry<String, Object> entry : map.entrySet()) { method = subType.getMethod(methods.get(entry.getKey())); info.setMetadataType(subType); final Object p = readColumn(info, method, toSearch); diff --git a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java index 1eb1fd9488..b6dcc0d6d5 100644 --- a/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java +++ b/endorsed/src/org.apache.sis.metadata/main/org/apache/sis/metadata/sql/MetadataWriter.java @@ -146,8 +146,11 @@ public class MetadataWriter extends MetadataSource { * @param schema the database schema were metadata tables are stored, or {@code null} if none. * @param properties additional options, or {@code null} if none. See class javadoc for a description. */ - public MetadataWriter(final MetadataStandard standard, final DataSource dataSource, final String schema, - final Map<String,?> properties) + @SuppressWarnings("LocalVariableHidesMemberVariable") + public MetadataWriter(final MetadataStandard standard, + final DataSource dataSource, + final String schema, + final Map<String, ?> properties) { super(standard, dataSource, schema, properties); Integer maximumIdentifierLength = Containers.property(properties, "maximumIdentifierLength", Integer.class); @@ -235,8 +238,11 @@ public class MetadataWriter extends MetadataSource { * @throws ClassCastException if the metadata object does not implement a metadata interface * of the expected package. */ - private String add(final Statement stmt, final Object metadata, final Map<Object,String> done, - final String parent) throws ClassCastException, SQLException, FactoryException + private String add(final Statement stmt, + final Object metadata, + final Map<Object, String> done, + final String parent) + throws ClassCastException, SQLException, FactoryException { final SQLBuilder helper = helper(); /* @@ -244,8 +250,8 @@ public class MetadataWriter extends MetadataSource { * concurrent changes in the metadata object. This protection is needed because we need to * perform multiple passes on the same metadata. */ - final Map<String,Object> asValueMap = asValueMap(metadata); - final Map<String,Object> asSingletons = new LinkedHashMap<>(); + final Map<String, Object> asValueMap = asValueMap(metadata); + final Map<String, Object> asSingletons = new LinkedHashMap<>(); for (final Map.Entry<String,Object> entry : asValueMap.entrySet()) { asSingletons.put(entry.getKey(), extractFromCollection(entry.getValue())); } @@ -288,8 +294,8 @@ public class MetadataWriter extends MetadataSource { * this process but will not create the constraints now because the foreigner tables may not exist yet. * They will be created later by recursive calls to this method a little bit below. */ - Map<String,Class<?>> colTypes = null, colTables = null; - final Map<String,FKey> foreigners = new LinkedHashMap<>(); + Map<String, Class<?>> colTypes = null, colTables = null; + final var foreigners = new LinkedHashMap<String, FKey>(); for (final String column : asSingletons.keySet()) { if (!columns.contains(column)) { if (colTypes == null) { @@ -401,8 +407,8 @@ public class MetadataWriter extends MetadataSource { * Once a dependency has been added to the database, the corresponding value in * the `asMap` HashMap is replaced by the identifier of the dependency we just added. */ - Map<String,FKey> referencedTables = null; - for (final Map.Entry<String,Object> entry : asSingletons.entrySet()) { + Map<String, FKey> referencedTables = null; + for (final Map.Entry<String, Object> entry : asSingletons.entrySet()) { Object value = entry.getValue(); final Class<?> type = value.getClass(); if (ControlledVocabulary.class.isAssignableFrom(type)) { @@ -689,7 +695,7 @@ public class MetadataWriter extends MetadataSource { * @return the proposed identifier, or {@code null} if this method does not have any suggestion. * @throws SQLException if an access to the database was desired but failed. */ - protected String suggestIdentifier(final Object metadata, final Map<String,Object> asValueMap) throws SQLException { + protected String suggestIdentifier(final Object metadata, final Map<String, Object> asValueMap) throws SQLException { String identifier = null; final Collection<? extends Identifier> identifiers; if (metadata instanceof Identifier) { @@ -717,7 +723,7 @@ public class MetadataWriter extends MetadataSource { if (tp != null) { final Object value = asValueMap.get(Strings.trimOrNull(tp.name())); if (value != null) { - identifier = Strings.trimOrNull(value.toString()); + identifier = Strings.trimOrNull(standard.getTitle(value).orElse(value).toString()); } } } diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/ModifiableMetadataTest.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/ModifiableMetadataTest.java index ce7abe57ac..389f94111b 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/ModifiableMetadataTest.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/ModifiableMetadataTest.java @@ -38,6 +38,7 @@ import static org.apache.sis.test.Assertions.assertSetEquals; * * @author Martin Desruisseaux (Geomatys) */ +@SuppressWarnings("exports") public final class ModifiableMetadataTest extends TestCase { /** * An arbitrary metadata on which to perform the tests. diff --git a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java index a7c2044c91..bf51fbac2f 100644 --- a/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java +++ b/endorsed/src/org.apache.sis.metadata/test/org/apache/sis/metadata/PropertyConsistencyCheck.java @@ -77,7 +77,7 @@ public abstract class PropertyConsistencyCheck extends AnnotationConsistencyChec } /** - * Returns the SIS implementation for the given GeoAPI interface. + * Returns the <abbr>SIS</abbr> implementation for the given GeoAPI interface. * * @return {@inheritDoc} */ @@ -136,7 +136,7 @@ public abstract class PropertyConsistencyCheck extends AnnotationConsistencyChec if (type == CodeList.class) { return null; } - final ControlledVocabulary[] codes = (ControlledVocabulary[]) type.getMethod("values", (Class[]) null).invoke(null, (Object[]) null); + final var codes = (ControlledVocabulary[]) type.getMethod("values", (Class[]) null).invoke(null, (Object[]) null); return codes[random.nextInt(codes.length)]; } catch (ReflectiveOperationException e) { fail(e.toString()); @@ -328,7 +328,7 @@ public abstract class PropertyConsistencyCheck extends AnnotationConsistencyChec if (an != null) { final String name = an.name(); final String message = impl.getSimpleName() + '.' + name; - final PropertyAccessor accessor = new PropertyAccessor(type, impl, impl); + final var accessor = new PropertyAccessor(type, impl, impl); // Property shall exist. final int index = accessor.indexOf(name, false); @@ -357,7 +357,7 @@ public abstract class PropertyConsistencyCheck extends AnnotationConsistencyChec for (final Class<?> type : types) { final Class<?> impl = standard.getImplementation(type); if (impl != null) { - Map<String,String> names = null; + Map<String, String> names = null; for (final Method method : impl.getDeclaredMethods()) { final Dependencies dep = method.getAnnotation(Dependencies.class); if (dep != null) {
