001    /*
002     *    GeoAPI - Java interfaces for OGC/ISO standards
003     *    http://www.geoapi.org
004     *
005     *    This file is hereby placed into the Public Domain.
006     *    This means anyone is free to do whatever they wish with this file.
007     *
008     *    The NetCDF wrappers are provided as code examples, in the hope to facilitate
009     *    GeoAPI implementations backed by other libraries. Implementors can take this
010     *    source code and use it for any purpose, commercial or non-commercial, copyrighted
011     *    or open-source, with no legal obligation to acknowledge the borrowing/copying
012     *    in any way.
013     */
014    package org.opengis.wrapper.netcdf;
015    
016    import java.util.Set;
017    import java.util.Collection;
018    import java.util.Collections;
019    import java.util.Date;
020    import java.io.Serializable;
021    
022    import ucar.nc2.VariableSimpleIF;
023    
024    import org.opengis.util.GenericName;
025    import org.opengis.util.InternationalString;
026    import org.opengis.metadata.extent.Extent;
027    import org.opengis.metadata.citation.Citation;
028    import org.opengis.referencing.IdentifiedObject;
029    import org.opengis.referencing.ReferenceIdentifier;
030    
031    
032    /**
033     * An {@link IdentifiedObject} abstract base class backed by some NetCDF object.
034     * All methods in this class delegate their work to the wrapped NetCDF object.
035     * Consequently any change in the wrapped object is immediately reflected in this
036     * {@code NetcdfIdentifiedObject} instance. However users are encouraged to not
037     * change the wrapped object after construction, since GeoAPI referencing objects
038     * are expected to be immutable.
039     *
040     * <p>This base class assumes that NetCDF objects have a single name and no alias. This assumption
041     * allows us to implement directly the {@link ReferenceIdentifier} interface. The NetCDF object
042     * name is returned by the {@link #getCode()} method.</p>
043     *
044     * @author  Martin Desruisseaux (Geomatys)
045     * @version 3.1
046     * @since   3.1
047     */
048    public abstract class NetcdfIdentifiedObject implements IdentifiedObject, ReferenceIdentifier, Serializable {
049        /**
050         * For cross-version compatibility.
051         */
052        private static final long serialVersionUID = 8740287489596438703L;
053    
054        /**
055         * Creates a new {@code NetcdfIdentifiedObject} instance.
056         */
057        protected NetcdfIdentifiedObject() {
058        }
059    
060        /**
061         * Returns the wrapped NetCDF object on which operations are delegated.
062         *
063         * @return The wrapped NetCDF object on which operations are delegated.
064         */
065        public abstract Object delegate();
066    
067        /**
068         * Returns the NetCDF citation.
069         */
070        @Override
071        public Citation getAuthority() {
072            return SimpleCitation.NETCDF;
073        }
074    
075        /**
076         * Returns the {@code "NetCDF"} constant, which is used as the code space.
077         */
078        @Override
079        public String getCodeSpace() {
080            return "NetCDF";
081        }
082    
083        /**
084         * Returns the version of the NetCDF library. The default implementation
085         * fetches this information from the {@code META-INF/MANIFEST.MF} file in
086         * the NetCDF JAR file.
087         */
088        @Override
089        public String getVersion() {
090            final Package p = VariableSimpleIF.class.getPackage();
091            return (p != null) ? p.getImplementationVersion() : null;
092        }
093    
094        /**
095         * Returns a code which identify this instance. This is typically the value
096         * returned by the {@code getName()} method of the wrapped NetCDF object.
097         */
098        @Override
099        public abstract String getCode();
100    
101        /**
102         * Returns the name of this identified object. The default implementation returns
103         * {@code this}, so subclasses shall returns the name in their implementation of
104         * the {@link #getCode()} method.
105         */
106        @Override
107        public ReferenceIdentifier getName() {
108            return this;
109        }
110    
111        /**
112         * Returns the aliases, or an empty set if none. The default implementation returns an empty
113         * set. Some subclasses will infer the aliases from the projection {@linkplain #getName()}.
114         */
115        @Override
116        public Collection<GenericName> getAlias() {
117            return Collections.emptySet();
118        }
119    
120        /**
121         * Returns an empty set, since NetCDF objects don't provide other identifiers than the name.
122         */
123        @Override
124        public Set<ReferenceIdentifier> getIdentifiers() {
125            return Collections.emptySet();
126        }
127    
128        /**
129         * Returns the area or region or timeframe in which this object is valid, or {@code null} if
130         * none. The default implementation returns a geographic extent for the world, since most NetCDF
131         * objects except {@link ucar.unidata.geoloc.Projection} are not restricted to a particular area.
132         *
133         * @return The valid domain, or {@code null} if not available.
134         *
135         * @see NetcdfCRS#getDomainOfValidity()
136         * @see NetcdfProjection#getDomainOfValidity()
137         */
138        public Extent getDomainOfValidity() {
139            return SimpleGeographicBoundingBox.WORLD;
140        }
141    
142        /**
143         * Returns the description of domain of usage, or limitations of usage, for which this object
144         * is valid. The default implementation returns {@code null} in all cases, since NetCDF objects
145         * don't specify their scope.
146         *
147         * <p>Scope is a
148         * {@link org.opengis.referencing.datum.Datum#getScope() Datum},
149         * {@link org.opengis.referencing.ReferenceSystem#getScope() ReferenceSystem} and
150         * {@link org.opengis.referencing.operation.CoordinateOperation#getScope() CoordinateOperation}
151         * property.</p>
152         *
153         * @return The domain of usage, or {@code null} if none.
154         */
155        public InternationalString getScope() {
156            return null;
157        }
158    
159        /**
160         * Returns a description, possibly including coordinates of an identified point or points,
161         * of the relationship used to anchor the coordinate system to the Earth or alternate object.
162         * The default implementation returns {@code null} since this simple implementation does not
163         * define anchor point.
164         *
165         * <p>Anchor point is a
166         * {@link org.opengis.referencing.datum.Datum#getAnchorPoint() Datum} property.</p>
167         *
168         * @return A description of the anchor point, or {@code null} if none.
169         */
170        public InternationalString getAnchorPoint() {
171            return null;
172        }
173    
174        /**
175         * Returns The time after which this datum definition is valid. The default implementation
176         * returns {@code null} since this simple implementation does not define realization epoch.
177         *
178         * <p>Anchor point is a
179         * {@link org.opengis.referencing.datum.Datum#getRealizationEpoch() Datum} property.</p>
180         *
181         * @return The datum realization epoch, or {@code null} if not available.
182         */
183        public Date getRealizationEpoch() {
184            return null;
185        }
186    
187        /**
188         * Returns the NetCDF object description, or {@code null} if none.
189         * The default implementation returns {@code null}.
190         *
191         * @return The remarks, or {@code null} if none.
192         *
193         * @see NetcdfAxis#getRemarks()
194         */
195        @Override
196        public InternationalString getRemarks() {
197            return null;
198        }
199    
200        /**
201         * Compares this object with the given object for equality. The default implementation
202         * returns {@code true} if the given object is non-null, wraps an object of the same
203         * class than this object and the wrapped NetCDF objects are equal.
204         *
205         * @param  other The other object to compare with this object.
206         * @return {@code true} if both objects are equal.
207         */
208        @Override
209        public boolean equals(final Object other) {
210            if (other == this) {
211                return true;
212            }
213            if (other != null && other.getClass() == getClass()) {
214                return Objects.equals(delegate(), ((NetcdfIdentifiedObject) other).delegate());
215            }
216            return false;
217        }
218    
219        /**
220         * Returns a hash code value for this object. The default implementation
221         * derives a value from the code returned by the wrapped NetCDF object.
222         */
223        @Override
224        public int hashCode() {
225            return ~delegate().hashCode();
226        }
227    
228        /**
229         * Returns a string representation of this object {@linkplain #getName() name}.
230         */
231        @Override
232        public String toString() {
233            final StringBuilder buffer = new StringBuilder(getCodeSpace()).append(':');
234            final String name = getCode().trim();
235            final boolean needsQuote = (name.indexOf(' ') >= 0);
236            if (needsQuote) {
237                buffer.append('"');
238            }
239            buffer.append(name);
240            if (needsQuote) {
241                buffer.append('"');
242            }
243            return buffer.toString();
244        }
245    
246        /**
247         * Returns a <cite>Well Known Text</cite> representation of this object, if this
248         * operation is supported. The default implementation thrown an exception in all
249         * cases.
250         */
251        @Override
252        public String toWKT() throws UnsupportedOperationException {
253            throw new UnsupportedOperationException();
254        }
255    }