001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/crs/Identifiable.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035     ----------------------------------------------------------------------------*/
036    
037    package org.deegree.crs;
038    
039    import static org.deegree.framework.log.LoggerFactory.getLogger;
040    
041    import java.io.Serializable;
042    
043    import org.deegree.framework.log.ILogger;
044    import org.deegree.i18n.Messages;
045    
046    /**
047     * The <code>Identifiable</code> class can be used to identify a crs, ellipsoid, Datum and primemeridian
048     * 
049     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
050     * 
051     * @author last edited by: $Author: aschmitz $
052     * 
053     * @version $Revision: 21454 $, $Date: 2009-12-15 15:10:55 +0100 (Di, 15. Dez 2009) $
054     * 
055     */
056    
057    public class Identifiable implements Serializable {
058    
059        private static final long serialVersionUID = 4687689233685635124L;
060    
061        private static final ILogger LOG = getLogger( Identifiable.class );
062    
063        private String[] identifiers;
064    
065        private String[] versions;
066    
067        private String[] names;
068    
069        private String[] descriptions;
070    
071        private String[] areasOfUse;
072    
073        private double[] areaOfUseBBox;
074    
075        /**
076         * Takes the references of the other object and stores them in this Identifiable Object.
077         * 
078         * @param other
079         *            identifiable object.
080         */
081        public Identifiable( Identifiable other ) {
082            this( other.getIdentifiers(), other.getNames(), other.getVersions(), other.getDescriptions(),
083                  other.getAreasOfUse() );
084        }
085    
086        /**
087         * 
088         * @param identifiers
089         * @param names
090         *            the human readable names of the object.
091         * @param versions
092         * @param descriptions
093         * @param areasOfUse
094         * @throws IllegalArgumentException
095         *             if no identifier(s) was/were given.
096         */
097        public Identifiable( String[] identifiers, String[] names, String[] versions, String[] descriptions,
098                             String[] areasOfUse ) {
099            if ( identifiers == null || identifiers.length == 0 ) {
100                throw new IllegalArgumentException( "An identifiable object must at least have one identifier." );
101            }
102            this.identifiers = identifiers;
103    
104            this.names = names;
105            this.versions = versions;
106            this.descriptions = descriptions;
107            this.areasOfUse = areasOfUse;
108        }
109    
110        /**
111         * Creates arrays fromt the given identifier and name without setting the versions, descriptions and areasOfUse.
112         * 
113         * @param identifiers
114         *            of the object.
115         */
116        public Identifiable( String[] identifiers ) {
117            this( identifiers, null, null, null, null );
118        }
119    
120        // /**
121        // * Creates arrays fromt the given identifier and name without setting the versions,
122        // descriptions and areasOfUse.
123        // *
124        // * @param identifier of the object.
125        // * @param name the human readable name of the object.
126        // */
127        // protected Identifiable( String identifier, String name ) {
128        // this( new String[]{identifier}, new String[]{name}, null, null, null );
129        // }
130    
131        /**
132         * @param id
133         *            of the Identifier
134         */
135        public Identifiable( String id ) {
136            this( new String[] { id } );
137        }
138    
139        /**
140         * @return the first of all areasOfUse or <code>null</code> if no areasOfUse were given.
141         */
142        public final String getAreaOfUse() {
143            return ( areasOfUse != null && areasOfUse.length > 0 ) ? areasOfUse[0] : null;
144        }
145    
146        /**
147         * @return the first of all descriptions or <code>null</code> if no descriptions were given.
148         */
149        public final String getDescription() {
150            return ( descriptions != null && descriptions.length > 0 ) ? descriptions[0] : null;
151        }
152    
153        /**
154         * @return the first of all identifiers.
155         */
156        public final String getIdentifier() {
157            return identifiers[0];
158        }
159    
160        /**
161         * @return the first of all names or <code>null</code> if no names were given.
162         */
163        public final String getName() {
164            return ( names != null && names.length > 0 ) ? names[0] : null;
165        }
166    
167        /**
168         * @return the first of all versions or <code>null</code> if no versions were given.
169         */
170        public final String getVersion() {
171            return ( versions != null && versions.length > 0 ) ? versions[0] : null;
172        }
173    
174        /**
175         * throws an InvalidParameterException if the given object is null
176         * 
177         * @param toBeChecked
178         *            for <code>null</code>
179         * @param message
180         *            to put into the exception. If absent, the default message (CRS_INVALID_NULL_PARAMETER) will be
181         *            inserted.
182         * @throws IllegalArgumentException
183         *             if the given object is <code>null</code>.
184         */
185        protected void checkForNullObject( Object toBeChecked, String message )
186                                throws IllegalArgumentException {
187            if ( toBeChecked == null ) {
188                if ( message == null || "".equals( message.trim() ) ) {
189                    message = Messages.getMessage( "CRS_INVALID_NULL_PARAMETER" );
190                }
191                throw new IllegalArgumentException( message );
192            }
193    
194        }
195    
196        /**
197         * throws an InvalidParameterException if the given object is null
198         * 
199         * @param toBeChecked
200         *            for <code>null</code>
201         * @param functionName
202         *            of the caller
203         * @param paramName
204         *            of the parameter to be checked.
205         * @throws IllegalArgumentException
206         *             if the given object is <code>null</code>.
207         */
208        public static void checkForNullObject( Object toBeChecked, String functionName, String paramName )
209                                throws IllegalArgumentException {
210            if ( toBeChecked == null ) {
211                throw new IllegalArgumentException( Messages.getMessage( "CRS_PARAMETER_NOT_NULL", functionName, paramName ) );
212            }
213        }
214    
215        /**
216         * throws an IllegalArgumentException if the given object array is null or empty
217         * 
218         * @param toBeChecked
219         *            for <code>null</code> or empty
220         * @param message
221         *            to put into the exception. If absent, the default message (CRS_INVALID_NULL_PARAMETER) will be
222         *            inserted.
223         * @throws IllegalArgumentException
224         *             if the given object array is <code>null</code> or empty.
225         */
226        public static void checkForNullObject( Object[] toBeChecked, String message )
227                                throws IllegalArgumentException {
228            if ( toBeChecked != null && toBeChecked.length != 0 ) {
229                return;
230            }
231            if ( message == null || "".equals( message.trim() ) ) {
232                message = Messages.getMessage( "CRS_INVALID_NULL_ARRAY" );
233            }
234            throw new IllegalArgumentException( message );
235        }
236    
237        @Override
238        public String toString() {
239            StringBuilder sb = new StringBuilder( "id: [" );
240            for ( int i = 0; i < identifiers.length; ++i ) {
241                sb.append( identifiers[i] );
242                if ( ( i + 1 ) < identifiers.length ) {
243                    sb.append( ", " );
244                }
245            }
246            if ( getName() != null ) {
247                sb.append( "], name: [" );
248                for ( int i = 0; i < names.length; ++i ) {
249                    sb.append( names[i] );
250                    if ( ( i + 1 ) < names.length ) {
251                        sb.append( ", " );
252                    }
253                }
254            }
255            if ( getVersion() != null ) {
256                sb.append( "], version: [" );
257                for ( int i = 0; i < versions.length; ++i ) {
258                    sb.append( versions[i] );
259                    if ( ( i + 1 ) < versions.length ) {
260                        sb.append( ", " );
261                    }
262                }
263            }
264            if ( getDescription() != null ) {
265                sb.append( "], description: [" );
266                for ( int i = 0; i < descriptions.length; ++i ) {
267                    sb.append( descriptions[i] );
268                    if ( ( i + 1 ) < descriptions.length ) {
269                        sb.append( ", " );
270                    }
271                }
272            }
273            if ( getAreaOfUse() != null ) {
274                sb.append( "], areasOfUse: [" );
275                for ( int i = 0; i < areasOfUse.length; ++i ) {
276                    sb.append( areasOfUse[i] );
277                    if ( ( i + 1 ) < areasOfUse.length ) {
278                        sb.append( ", " );
279                    }
280                }
281            }
282            return sb.toString();
283        }
284    
285        /**
286         * @return the first id and the name (if given) as id: id, name: name.
287         */
288        public String getIdAndName() {
289            StringBuilder sb = new StringBuilder( "id: " ).append( getIdentifier() );
290            if ( getName() != null ) {
291                sb.append( ", name: " ).append( getName() );
292            }
293            return sb.toString();
294        }
295    
296        @Override
297        public boolean equals( Object other ) {
298            if ( other != null && other instanceof Identifiable ) {
299                final Identifiable that = (Identifiable) other;
300                boolean isThisEPSG = false;
301                boolean isThatEPSG = false;
302                for ( String id : getIdentifiers() ) {
303                    if ( id.toLowerCase().contains( "epsg" ) ) {
304                        isThisEPSG = true;
305                        break;
306                    }
307                }
308                for ( String id : that.getIdentifiers() ) {
309                    if ( id.toLowerCase().contains( "epsg" ) ) {
310                        isThatEPSG = true;
311                        break;
312                    }
313                }
314                if ( isThatEPSG && isThisEPSG ) {
315                    return idsMatch( that.identifiers );
316                }
317                return true;// idsMatch( that.identifiers );
318            }
319            return false;
320        }
321    
322        /**
323         * Checks for the equality of id's between to different identifiable objects.
324         * 
325         * @param otherIDs
326         *            of the other identifiable object.
327         * @return true if the given strings match this.identifiers false otherwise.
328         */
329        private boolean idsMatch( String[] otherIDs ) {
330            if ( otherIDs == null || identifiers.length != otherIDs.length ) {
331                return false;
332            }
333            for ( int i = 0; i < identifiers.length; ++i ) {
334                String tmp = identifiers[i];
335                String other = otherIDs[i];
336                if ( tmp != null ) {
337                    if ( !tmp.equals( other ) ) {
338                        return false;
339                    }
340                } else if ( other != null ) {
341                    return false;
342                }
343            }
344            return true;
345    
346        }
347    
348        /**
349         * @return the areasOfUse or <code>null</code> if no areasOfUse were given.
350         */
351        public final String[] getAreasOfUse() {
352            return areasOfUse;
353        }
354    
355        /**
356         * @return the descriptions or <code>null</code> if no descriptions were given.
357         */
358        public final String[] getDescriptions() {
359            return descriptions;
360        }
361    
362        /**
363         * @return the identifiers, each identifiable object has atleast one id.
364         */
365        public final String[] getIdentifiers() {
366            return identifiers;
367        }
368    
369        /**
370         * @return the names or <code>null</code> if no names were given.
371         */
372        public final String[] getNames() {
373            return names;
374        }
375    
376        /**
377         * @return the versions or <code>null</code> if no versions were given.
378         */
379        public final String[] getVersions() {
380            return versions;
381        }
382    
383        /**
384         * @param id
385         *            a string which could match this identifiable.
386         * @return true if this identifiable can be identified with the given string, false otherwise.
387         */
388        public boolean hasID( String id ) {
389            if ( id == null || "".equals( id.trim() ) ) {
390                return false;
391            }
392            for ( String s : getIdentifiers() ) {
393                if ( id.equalsIgnoreCase( s ) ) {
394                    return true;
395                }
396            }
397            return false;
398        }
399    
400        /**
401         * Returns the area of use, i.e. the domain where this {@link Identifiable} is valid.
402         * 
403         * @return the domain of validity (EPSG:4326 coordinates), order: minX, minY, maxX, maxY, never <code>null</code>
404         *         (-180,-90,180,90) if no such information is available
405         */
406        public double[] getAreaOfUseBBox() {
407    
408            if ( areaOfUseBBox == null ) {
409                areaOfUseBBox = new double[4];
410                areaOfUseBBox[0] = Double.NaN;
411                areaOfUseBBox[1] = Double.NaN;
412                areaOfUseBBox[2] = Double.NaN;
413                areaOfUseBBox[3] = Double.NaN;
414                if ( areasOfUse != null ) {
415                    for ( String bboxString : areasOfUse ) {
416                        try {
417                            double[] ords = parseAreaBBox( bboxString );
418                            for ( int i = 0; i < 4; i++ ) {
419                                if ( Double.isNaN( areaOfUseBBox[i] ) || areaOfUseBBox[i] > ords[i] ) {
420                                    areaOfUseBBox[i] = ords[i];
421                                }
422                            }
423                        } catch ( Exception e ) {
424                            LOG.logDebug( "Error parsing areaOfUse bbox (ignoring it): '" + e.getMessage() + "'" );
425                        }
426                    }
427                }
428                if ( Double.isNaN( areaOfUseBBox[0] ) ) {
429                    LOG.logDebug( "No areaOfUse BBox available, assuming world." );
430                    areaOfUseBBox[0] = -180;
431                    areaOfUseBBox[1] = -90;
432                    areaOfUseBBox[2] = 180;
433                    areaOfUseBBox[3] = 90;
434                }
435            }
436            return areaOfUseBBox;
437        }
438    
439        private double[] parseAreaBBox( String s )
440                                throws IllegalArgumentException, NumberFormatException {
441            String[] tokens = s.split( "," );
442            if ( tokens.length != 4 ) {
443                throw new IllegalArgumentException( "Invalid areaOfUse: expected CSV-list of length 4." );
444            }
445            double[] ords = new double[4];
446            for ( int i = 0; i < 4; i++ ) {
447                ords[i] = Double.parseDouble( tokens[i] );
448            }
449            return ords;
450        }
451    }