001    //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/io/datastore/FeatureId.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.io.datastore;
038    
039    import org.deegree.datatypes.Types;
040    import org.deegree.datatypes.UnknownTypeException;
041    import org.deegree.i18n.Messages;
042    import org.deegree.io.datastore.idgenerator.IdGenerationException;
043    import org.deegree.io.datastore.schema.MappedFeatureType;
044    import org.deegree.io.datastore.schema.MappedGMLId;
045    import org.deegree.io.datastore.schema.content.MappingField;
046    
047    /**
048     * Used to identify persistent (stored) feature instances.
049     *
050     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
051     * @author last edited by: $Author: apoth $
052     *
053     * @version $Revision: 29953 $, $Date: 2011-03-09 13:15:46 +0100 (Wed, 09 Mar 2011) $
054     */
055    public class FeatureId implements Comparable<FeatureId> {
056    
057        private MappedFeatureType ft;
058    
059        private MappedGMLId fidDefinition;
060    
061        private Object[] values;
062    
063        /**
064         * Creates a new instance of <code>FeatureId</code> from the given parameters.
065         *
066         * @param ft
067         *            cannot be null, must not be an abstract feature type
068         * @param values
069         *            cannot be null or empty
070         */
071        public FeatureId( MappedFeatureType ft, Object[] values ) {
072            this.ft = ft;
073            this.fidDefinition = ft.getGMLId();
074            this.values = values;
075        }
076    
077        /**
078         * Creates a new instance of <code>FeatureId</code> from the given parameters.
079         *
080         * @param ft
081         *            cannot be null, must not be an abstract feature type
082         * @param fid
083         *            cannot be null
084         * @throws IdGenerationException
085         */
086        public FeatureId( MappedFeatureType ft, String fid ) throws IdGenerationException {
087            this.ft = ft;
088            this.fidDefinition = ft.getGMLId();
089            this.values = new Object[1];
090            this.values[0] = removeFIDPrefix( fid, fidDefinition );
091        }
092    
093      
094    
095        /**
096         * Return the {@link MappedFeatureType} of the identified feature.
097         * <p>
098         * The feature type is concrete, never abstract.
099         *
100         * @return type of the identified feature, never abstract
101         */
102        public MappedFeatureType getFeatureType() {
103            return this.ft;
104        }
105    
106        /**
107         * Return the underlying {@link MappedGMLId}.
108         *
109         * @return MappedGMLId
110         */
111        public MappedGMLId getFidDefinition() {
112            return this.fidDefinition;
113        }
114    
115        /**
116         * Returns the number of components that the key consists of.
117         *
118         * @return the number of components that the key consists of
119         */
120        public int getLength() {
121            return this.values.length;
122        }
123    
124        /**
125         * Returns all column values of the key.
126         *
127         * @return all column values of the key
128         */
129        public Object[] getValues() {
130            return this.values;
131        }
132    
133        /**
134         * Returns a certain column value of the key.
135         *
136         * @param i
137         *            requested column
138         * @return the requested column value of the key
139         */
140        public Object getValue( int i ) {
141            return this.values[i];
142        }
143    
144        /**
145         * Returns the canonical textual representation, i.e. the key components, separated by the separator defined in the
146         * associated {@link MappedGMLId}.
147         *
148         * @return the canonical textual representation
149         */
150        public String getAsString() {
151            StringBuffer sb = new StringBuffer( fidDefinition.getPrefix() );
152            for ( int i = 0; i < this.values.length; i++ ) {
153                sb.append( values[i] );
154                if ( i != this.values.length - 1 ) {
155                    sb.append( fidDefinition.getSeparator() );
156                }
157            }
158            return sb.toString();
159        }
160    
161        public int compareTo( FeatureId that ) {
162            return this.getAsString().compareTo( that.getAsString() );
163        }
164    
165        @Override
166        public int hashCode() {
167            int hashCode = fidDefinition.hashCode();
168            for ( int i = 0; i < this.values.length; i++ ) {
169                hashCode += this.values[i].toString().hashCode();
170            }
171            return hashCode;
172        }
173    
174        @Override
175        public boolean equals( Object obj ) {
176            if ( obj == null || !( obj instanceof FeatureId ) ) {
177                return false;
178            }
179            FeatureId that = (FeatureId) obj;
180            if ( this.fidDefinition != that.fidDefinition ) {
181                return false;
182            }
183            if ( this.values == null && that.values == null ) {
184                return true;
185            }
186            if ( ( this.values != null && that.values == null ) || ( this.values == null && that.values != null )
187                 || ( this.values.length != that.values.length ) ) {
188                return false;
189            }
190            for ( int i = 0; i < this.values.length; i++ ) {
191                if ( ( this.values[i] != null && that.values[i] == null )
192                     || ( this.values[i] == null && that.values[i] != null ) ) {
193                    return false;
194                }
195                if ( this.values[i] != null && that.values[i] != null ) {
196                    if ( !this.values[i].equals( that.values[i] ) ) {
197                        return false;
198                    }
199                }
200            }
201            return true;
202        }
203    
204        /**
205         * Returns a string representation of the object.
206         *
207         * @return a string representation of the object
208         */
209        @Override
210        public String toString() {
211            StringBuffer sb = new StringBuffer();
212            sb.append( "fid=" );
213            sb.append( getAsString() );
214            sb.append( ", " );
215            MappingField[] fidFields = fidDefinition.getIdFields();
216            for ( int i = 0; i < fidFields.length; i++ ) {
217                sb.append( fidFields[i].getField() );
218                sb.append( "=" );
219                sb.append( values[i] );
220                if ( i != fidFields.length - 1 ) {
221                    sb.append( ", " );
222                }
223            }
224            return sb.toString();
225        }
226    
227        /**
228         * Removes the prefix from the given feature id.
229         * <p>
230         * The prefix is taken from the given gml:id mapping.
231         *
232         * @param id
233         *            feature id (including prefix).
234         * @param idMapping
235         *            target gml:id mapping (where the fid will be stored)
236         * @return feature id (without prefix) as an object of the right type (matching the table column)
237         * @throws IdGenerationException
238         *             if the given fid does not begin with the expected prefix from the gml:id mapping
239         */
240        public static Object removeFIDPrefix( String id, MappedGMLId idMapping )
241                                throws IdGenerationException {
242            String plainIdValue = id;
243            String prefix = idMapping.getPrefix();
244            if ( prefix != null && prefix.length() > 0 ) {
245                if ( !id.startsWith( prefix ) ) {
246                    String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_NO_PREFIX", id, prefix );
247                    throw new IdGenerationException( msg );
248                }
249                plainIdValue = id.substring( prefix.length() );
250            }
251    
252            String[] plainIdValues = null;
253            String separator = idMapping.getSeparator();
254            if ( separator != null && separator.length() > 0 ) {
255                plainIdValues = plainIdValue.split( separator );
256            } else {
257                plainIdValues = new String[1];
258                plainIdValues[0] = plainIdValue;
259            }
260    
261            MappingField[] idFields = idMapping.getIdFields();
262            if ( idFields.length != plainIdValues.length ) {
263                String msg = "The given feature id \"" + id + "\" consists of " + plainIdValues.length
264                             + " value parts. Should be " + idFields.length + ".";
265                throw new IdGenerationException( msg );
266            }
267    
268            int sqlTypeCode;
269            Object fidObject = null;
270            Object[] fidObjects = new Object[idFields.length];
271            for ( int i = 0; i < idFields.length; i++ ) {
272                plainIdValue = plainIdValues[i];
273                sqlTypeCode = idFields[i].getType();
274                switch ( sqlTypeCode ) {
275                case Types.NUMERIC:
276                case Types.DECIMAL:
277                case Types.DOUBLE: {
278                    try {
279                        fidObject = Double.parseDouble( plainIdValue );
280                    } catch ( NumberFormatException e ) {
281                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Double" );
282                        throw new IdGenerationException( msg );
283                    }
284                    break;
285                }
286                case Types.FLOAT: {
287                    try {
288                        fidObject = Float.parseFloat( plainIdValue );
289                    } catch ( NumberFormatException e ) {
290                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Float" );
291                        throw new IdGenerationException( msg );
292                    }
293                    break;
294                }
295                case Types.INTEGER: {
296                    try {
297                        fidObject = Integer.parseInt( plainIdValue );
298                    } catch ( NumberFormatException e ) {
299                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Integer" );
300                        throw new IdGenerationException( msg );
301                    }
302                    break;
303                }
304                case Types.VARCHAR:
305                case Types.CHAR: {
306                    fidObject = plainIdValue;
307                    break;
308                }
309                default: {
310                    String msg = null;
311                    try {
312                        msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
313                                                   Types.getTypeNameForSQLTypeCode( sqlTypeCode ) );
314                    } catch ( UnknownTypeException e ) {
315                        throw new IdGenerationException( e.getMessage() );
316                    }
317                    throw new IdGenerationException( msg );
318                }
319                }
320                fidObjects[i] = fidObject;
321            }
322    
323            if ( fidObjects.length > 1 ) {
324                return fidObjects;
325            }
326            return fidObject;
327        }
328    
329    }