    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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: aschmitz $
    052     *
    053     * @version $Revision: 22080 $, $Date: 2010-01-21 13:24:52 +0100 (Do, 21. Jan 2010) $
    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.DECIMAL:
    306                case Types.DOUBLE: {
    307                    try {
    308                        fidObject = Double.parseDouble( plainIdValue );
    309                    } catch ( NumberFormatException e ) {
    310                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Double" );
    311                        throw new IdGenerationException( msg );
    312                    }
    313                    break;
    314                }
    315                case Types.FLOAT: {
    316                    try {
    317                        fidObject = Float.parseFloat( plainIdValue );
    318                    } catch ( NumberFormatException e ) {
    319                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Float" );
    320                        throw new IdGenerationException( msg );
    321                    }
    322                    break;
    323                }
    324                case Types.INTEGER: {
    325                    try {
    326                        fidObject = Integer.parseInt( plainIdValue );
    327                    } catch ( NumberFormatException e ) {
    328                        String msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue, "Integer" );
    329                        throw new IdGenerationException( msg );
    330                    }
    331                    break;
    332                }
    333                case Types.VARCHAR:
    334                case Types.CHAR: {
    335                    fidObject = plainIdValue;
    336                    break;
    337                }
    338                default: {
    339                    String msg = null;
    340                    try {
    341                        msg = Messages.getMessage( "DATASTORE_FEATURE_ID_CONVERT", plainIdValue,
    342                                                   Types.getTypeNameForSQLTypeCode( sqlTypeCode ) );
    343                    } catch ( UnknownTypeException e ) {
    344                        throw new IdGenerationException( e.getMessage() );
    345                    }
    346                    throw new IdGenerationException( msg );
    347                }
    348                }
    349                fidObjects[i] = fidObject;
    350            }
    351
    352            if ( fidObjects.length > 1 ) {
    353                return fidObjects;
    354            }
    355            return fidObject;
    356        }
    357
    358    }