001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/io/datastore/FeatureId.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2006 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014    
015     This library is distributed in the hope that it will be useful,
016     but WITHOUT ANY WARRANTY; without even the implied warranty of
017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
018     Lesser General Public License for more details.
019    
020     You should have received a copy of the GNU Lesser General Public
021     License along with this library; if not, write to the Free Software
022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact:
025    
026     Andreas Poth
027     lat/lon GmbH
028     Aennchenstraße 19
029     53177 Bonn
030     Germany
031     E-Mail: poth@lat-lon.de
032    
033     Jens Fitzke
034     lat/lon GmbH
035     Aennchenstraße 19
036     53177 Bonn
037     Germany
038     E-Mail: jens.fitzke@uni-bonn.de
039     ---------------------------------------------------------------------------*/
040    
041    package org.deegree.io.datastore;
042    
043    import org.deegree.datatypes.Types;
044    import org.deegree.datatypes.UnknownTypeException;
045    import org.deegree.i18n.Messages;
046    import org.deegree.io.datastore.idgenerator.IdGenerationException;
047    import org.deegree.io.datastore.schema.MappedFeatureType;
048    import org.deegree.io.datastore.schema.MappedGMLId;
049    import org.deegree.io.datastore.schema.content.MappingField;
050    
051    /**
052     * Used to identify persistent (stored) feature instances.
053     * 
054     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
055     * @author last edited by: $Author: mschneider $
056     * 
057     * @version $Revision: 6433 $, $Date: 2007-03-28 21:03:26 +0200 (Mi, 28 Mär 2007) $
058     */
059    public class FeatureId implements Comparable {
060    
061        private MappedFeatureType ft;
062    
063        private MappedGMLId fidDefinition;
064    
065        private Object[] values;
066    
067        /**
068         * Creates a new instance of <code>FeatureId</code> from the given parameters.
069         * 
070         * @param ft
071         *            cannot be null, must not be an abstract feature type
072         * @param values
073         *            cannot be null or empty
074         */
075        public FeatureId( MappedFeatureType ft, Object[] values ) {
076            this.ft = ft;
077            this.fidDefinition = ft.getGMLId();
078            this.values = values;
079        }
080    
081        /**
082         * Creates a new instance of <code>FeatureId</code> from the given parameters.
083         * 
084         * @param ft
085         *            cannot be null, must not be an abstract feature type
086         * @param fid
087         *            cannot be null
088         * @throws IdGenerationException
089         */
090        public FeatureId( MappedFeatureType ft, String fid ) throws IdGenerationException {
091            this.ft = ft;
092            this.fidDefinition = ft.getGMLId();
093            this.values = new Object[1];
094            this.values[0] = removeFIDPrefix( fid, fidDefinition );
095        }
096    
097        /**
098         * Creates a new instance of <code>FeatureId</code> from the given parameters.
099         * 
100         * @param fidDefinition
101         *            cannot be null
102         * @param values
103         * @deprecated use {@link #FeatureId(MappedFeatureType, Object[])} instead
104         */
105        @Deprecated
106        public FeatureId( MappedGMLId fidDefinition, Object[] values ) {
107            this.fidDefinition = fidDefinition;
108            this.values = values;
109        }
110    
111        /**
112         * Creates a new instance of <code>FeatureId</code> from the given parameters.
113         * 
114         * @param fidDefinition
115         *            cannot be null
116         * @param fid
117         *            cannot be null
118         * @throws IdGenerationException
119         * @deprecated use {@link #FeatureId(MappedFeatureType, String)} instead
120         */
121        @Deprecated
122        public FeatureId( MappedGMLId fidDefinition, String fid ) throws IdGenerationException {
123            this.fidDefinition = fidDefinition;
124            this.values = new Object[1];
125            this.values[0] = removeFIDPrefix( fid, fidDefinition );
126        }
127    
128        /**
129         * Return the {@link MappedFeatureType} of the identified feature.
130         * <p>
131         * The feature type is concrete, never abstract.
132         * 
133         * @return type of the identified feature, never abstract
134         */    
135        public MappedFeatureType getFeatureType () {
136            return this.ft;
137        }
138        
139        /**
140         * Return the underlying {@link MappedGMLId}.
141         * 
142         * @return MappedGMLId
143         */
144        public MappedGMLId getFidDefinition() {
145            return this.fidDefinition;
146        }
147    
148        /**
149         * Returns the number of components that the key consists of.
150         * 
151         * @return the number of components that the key consists of
152         */
153        public int getLength() {
154            return this.values.length;
155        }
156    
157        /**
158         * Returns all column values of the key.
159         * 
160         * @return all column values of the key
161         */
162        public Object[] getValues() {
163            return this.values;
164        }
165    
166        /**
167         * Returns a certain column value of the key.
168         * 
169         * @param i
170         *            requested column
171         * @return the requested column value of the key
172         */
173        public Object getValue( int i ) {
174            return this.values[i];
175        }
176    
177        /**
178         * Returns the canonical textual representation, i.e. the key components, separated by the
179         * separator defined in the associated {@link MappedGMLId}.
180         * 
181         * @return the canonical textual representation
182         */
183        public String getAsString() {
184            StringBuffer sb = new StringBuffer( fidDefinition.getPrefix() );
185            for ( int i = 0; i < this.values.length; i++ ) {
186                sb.append( values[i] );
187                if ( i != this.values.length - 1 ) {
188                    sb.append( fidDefinition.getSeparator() );
189                }
190            }
191            return sb.toString();
192        }
193    
194        public int compareTo( Object obj ) {
195            FeatureId that = (FeatureId) obj;
196            return this.getAsString().compareTo( that.getAsString() );
197        }    
198        
199        @Override
200        public int hashCode() {
201            int hashCode = fidDefinition.hashCode();
202            for ( int i = 0; i < this.values.length; i++ ) {
203                hashCode += this.values[i].toString().hashCode();
204            }
205            return hashCode;
206        }
207    
208        @Override
209        public boolean equals( Object obj ) {
210            if ( obj == null || !( obj instanceof FeatureId ) ) {
211                return false;
212            }
213            FeatureId that = (FeatureId) obj;
214            if ( this.fidDefinition != that.fidDefinition ) {
215                return false;
216            }
217            if ( this.values == null && that.values == null ) {
218                return true;
219            }
220            if ( ( this.values != null && that.values == null )
221                 || ( this.values == null && that.values != null )
222                 || ( this.values.length != that.values.length ) ) {
223                return false;
224            }
225            for ( int i = 0; i < this.values.length; i++ ) {
226                if ( ( this.values[i] != null && that.values[i] == null )
227                     || ( this.values[i] == null && that.values[i] != null ) ) {
228                    return false;
229                }
230                if ( this.values[i] != null && that.values[i] != null ) {
231                    if ( !this.values[i].equals( that.values[i] ) ) {
232                        return false;
233                    }
234                }
235            }
236            return true;
237        }
238    
239        /**
240         * Returns a string representation of the object.
241         * 
242         * @return a string representation of the object
243         */
244        @Override
245        public String toString() {
246            StringBuffer sb = new StringBuffer();
247            sb.append( "fid=" );
248            sb.append( getAsString() );
249            sb.append( ", " );
250            MappingField[] fidFields = fidDefinition.getIdFields();
251            for ( int i = 0; i < fidFields.length; i++ ) {
252                sb.append( fidFields[i].getField() );
253                sb.append( "=" );
254                sb.append( values[i] );
255                if ( i != fidFields.length - 1 ) {
256                    sb.append( ", " );
257                }
258            }
259            return sb.toString();
260        }
261    
262        /**
263         * Removes the prefix from the given feature id.
264         * <p>
265         * The prefix is taken from the given gml:id mapping.
266         * 
267         * @param id
268         *            feature id (including prefix).
269         * @param idMapping
270         *            target gml:id mapping (where the fid will be stored)
271         * @return feature id (without prefix) as an object of the right type (matching the table
272         *         column)
273         * @throws IdGenerationException
274         *             if the given fid does not begin with the expected prefix from the gml:id mapping
275         */
276        public static Object removeFIDPrefix( String id, MappedGMLId idMapping )
277                                throws IdGenerationException {
278            Object fidObject = null;
279            String plainIdValue = id;
280            String prefix = idMapping.getPrefix();
281            if ( prefix != null && prefix.length() > 0 ) {
282                if ( !id.startsWith( prefix ) ) {
283                    String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_NO_PREFIX", id, prefix );
284                    throw new IdGenerationException( msg );
285                }
286                plainIdValue = id.substring( prefix.length() );
287            }
288    
289            if ( idMapping.getIdFields().length > 1 ) {
290                String msg = "Compound feature ids not supported in FeatureId.removeFIDPrefix().";
291                throw new IdGenerationException( msg );
292            }
293    
294            int sqlTypeCode = idMapping.getIdFields()[0].getType();
295            switch ( sqlTypeCode ) {
296            case Types.NUMERIC:
297            case Types.DOUBLE: {
298                try {
299                    fidObject = Double.parseDouble( plainIdValue );
300                } catch ( NumberFormatException e ) {
301                    String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
302                                                      "Double" );
303                    throw new IdGenerationException( msg );
304                }
305                break;
306            }
307            case Types.FLOAT: {
308                try {
309                    fidObject = Float.parseFloat( plainIdValue );
310                } catch ( NumberFormatException e ) {
311                    String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
312                                                      "Float" );
313                    throw new IdGenerationException( msg );
314                }
315                break;
316            }
317            case Types.INTEGER: {
318                try {
319                    fidObject = Integer.parseInt( plainIdValue );
320                } catch ( NumberFormatException e ) {
321                    String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
322                                                      "Integer" );
323                    throw new IdGenerationException( msg );
324                }
325                break;
326            }
327            case Types.VARCHAR: 
328            case Types.CHAR:{
329                fidObject = plainIdValue;
330                break;
331            }
332            default: {
333                String msg = null;
334                try {
335                    msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
336                                               Types.getTypeNameForSQLTypeCode( sqlTypeCode ) );
337                } catch ( UnknownTypeException e ) {
338                    throw new IdGenerationException( e.getMessage() );
339                }
340                throw new IdGenerationException( msg );
341            }
342            }
343            return fidObject;
344        }
345    }