This is an automated email from the ASF dual-hosted git repository. desruisseaux pushed a commit to branch geoapi-4.0 in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/geoapi-4.0 by this push: new ff9dc10dcb Complete `MetadataBuilder.useParentElements()`. ff9dc10dcb is described below commit ff9dc10dcb5e79287940f48211b91df954223dfe Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Fri Dec 8 18:20:58 2023 +0100 Complete `MetadataBuilder.useParentElements()`. --- .../apache/sis/storage/base/MetadataBuilder.java | 94 ++++++++++++++++------ .../main/org/apache/sis/pending/jdk/JDK21.java | 62 ++++++++++++++ .../apache/sis/util/internal/CollectionsExt.java | 16 ---- 3 files changed, 131 insertions(+), 41 deletions(-) diff --git a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java index 9236535864..e43960eec0 100644 --- a/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java +++ b/endorsed/src/org.apache.sis.storage/main/org/apache/sis/storage/base/MetadataBuilder.java @@ -92,6 +92,7 @@ import org.apache.sis.storage.internal.Resources; import org.apache.sis.coverage.SampleDimension; import org.apache.sis.coverage.grid.GridGeometry; import org.apache.sis.coverage.grid.GridExtent; +import org.apache.sis.pending.jdk.JDK21; import org.apache.sis.measure.Units; import static org.apache.sis.util.internal.StandardDateFormat.MILLISECONDS_PER_DAY; @@ -172,6 +173,18 @@ public class MetadataBuilder { public MetadataBuilder() { } + /** + * Creates a new metadata builder for completing an existing metadata. + * The given metadata shall may be modifiable. When a metadata element accepts many instances, + * the instance which will be modified is the last one. + * + * @param edit the metadata to modify, or {@code null} if none. + */ + public MetadataBuilder(final Metadata edit) { + metadata = DefaultMetadata.castOrCopy(edit); + useParentElements(); + } + /** * The metadata created by this builder, or {@code null} if not yet created. */ @@ -3323,25 +3336,34 @@ parse: for (int i = 0; i < length;) { public boolean mergeMetadata(final Object source, final Locale locale) { flush(); final ModifiableMetadata target; + /* + * In the following `instanceof` checks, objects closer to root should be tested first. + * For example, we should finish the checks of all `Metadata` elements before to check + * if the object is a sub-element of a `Metadata` element. This ordering is because an + * implementation may implement many interfaces: the main element together with some of + * its sub-elements. We want to use the object with most information. Furthermore, the + * main object may not use a type (e.g. `Citation`) for the same sub-element than what + * the code below assumes. + */ if (source instanceof Metadata) target = metadata(); else if (source instanceof DataIdentification) target = identification(); + else if (source instanceof GridSpatialRepresentation) target = gridRepresentation(); + else if (source instanceof CoverageDescription) target = coverageDescription(); + else if (source instanceof FeatureCatalogueDescription) target = featureDescription(); + else if (source instanceof AcquisitionInformation) target = acquisition(); + else if (source instanceof Lineage) target = lineage(); + else if (source instanceof Distribution) target = distribution(); else if (source instanceof Citation) target = citation(); + else if (source instanceof Extent) target = extent(); + else if (source instanceof LegalConstraints) target = constraints(); else if (source instanceof Series) target = series(); else if (source instanceof Responsibility) target = responsibility(); else if (source instanceof Party) target = party(); - else if (source instanceof LegalConstraints) target = constraints(); - else if (source instanceof Extent) target = extent(); - else if (source instanceof AcquisitionInformation) target = acquisition(); - else if (source instanceof Platform) target = platform(); - else if (source instanceof FeatureCatalogueDescription) target = featureDescription(); - else if (source instanceof CoverageDescription) target = coverageDescription(); else if (source instanceof AttributeGroup) target = attributeGroup(); else if (source instanceof SampleDimension) target = sampleDimension(); - else if (source instanceof GridSpatialRepresentation) target = gridRepresentation(); else if (source instanceof GCPCollection) target = groundControlPoints(); - else if (source instanceof Distribution) target = distribution(); else if (source instanceof Format) target = format(); - else if (source instanceof Lineage) target = lineage(); + else if (source instanceof Platform) target = platform(); else if (source instanceof ProcessStep) target = processStep(); else if (source instanceof Processing) target = processing(); else return false; @@ -3356,28 +3378,43 @@ parse: for (int i = 0; i < length;) { * This is used for continuing the edition of an existing metadata. */ private void useParentElements() { - if (identification == null) identification = last (DefaultDataIdentification.class, metadata, Metadata::getIdentificationInfo); - if (citation == null) citation = fetch(DefaultCitation.class, identification, Identification::getCitation); - if (responsibility == null) responsibility = last (DefaultResponsibility.class, citation, Citation::getCitedResponsibleParties); - if (party == null) party = last (AbstractParty.class, responsibility, Responsibility::getParties); - if (constraints == null) constraints = last (DefaultLegalConstraints.class, identification, Identification::getResourceConstraints); - if (extent == null) extent = last (DefaultExtent.class, identification, Identification::getExtents); - if (acquisition == null) acquisition = last (DefaultAcquisitionInformation.class, metadata, Metadata::getAcquisitionInformation); - if (platform == null) platform = last (DefaultPlatform.class, acquisition, AcquisitionInformation::getPlatforms); + if (identification == null) identification = last (DefaultDataIdentification.class, metadata, DefaultMetadata::getIdentificationInfo); + if (gridRepresentation == null) gridRepresentation = last (DefaultGridSpatialRepresentation.class, metadata, DefaultMetadata::getSpatialRepresentationInfo); + if (coverageDescription == null) coverageDescription = last (DefaultCoverageDescription.class, metadata, DefaultMetadata::getContentInfo); + if (featureDescription == null) featureDescription = last (DefaultFeatureCatalogueDescription.class, metadata, DefaultMetadata::getContentInfo); + if (acquisition == null) acquisition = last (DefaultAcquisitionInformation.class, metadata, DefaultMetadata::getAcquisitionInformation); + if (lineage == null) lineage = last (DefaultLineage.class, metadata, DefaultMetadata::getResourceLineages); + if (distribution == null) distribution = last (DefaultDistribution.class, metadata, DefaultMetadata::getDistributionInfo); + if (citation == null) citation = fetch(DefaultCitation.class, identification, AbstractIdentification::getCitation); + if (extent == null) extent = last (DefaultExtent.class, identification, AbstractIdentification::getExtents); + if (constraints == null) constraints = last (DefaultLegalConstraints.class, identification, AbstractIdentification::getResourceConstraints); + if (responsibility == null) responsibility = last (DefaultResponsibility.class, citation, DefaultCitation::getCitedResponsibleParties); + if (party == null) party = last (AbstractParty.class, responsibility, DefaultResponsibility::getParties); + if (attributeGroup == null) attributeGroup = last (DefaultAttributeGroup.class, coverageDescription, DefaultCoverageDescription::getAttributeGroups); + if (sampleDimension == null) sampleDimension = last (DefaultSampleDimension.class, attributeGroup, DefaultAttributeGroup::getAttributes); + if (format == null) format = last (DefaultFormat.class, distribution, DefaultDistribution::getDistributionFormats); + if (platform == null) platform = last (DefaultPlatform.class, acquisition, DefaultAcquisitionInformation::getPlatforms); + if (processStep == null) processStep = last (DefaultProcessStep.class, lineage, DefaultLineage::getProcessSteps); + if (processing == null) processing = fetch(DefaultProcessing.class, processStep, DefaultProcessStep::getProcessingInformation); } /** * Returns the element of the given source metadata if it is of the desired class. * This method is equivalent to {@link #last(Class, Object, Function)} but for a singleton. * - * @param target the desired class. + * @param <S> the type of the source metadata. + * @param <E> the type of metadata element provided by the source. + * @param <T> the type of the desired metadata element. + * @param target the type of the desired metadata element. * @param source the source metadata, or {@code null} if none. * @param getter the getter to use for fetching elements from the source metadata. * @return the metadata element from the source, or {@code null} if none. */ - private static <S,T> T fetch(final Class<T> target, final S source, final Function<S,?> getter) { + private static <S extends ISOMetadata, E, T extends E> T fetch(final Class<T> target, final S source, + final Function<S,E> getter) + { if (source != null) { - final Object last = getter.apply(source); + final E last = getter.apply(source); if (target.isInstance(last)) { return target.cast(last); } @@ -3389,16 +3426,23 @@ parse: for (int i = 0; i < length;) { * Returns the element of the given source metadata if it is of the desired class. * This method is equivalent to {@link #fetch(Class, Object, Function)} but for a collection. * - * @param target the desired class. + * @param <S> the type of the source metadata. + * @param <E> the type of metadata element provided by the source. + * @param <T> the type of the desired metadata element. + * @param target the type of the desired metadata element. * @param source the source metadata, or {@code null} if none. * @param getter the getter to use for fetching elements from the source metadata. * @return the metadata element from the source, or {@code null} if none. */ - private static <S,T> T last(final Class<T> target, final S source, final Function<S,Collection<?>> getter) { + private static <S extends ISOMetadata, E, T extends E> T last(final Class<T> target, final S source, + final Function<S,Collection<E>> getter) + { if (source != null) { - final Object last = CollectionsExt.last(getter.apply(source)); - if (target.isInstance(last)) { - return target.cast(last); + // If not a sequenced collection, the iteration may be in any order. + for (final E last : JDK21.reversed(getter.apply(source))) { + if (target.isInstance(last)) { + return target.cast(last); + } } } return null; diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK21.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK21.java new file mode 100644 index 0000000000..33580297b4 --- /dev/null +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/pending/jdk/JDK21.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.sis.pending.jdk; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + + +/** + * Place holder for some functionalities defined in a JDK more recent than Java 11. + * + * @author Martin Desruisseaux (Geomatys) + */ +public final class JDK21 { + /** + * Do not allow instantiation of this class. + */ + private JDK21() { + } + + /** + * Placeholder for {@code SequencedCollection.reversed()}. + * + * @param <E> type of elements in the collection. + * @param sequenced the sequenced collection for which to get elements in reverse order. + * @return elements of the given collection in reverse order. + */ + public static <E> Iterable<E> reversed(final Collection<E> sequenced) { + final List<E> list; + if (sequenced instanceof List<?>) { + list = (List<E>) sequenced; + } else { + list = new ArrayList<>(sequenced); + } + return new Iterable<>() { + @Override public Iterator<E> iterator() { + final ListIterator<E> it = list.listIterator(list.size()); + return new Iterator<E>() { + @Override public boolean hasNext() {return it.hasPrevious();} + @Override public E next() {return it.previous();} + }; + } + }; + } +} diff --git a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/CollectionsExt.java b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/CollectionsExt.java index e43ae7e5d2..d24b297d2e 100644 --- a/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/CollectionsExt.java +++ b/endorsed/src/org.apache.sis.util/main/org/apache/sis/util/internal/CollectionsExt.java @@ -139,22 +139,6 @@ public final class CollectionsExt extends Static { return null; } - /** - * Returns the last element of the given iterable if it is a list, or an arbitrary element otherwise. - * - * @todo Check for sequenced collection in JDK21. - * - * @param <T> the type of elements contained in the iterable. - * @param collection the iterable from which to get the last element, or {@code null}. - * @return the last element, or {@code null} if the given iterable is null or empty. - */ - public static <T> T last(final Collection<T> collection) { - if (collection instanceof List<?> && !collection.isEmpty()) { - return ((List<T>) collection).get(collection.size() - 1); - } - return null; - } - /** * If the given iterable contains exactly one non-null element, returns that element. * Otherwise returns {@code null}.