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