blob: 166ad72143da93571072c05e5b56e3de5e2e636e [file] [log] [blame]
/*
* 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.citation;
import java.util.Date;
import java.util.Collection;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import org.opengis.metadata.Identifier;
import org.opengis.metadata.citation.Citation;
import org.opengis.metadata.citation.CitationDate;
import org.opengis.metadata.citation.OnlineResource;
import org.opengis.metadata.citation.PresentationForm;
import org.opengis.metadata.citation.ResponsibleParty;
import org.opengis.metadata.citation.Series;
import org.opengis.metadata.identification.BrowseGraphic;
import org.opengis.util.InternationalString;
import org.apache.sis.util.iso.Types;
import org.apache.sis.internal.jaxb.FilterByVersion;
import org.apache.sis.internal.xml.LegacyNamespaces;
import org.apache.sis.internal.jaxb.NonMarshalledAuthority;
import org.apache.sis.metadata.TitleProperty;
import org.apache.sis.metadata.iso.ISOMetadata;
import org.apache.sis.xml.IdentifierSpace;
import org.apache.sis.xml.IdentifierMap;
// Branch-specific imports
import org.opengis.annotation.UML;
import static org.opengis.annotation.Obligation.OPTIONAL;
import static org.opengis.annotation.Specification.ISO_19115;
import static org.apache.sis.util.collection.Containers.isNullOrEmpty;
import static org.apache.sis.internal.metadata.MetadataUtilities.toDate;
import static org.apache.sis.internal.metadata.MetadataUtilities.toMilliseconds;
/**
* Standardized resource reference.
* The following properties are mandatory in a well-formed metadata according ISO 19115:
*
* <div class="preformat">{@code CI_Citation}
* {@code   ├─title………………………} Name by which the cited resource is known.
* {@code   └─date…………………………} Reference date for the cited resource.</div>
*
* <h2>Unified identifiers view</h2>
* The ISO 19115 model provides specific attributes for the {@linkplain #getISBN() ISBN} and
* {@linkplain #getISSN() ISSN} codes. However the SIS library handles those codes like any
* other identifiers. Consequently the ISBN and ISSN codes are included in the collection
* returned by {@link #getIdentifiers()}, except at XML marshalling time (for ISO 19115-3 compliance).
*
* <h2>Limitations</h2>
* <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 (IRD, Geomatys)
* @author Cédric Briançon (Geomatys)
* @author Rémi Maréchal (Geomatys)
* @author Cullen Rombach (Image Matters)
* @version 1.0
* @since 0.3
* @module
*/
@TitleProperty(name = "title")
@XmlType(name = "CI_Citation_Type", propOrder = {
"title",
"alternateTitles",
"dates",
"edition",
"editionDate",
"identifiers",
"citedResponsibleParties",
"presentationForms",
"series",
"otherCitationDetails",
"collectiveTitle",
"ISBN",
"ISSN",
"onlineResource", // New in ISO 19115:2014
"graphic" // Ibid.
})
@XmlRootElement(name = "CI_Citation")
public class DefaultCitation extends ISOMetadata implements Citation {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = 3490090845236158848L;
/**
* Name by which the cited resource is known.
*/
private InternationalString title;
/**
* Short name or other language name by which the cited information is known.
* Example: "DCW" as an alternative title for "Digital Chart of the World.
*/
private Collection<InternationalString> alternateTitles;
/**
* Reference date for the cited resource.
*/
private Collection<CitationDate> dates;
/**
* Version of the cited resource.
*/
private InternationalString edition;
/**
* Date of the edition in milliseconds elapsed sine January 1st, 1970,
* or {@link Long#MIN_VALUE} if none.
*/
private long editionDate = Long.MIN_VALUE;
/**
* Roles, Name, contact, and position information for an individual or organization that is responsible
* for the resource.
*/
private Collection<ResponsibleParty> citedResponsibleParties;
/**
* Mode in which the resource is represented, or an empty collection if none.
*/
private Collection<PresentationForm> presentationForms;
/**
* Information about the series, or aggregate dataset, of which the dataset is a part.
* May be {@code null} if none.
*/
private Series series;
/**
* Other information required to complete the citation that is not recorded elsewhere.
* May be {@code null} if none.
*/
private InternationalString otherCitationDetails;
/**
* Common title with holdings note. Note: title identifies elements of a series
* collectively, combined with information about what volumes are available at the
* source cited. May be {@code null} if there is no title.
*
* @deprecated Removed as of ISO 19115:2014.
*/
@Deprecated
private InternationalString collectiveTitle;
/**
* Online references to the cited resource.
*/
private Collection<OnlineResource> onlineResources;
/**
* Citation graphic or logo for cited party.
*/
private Collection<BrowseGraphic> graphics;
/**
* Constructs an initially empty citation.
*/
public DefaultCitation() {
}
/**
* Constructs a citation with the specified title.
*
* @param title the title as a {@link String} or an {@link InternationalString} object,
* or {@code null} if none.
*/
public DefaultCitation(final CharSequence title) {
this.title = Types.toInternationalString(title);
}
/**
* Constructs a new instance initialized with the values from the specified metadata object.
* This is a <cite>shallow</cite> copy constructor, since the other metadata contained in the
* given object are not recursively copied.
*
* @param object the metadata to copy values from, or {@code null} if none.
*
* @see #castOrCopy(Citation)
*/
public DefaultCitation(final Citation object) {
super(object);
if (object != null) {
title = object.getTitle();
alternateTitles = copyCollection(object.getAlternateTitles(), InternationalString.class);
dates = copyCollection(object.getDates(), CitationDate.class);
edition = object.getEdition();
editionDate = toMilliseconds(object.getEditionDate());
identifiers = copyCollection(object.getIdentifiers(), Identifier.class);
citedResponsibleParties = copyCollection(object.getCitedResponsibleParties(), ResponsibleParty.class);
presentationForms = copyCollection(object.getPresentationForms(), PresentationForm.class);
series = object.getSeries();
otherCitationDetails = object.getOtherCitationDetails();
collectiveTitle = object.getCollectiveTitle();
if (object instanceof DefaultCitation) {
final DefaultCitation c = (DefaultCitation) object;
onlineResources = copyCollection(c.getOnlineResources(), OnlineResource.class);
graphics = copyCollection(c.getGraphics(), BrowseGraphic.class);
}
final String id1 = object.getISBN();
final String id2 = object.getISSN();
if (id1 != null || id2 != null) {
final IdentifierMap map = super.getIdentifierMap();
if (id1 != null) map.putSpecialized(Citations.ISBN, id1);
if (id2 != null) map.putSpecialized(Citations.ISSN, id2);
}
}
}
/**
* Returns a SIS metadata implementation with the values of the given arbitrary implementation.
* This method performs the first applicable action in the following choices:
*
* <ul>
* <li>If the given object is {@code null}, then this method returns {@code null}.</li>
* <li>Otherwise if the given object is already an instance of
* {@code DefaultCitation}, then it is returned unchanged.</li>
* <li>Otherwise a new {@code DefaultCitation} instance is created using the
* {@linkplain #DefaultCitation(Citation) copy constructor}
* and returned. Note that this is a <cite>shallow</cite> copy operation, since the other
* metadata contained in the given object are not recursively copied.</li>
* </ul>
*
* @param object the object to get as a SIS implementation, or {@code null} if none.
* @return a SIS implementation containing the values of the given object (may be the
* given object itself), or {@code null} if the argument was null.
*/
public static DefaultCitation castOrCopy(final Citation object) {
if (object == null || object instanceof DefaultCitation) {
return (DefaultCitation) object;
}
return new DefaultCitation(object);
}
/**
* Returns the name by which the cited resource is known.
*
* @return the cited resource name, or {@code null}.
*/
@Override
@XmlElement(name = "title", required = true)
public InternationalString getTitle() {
return title;
}
/**
* Sets the name by which the cited resource is known.
*
* @param newValue the new title, or {@code null} if none.
*/
public void setTitle(final InternationalString newValue) {
checkWritePermission(title);
title = newValue;
}
/**
* Returns short name or other language name by which the cited information is known.
*
* <div class="note"><b>Example:</b> "DCW" as an alternative title for "Digital Chart of the World".</div>
*
* @return other names for the resource, or an empty collection if none.
*/
@Override
@XmlElement(name = "alternateTitle")
public Collection<InternationalString> getAlternateTitles() {
return alternateTitles = nonNullCollection(alternateTitles, InternationalString.class);
}
/**
* Sets the short name or other language name by which the cited information is known.
*
* @param newValues the new alternate titles, or {@code null} if none.
*/
public void setAlternateTitles(final Collection<? extends InternationalString> newValues) {
alternateTitles = writeCollection(newValues, alternateTitles, InternationalString.class);
}
/**
* Returns the reference date for the cited resource.
*
* @return the reference date.
*/
@Override
@XmlElement(name = "date")
public Collection<CitationDate> getDates() {
return dates = nonNullCollection(dates, CitationDate.class);
}
/**
* Sets the reference date for the cited resource.
*
* @param newValues the new dates, or {@code null} if none.
*/
public void setDates(final Collection<? extends CitationDate> newValues) {
dates = writeCollection(newValues, dates, CitationDate.class);
}
/**
* Returns the version of the cited resource.
*
* @return the version, or {@code null} if none.
*/
@Override
@XmlElement(name = "edition")
public InternationalString getEdition() {
return edition;
}
/**
* Sets the version of the cited resource.
*
* @param newValue the new edition, or {@code null} if none.
*/
public void setEdition(final InternationalString newValue) {
checkWritePermission(edition);
edition = newValue;
}
/**
* Returns the date of the edition.
*
* @return the edition date, or {@code null} if none.
*/
@Override
@XmlElement(name = "editionDate")
public Date getEditionDate() {
return toDate(editionDate);
}
/**
* Sets the date of the edition.
*
* @param newValue the new edition date, or {@code null} if none.
*/
public void setEditionDate(final Date newValue) {
checkWritePermission(toDate(editionDate));
editionDate = toMilliseconds(newValue);
}
/**
* Returns the unique identifier for the resource.
*
* <div class="note"><b>Example:</b> Universal Product Code (UPC), National Stock Number (NSN).</div>
*
* <h4>Unified identifiers view</h4>
* In this SIS implementation, the collection returned by this method includes the XML identifiers
* ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>),
* as well as the {@linkplain #getISBN() ISBN} and {@linkplain #getISSN() ISSN} codes, thus
* providing a unified view of every kind of identifiers associated to this citation.
*
* <div class="note"><b>XML note:</b>
* The {@code <cit:identifier>} element marshalled to XML will exclude all the above cited identifiers,
* for ISO 19115-3 compliance. Those identifiers will appear in other XML elements or attributes.</div>
*
* @return the identifiers, or an empty collection if none.
*
* @see #getISBN()
* @see #getISSN()
* @see #getIdentifierMap()
*/
@Override
@XmlElement(name = "identifier")
public Collection<Identifier> getIdentifiers() {
return NonMarshalledAuthority.filterOnMarshalling(super.getIdentifiers());
}
/**
* Sets the unique identifier for the resource.
* Example: Universal Product Code (UPC), National Stock Number (NSN).
*
* <p>XML identifiers ({@linkplain IdentifierSpace#ID ID}, {@linkplain IdentifierSpace#UUID UUID}, <i>etc.</i>),
* {@linkplain #getISBN() ISBN} and {@linkplain #getISSN() ISSN} codes are not affected by this method, unless
* they are explicitly provided in the given collection.</p>
*
* @param newValues the new identifiers, or {@code null} if none.
*
* @see #setISBN(String)
* @see #setISSN(String)
*/
public void setIdentifiers(Collection<? extends Identifier> newValues) {
newValues = NonMarshalledAuthority.setMarshallables(identifiers, newValues);
identifiers = writeCollection(newValues, identifiers, Identifier.class);
}
/**
* Returns the role, name, contact and position information for an individual or organization
* that is responsible for the resource.
*
* <div class="warning"><b>Upcoming API change — generalization</b><br>
* As of ISO 19115:2014, {@code ResponsibleParty} is replaced by the {@code Responsibility} parent interface.
* This change may be applied in GeoAPI 4.0.
* </div>
*
* @return the individual or organization that is responsible, or an empty collection if none.
*/
@Override
@XmlElement(name = "citedResponsibleParty")
public Collection<ResponsibleParty> getCitedResponsibleParties() {
return citedResponsibleParties = nonNullCollection(citedResponsibleParties, ResponsibleParty.class);
}
/**
* Sets the role, name, contact and position information for an individual or organization
* that is responsible for the resource.
*
* <div class="warning"><b>Upcoming API change — generalization</b><br>
* As of ISO 19115:2014, {@code ResponsibleParty} is replaced by the {@code Responsibility} parent interface.
* This change may be applied in GeoAPI 4.0.
* </div>
*
* @param newValues the new cited responsible parties, or {@code null} if none.
*/
public void setCitedResponsibleParties(final Collection<? extends ResponsibleParty> newValues) {
citedResponsibleParties = writeCollection(newValues, citedResponsibleParties, ResponsibleParty.class);
}
/**
* Returns the mode in which the resource is represented.
*
* @return the presentation modes, or an empty collection if none.
*/
@Override
@XmlElement(name = "presentationForm")
public Collection<PresentationForm> getPresentationForms() {
return presentationForms = nonNullCollection(presentationForms, PresentationForm.class);
}
/**
* Sets the mode in which the resource is represented.
*
* @param newValues the new presentation form, or {@code null} if none.
*/
public void setPresentationForms(final Collection<? extends PresentationForm> newValues) {
presentationForms = writeCollection(newValues, presentationForms, PresentationForm.class);
}
/**
* Returns the information about the series, or aggregate dataset, of which the dataset is a part.
*
* @return the series of which the dataset is a part, or {@code null} if none.
*/
@Override
@XmlElement(name = "series")
public Series getSeries() {
return series;
}
/**
* Sets the information about the series, or aggregate dataset, of which the dataset is a part.
*
* @param newValue the new series.
*/
public void setSeries(final Series newValue) {
checkWritePermission(series);
series = newValue;
}
/**
* Returns other information required to complete the citation that is not recorded elsewhere.
*
* <div class="warning"><b>Upcoming API change — multiplicity</b><br>
* As of ISO 19115:2014, this singleton has been replaced by a collection.
* This change may be applied in GeoAPI 4.0.
* </div>
*
* @return other details, or {@code null} if none.
*/
@Override
@XmlElement(name = "otherCitationDetails")
public InternationalString getOtherCitationDetails() {
return otherCitationDetails;
}
/**
* Sets other information required to complete the citation that is not recorded elsewhere.
*
* <div class="warning"><b>Upcoming API change — multiplicity</b><br>
* As of ISO 19115:2014, this singleton has been replaced by a collection.
* This change may be applied in GeoAPI 4.0.
* </div>
*
* @param newValue Other citations details, or {@code null} if none.
*/
public void setOtherCitationDetails(final InternationalString newValue) {
checkWritePermission(otherCitationDetails);
otherCitationDetails = newValue;
}
/**
* Returns the common title with holdings note.
*
* @return the common title, or {@code null} if none.
*
* @deprecated Removed as of ISO 19115:2014.
*/
@Override
@Deprecated
@XmlElement(name = "collectiveTitle", namespace = LegacyNamespaces.GMD)
public InternationalString getCollectiveTitle() {
return FilterByVersion.LEGACY_METADATA.accept() ? collectiveTitle : null;
}
/**
* Sets the common title with holdings note. This title identifies elements of a series collectively,
* combined with information about what volumes are available at the source cited.
*
* @param newValue the new collective title, or {@code null} if none.
*
* @deprecated Removed as of ISO 19115:2014.
*/
@Deprecated
public void setCollectiveTitle(final InternationalString newValue) {
checkWritePermission(collectiveTitle);
collectiveTitle = newValue;
}
/**
* Returns the ISBN or ISSN identifier for the given authority, or {@code null} if none.
*/
private String getIdentifier(final Citation authority) {
return isNullOrEmpty(identifiers) ? null : getIdentifierMap().get(authority);
}
/**
* Returns the International Standard Book Number.
* In this SIS implementation, invoking this method is equivalent to:
*
* {@preformat java
* return getIdentifierMap().getSpecialized(Citations.ISBN);
* }
*
* @return the ISBN, or {@code null} if none.
*
* @see #getIdentifiers()
* @see Citations#ISBN
*/
@Override
@XmlElement(name = "ISBN")
public String getISBN() {
return getIdentifier(Citations.ISBN);
}
/**
* Sets the International Standard Book Number.
* In this SIS implementation, invoking this method is equivalent to:
*
* {@preformat java
* getIdentifierMap().putSpecialized(Citations.ISBN, newValue);
* }
*
* @param newValue the new ISBN, or {@code null} if none.
*
* @see #setIdentifiers(Collection)
* @see Citations#ISBN
*/
public void setISBN(final String newValue) {
checkWritePermission(getIdentifier(Citations.ISBN));
if (newValue != null || !isNullOrEmpty(identifiers)) {
getIdentifierMap().putSpecialized(Citations.ISBN, newValue);
}
}
/**
* Returns the International Standard Serial Number.
* In this SIS implementation, invoking this method is equivalent to:
*
* {@preformat java
* return getIdentifierMap().getSpecialized(Citations.ISSN);
* }
*
* @return the ISSN, or {@code null} if none.
*
* @see #getIdentifiers()
* @see Citations#ISSN
*/
@Override
@XmlElement(name = "ISSN")
public String getISSN() {
return getIdentifier(Citations.ISSN);
}
/**
* Sets the International Standard Serial Number.
* In this SIS implementation, invoking this method is equivalent to:
*
* {@preformat java
* getIdentifierMap().putSpecialized(Citations.ISSN, newValue);
* }
*
* @param newValue the new ISSN.
*
* @see #setIdentifiers(Collection)
* @see Citations#ISSN
*/
public void setISSN(final String newValue) {
checkWritePermission(getIdentifier(Citations.ISSN));
if (newValue != null || !isNullOrEmpty(identifiers)) {
getIdentifierMap().putSpecialized(Citations.ISSN, newValue);
}
}
/**
* Returns online references to the cited resource.
*
* @return online references to the cited resource, or an empty collection if there is none.
*
* @since 0.5
*/
// @XmlElement at the end of this class.
@UML(identifier="onlineResource", obligation=OPTIONAL, specification=ISO_19115)
public Collection<OnlineResource> getOnlineResources() {
return onlineResources = nonNullCollection(onlineResources, OnlineResource.class);
}
/**
* Sets online references to the cited resource.
*
* @param newValues the new online references to the cited resource.
*
* @since 0.5
*/
public void setOnlineResources(final Collection<? extends OnlineResource> newValues) {
onlineResources = writeCollection(newValues, onlineResources, OnlineResource.class);
}
/**
* Returns citation graphics or logo for cited party.
*
* @return graphics or logo for cited party, or an empty collection if there is none.
*
* @since 0.5
*/
// @XmlElement at the end of this class.
@UML(identifier="graphic", obligation=OPTIONAL, specification=ISO_19115)
public Collection<BrowseGraphic> getGraphics() {
return graphics = nonNullCollection(graphics, BrowseGraphic.class);
}
/**
* Sets citation graphics or logo for cited party.
*
* @param newValues the new citation graphics or logo for cited party.
*
* @since 0.5
*/
public void setGraphics(final Collection<? extends BrowseGraphic> newValues) {
graphics = writeCollection(newValues, graphics, BrowseGraphic.class);
}
//////////////////////////////////////////////////////////////////////////////////////////////////
//////// ////////
//////// 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. ////////
//////// ////////
//////////////////////////////////////////////////////////////////////////////////////////////////
/**
* Invoked by JAXB at both marshalling and unmarshalling time.
* This attribute has been added by ISO 19115:2014 standard.
* If (and only if) marshalling an older standard version, we omit this attribute.
*/
@XmlElement(name = "onlineResource")
private Collection<OnlineResource> getOnlineResource() {
return FilterByVersion.CURRENT_METADATA.accept() ? getOnlineResources() : null;
}
@XmlElement(name = "graphic")
private Collection<BrowseGraphic> getGraphic() {
return FilterByVersion.CURRENT_METADATA.accept() ? getGraphics() : null;
}
}