| /* |
| * 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.metadata.iso; |
| |
| import java.util.Set; |
| import java.util.List; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.io.Serializable; |
| import javax.xml.bind.annotation.XmlID; |
| import javax.xml.bind.annotation.XmlAttribute; |
| import javax.xml.bind.annotation.XmlTransient; |
| import javax.xml.bind.annotation.XmlSchemaType; |
| import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; |
| import javax.xml.bind.annotation.adapters.CollapsedStringAdapter; |
| import org.opengis.metadata.Identifier; |
| import org.apache.sis.xml.IdentifierMap; |
| import org.apache.sis.xml.IdentifierSpace; |
| import org.apache.sis.xml.IdentifiedObject; |
| import org.apache.sis.metadata.MetadataStandard; |
| import org.apache.sis.metadata.ModifiableMetadata; |
| import org.apache.sis.internal.jaxb.IdentifierMapAdapter; |
| import org.apache.sis.internal.jaxb.ModifiableIdentifierMap; |
| import org.apache.sis.internal.jaxb.NonMarshalledAuthority; |
| import org.apache.sis.internal.metadata.MetadataUtilities; |
| import org.apache.sis.internal.util.CollectionsExt; |
| import org.apache.sis.internal.system.Modules; |
| import org.apache.sis.util.collection.Containers; |
| |
| import static org.apache.sis.util.collection.Containers.isNullOrEmpty; |
| import static org.apache.sis.internal.metadata.MetadataUtilities.valueIfDefined; |
| |
| |
| /** |
| * The base class of ISO 19115 implementation classes. Each sub-classes implements one |
| * of the ISO Metadata interface provided by <a href="http://www.geoapi.org">GeoAPI</a>. |
| * |
| * <p><b>Limitations:</b></p> |
| * <ul> |
| * <li>Instances of this class are not synchronized for multi-threading. |
| * Synchronization, if needed, is caller's responsibility.</li> |
| * <li>Serialized objects of this class are not guaranteed to be compatible with future Apache SIS releases. |
| * Serialization support is appropriate for short term storage or RMI between applications running the |
| * same version of Apache SIS. For long term storage, use {@link org.apache.sis.xml.XML} instead.</li> |
| * </ul> |
| * |
| * @author Martin Desruisseaux (Geomatys) |
| * @version 1.0 |
| * @since 0.3 |
| * @module |
| */ |
| @XmlTransient |
| public class ISOMetadata extends ModifiableMetadata implements IdentifiedObject, Serializable { |
| /** |
| * Serial number for inter-operability with different versions. |
| */ |
| private static final long serialVersionUID = -4997239501383133209L; |
| |
| /** |
| * All identifiers associated with this metadata, or {@code null} if none. |
| * This field is initialized to a non-null value when first needed. |
| */ |
| protected Collection<Identifier> identifiers; |
| |
| /** |
| * Constructs an initially empty metadata. |
| */ |
| protected ISOMetadata() { |
| } |
| |
| /** |
| * Constructs a new metadata initialized with the values from the specified object. |
| * If the given object is an instance of {@link IdentifiedObject}, then this constructor |
| * copies the {@linkplain #identifiers collection of identifiers}. |
| * |
| * @param object the metadata to copy values from, or {@code null} if none. |
| */ |
| protected ISOMetadata(final Object object) { |
| if (object instanceof IdentifiedObject) { |
| if (object instanceof ISOMetadata && Containers.isNullOrEmpty(((ISOMetadata) object).identifiers)) { |
| /* |
| * If the other object is an ISOMetadata instance, take a look at its 'identifiers' collection |
| * before to invoke object.getIdentifiers() in order to avoid unnecessary initialization of its |
| * backing collection. We do this optimization because the vast majority of metadata objects do |
| * not have 'identifiers' collection. |
| * |
| * Actually this optimization is a little bit dangerous, since users could override getIdentifiers() |
| * without invoking super.getIdentifiers(), in which case their identifiers will not be copied. |
| * For safety, we will do this optimization only if the implementation is an Apache SIS one. |
| */ |
| if (object.getClass().getName().startsWith(Modules.CLASSNAME_PREFIX)) { |
| return; |
| } |
| } |
| identifiers = copyCollection(((IdentifiedObject) object).getIdentifiers(), Identifier.class); |
| } |
| } |
| |
| /** |
| * Returns the metadata standard implemented by subclasses, |
| * which is {@linkplain MetadataStandard#ISO_19115 ISO 19115}. |
| * |
| * <h4>Note for implementers</h4> |
| * Subclasses shall not override this method in a way that depends on the object state, |
| * since this method may be indirectly invoked by copy constructors (i.e. is may be |
| * invoked before this metadata object is fully constructed). |
| * |
| * @return the metadata standard, which is {@linkplain MetadataStandard#ISO_19115 ISO 19115} by default. |
| */ |
| @Override |
| public MetadataStandard getStandard() { |
| return MetadataStandard.ISO_19115; |
| } |
| |
| |
| |
| |
| // -------------------------------------------------------------------------------------- |
| // Identifier methods below also appear in other IdentifiedObject implementations. |
| // If this code is modified, consider revisiting also the following classes: |
| // |
| // * org.apache.sis.metadata.iso.identification.DefaultRepresentativeFraction |
| // -------------------------------------------------------------------------------------- |
| |
| /** |
| * Returns all identifiers associated to this object (from conceptual model and from XML document). |
| * This collection may contain identifiers from different sources: |
| * |
| * <ul class="verbose"> |
| * <li>Identifiers specified in the ISO 19115-1 or 19115-2 abstract models, |
| * typically (but not necessarily) as an {@code identifier} property |
| * (may also be {@link DefaultMetadata#getMetadataIdentifier() metadataIdentifier}, |
| * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getISBN() ISBN} or |
| * {@link org.apache.sis.metadata.iso.citation.DefaultCitation#getISSN() ISSN} properties).</li> |
| * <li>Identifiers specified in the ISO 19115-3 or 19115-4 XML schemas. |
| * Those identifiers are typically stored as a result of unmarshalling an XML document. |
| * Those identifiers can be recognized by an {@linkplain Identifier#getAuthority() authority} |
| * sets as one of the {@link IdentifierSpace} constants.</li> |
| * </ul> |
| */ |
| @Override |
| public Collection<Identifier> getIdentifiers() { |
| return identifiers = nonNullCollection(identifiers, Identifier.class); |
| } |
| |
| /** |
| * {@inheritDoc} |
| * |
| * <p>The default implementation returns a wrapper around the {@link #identifiers} list. |
| * That map is <cite>live</cite>: changes in the identifiers list will be reflected in the map, |
| * and conversely.</p> |
| */ |
| @Override |
| public IdentifierMap getIdentifierMap() { |
| /* |
| * Do not invoke getIdentifiers(), because some subclasses like DefaultCitation and |
| * DefaultObjective override getIdentifiers() in order to return a filtered list. |
| */ |
| identifiers = nonNullCollection(identifiers, Identifier.class); |
| if (identifiers == null) { |
| return IdentifierMapAdapter.EMPTY; |
| } |
| /* |
| * We do not cache (for now) the IdentifierMap because it is cheap to create, and if we were |
| * caching it we would need anyway to check if 'identifiers' still references the same list. |
| */ |
| return (super.state() != State.FINAL) ? new ModifiableIdentifierMap(identifiers) |
| : new IdentifierMapAdapter(identifiers); |
| } |
| |
| /** |
| * Returns the first identifier which is presumed to be defined by ISO 19115 conceptual model. |
| * This method checks the {@linkplain Identifier#getAuthority() authority} for filtering ignorable |
| * identifiers like ISBN/ISSN codes and XML attributes. |
| * This convenience method is provided for implementation of public {@code getIdentifier(Identifier)} |
| * methods in subclasses having an {@code identifier} property with [0 … 1] multiplicity. |
| * |
| * @return an identifier from ISO 19115-3 conceptual model (excluding XML identifiers), |
| * or {@code null} if none. |
| * |
| * @since 1.0 |
| */ |
| protected Identifier getIdentifier() { |
| return NonMarshalledAuthority.getMarshallable(identifiers); |
| } |
| |
| /** |
| * Sets the identifier for metadata objects that are expected to contain at most one ISO 19115 identifier. |
| * This convenience method is provided for implementation of public {@code setIdentifier(Identifier)} methods |
| * in subclasses having an {@code identifier} property with [0 … 1] multiplicity. |
| * The default implementation removes all identifiers that would be returned by {@link #getIdentifier()} |
| * before to add the given one in the {@link #identifiers} collection. |
| * |
| * @param newValue the new identifier value, or {@code null} for removing the identifier. |
| * |
| * @since 1.0 |
| */ |
| protected void setIdentifier(final Identifier newValue) { |
| checkWritePermission(valueIfDefined(identifiers)); |
| identifiers = nonNullCollection(identifiers, Identifier.class); |
| identifiers = writeCollection(NonMarshalledAuthority.setMarshallable(identifiers, newValue), identifiers, Identifier.class); |
| } |
| |
| // -------------------------------------------------------------------------------------- |
| // End of identifier methods. |
| // -------------------------------------------------------------------------------------- |
| |
| /** |
| * {@inheritDoc} |
| */ |
| @Override |
| public boolean transitionTo(final State target) { |
| final Collection<Identifier> p = identifiers; |
| final boolean changed = super.transitionTo(target); |
| if (changed) { |
| /* |
| * The 'identifiers' collection will have been replaced by an unmodifiable collection if |
| * subclass has an "identifiers" property. If this is not the case, then the collection |
| * is unchanged (or null) so we have to make it unmodifiable here. |
| */ |
| if (p != null && p == identifiers) { |
| if (p instanceof Set<?>) { |
| identifiers = CollectionsExt.unmodifiableOrCopy((Set<Identifier>) p); |
| } else if (p instanceof List<?>) { |
| identifiers = CollectionsExt.unmodifiableOrCopy((List<Identifier>) p); |
| } else { |
| identifiers = Collections.unmodifiableCollection(p); |
| } |
| } |
| } |
| return changed; |
| } |
| |
| |
| |
| |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| //////// //////// |
| //////// XML support with JAXB //////// |
| //////// //////// |
| //////// The following methods are invoked by JAXB using reflection (even if //////// |
| //////// they are private) or are helpers for other methods invoked by JAXB. //////// |
| //////// Those methods can be safely removed if Geographic Markup Language //////// |
| //////// (GML) support is not needed. //////// |
| //////// //////// |
| ////////////////////////////////////////////////////////////////////////////////////////////////// |
| |
| /** |
| * Returns an identifier unique for the XML document, or {@code null} if none. |
| * This method is invoked automatically by JAXB and should never be invoked explicitly. |
| */ |
| @XmlID |
| @XmlAttribute // Defined in "gco" as unqualified attribute. |
| @XmlSchemaType(name = "ID") |
| @XmlJavaTypeAdapter(CollapsedStringAdapter.class) |
| private String getID() { |
| return isNullOrEmpty(identifiers) ? null : MetadataUtilities.getObjectID(this); |
| } |
| |
| /** |
| * Sets an identifier unique for the XML document. |
| * This method is invoked automatically by JAXB and should never be invoked explicitly. |
| */ |
| private void setID(final String id) { |
| MetadataUtilities.setObjectID(this, id); |
| } |
| |
| /** |
| * Returns an unique identifier, or {@code null} if none. |
| * This method is invoked automatically by JAXB and should never be invoked explicitly. |
| */ |
| @XmlAttribute // Defined in "gco" as unqualified attribute. |
| @XmlJavaTypeAdapter(CollapsedStringAdapter.class) |
| private String getUUID() { |
| /* |
| * IdentifierMapAdapter will take care of converting UUID to String, |
| * or to return a previously stored String if it was an unparsable UUID. |
| */ |
| return isNullOrEmpty(identifiers) ? null : getIdentifierMap().get(IdentifierSpace.UUID); |
| } |
| |
| /** |
| * Sets an unique identifier. |
| * This method is invoked automatically by JAXB and should never be invoked explicitly. |
| */ |
| private void setUUID(final String id) { |
| /* |
| * IdentifierMapAdapter will take care of converting the String to UUID if possible, or |
| * will store the value as a plain String if it can not be converted. In the later case, |
| * a warning will be emitted (logged or processed by listeners). |
| */ |
| getIdentifierMap().put(IdentifierSpace.UUID, id); |
| } |
| } |