001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/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: mschneider $
052     *
053     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
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         * Creates a new instance of <code>FeatureId</code> from the given parameters.
095         *
096         * @param fidDefinition
097         *            cannot be null
098         * @param values
099         * @deprecated use {@link #FeatureId(MappedFeatureType, Object[])} instead
100         */
101        @Deprecated
102        public FeatureId( MappedGMLId fidDefinition, Object[] values ) {
103            this.fidDefinition = fidDefinition;
104            this.values = values;
105        }
106    
107        /**
108         * Creates a new instance of <code>FeatureId</code> from the given parameters.
109         *
110         * @param fidDefinition
111         *            cannot be null
112         * @param fid
113         *            cannot be null
114         * @throws IdGenerationException
115         * @deprecated use {@link #FeatureId(MappedFeatureType, String)} instead
116         */
117        @Deprecated
118        public FeatureId( MappedGMLId fidDefinition, String fid ) throws IdGenerationException {
119            this.fidDefinition = fidDefinition;
120            this.values = new Object[1];
121            this.values[0] = removeFIDPrefix( fid, fidDefinition );
122        }
123    
124        /**
125         * Return the {@link MappedFeatureType} of the identified feature.
126         * <p>
127         * The feature type is concrete, never abstract.
128         *
129         * @return type of the identified feature, never abstract
130         */
131        public MappedFeatureType getFeatureType() {
132            return this.ft;
133        }
134    
135        /**
136         * Return the underlying {@link MappedGMLId}.
137         *
138         * @return MappedGMLId
139         */
140        public MappedGMLId getFidDefinition() {
141            return this.fidDefinition;
142        }
143    
144        /**
145         * Returns the number of components that the key consists of.
146         *
147         * @return the number of components that the key consists of
148         */
149        public int getLength() {
150            return this.values.length;
151        }
152    
153        /**
154         * Returns all column values of the key.
155         *
156         * @return all column values of the key
157         */
158        public Object[] getValues() {
159            return this.values;
160        }
161    
162        /**
163         * Returns a certain column value of the key.
164         *
165         * @param i
166         *            requested column
167         * @return the requested column value of the key
168         */
169        public Object getValue( int i ) {
170            return this.values[i];
171        }
172    
173        /**
174         * Returns the canonical textual representation, i.e. the key components, separated by the separator defined in the
175         * associated {@link MappedGMLId}.
176         *
177         * @return the canonical textual representation
178         */
179        public String getAsString() {
180            StringBuffer sb = new StringBuffer( fidDefinition.getPrefix() );
181            for ( int i = 0; i < this.values.length; i++ ) {
182                sb.append( values[i] );
183                if ( i != this.values.length - 1 ) {
184                    sb.append( fidDefinition.getSeparator() );
185                }
186            }
187            return sb.toString();
188        }
189    
190        public int compareTo( FeatureId that ) {
191            return this.getAsString().compareTo( that.getAsString() );
192        }
193    
194        @Override
195        public int hashCode() {
196            int hashCode = fidDefinition.hashCode();
197            for ( int i = 0; i < this.values.length; i++ ) {
198                hashCode += this.values[i].toString().hashCode();
199            }
200            return hashCode;
201        }
202    
203        @Override
204        public boolean equals( Object obj ) {
205            if ( obj == null || !( obj instanceof FeatureId ) ) {
206                return false;
207            }
208            FeatureId that = (FeatureId) obj;
209            if ( this.fidDefinition != that.fidDefinition ) {
210                return false;
211            }
212            if ( this.values == null && that.values == null ) {
213                return true;
214            }
215            if ( ( this.values != null && that.values == null ) || ( this.values == null && that.values != null )
216                 || ( this.values.length != that.values.length ) ) {
217                return false;
218            }
219            for ( int i = 0; i < this.values.length; i++ ) {
220                if ( ( this.values[i] != null && that.values[i] == null )
221                     || ( this.values[i] == null && that.values[i] != null ) ) {
222                    return false;
223                }
224                if ( this.values[i] != null && that.values[i] != null ) {
225                    if ( !this.values[i].equals( that.values[i] ) ) {
226                        return false;
227                    }
228                }
229            }
230            return true;
231        }
232    
233        /**
234         * Returns a string representation of the object.
235         *
236         * @return a string representation of the object
237         */
238        @Override
239        public String toString() {
240            StringBuffer sb = new StringBuffer();
241            sb.append( "fid=" );
242            sb.append( getAsString() );
243            sb.append( ", " );
244            MappingField[] fidFields = fidDefinition.getIdFields();
245            for ( int i = 0; i < fidFields.length; i++ ) {
246                sb.append( fidFields[i].getField() );
247                sb.append( "=" );
248                sb.append( values[i] );
249                if ( i != fidFields.length - 1 ) {
250                    sb.append( ", " );
251                }
252            }
253            return sb.toString();
254        }
255    
256        /**
257         * Removes the prefix from the given feature id.
258         * <p>
259         * The prefix is taken from the given gml:id mapping.
260         *
261         * @param id
262         *            feature id (including prefix).
263         * @param idMapping
264         *            target gml:id mapping (where the fid will be stored)
265         * @return feature id (without prefix) as an object of the right type (matching the table column)
266         * @throws IdGenerationException
267         *             if the given fid does not begin with the expected prefix from the gml:id mapping
268         */
269        public static Object removeFIDPrefix( String id, MappedGMLId idMapping )
270                                throws IdGenerationException {
271            String plainIdValue = id;
272            String prefix = idMapping.getPrefix();
273            if ( prefix != null && prefix.length() > 0 ) {
274                if ( !id.startsWith( prefix ) ) {
275                    String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_NO_PREFIX", id, prefix );
276                    throw new IdGenerationException( msg );
277                }
278                plainIdValue = id.substring( prefix.length() );
279            }
280    
281            String[] plainIdValues = null;
282            String separator = idMapping.getSeparator();
283            if ( separator != null && separator.length() > 0 ) {
284                plainIdValues = plainIdValue.split( separator );
285            } else {
286                plainIdValues = new String[1];
287                plainIdValues[0] = plainIdValue;
288            }
289    
290            MappingField[] idFields = idMapping.getIdFields();
291            if ( idFields.length != plainIdValues.length ) {
292                String msg = "The given feature id \"" + id + "\" consists of " + plainIdValues.length
293                             + " value parts. Should be " + idFields.length + ".";
294                throw new IdGenerationException( msg );
295            }
296    
297            int sqlTypeCode;
298            Object fidObject = null;
299            Object[] fidObjects = new Object[idFields.length];
300            for ( int i = 0; i < idFields.length; i++ ) {
301                plainIdValue = plainIdValues[i];
302                sqlTypeCode = idFields[i].getType();
303                switch ( sqlTypeCode ) {
304                case Types.NUMERIC:
305                case Types.DOUBLE: {
306                    try {
307                        fidObject = Double.parseDouble( plainIdValue );
308                    } catch ( NumberFormatException e ) {
309                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Double" );
310                        throw new IdGenerationException( msg );
311                    }
312                    break;
313                }
314                case Types.FLOAT: {
315                    try {
316                        fidObject = Float.parseFloat( plainIdValue );
317                    } catch ( NumberFormatException e ) {
318                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Float" );
319                        throw new IdGenerationException( msg );
320                    }
321                    break;
322                }
323                case Types.INTEGER: {
324                    try {
325                        fidObject = Integer.parseInt( plainIdValue );
326                    } catch ( NumberFormatException e ) {
327                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Integer" );
328                        throw new IdGenerationException( msg );
329                    }
330                    break;
331                }
332                case Types.VARCHAR:
333                case Types.CHAR: {
334                    fidObject = plainIdValue;
335                    break;
336                }
337                default: {
338                    String msg = null;
339                    try {
340                        msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
341                                                   Types.getTypeNameForSQLTypeCode( sqlTypeCode ) );
342                    } catch ( UnknownTypeException e ) {
343                        throw new IdGenerationException( e.getMessage() );
344                    }
345                    throw new IdGenerationException( msg );
346                }
347                }
348                fidObjects[i] = fidObject;
349            }
350    
351            if ( fidObjects.length > 1 ) {
352                return fidObjects;
353            }
354            return fidObject;
355        }
356    
357    }