001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/model/csct/cs/Info.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001 by:
006     EXSE, Department of Geography, University of Bonn
007     http://www.giub.uni-bonn.de/exse/
008     lat/lon GmbH
009     http://www.lat-lon.de
010    
011     It has been implemented within SEAGIS - An OpenSource implementation of OpenGIS specification
012     (C) 2001, Institut de Recherche pour le D�veloppement (http://sourceforge.net/projects/seagis/)
013     SEAGIS Contacts:  Surveillance de l'Environnement Assist�e par Satellite
014     Institut de Recherche pour le D�veloppement / US-Espace
015     mailto:seasnet@teledetection.fr
016    
017    
018     This library is free software; you can redistribute it and/or
019     modify it under the terms of the GNU Lesser General Public
020     License as published by the Free Software Foundation; either
021     version 2.1 of the License, or (at your option) any later version.
022    
023     This library is distributed in the hope that it will be useful,
024     but WITHOUT ANY WARRANTY; without even the implied warranty of
025     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
026     Lesser General Public License for more details.
027    
028     You should have received a copy of the GNU Lesser General Public
029     License along with this library; if not, write to the Free Software
030     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
031    
032     Contact:
033    
034     Andreas Poth
035     lat/lon GmbH
036     Aennchenstr. 19
037     53115 Bonn
038     Germany
039     E-Mail: poth@lat-lon.de
040    
041     Klaus Greve
042     Department of Geography
043     University of Bonn
044     Meckenheimer Allee 166
045     53115 Bonn
046     Germany
047     E-Mail: klaus.greve@uni-bonn.de
048    
049     
050     ---------------------------------------------------------------------------*/
051    package org.deegree.model.csct.cs;
052    
053    // OpenGIS dependencies
054    import java.io.Serializable;
055    import java.util.Map;
056    
057    import org.deegree.model.csct.resources.Utilities;
058    import org.deegree.model.csct.resources.WeakHashSet;
059    import org.deegree.model.csct.resources.css.ResourceKeys;
060    import org.deegree.model.csct.resources.css.Resources;
061    import org.deegree.model.csct.units.Unit;
062    
063    /**
064     * A base class for metadata applicable to coordinate system objects. The metadata items
065     * "Abbreviation", "Alias", "Authority", "AuthorityCode", "Name" and "Remarks" were specified in the
066     * Simple Features interfaces, so they have been kept here.
067     * 
068     * This specification does not dictate what the contents of these items should be. However, the
069     * following guidelines are suggested:
070     * <ul>
071     * <li>When {@link org.deegree.model.csct.cs.CoordinateSystemAuthorityFactory} is used to create an
072     * object, the "Authority" and "AuthorityCode" values should be set to the authority name of the
073     * factory object, and the authority code supplied by the client, respectively. The other values may
074     * or may not be set. (If the authority is EPSG, the implementer may consider using the
075     * corresponding metadata values in the EPSG tables.)</li>
076     * <li>When {@link org.deegree.model.csct.cs.CoordinateSystemFactory} creates an object, the "Name"
077     * should be set to the value supplied by the client. All of the other metadata items should be left
078     * empty.</li>
079     * </ul>
080     * 
081     * @version 1.00
082     * @author OpenGIS (www.opengis.org)
083     * @author Martin Desruisseaux
084     * 
085     * @author last edited by: $Author: bezema $
086     * 
087     * @version $Revision: 6259 $, $Date: 2007-03-20 10:15:15 +0100 (Di, 20 Mär 2007) $
088     * 
089     * @see "org.opengis.cs.CS_Info"
090     */
091    public class Info implements Serializable {
092        /**
093         * Serial number for interoperability with different versions.
094         */
095        private static final long serialVersionUID = -771181600202966524L;
096    
097        /**
098         * Set of weak references to existing coordinate systems. This set is used in order to return
099         * pre-existing object instead of creating new one.
100         */
101        static final WeakHashSet pool = new WeakHashSet();
102    
103        /**
104         * The non-localized object name.
105         */
106        private final String name;
107    
108        /**
109         * Properties for all methods except {@link #getName}. For example the method
110         * {@link #getAuthorityCode} returns the value of property <code>"authorityCode"</code>. May
111         * be null if there is no properties for this object.
112         */
113        private final Map properties;
114    
115        /**
116         * Create an object with the specified name.
117         * 
118         * @param name
119         *            This object name.
120         */
121        public Info( final String name ) {
122            this.name = name;
123            this.properties = null;
124            ensureNonNull( "name", name );
125        }
126    
127        /**
128         * Create an object with the specified properties. Property keys are any of the following
129         * strings:
130         * <ul>
131         * <li>"name" (mandatory)</li>
132         * <li>"authority"</li>
133         * <li>"authorityCode"</li>
134         * <li>"alias"</li>
135         * <li>"abbreviation"</li>
136         * <li>"remarks"</li>
137         * </ul>
138         * Values are usually {@link String}, or may be <code>null</code> if a particular property is
139         * not defined. The "name" property is mandatory.
140         * 
141         * @param properties
142         *            The set of properties.
143         */
144        Info( final Map properties ) {
145            ensureNonNull( "properties", properties );
146            this.properties = properties;
147            this.name = (String) properties.get( "name" );
148        }
149    
150        /**
151         * Gets the name of this object. The default implementation returns the non-localized name given
152         * at construction time.
153         * 
154         * @param locale
155         *            The desired locale, or <code>null</code> for a default locale. If no string is
156         *            available for the specified locale, an arbitrary locale is used.
157         * @return the name of this object.
158         * 
159         * @see "org.opengis.cs.CS_Info#getName()"
160         */
161        public String getName() {
162            return name;
163        }
164    
165        /**
166         * Gets the authority name, or <code>null</code> if unspecified. An Authority is an
167         * organization that maintains definitions of Authority Codes. For example the European
168         * Petroleum Survey Group (EPSG) maintains a database of coordinate systems, and other spatial
169         * referencing objects, where each object has a code number ID. For example, the EPSG code for a
170         * WGS84 Lat/Lon coordinate system is '4326'.
171         * 
172         * @return the authority name, or <code>null</code> if unspecified.
173         * 
174         * @see "org.opengis.cs.CS_Info#getAuthority()"
175         */
176        public String getAuthority() {
177            return ( properties != null ) ? (String) properties.get( "authority" ) : null;
178        }
179    
180        /**
181         * Gets the authority-specific identification code, or <code>null</code> if unspecified. The
182         * AuthorityCode is a compact string defined by an Authority to reference a particular spatial
183         * reference object. For example, the European Survey Group (EPSG) authority uses 32 bit
184         * integers to reference coordinate systems, so all their code strings will consist of a few
185         * digits. The EPSG code for WGS84 Lat/Lon is '4326'.
186         * 
187         * @return the authority-specific identification code, or <code>null</code> if unspecified.
188         * 
189         * 
190         * @see "org.opengis.cs.CS_Info#getAuthorityCode()"
191         */
192        public String getAuthorityCode() {
193            return ( properties != null ) ? (String) properties.get( "authorityCode" ) : null;
194        }
195    
196        /**
197         * Gets the alias, or <code>null</code> if there is none.
198         * 
199         * @return he alias, or <code>null</code> if there is none.
200         * 
201         * @see "org.opengis.cs.CS_Info#getAlias()"
202         */
203        public String getAlias() {
204            return ( properties != null ) ? (String) properties.get( "alias" ) : null;
205        }
206    
207        /**
208         * Gets the abbreviation, or <code>null</code> if there is none.
209         * 
210         * @return the abbreviation, or <code>null</code> if there is none.
211         * 
212         * @see "org.opengis.cs.CS_Info#getAbbreviation()"
213         */
214        public String getAbbreviation() {
215            return ( properties != null ) ? (String) properties.get( "abbreviation" ) : null;
216        }
217    
218        /**
219         * Gets the provider-supplied remarks, or <code>null</code> if there is none.
220         * 
221         * @return the provider-supplied remarks, or <code>null</code> if there is none.
222         * 
223         * @see "org.opengis.cs.CS_Info#getRemarks()"
224         */
225        public String getRemarks() {
226            return ( properties != null ) ? (String) properties.get( "remarks" ) : null;
227        }
228    
229        /**
230         * Returns a hash value for this info.
231         * 
232         * @return a hash value for this info.
233         */
234        public int hashCode() {
235            final String name = getName();
236            return ( name != null ) ? name.hashCode() : 369781;
237        }
238    
239        /**
240         * Compares the specified object with this info for equality.
241         * 
242         * @return
243         */
244        public boolean equals( final Object object ) {
245            if ( object != null && getClass().equals( object.getClass() ) ) {
246                final Info that = (Info) object;
247                return Utilities.equals( this.name, that.name )
248                       && Utilities.equals( this.properties, that.properties );
249            }
250            return false;
251        }
252    
253        /**
254         * Returns a <em>Well Know Text</em> (WKT) for this info. "Well know text" are part of
255         * OpenGIS's specification.
256         * 
257         * @return a <em>Well Know Text</em> (WKT) for this info.
258         */
259        public String toString() {
260            final StringBuffer buffer = new StringBuffer( 40 );
261            buffer.append( "[\"" );
262            buffer.append( getName() );
263            buffer.append( '"' );
264            buffer.insert( 0, addString() );
265            if ( properties != null ) {
266                final Object authority = properties.get( "authority" );
267                if ( authority != null ) {
268                    buffer.append( ", AUTHORITY[" );
269                    buffer.append( authority );
270                    // TODO: Add code (as is AUTHORITY["EPSG","8901"])
271                    buffer.append( "\"]" );
272                }
273            }
274            buffer.append( ']' );
275            return buffer.toString();
276        }
277    
278        /**
279         * Add more information inside the "[...]" part of {@link #toString}. The default
280         * implementation add nothing. Subclasses will override this method in order to complete string
281         * representation.
282         * 
283         * @return The WKT code name (e.g. "GEOGCS").
284         */
285        String addString() {
286            return Utilities.getShortClassName( this );
287        }
288    
289        /**
290         * Add a unit in WKT form.
291         * 
292         * @param buffer
293         * @param unit
294         */
295        final void addUnit( final StringBuffer buffer, final Unit unit ) {
296            if ( unit != null ) {
297                buffer.append( "UNIT[" );
298                if ( Unit.METRE.canConvert( unit ) ) {
299                    buffer.append( "\"metre\"," );
300                    buffer.append( Unit.METRE.convert( 1, unit ) );
301                } else if ( Unit.DEGREE.canConvert( unit ) ) {
302                    buffer.append( "\"degree\"," );
303                    buffer.append( Unit.DEGREE.convert( 1, unit ) );
304                } else if ( Unit.SECOND.canConvert( unit ) ) {
305                    buffer.append( "\"second\"," );
306                    buffer.append( Unit.SECOND.convert( 1, unit ) );
307                }
308                buffer.append( ']' );
309            }
310        }
311    
312        /**
313         * Make sure an argument is non-null. This is a convenience method for subclasses constructors.
314         * 
315         * @param name
316         *            Argument name.
317         * @param object
318         *            User argument.
319         * @throws IllegalArgumentException
320         *             if <code>object</code> is null.
321         */
322        protected static void ensureNonNull( final String name, final Object object )
323                                throws IllegalArgumentException {
324            if ( object == null )
325                throw new IllegalArgumentException(
326                                                    Resources.format(
327                                                                      ResourceKeys.ERROR_NULL_ARGUMENT_$1,
328                                                                      name ) );
329        }
330    
331        /**
332         * Make sure an array element is non-null.
333         * 
334         * @param name
335         *            Argument name.
336         * @param array
337         *            User argument.
338         * @param index
339         *            Element to check.
340         * @throws IllegalArgumentException
341         *             if <code>array[i]</code> is null.
342         */
343        static void ensureNonNull( final String name, final Object[] array, final int index )
344                                throws IllegalArgumentException {
345            if ( array[index] == null )
346                throw new IllegalArgumentException(
347                                                    Resources.format(
348                                                                      ResourceKeys.ERROR_NULL_ARGUMENT_$1,
349                                                                      name + '[' + index + ']' ) );
350        }
351    
352        /**
353         * Make sure that the specified unit is a temporal one.
354         * 
355         * @param unit
356         *            Unit to check.
357         * @throws IllegalArgumentException
358         *             if <code>unit</code> is not a temporal unit.
359         */
360        static void ensureTimeUnit( final Unit unit )
361                                throws IllegalArgumentException {
362            if ( !Unit.SECOND.canConvert( unit ) )
363                throw new IllegalArgumentException(
364                                                    Resources.format(
365                                                                      ResourceKeys.ERROR_NON_TEMPORAL_UNIT_$1,
366                                                                      unit ) );
367        }
368    
369        /**
370         * Make sure that the specified unit is a linear one.
371         * 
372         * @param unit
373         *            Unit to check.
374         * @throws IllegalArgumentException
375         *             if <code>unit</code> is not a linear unit.
376         */
377        static void ensureLinearUnit( final Unit unit )
378                                throws IllegalArgumentException {
379            if ( !Unit.METRE.canConvert( unit ) )
380                throw new IllegalArgumentException(
381                                                    Resources.format(
382                                                                      ResourceKeys.ERROR_NON_LINEAR_UNIT_$1,
383                                                                      unit ) );
384        }
385    
386        /**
387         * Make sure that the specified unit is an angular one.
388         * 
389         * @param unit
390         *            Unit to check.
391         * @throws IllegalArgumentException
392         *             if <code>unit</code> is not an angular unit.
393         */
394        static void ensureAngularUnit( final Unit unit )
395                                throws IllegalArgumentException {
396            if ( !Unit.DEGREE.canConvert( unit ) )
397                throw new IllegalArgumentException(
398                                                    Resources.format(
399                                                                      ResourceKeys.ERROR_NON_ANGULAR_UNIT_$1,
400                                                                      unit ) );
401        }
402    
403        /**
404         * Returns a reference to an unique instance of this <code>Info</code>. This method is
405         * automatically invoked during deserialization.
406         * 
407         * NOTE ABOUT ACCESS-MODIFIER: This method can't be private, because it would prevent it from
408         * being invoked from subclasses in this package (e.g. {@link CoordinateSystem}). This method
409         * <em>will not</em> be invoked for classes outside this package, unless we give it
410         * <code>protected</code> access. TODO: Would it be a good idea?
411         * 
412         * @return a reference to an unique instance of this <code>Info</code>.
413         */
414        Object readResolve() {
415            return pool.intern( this );
416        }
417    
418    }