blob: 76706222e4d2b21a89a771df8af01c6c6201203b [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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import org.opengis.metadata.citation.Telephone;
import org.apache.sis.metadata.iso.ISOMetadata;
import org.apache.sis.internal.util.CollectionsExt;
import org.apache.sis.internal.jaxb.FilterByVersion;
import org.apache.sis.internal.xml.LegacyNamespaces;
import org.apache.sis.internal.jaxb.gco.StringAdapter;
import org.apache.sis.internal.jaxb.code.CI_TelephoneTypeCode;
import org.apache.sis.internal.metadata.Dependencies;
import static org.opengis.annotation.Obligation.OPTIONAL;
import static org.opengis.annotation.Obligation.MANDATORY;
import static org.opengis.annotation.Specification.ISO_19115;
// Branch-specific imports
import org.opengis.util.CodeList;
import org.opengis.annotation.UML;
import org.apache.sis.internal.geoapi.evolution.InterimType;
import org.apache.sis.internal.geoapi.evolution.UnsupportedCodeList;
/**
* Telephone numbers for contacting the responsible individual or organization.
* The following property is mandatory in a well-formed metadata according ISO 19115:
*
* <div class="preformat">{@code CI_Telephone}
* {@code   └─number……} Telephone number by which individuals can contact responsible organisation or individual.</div>
*
* <div class="section">Differences between versions 2003 and 2014 of ISO 19115</div>
* For any contact having more than one telephone number, the way to organize the information
* changed significantly between the two versions of ISO standard:
*
* <ul>
* <li>In ISO 19115:2003, each {@code Contact} had only one {@code Telephone} instance, but that instance
* could have an arbitrary amount of "voice" and "facsimile" numbers. The methods (now deprecated) were
* {@link DefaultContact#getPhone()}, {@link #getVoices()} and {@link #getFacsimiles()}.</li>
* <li>In ISO 19115:2014, each {@code Contact} has an arbitrary amount of {@code Telephone} instances, and
* each telephone has exactly one number. The new methods are {@link DefaultContact#getPhones()},
* {@link #getNumber()} and {@link #getNumberType()}.</li>
* </ul>
*
* <div class="section">Limitations</div>
* <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
*
* @see DefaultContact#getPhones()
*
* @since 0.5
* @module
*/
@XmlType(name = "CI_Telephone_Type", propOrder = {
"number", // New in ISO 19115:2014
"numberType", // Ibid.
"voices", // Legacy ISO 19115:2003
"facsimiles" // Ibid.
})
@XmlRootElement(name = "CI_Telephone")
public class DefaultTelephone extends ISOMetadata implements Telephone {
/**
* Serial number for inter-operability with different versions.
*/
private static final long serialVersionUID = 5156405432420742237L;
/**
* Telephone number by which individuals can contact responsible organization or individual.
*/
private String number;
/**
* Type of telephone number.
*/
CodeList<?> numberType;
/**
* Constructs a default telephone.
*/
public DefaultTelephone() {
}
/**
* Constructs a telephone with the given number and type.
*
* @param number the telephone number, or {@code null}.
* @param numberType the type of telephone number, or {@code null}.
*
* @since 0.5
*/
DefaultTelephone(final String number, final CodeList<?> numberType) {
this.number = number;
this.numberType = numberType;
}
/**
* 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(Telephone)
*/
public DefaultTelephone(final Telephone object) {
super(object);
if (object != null) {
if (object instanceof DefaultTelephone) {
number = ((DefaultTelephone) object).getNumber();
numberType = ((DefaultTelephone) object).numberType;
} else {
setVoices(object.getVoices());
setFacsimiles(object.getFacsimiles());
}
}
}
/**
* 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 DefaultTelephone}, then it is returned unchanged.</li>
* <li>Otherwise a new {@code DefaultTelephone} instance is created using the
* {@linkplain #DefaultTelephone(Telephone) 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 DefaultTelephone castOrCopy(final Telephone object) {
if (object == null || object instanceof DefaultTelephone) {
return (DefaultTelephone) object;
}
return new DefaultTelephone(object);
}
/**
* Returns the telephone number by which individuals can contact responsible organization or individual.
*
* @return telephone number by which individuals can contact responsible organization or individual.
*
* @since 0.5
*/
@XmlElement(name = "number", required = true)
@XmlJavaTypeAdapter(StringAdapter.Since2014.class)
@UML(identifier="number", obligation=MANDATORY, specification=ISO_19115)
public String getNumber() {
return number;
}
/**
* Sets the telephone number by which individuals can contact responsible organization or individual.
*
* @param newValue the new telephone number by which individuals can contact responsible organization or individual.
*
* @since 0.5
*/
public void setNumber(final String newValue) {
checkWritePermission(number);
number = newValue;
}
/**
* Returns the type of telephone number, or {@code null} if none.
* If non-null, the type can be {@code "VOICE"}, {@code "FACSIMILE"} or {@code "SMS"}.
*
* <div class="warning"><b>Upcoming API change — specialization</b><br>
* The return type will be changed to the {@code TelephoneType} code list
* when GeoAPI will provide it (tentatively in GeoAPI 3.1).
* </div>
*
* @return type of telephone number, or {@code null} if none.
*
* @since 0.5
*/
@InterimType(UnsupportedCodeList.class)
@XmlElement(name = "numberType")
@XmlJavaTypeAdapter(CI_TelephoneTypeCode.Since2014.class)
@UML(identifier="numberType", obligation=OPTIONAL, specification=ISO_19115)
public CodeList<?> getNumberType() {
return numberType;
}
/**
* Sets the type of telephone number.
* If non-null, the type can only be {@code "VOICE"}, {@code "FACSIMILE"} or {@code "SMS"}.
*
* <div class="warning"><b>Upcoming API change — specialization</b><br>
* The argument type will be changed to the {@code TelephoneType} code list when GeoAPI will provide it
* (tentatively in GeoAPI 3.1). In the meantime, users can define their own code list class as below:
*
* {@preformat java
* final class UnsupportedCodeList extends CodeList<UnsupportedCodeList> {
* private static final List<UnsupportedCodeList> VALUES = new ArrayList<UnsupportedCodeList>();
*
* // Need to declare at least one code list element.
* public static final UnsupportedCodeList MY_CODE_LIST = new UnsupportedCodeList("MY_CODE_LIST");
*
* private UnsupportedCodeList(String name) {
* super(name, VALUES);
* }
*
* public static UnsupportedCodeList valueOf(String code) {
* return valueOf(UnsupportedCodeList.class, code);
* }
*
* &#64;Override
* public UnsupportedCodeList[] family() {
* synchronized (VALUES) {
* return VALUES.toArray(new UnsupportedCodeList[VALUES.size()]);
* }
* }
* }
* }
* </div>
*
* @param newValue the new type of telephone number.
*
* @since 0.5
*/
public void setNumberType(final CodeList<?> newValue) {
checkWritePermission(numberType);
numberType = newValue;
}
/**
* For implementation of {@link #getVoices()} and {@link #getFacsimiles()} deprecated methods.
* Shall be the telephones list of the enclosing {@link DefaultContact} object.
*
* <p>This field references the same collection than {@link DefaultContact#phones} when possible.
* Note that the link between this collection and {@code DefaultContact.phones} is broken when
* {@link DefaultContact} is copied by {@link org.apache.sis.metadata.MetadataCopier}, since the
* {@code Cloner.clone(Object)} method creates a new (unmodifiable) collection.</p>
*
* @deprecated This field will be removed after we removed the deprecated public methods.
*/
@Deprecated
private Collection<Telephone> owner;
/**
* Specifies the collection which contains this telephone number.
* This method is invoked by {@link DefaultContact#setPhones(Collection)}
* when the contact "takes possession" of a {@code DefaultTelephone}.
*
* <p>This method will be removed after we removed the deprecated public methods.</p>
*
* @param phones the collection which should contains this telephone number.
* @return {@code this}, or a copy of this instance if we conservatively choose to not modify this instance.
*/
final DefaultTelephone setOwner(final Collection<Telephone> phones) {
if (owner != phones) {
if (owner != null && !CollectionsExt.identityEquals(owner.iterator(), phones.iterator())) {
final DefaultTelephone copy = new DefaultTelephone(this);
copy.owner = phones;
return copy;
}
owner = phones;
}
return this;
}
/**
* Returns the collection that own this telephone number, or create a new collection.
* Creating a new collection is needed when this phone number has not yet been given
* to a {@link DefaultContact}.
*
* <p>This method will be removed after we removed the deprecated public methods.</p>
*/
final Collection<Telephone> getOwner() {
if (owner == null) {
if (super.state() != State.FINAL) {
owner = new ArrayList<>(4);
owner.add(this);
} else {
owner = Collections.singletonList(this);
}
}
return owner;
}
/**
* Returns the telephone numbers by which individuals can speak to the responsible organization or individual.
* This method searches in the {@linkplain DefaultContact#getPhones() contact phones}, if the contact that own
* this phone is known.
*
* @return telephone numbers by which individuals can speak to the responsible organization or individual.
*
* @deprecated As of ISO 19115:2014, replaced by a {@linkplain #getNumber() number}
* with {@code TelephoneType.VOICE}.
*/
@Override
@Deprecated
@Dependencies({"getNumber", "getNumberType"})
@XmlElement(name = "voice", namespace = LegacyNamespaces.GMD)
public final Collection<String> getVoices() {
if (FilterByVersion.LEGACY_METADATA.accept()) {
return new LegacyTelephones(getOwner(), UnsupportedCodeList.VOICE);
}
return null;
}
/**
* Sets the telephone numbers by which individuals can speak to the responsible organization or individual.
* This method writes in the {@linkplain DefaultContact#getPhones() contact phones}, if the contact that own
* this phone is known.
*
* @param newValues the new telephone numbers, or {@code null} if none.
*
* @deprecated As of ISO 19115:2014, replaced by a {@linkplain #setNumber(String) number}
* code {@code TelephoneType.VOICE}.
*/
@Deprecated
public void setVoices(final Collection<? extends String> newValues) {
((LegacyTelephones) getVoices()).setValues(newValues);
}
/**
* Returns the telephone numbers of a facsimile machine for the responsible organization or individual.
* This method searches in the {@linkplain DefaultContact#getPhones() contact phones}, if the contact
* that own this phone is known.
*
* @return telephone numbers of a facsimile machine for the responsible organization or individual.
*
* @deprecated As of ISO 19115:2014, replaced by a {@linkplain #getNumber() number}
* code {@code TelephoneType.FACSIMILE}.
*/
@Override
@Deprecated
@Dependencies({"getNumber", "getNumberType"})
@XmlElement(name = "facsimile", namespace = LegacyNamespaces.GMD)
public final Collection<String> getFacsimiles() {
if (FilterByVersion.LEGACY_METADATA.accept()) {
return new LegacyTelephones(getOwner(), UnsupportedCodeList.FACSIMILE);
}
return null; // Marshalling newer ISO 19115-3 document.
}
/**
* Sets the telephone number of a facsimile machine for the responsible organization or individual.
* This method writes in the {@linkplain DefaultContact#getPhones() contact phones}, if the contact
* that own this phone is known.
*
* @param newValues the new telephone number, or {@code null} if none.
*
* @deprecated As of ISO 19115:2014, replaced by a {@linkplain #setNumber(String) number}
* with {@code TelephoneType.FACSIMILE}.
*/
@Deprecated
public void setFacsimiles(final Collection<? extends String> newValues) {
((LegacyTelephones) getFacsimiles()).setValues(newValues);
}
}