Source Code Cross Referenced for PostgisDataStore.java in  » GIS » GeoTools-2.4.1 » org » geotools » data » postgis » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » GIS » GeoTools 2.4.1 » org.geotools.data.postgis 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         *    GeoTools - OpenSource mapping toolkit
0003:         *    http://geotools.org
0004:         *    (C) 2002-2006, GeoTools Project Managment Committee (PMC)
0005:         * 
0006:         *    This library is free software; you can redistribute it and/or
0007:         *    modify it under the terms of the GNU Lesser General Public
0008:         *    License as published by the Free Software Foundation;
0009:         *    version 2.1 of the License.
0010:         *
0011:         *    This library is distributed in the hope that it will be useful,
0012:         *    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013:         *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0014:         *    Lesser General Public License for more details.
0015:         */
0016:        package org.geotools.data.postgis;
0017:
0018:        import java.io.IOException;
0019:        import java.math.BigDecimal;
0020:        import java.sql.Connection;
0021:        import java.sql.DatabaseMetaData;
0022:        import java.sql.ResultSet;
0023:        import java.sql.SQLException;
0024:        import java.sql.Statement;
0025:        import java.util.ArrayList;
0026:        import java.util.Arrays;
0027:        import java.util.Collections;
0028:        import java.util.HashMap;
0029:        import java.util.HashSet;
0030:        import java.util.Iterator;
0031:        import java.util.List;
0032:        import java.util.Map;
0033:        import java.util.Set;
0034:        import java.util.logging.Level;
0035:        import java.util.logging.Logger;
0036:
0037:        import javax.sql.DataSource;
0038:
0039:        import org.geotools.data.DataSourceException;
0040:        import org.geotools.data.DataStore;
0041:        import org.geotools.data.DataUtilities;
0042:        import org.geotools.data.DefaultQuery;
0043:        import org.geotools.data.EmptyFeatureReader;
0044:        import org.geotools.data.FeatureReader;
0045:        import org.geotools.data.FeatureSource;
0046:        import org.geotools.data.FeatureWriter;
0047:        import org.geotools.data.InProcessLockingManager;
0048:        import org.geotools.data.LockingManager;
0049:        import org.geotools.data.Query;
0050:        import org.geotools.data.ReTypeFeatureReader;
0051:        import org.geotools.data.Transaction;
0052:        import org.geotools.data.jdbc.ConnectionPool;
0053:        import org.geotools.data.jdbc.FeatureTypeInfo;
0054:        import org.geotools.data.jdbc.JDBCDataStore;
0055:        import org.geotools.data.jdbc.JDBCDataStoreConfig;
0056:        import org.geotools.data.jdbc.JDBCFeatureLocking;
0057:        import org.geotools.data.jdbc.JDBCFeatureSource;
0058:        import org.geotools.data.jdbc.JDBCFeatureStore;
0059:        import org.geotools.data.jdbc.JDBCFeatureWriter;
0060:        import org.geotools.data.jdbc.JDBCUtils;
0061:        import org.geotools.data.jdbc.QueryData;
0062:        import org.geotools.data.jdbc.SQLBuilder;
0063:        import org.geotools.data.jdbc.attributeio.AttributeIO;
0064:        import org.geotools.data.jdbc.attributeio.WKTAttributeIO;
0065:        import org.geotools.data.jdbc.fidmapper.FIDMapper;
0066:        import org.geotools.data.jdbc.fidmapper.FIDMapperFactory;
0067:        import org.geotools.data.postgis.attributeio.EWKTAttributeIO;
0068:        import org.geotools.data.postgis.attributeio.PgWKBAttributeIO;
0069:        import org.geotools.data.postgis.fidmapper.PostgisFIDMapperFactory;
0070:        import org.geotools.data.postgis.referencing.PostgisAuthorityFactory;
0071:        import org.geotools.factory.GeoTools;
0072:        import org.geotools.factory.Hints;
0073:        import org.geotools.feature.AttributeType;
0074:        import org.geotools.feature.AttributeTypeFactory;
0075:        import org.geotools.feature.FeatureType;
0076:        import org.geotools.feature.GeometryAttributeType;
0077:        import org.geotools.filter.CompareFilter;
0078:        import org.opengis.filter.Filter;
0079:        import org.opengis.filter.PropertyIsLessThan;
0080:        import org.opengis.filter.PropertyIsLessThanOrEqualTo;
0081:        import org.geotools.filter.FilterType;
0082:        import org.geotools.filter.LengthFunction;
0083:        import org.geotools.filter.LiteralExpression;
0084:        import org.geotools.filter.SQLEncoderPostgis;
0085:        import org.geotools.referencing.CRS;
0086:        import org.geotools.referencing.NamedIdentifier;
0087:        import org.geotools.referencing.crs.DefaultGeographicCRS;
0088:        import org.opengis.referencing.FactoryException;
0089:        import org.opengis.referencing.NoSuchAuthorityCodeException;
0090:        import org.opengis.referencing.crs.CoordinateReferenceSystem;
0091:
0092:        import com.vividsolutions.jts.geom.Envelope;
0093:        import com.vividsolutions.jts.geom.Geometry;
0094:        import com.vividsolutions.jts.geom.GeometryCollection;
0095:        import com.vividsolutions.jts.geom.GeometryFactory;
0096:        import com.vividsolutions.jts.geom.LineString;
0097:        import com.vividsolutions.jts.geom.MultiLineString;
0098:        import com.vividsolutions.jts.geom.MultiPoint;
0099:        import com.vividsolutions.jts.geom.MultiPolygon;
0100:        import com.vividsolutions.jts.geom.Point;
0101:        import com.vividsolutions.jts.geom.Polygon;
0102:        import com.vividsolutions.jts.io.WKTReader;
0103:
0104:        /**
0105:         * Postgis DataStore implementation.
0106:         * 
0107:         * <p>
0108:         * This datastore by default will read/write geometries in WKT format.<br>
0109:         * Optionally use of WKB can be turned on, in which case you may want to turn
0110:         * on also the use of the bytea function, that fasten the data trasfer, but
0111:         * that it's available only from version 0.7.2 onwards.
0112:         * </p>
0113:         *
0114:         * @author Chris Holmes, TOPP
0115:         * @author Andrea Aime
0116:         * @author Paolo Rizzi
0117:         * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/plugin/postgis/src/main/java/org/geotools/data/postgis/PostgisDataStore.java $
0118:         * @version $Id: PostgisDataStore.java 29506 2008-02-29 09:27:36Z aaime $
0119:         *
0120:         * @task REVISIT: So Paolo Rizzi has a number of improvements in
0121:         *       http://jira.codehuas.org/browse/GEOT-379  I rolled in a few of them,
0122:         *       but  some beg more fundamental questions - like the use of primary
0123:         *       keys - in the geotools model.  See the issue for a bit more
0124:         *       discussion, and I will attempt to write my thoughts up on wiki soon.
0125:         *       -ch
0126:         */
0127:        public class PostgisDataStore extends JDBCDataStore implements 
0128:                DataStore {
0129:
0130:            /** The logger for the postgis module. */
0131:            protected static final Logger LOGGER = org.geotools.util.logging.Logging
0132:                    .getLogger("org.geotools.data.postgis");
0133:
0134:            /** Factory for producing geometries (from JTS). */
0135:            protected static GeometryFactory geometryFactory = new GeometryFactory();
0136:
0137:            /** Well Known Text reader (from JTS). */
0138:            protected static WKTReader geometryReader = new WKTReader(
0139:                    geometryFactory);
0140:
0141:            /** Map of postgis geometries to jts geometries */
0142:            private static Map GEOM_TYPE_MAP = new HashMap();
0143:
0144:            static {
0145:                GEOM_TYPE_MAP.put("GEOMETRY", Geometry.class);
0146:                GEOM_TYPE_MAP.put("POINT", Point.class);
0147:                GEOM_TYPE_MAP.put("POINTM", Point.class);
0148:                GEOM_TYPE_MAP.put("LINESTRING", LineString.class);
0149:                GEOM_TYPE_MAP.put("LINESTRINGM", LineString.class);
0150:                GEOM_TYPE_MAP.put("POLYGON", Polygon.class);
0151:                GEOM_TYPE_MAP.put("POLYGONM", Polygon.class);
0152:                GEOM_TYPE_MAP.put("MULTIPOINT", MultiPoint.class);
0153:                GEOM_TYPE_MAP.put("MULTIPOINTM", MultiPoint.class);
0154:                GEOM_TYPE_MAP.put("MULTILINESTRING", MultiLineString.class);
0155:                GEOM_TYPE_MAP.put("MULTILINESTRINGM", MultiLineString.class);
0156:                GEOM_TYPE_MAP.put("MULTIPOLYGON", MultiPolygon.class);
0157:                GEOM_TYPE_MAP.put("MULTIPOLYGONM", MultiPolygon.class);
0158:                GEOM_TYPE_MAP.put("GEOMETRYCOLLECTION",
0159:                        GeometryCollection.class);
0160:                GEOM_TYPE_MAP.put("GEOMETRYCOLLECTIONM",
0161:                        GeometryCollection.class);
0162:            }
0163:
0164:            private static Map CLASS_MAPPINGS = new HashMap();
0165:
0166:            static {
0167:                CLASS_MAPPINGS.put(String.class, "VARCHAR");
0168:
0169:                CLASS_MAPPINGS.put(Boolean.class, "BOOLEAN");
0170:
0171:                CLASS_MAPPINGS.put(Integer.class, "INTEGER");
0172:
0173:                CLASS_MAPPINGS.put(Float.class, "REAL");
0174:                CLASS_MAPPINGS.put(Double.class, "DOUBLE PRECISION");
0175:
0176:                CLASS_MAPPINGS.put(BigDecimal.class, "DECIMAL");
0177:
0178:                CLASS_MAPPINGS.put(java.sql.Date.class, "DATE");
0179:                CLASS_MAPPINGS.put(java.util.Date.class, "DATE");
0180:                CLASS_MAPPINGS.put(java.sql.Time.class, "TIME");
0181:                CLASS_MAPPINGS.put(java.sql.Timestamp.class, "TIMESTAMP");
0182:            }
0183:
0184:            private static Map GEOM_CLASS_MAPPINGS = new HashMap();
0185:
0186:            //why don't we just stick this in with the non-geom class mappings?
0187:            static {
0188:                // init the inverse map
0189:                Set keys = GEOM_TYPE_MAP.keySet();
0190:
0191:                for (Iterator it = keys.iterator(); it.hasNext();) {
0192:                    String name = (String) it.next();
0193:                    Class geomClass = (Class) GEOM_TYPE_MAP.get(name);
0194:                    GEOM_CLASS_MAPPINGS.put(geomClass, name);
0195:                }
0196:            }
0197:
0198:            /** OPTIMIZE_MODE constants */
0199:            public static final int OPTIMIZE_SAFE = 0;
0200:            public static final int OPTIMIZE_SQL = 1;
0201:
0202:            /** Maximum string size for postgres */
0203:            private static final int MAX_ALLOWED_VALUE = 10485760;
0204:
0205:            //JD: GEOT-723, keeping this reference static allows the authority factory
0206:            // to hold onto a stale connection pool when a new datastore is created.
0207:            //private static PostgisAuthorityFactory paf = null;
0208:            private PostgisAuthorityFactory paf = null;
0209:
0210:            /** PostGIS version information (persisted here so we don't have to keep asking the database what version it is, in perpituity. */
0211:            protected PostgisDBInfo dbInfo;
0212:
0213:            /** Enables the use of geos operators */
0214:            protected boolean useGeos;
0215:
0216:            /** 
0217:             * Current optimize mode 
0218:             * @deprecated Dot not use this directly, use {@link #getOptimizeMode()}.
0219:             */
0220:            public int OPTIMIZE_MODE;
0221:
0222:            /** If true, WKB format is used instead of WKT */
0223:            protected boolean WKBEnabled = false;
0224:
0225:            /**
0226:             * If true, the bytea function will be used to optimize even further data
0227:             * loading when using WKB format
0228:             */
0229:            protected boolean byteaEnabled = false;
0230:
0231:            /**
0232:             *  postgis 1.0 changed the way WKB is handled, this needs to be
0233:             *  set if version >1.
0234:             *  (it affects the way you send WKB to the database)
0235:             */
0236:            protected boolean byteaWKB = false;
0237:
0238:            /**
0239:             * If true then the bounding box filters will use the && postgis operator,
0240:             * which uses the spatial index and performs against the envelope of the
0241:             * geom, leading to greater speed and slightly less accuracy.
0242:             */
0243:            protected boolean looseBbox;
0244:
0245:            /**
0246:             * set to true if the bounds for a table should be computed using the  
0247:             * estimated_extent' function, but beware that this function is less accurate
0248:             * and in some cases *far* less accurate if the data within the actual bounds
0249:             * does not follow a uniform distribution.
0250:             */
0251:            protected boolean estimatedExtent;
0252:
0253:            /** Flag indicating whether schema support **/
0254:            protected boolean schemaEnabled = true;
0255:
0256:            protected PostgisDataStore(DataSource dataSource)
0257:                    throws IOException {
0258:                this (dataSource, (String) null);
0259:            }
0260:
0261:            protected PostgisDataStore(DataSource dataSource, String namespace)
0262:                    throws IOException {
0263:                this (dataSource, schema(null), namespace);
0264:            }
0265:
0266:            protected PostgisDataStore(DataSource dataSource, String schema,
0267:                    String namespace) throws IOException {
0268:                this (dataSource, new JDBCDataStoreConfig(namespace,
0269:                        schema(schema), new HashMap(), new HashMap()),
0270:                        OPTIMIZE_SQL);
0271:            }
0272:
0273:            protected PostgisDataStore(DataSource dataSource, String schema,
0274:                    String namespace, int optimizeMode) throws IOException {
0275:                this (dataSource, new JDBCDataStoreConfig(namespace,
0276:                        schema(schema), new HashMap(), new HashMap()),
0277:                        optimizeMode);
0278:            }
0279:
0280:            /** 
0281:             * Simple helper method to ensure that a schema is always set.
0282:             */
0283:            protected static String schema(String schema) {
0284:                if (schema != null && !"".equals(schema))
0285:                    return schema;
0286:
0287:                return (String) PostgisDataStoreFactory.SCHEMA.sample;
0288:            }
0289:
0290:            public PostgisDataStore(DataSource dataSource,
0291:                    JDBCDataStoreConfig config, int optimizeMode)
0292:                    throws IOException {
0293:                super (dataSource, config);
0294:                guessDataStoreOptions();
0295:                OPTIMIZE_MODE = optimizeMode;
0296:
0297:                // use the specific postgis fid mapper factory
0298:                setFIDMapperFactory(buildFIDMapperFactory(config));
0299:            }
0300:
0301:            /**
0302:             * Allows subclass to create LockingManager to support their needs.
0303:             *
0304:             */
0305:            protected LockingManager createLockingManager() {
0306:                return new InProcessLockingManager();
0307:            }
0308:
0309:            /**
0310:             * Creates a new sql builder for encoding raw sql statements;
0311:             * 
0312:             */
0313:            protected PostgisSQLBuilder createSQLBuilder() {
0314:                PostgisSQLBuilder builder = new PostgisSQLBuilder(
0315:                        new SQLEncoderPostgis(), config);
0316:                initBuilder(builder);
0317:                return builder;
0318:            }
0319:
0320:            /**
0321:             * Attempts to figure out some optimization options, based on some postgis
0322:             * metadata.  If the version is later than 0.7.2 then bytea will be used
0323:             * to read geometries if WKB is enabled.  And it will read if GEOS is
0324:             * enabled from the version string as well.
0325:             *
0326:             * @throws IOException
0327:             */
0328:            protected void guessDataStoreOptions() throws IOException {
0329:                PostgisDBInfo dbInfo = getDBInfo();
0330:                if (dbInfo == null) { //assume
0331:                    LOGGER.severe("Could not obtain PostgisDBInfo");
0332:                    byteaEnabled = true;
0333:                    byteaWKB = false;
0334:                    useGeos = true;
0335:                    schemaEnabled = true;
0336:                } else {
0337:                    byteaEnabled = dbInfo.isByteaEnabled();
0338:                    if (dbInfo.getMajorVersion() >= 1) {
0339:                        byteaWKB = true; // force ew wkb writing format
0340:                    }
0341:                    useGeos = dbInfo.isGeosEnabled();
0342:                    schemaEnabled = dbInfo.isSchemaEnabled();
0343:                }
0344:            }
0345:
0346:            /*
0347:             * (non-Javadoc)
0348:             *
0349:             * @see org.geotools.data.DataStore#getTypeNames()
0350:             */
0351:            public String[] getTypeNames() throws IOException {
0352:
0353:                final int TABLE_NAME_COL = 3;
0354:                Connection conn = null;
0355:                List list = new ArrayList();
0356:
0357:                try {
0358:                    conn = getConnection(Transaction.AUTO_COMMIT);
0359:
0360:                    DatabaseMetaData meta = conn.getMetaData(); // DB: shouldnt this be done by looking at geometry_columns?  or are you trying to allow non-spatial tables in as well?
0361:                    String[] tableType = { "TABLE", "VIEW" };
0362:                    ResultSet tables = meta.getTables(null, config
0363:                            .getDatabaseSchemaName(), "%", tableType);
0364:
0365:                    while (tables.next()) {
0366:                        String tableName = tables.getString(TABLE_NAME_COL);
0367:
0368:                        if (allowTable(tableName)) {
0369:                            list.add(tableName);
0370:                        }
0371:                    }
0372:                    tables.close();
0373:
0374:                    return (String[]) list.toArray(new String[list.size()]);
0375:                } catch (SQLException sqlException) {
0376:                    JDBCUtils
0377:                            .close(conn, Transaction.AUTO_COMMIT, sqlException);
0378:                    conn = null;
0379:
0380:                    String message = "Error querying database for list of tables:"
0381:                            + sqlException.getMessage();
0382:                    throw new DataSourceException(message, sqlException);
0383:                } finally {
0384:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
0385:                }
0386:
0387:                /*
0388:                //Justin's patch from uDig, should be faster, but untested.
0389:                Connection conn = null;    	
0390:                String namespace = config.getNamespace();
0391:                try {
0392:                	conn = getConnection(Transaction.AUTO_COMMIT);
0393:                	
0394:                	PreparedStatement st = null;
0395:                	
0396:                	if (namespace != null && !namespace.trim().equals("")) { //$NON-NLS-1$
0397:                		st = conn.prepareStatement(
0398:                			"SELECT distinct a.relname "  //$NON-NLS-1$
0399:                			+ "FROM pg_class a, pg_attribute b, pg_namespace c, pg_type d " //$NON-NLS-1$
0400:                		   + "WHERE a.oid = b.attrelid " //$NON-NLS-1$
0401:                		   	 + "AND b.atttypid = d.oid "  //$NON-NLS-1$
0402:                		   	 + "AND a.relnamespace = c.oid "  //$NON-NLS-1$
0403:                		   	 + "AND c.nspname = ? " //$NON-NLS-1$
0404:                		   	 + "AND d.typname = ? " //$NON-NLS-1$
0405:                		   	 + "AND a.relname in (SELECT f_table_name FROM geometry_columns)" //$NON-NLS-1$
0406:                		);
0407:                		st.setString(1, namespace);
0408:                		st.setString(2, "geometry"); //$NON-NLS-1$
0409:                	}
0410:                	else {
0411:                		st = conn.prepareStatement(
0412:                			"SELECT distinct a.relname "  //$NON-NLS-1$
0413:                			+ "FROM pg_class a, pg_attribute b, pg_type d " //$NON-NLS-1$
0414:                		   + "WHERE a.oid = b.attrelid " //$NON-NLS-1$
0415:                		   	 + "AND b.atttypid = d.oid "  //$NON-NLS-1$
0416:                		   	 + "AND d.typname = ? " //$NON-NLS-1$
0417:                		   	 + "AND a.relname in (SELECT f_table_name FROM geometry_columns)" //$NON-NLS-1$
0418:                		);
0419:                		st.setString(1, "geometry"); //$NON-NLS-1$
0420:                	}
0421:                	
0422:                	ResultSet rs = st.executeQuery(); 
0423:                	ArrayList names = new ArrayList();
0424:                	while(rs.next()) {
0425:                		String table = rs.getString(1);
0426:                		if (allowTable(table)){
0427:                			names.add(table);
0428:                		}
0429:                			
0430:                	}
0431:                	
0432:                	return (String[])names.toArray(new String[names.size()]);
0433:                }
0434:                catch (SQLException sqlException) {
0435:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, sqlException);
0436:                    conn = null;
0437:                    throw new DataSourceException( sqlException );
0438:                } 
0439:                finally {
0440:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
0441:                }    
0442:                 */
0443:            }
0444:
0445:            /**
0446:             * Retrieve approx bounds of all Features.
0447:             * <p>
0448:             * This result is suitable for a quick map display, illustrating the data.
0449:             * This value is often stored as metadata in databases such as oraclespatial.
0450:             * </p>
0451:             * @return null as a generic implementation is not provided.
0452:             */
0453:            public Envelope getEnvelope(String typeName) {
0454:                Connection conn = null;
0455:
0456:                try {
0457:                    conn = createConnection();
0458:                    Statement st = null;
0459:                    ResultSet rs = null;
0460:                    Envelope envelope = null;
0461:
0462:                    FeatureType schema = getSchema(typeName);
0463:                    String geomName = schema.getDefaultGeometry().getName();
0464:
0465:                    // optimization, postgis version >= 1.0 contains estimated_extent
0466:                    // function to query the stats of the table to determine the bbox,
0467:                    // however, it may return null
0468:                    if (getDBInfo().getMajorVersion() >= 1) {
0469:                        //try the estimated_extent([schema], table, geocolumn) function
0470:                        String q;
0471:                        String dbSchema = config.getDatabaseSchemaName();
0472:                        if (!schemaEnabled || dbSchema == null
0473:                                || "".equals(dbSchema)) {
0474:                            q = "SELECT AsText(force_2d(envelope(estimated_extent('"
0475:                                    + typeName + "','" + geomName + "'))))";
0476:                        } else {
0477:                            q = "SELECT AsText(force_2d(envelope(estimated_extent('"
0478:                                    + dbSchema
0479:                                    + "','"
0480:                                    + typeName
0481:                                    + "','"
0482:                                    + geomName + "'))))";
0483:                        }
0484:                        st = conn.createStatement();
0485:                        rs = st.executeQuery(q);
0486:
0487:                        if (rs.next()) {
0488:                            //parse return value
0489:                            String wkt = rs.getString(1);
0490:                            if (wkt != null && !wkt.trim().equals("")) { //$NON-NLS-1$
0491:                                envelope = geometryReader.read(wkt)
0492:                                        .getEnvelopeInternal();
0493:
0494:                                // expand the bounds by 20% (10% in each direction)
0495:                                // Works whether or not the bounds are at the origin
0496:                                double minX = envelope.getMinX();
0497:                                double minY = envelope.getMinY();
0498:                                double maxX = envelope.getMaxX();
0499:                                double maxY = envelope.getMaxY();
0500:                                double deltaX = (maxX - minX) * 0.1;
0501:                                double deltaY = (maxY - minY) * 0.1;
0502:                                envelope.expandToInclude(minX - deltaX, minY
0503:                                        - deltaY);
0504:                                envelope.expandToInclude(maxX + deltaX, maxY
0505:                                        + deltaY);
0506:                            } else {
0507:                                LOGGER
0508:                                        .warning("PostGIS estimated_extent function did not return a result."
0509:                                                + "\nPerhaps 'ANALYZE "
0510:                                                + typeName
0511:                                                + ";' needs to be run or the table is empty?");
0512:                            }
0513:                        }
0514:
0515:                        rs.close();
0516:                        st.close();
0517:                    }
0518:
0519:                    if (envelope == null) {
0520:
0521:                        //try to generate an approximation
0522:                        envelope = new Envelope();
0523:                        //this is an attempt to grab a handful of envelopes without counting the features
0524:                        final int blockSize = 10; //how many features to grab on each postgis hit
0525:                        final int fetchAllLimit = 99; //if we don't exceed this value, just fetch all features
0526:                        //final int upperLimit = 1000000; //aim for this many features
0527:                        //final int nBlocks = 7; //number of times to hit postgis
0528:                        //int[] offset = new int[nBlocks];
0529:                        //automatic range calculation (for tweaking)
0530:                        //once we hit 100,000 features in our scan, things get really slow
0531:                        //therefore we'll stop around 50k
0532:                        //offset[0] = 1;
0533:                        //double magicNumber = Math.pow(upperLimit, 1.0 / (nBlocks - 1));
0534:                        //for (int i = 1; i < nBlocks; i++) {
0535:                        //    offset[i] = (int) Math.ceil(offset[i-1] * magicNumber);
0536:                        //    System.out.println(offset[i]);
0537:                        //}
0538:                        //offset[0] = 0;
0539:                        int[] offset = new int[] { 0, 10, 100, 1000, 10000,
0540:                                20000, 40000 };
0541:
0542:                        int hits = 0;
0543:                        int misses = 0;
0544:                        for (int i = 0; i < offset.length && misses < 4; i++) {
0545:                            String limit = " LIMIT " + blockSize + " OFFSET "
0546:                                    + offset[i];
0547:                            ;
0548:                            if (i + 1 < offset.length
0549:                                    && offset[i + 1] - offset[i] <= blockSize) {
0550:                                limit = " LIMIT " + blockSize * 2 + " OFFSET "
0551:                                        + offset[i];
0552:                                offset[i + 1] = offset[i] + blockSize;
0553:                                i++;
0554:                            }
0555:                            String q = "SELECT AsText(force_2d(envelope("
0556:                                    + geomName + "))) FROM " + typeName;
0557:                            if (offset[i] > -1) {
0558:                                q = q + limit;
0559:                            }
0560:                            st = conn.createStatement();
0561:                            rs = st.executeQuery(q);
0562:                            boolean gotEnvelope = false;
0563:                            while (rs.next()) {
0564:                                gotEnvelope = true;
0565:                                String wkt = rs.getString(1);
0566:                                if (wkt != null && !wkt.trim().equals("")) { //$NON-NLS-1$
0567:                                    Envelope e = geometryReader.read(wkt)
0568:                                            .getEnvelopeInternal();
0569:
0570:                                    if (envelope.isNull())
0571:                                        envelope.init(e);
0572:                                    else
0573:                                        envelope.expandToInclude(e);
0574:                                }
0575:                            }
0576:                            if (gotEnvelope) {
0577:                                hits++;
0578:                            } else {
0579:                                misses++;
0580:                                if (hits == 0) { //there are no features!
0581:                                    rs.close();
0582:                                    st.close();
0583:                                    return new Envelope();
0584:                                }
0585:                                if (offset[i - 1] < fetchAllLimit) { //just fetch everything
0586:                                    offset[i] = -1;
0587:                                } else {
0588:                                    //went beyond the last feature
0589:                                    //on our first miss, we move back 50% and try again
0590:                                    //on our second miss, we stop guessing and look between last 2 hits
0591:                                    int min = offset[i - 1];
0592:                                    int max = offset[i];
0593:                                    if (misses == 2) {
0594:                                        min = offset[i - 2];
0595:                                        max = offset[i - 1];
0596:                                    }
0597:                                    if (misses < 3) {
0598:                                        offset[i] = (int) ((min + max) / 2.0);
0599:                                        int width = (int) ((max - min) / (double) (offset.length - i));
0600:                                        for (int j = i + 1; j < offset.length; j++) {
0601:                                            offset[j] = min + (width * (j - i));
0602:                                        }
0603:                                    } else {
0604:                                        rs.close();
0605:                                        st.close();
0606:                                        break;
0607:                                    }
0608:                                }
0609:                                i--;
0610:                            }
0611:                            rs.close();
0612:                            st.close();
0613:                            if (offset[i] == -1)
0614:                                break;
0615:                        }
0616:
0617:                        // expand since this is an approximation
0618:                        // Works whether or not the bounds are at the origin
0619:                        double minX = envelope.getMinX();
0620:                        double minY = envelope.getMinY();
0621:                        double maxX = envelope.getMaxX();
0622:                        double maxY = envelope.getMaxY();
0623:                        double deltaX = (maxX - minX) * 1.0;
0624:                        double deltaY = (maxY - minY) * 1.0;
0625:                        envelope.expandToInclude(minX - deltaX, minY - deltaY);
0626:                        envelope.expandToInclude(maxX + deltaX, maxY + deltaY);
0627:
0628:                    }
0629:                    return envelope;
0630:                } catch (Exception ignore) {
0631:                    return null;
0632:                } finally {
0633:                    if (conn != null) {
0634:                        try {
0635:                            conn.close();
0636:                        } catch (SQLException e) {
0637:                            // I give up
0638:                        }
0639:                    }
0640:                }
0641:            }
0642:
0643:            protected boolean allowTable(String tablename) {
0644:                if (tablename.equals("geometry_columns")) {
0645:                    return false;
0646:                } else if (tablename.startsWith("spatial_ref_sys")) {
0647:                    return false;
0648:                }
0649:
0650:                //others?
0651:                return true;
0652:            }
0653:
0654:            /**
0655:             * Override this method to perform a few permission checks before the super 
0656:             * class has a chance to do its thing.
0657:             */
0658:            protected FeatureType buildSchema(String typeName, FIDMapper mapper)
0659:                    throws IOException {
0660:                //be sure we can query the necessary tables
0661:                //TODO: should spatial_ref_sys be in here?
0662:                Connection conn = getConnection(Transaction.AUTO_COMMIT);
0663:
0664:                try {
0665:                    Statement st = conn.createStatement();
0666:
0667:                    try {
0668:                        st.execute("SELECT * FROM geometry_columns LIMIT 0;");
0669:                    } catch (Throwable t) {
0670:                        String msg = "Error querying relation: geometry_columns."
0671:                                + " Possible cause:" + t.getLocalizedMessage();
0672:                        throw new DataSourceException(msg, t);
0673:                    }
0674:                    try {
0675:                        SQLEncoderPostgis encoder = new SQLEncoderPostgis(-1);
0676:                        encoder.setSupportsGEOS(useGeos);
0677:                        PostgisSQLBuilder builder = new PostgisSQLBuilder(
0678:                                encoder, config);
0679:                        initBuilder(builder);
0680:
0681:                        st.execute("SELECT * FROM "
0682:                                + builder.encodeTableName(typeName)
0683:                                + " LIMIT 0;");
0684:                    } catch (Throwable t) {
0685:                        String msg = "Error querying relation:" + typeName
0686:                                + "." + " Possible cause:"
0687:                                + t.getLocalizedMessage();
0688:                        throw new DataSourceException(msg, t);
0689:                    }
0690:                    st.close();
0691:                } catch (SQLException e) {
0692:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, e);
0693:                    throw new DataSourceException(e);
0694:                } finally {
0695:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
0696:                }
0697:
0698:                //everything is cool, keep going
0699:                return super .buildSchema(typeName, mapper);
0700:            }
0701:
0702:            /**
0703:             * This is a public entry point to the DataStore.
0704:             * 
0705:             * <p>
0706:             * We have given some though to changing this api to be based on query.
0707:             * </p>
0708:             * 
0709:             * <p>
0710:             * Currently this is the only way to retype your features to different
0711:             * name spaces.
0712:             * </p>
0713:             * (non-Javadoc)
0714:             *
0715:             * @see org.geotools.data.DataStore#getFeatureReader(org.geotools.feature.FeatureType,
0716:             *      org.geotools.filter.Filter, org.geotools.data.Transaction)
0717:             */
0718:            public FeatureReader getFeatureReader(
0719:                    final FeatureType requestType, final Filter filter,
0720:                    final Transaction transaction) throws IOException {
0721:                String typeName = requestType.getTypeName();
0722:                FeatureType schemaType = getSchema(typeName);
0723:
0724:                int compare = DataUtilities.compare(requestType, schemaType);
0725:
0726:                Query query;
0727:
0728:                if (compare == 0) {
0729:                    // they are the same type
0730:                    //
0731:                    query = new DefaultQuery(typeName, filter);
0732:                } else if (compare == 1) {
0733:                    // featureType is a proper subset and will require reTyping
0734:                    //
0735:                    String[] names = attributeNames(requestType, filter);
0736:                    query = new DefaultQuery(typeName, filter,
0737:                            Query.DEFAULT_MAX, names, "getFeatureReader");
0738:                } else {
0739:                    // featureType is not compatiable
0740:                    //
0741:                    throw new IOException("Type " + typeName
0742:                            + " does match request");
0743:                }
0744:
0745:                if ((filter == Filter.EXCLUDE) || filter.equals(Filter.EXCLUDE)) {
0746:                    return new EmptyFeatureReader(requestType);
0747:                }
0748:
0749:                FeatureReader reader = getFeatureReader(query, transaction);
0750:
0751:                if (compare == 1) {
0752:                    reader = new ReTypeFeatureReader(reader, requestType, false);
0753:                }
0754:
0755:                return reader;
0756:            }
0757:
0758:            /**
0759:             * Gets the list of attribute names required for both featureType and
0760:             * filter
0761:             *
0762:             * @param featureType The FeatureType to get attribute names for.
0763:             * @param filter The filter which needs attributes to filter.
0764:             *
0765:             * @return The list of attribute names required by a filter.
0766:             *
0767:             * @throws IOException If we can't get the schema.
0768:             */
0769:            protected String[] attributeNames(FeatureType featureType,
0770:                    Filter filter) throws IOException {
0771:                String typeName = featureType.getTypeName();
0772:                FeatureType original = getSchema(typeName);
0773:                SQLBuilder sqlBuilder = getSqlBuilder(typeName);
0774:
0775:                if (featureType.getAttributeCount() == original
0776:                        .getAttributeCount()) {
0777:                    // featureType is complete (so filter must require subset
0778:                    return DataUtilities.attributeNames(featureType);
0779:                }
0780:
0781:                String[] typeAttributes = DataUtilities
0782:                        .attributeNames(featureType);
0783:                String[] filterAttributes = DataUtilities
0784:                        .attributeNames(sqlBuilder.getPostQueryFilter(filter));
0785:
0786:                if ((filterAttributes == null)
0787:                        || (filterAttributes.length == 0)) {
0788:                    // no filter attributes required
0789:                    return typeAttributes;
0790:                }
0791:
0792:                Set set = new HashSet();
0793:                set.addAll(Arrays.asList(typeAttributes));
0794:                set.addAll(Arrays.asList(filterAttributes));
0795:
0796:                if (set.size() == typeAttributes.length) {
0797:                    // filter required a subset of featureType attributes
0798:                    return typeAttributes;
0799:                } else {
0800:                    return (String[]) set.toArray(new String[set.size()]);
0801:                }
0802:            }
0803:
0804:            /**
0805:             * DOCUMENT ME!
0806:             *
0807:             * @param typeName
0808:             *
0809:             * @return DOCUMENT ME!
0810:             *
0811:             * @throws IOException DOCUMENT ME!
0812:             */
0813:            public SQLBuilder getSqlBuilder(String typeName) throws IOException {
0814:                FeatureTypeInfo info = typeHandler.getFeatureTypeInfo(typeName);
0815:                int srid = -1;
0816:
0817:                SQLEncoderPostgis encoder = new SQLEncoderPostgis();
0818:                encoder.setSupportsGEOS(useGeos);
0819:                encoder.setFIDMapper(typeHandler.getFIDMapper(typeName));
0820:
0821:                if (info.getSchema().getDefaultGeometry() != null) {
0822:                    String geom = info.getSchema().getDefaultGeometry()
0823:                            .getName();
0824:                    srid = info.getSRID(geom);
0825:                    encoder.setDefaultGeometry(geom);
0826:                }
0827:
0828:                encoder.setFeatureType(info.getSchema());
0829:                encoder.setSRID(srid);
0830:                encoder.setLooseBbox(looseBbox);
0831:
0832:                PostgisSQLBuilder builder = new PostgisSQLBuilder(encoder,
0833:                        config, info.getSchema());
0834:                initBuilder(builder);
0835:
0836:                return builder;
0837:            }
0838:
0839:            protected void initBuilder(PostgisSQLBuilder builder) {
0840:                builder.setWKBEnabled(WKBEnabled);
0841:                builder.setByteaEnabled(byteaEnabled);
0842:                builder.setSchemaEnabled(schemaEnabled);
0843:            }
0844:
0845:            /**
0846:             * DOCUMENT ME!
0847:             *
0848:             * @param tableName
0849:             * @param geometryColumnName
0850:             *
0851:             *
0852:             * @throws IOException DOCUMENT ME!
0853:             * @throws DataSourceException DOCUMENT ME!
0854:             */
0855:            protected int determineSRID(String tableName,
0856:                    String geometryColumnName) throws IOException {
0857:                Connection dbConnection = null;
0858:
0859:                try {
0860:                    String dbSchema = config.getDatabaseSchemaName();
0861:                    StringBuffer sql = new StringBuffer();
0862:                    sql.append("SELECT srid FROM geometry_columns WHERE ");
0863:                    if (schemaEnabled && dbSchema != null
0864:                            && dbSchema.length() > 0) {
0865:                        sql.append("f_table_schema='");
0866:                        sql.append(dbSchema);
0867:                        sql.append("' AND ");
0868:                    }
0869:                    sql.append("f_table_name='");
0870:                    sql.append(tableName);
0871:                    sql.append("' AND f_geometry_column='");
0872:                    sql.append(geometryColumnName);
0873:                    sql.append("';");
0874:
0875:                    String sqlStatement = sql.toString();
0876:                    LOGGER.fine("srid statement is " + sqlStatement);
0877:
0878:                    dbConnection = getConnection(Transaction.AUTO_COMMIT);
0879:                    Statement statement = dbConnection.createStatement();
0880:                    ResultSet result = statement.executeQuery(sqlStatement);
0881:
0882:                    if (result.next()) {
0883:                        int retSrid = result.getInt("srid");
0884:                        JDBCUtils.close(statement);
0885:
0886:                        return retSrid;
0887:                    }
0888:                    result.close();
0889:
0890:                    //try asking the first feature for its srid
0891:                    sql = new StringBuffer();
0892:                    sql.append("SELECT SRID(\"");
0893:                    sql.append(geometryColumnName);
0894:                    sql.append("\") FROM \"");
0895:                    if (schemaEnabled && dbSchema != null
0896:                            && dbSchema.length() > 0) {
0897:                        sql.append(dbSchema);
0898:                        sql.append("\".\"");
0899:                    }
0900:                    sql.append(tableName);
0901:                    sql.append("\" LIMIT 1");
0902:                    sqlStatement = sql.toString();
0903:                    result = statement.executeQuery(sqlStatement);
0904:                    if (result.next()) {
0905:                        int retSrid = result.getInt(1);
0906:                        JDBCUtils.close(statement);
0907:                        return retSrid;
0908:                    }
0909:
0910:                    String mesg = "No geometry column row for srid in table: "
0911:                            + tableName + ", geometry column "
0912:                            + geometryColumnName;
0913:                    throw new DataSourceException(mesg);
0914:                } catch (SQLException sqle) {
0915:                    String message = sqle.getMessage();
0916:                    throw new DataSourceException(message, sqle);
0917:                } finally {
0918:                    JDBCUtils
0919:                            .close(dbConnection, Transaction.AUTO_COMMIT, null);
0920:                }
0921:            }
0922:
0923:            /**
0924:             * Provides the default implementation of determining the FID column.
0925:             * 
0926:             * <p>
0927:             * The default implementation of determining the FID column name is to use
0928:             * the primary key as the FID column. If no primary key is present, null
0929:             * will be returned. Sub classes can override this behaviour to define
0930:             * primary keys for vendor specific cases.
0931:             * </p>
0932:             * 
0933:             * <p>
0934:             * There is an unresolved issue as to what to do when there are multiple
0935:             * primary keys. Maybe a restriction that table much have a single column
0936:             * primary key is appropriate.
0937:             * </p>
0938:             * 
0939:             * <p>
0940:             * This should not be called by subclasses to retreive the FID column name.
0941:             * Instead, subclasses should call getFeatureTypeInfo(String) to get the
0942:             * FeatureTypeInfo for a feature type and get the fidColumn name from the
0943:             * fidColumn name memeber.
0944:             * </p>
0945:             *
0946:             * @param array DOCUMENT ME!
0947:             * @param value DOCUMENT ME!
0948:             *
0949:             * @return The name of the primay key column or null if one does not exist.
0950:             */
0951:
0952:            //    protected String determineFidColumnName(String typeName)
0953:            //        throws IOException {
0954:            //        String fidColumn = super.determineFidColumnName(typeName);
0955:            //        
0956:            //        if(fidColumn == null)
0957:            //        	fidColumn = DEFAULT_FID_COLUMN;
0958:            //        	
0959:            //        return fidColumn;
0960:            //    }
0961:            /*
0962:            private static boolean isPresent(String[] array, String value) {
0963:                if (array != null) {
0964:                    for (int i = 0; i < array.length; i++) {
0965:                        if ((array[i] != null) && (array[i].equals(value))) {
0966:                            return (true);
0967:                        }
0968:                    }
0969:                }
0970:
0971:                return (false);
0972:            }
0973:             */
0974:            /**
0975:             * Constructs an AttributeType from a row in a ResultSet. The ResultSet
0976:             * contains the information retrieved by a call to getColumns() on the
0977:             * DatabaseMetaData object. This information can be used to construct an
0978:             * Attribute Type.
0979:             * 
0980:             * <p>
0981:             * This implementation construct an AttributeType using the default JDBC
0982:             * type mappings defined in JDBCDataStore. These type mappings only handle
0983:             * native Java classes and SQL standard column types. If a geometry type
0984:             * is found then getGeometryAttribute is called.
0985:             * </p>
0986:             * 
0987:             * <p>
0988:             * Note: Overriding methods must never move the current row pointer in the
0989:             * result set.
0990:             * </p>
0991:             *
0992:             * @param metadataRs The ResultSet containing the result of a
0993:             *        DatabaseMetaData.getColumns call.
0994:             *
0995:             * @return The AttributeType built from the ResultSet.
0996:             *
0997:             * @throws IOException If an error occurs processing the ResultSet.
0998:             */
0999:            protected AttributeType buildAttributeType(ResultSet metadataRs)
1000:                    throws IOException {
1001:                try {
1002:                    final int TABLE_NAME = 3;
1003:                    final int COLUMN_NAME = 4;
1004:                    final int TYPE_NAME = 6;
1005:                    final int NULLABLE = 11;
1006:                    String typeName = metadataRs.getString(TYPE_NAME);
1007:
1008:                    if (typeName.equals("geometry")) {
1009:                        String tableName = metadataRs.getString(TABLE_NAME);
1010:                        String columnName = metadataRs.getString(COLUMN_NAME);
1011:
1012:                        // check for nullability
1013:                        int nullCode = metadataRs.getInt(NULLABLE);
1014:                        boolean nillable = true;
1015:                        switch (nullCode) {
1016:                        case DatabaseMetaData.columnNoNulls:
1017:                            nillable = false;
1018:                            break;
1019:
1020:                        case DatabaseMetaData.columnNullable:
1021:                            nillable = true;
1022:                            break;
1023:
1024:                        case DatabaseMetaData.columnNullableUnknown:
1025:                            nillable = true;
1026:                            break;
1027:                        }
1028:
1029:                        return getGeometryAttribute(tableName, columnName,
1030:                                nillable);
1031:                    } else {
1032:                        return super .buildAttributeType(metadataRs);
1033:                    }
1034:                } catch (SQLException e) {
1035:                    throw new IOException("Sql error occurred: "
1036:                            + e.getMessage());
1037:                }
1038:            }
1039:
1040:            /**
1041:             * @see org.geotools.data.jdbc.JDBCDataStore#buildFIDMapperFactory(org.geotools.data.jdbc.JDBCDataStoreConfig)
1042:             */
1043:            protected FIDMapperFactory buildFIDMapperFactory(
1044:                    JDBCDataStoreConfig config) {
1045:                return new PostgisFIDMapperFactory(config);
1046:            }
1047:
1048:            protected FIDMapper buildFIDMapper(String typeName,
1049:                    FIDMapperFactory factory) throws IOException {
1050:                Connection conn = null;
1051:
1052:                try {
1053:                    conn = getConnection(Transaction.AUTO_COMMIT);
1054:
1055:                    String dbSchema = config.getDatabaseSchemaName();
1056:                    FIDMapper mapper = factory.getMapper(null, dbSchema,
1057:                            typeName, conn);
1058:
1059:                    return mapper;
1060:                } finally {
1061:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
1062:                }
1063:            }
1064:
1065:            /**
1066:             * Returns an attribute type for a geometry column in a feature table.
1067:             *
1068:             * @param tableName The feature table name.
1069:             * @param columnName The geometry column name.
1070:             * @param nillable 
1071:             *
1072:             * @return Geometric attribute.
1073:             *
1074:             * @throws IOException DOCUMENT ME!
1075:             *
1076:             * @task REVISIT: combine with querySRID, as they use the same select
1077:             *       statement.
1078:             * @task This should probably take a Transaction, so if things mess up then
1079:             *       we can rollback.
1080:             */
1081:            AttributeType getGeometryAttribute(String tableName,
1082:                    String columnName, boolean nillable) throws IOException {
1083:                Connection dbConnection = null;
1084:                Class type = null;
1085:                int srid = 0;
1086:                try {
1087:                    dbConnection = getConnection(Transaction.AUTO_COMMIT);
1088:                    StringBuffer sql = new StringBuffer();
1089:                    sql.append("SELECT type FROM geometry_columns WHERE ");
1090:                    String dbSchema = config.getDatabaseSchemaName();
1091:                    if (schemaEnabled && dbSchema != null
1092:                            && dbSchema.length() > 0) {
1093:                        sql.append("f_table_schema='");
1094:                        sql.append(dbSchema);
1095:                        sql.append("' AND ");
1096:                    }
1097:                    sql.append("f_table_name='");
1098:                    sql.append(tableName);
1099:                    sql.append("' AND f_geometry_column='");
1100:                    sql.append(columnName);
1101:                    sql.append("';");
1102:
1103:                    String sqlStatement = sql.toString();
1104:                    LOGGER.fine("geometry type sql statement is "
1105:                            + sqlStatement);
1106:
1107:                    String geometryType = null;
1108:
1109:                    // retrieve the result set from the JDBC driver
1110:                    Statement statement = dbConnection.createStatement();
1111:                    ResultSet result = statement.executeQuery(sqlStatement);
1112:
1113:                    if (result.next()) {
1114:                        geometryType = result.getString("type");
1115:                        LOGGER.fine("geometry type is: " + geometryType);
1116:                    }
1117:                    result.close();
1118:
1119:                    if (geometryType == null) {
1120:                        //no geometry_columns entry, try grabbing a feature
1121:                        sql = new StringBuffer();
1122:                        if (WKBEnabled) {
1123:                            sql.append("SELECT encode(AsBinary(force_2d(\"");
1124:                            sql.append(columnName);
1125:                            sql.append("\"), 'XDR'),'base64') FROM \"");
1126:                        } else {
1127:                            sql.append("SELECT AsText(\"");
1128:                            sql.append(columnName);
1129:                            sql.append("\") FROM \"");
1130:                        }
1131:                        if (schemaEnabled && dbSchema != null
1132:                                && dbSchema.length() > 0) {
1133:                            sql.append(dbSchema);
1134:                            sql.append("\".\"");
1135:                        }
1136:                        sql.append(tableName);
1137:                        sql.append("\" LIMIT 1");
1138:                        sqlStatement = sql.toString();
1139:                        result = statement.executeQuery(sqlStatement);
1140:                        if (result.next()) {
1141:                            AttributeIO attrIO = getGeometryAttributeIO(null,
1142:                                    null);
1143:                            Object object = attrIO.read(result, 1);
1144:                            if (object instanceof  Geometry) {
1145:                                Geometry geom = (Geometry) object;
1146:                                geometryType = geom.getGeometryType()
1147:                                        .toUpperCase();
1148:                                type = geom.getClass();
1149:                                srid = geom.getSRID(); //will return 0 unless we support EWKB
1150:                            }
1151:                        }
1152:                        result.close();
1153:                    }
1154:                    statement.close();
1155:
1156:                    if (geometryType == null) {
1157:                        String msg = " no geometry found in the GEOMETRY_COLUMNS table"
1158:                                + " for "
1159:                                + tableName
1160:                                + " of the postgis install.  A row"
1161:                                + " for "
1162:                                + columnName
1163:                                + " is required"
1164:                                + " for geotools to work correctly";
1165:                        throw new DataSourceException(msg);
1166:                    }
1167:
1168:                    if (type == null) {
1169:                        type = (Class) GEOM_TYPE_MAP.get(geometryType);
1170:                    }
1171:
1172:                } catch (SQLException sqe) {
1173:                    throw new IOException("An SQL exception occurred: "
1174:                            + sqe.getMessage());
1175:                } finally {
1176:                    JDBCUtils
1177:                            .close(dbConnection, Transaction.AUTO_COMMIT, null);
1178:                }
1179:
1180:                if (srid < 1) {
1181:                    //try again
1182:                    srid = determineSRID(tableName, columnName);
1183:                }
1184:                CoordinateReferenceSystem crs = null;
1185:
1186:                try {
1187:                    crs = getPostgisAuthorityFactory().createCRS(srid);
1188:                } catch (FactoryException e) {
1189:                    // use EPSG code as fallback
1190:                    try {
1191:                        crs = CRS.decode("EPSG:" + srid);
1192:                    } catch (NoSuchAuthorityCodeException e1) {
1193:                        crs = null;
1194:                    } catch (FactoryException e1) {
1195:                        crs = null;
1196:                    }
1197:                }
1198:
1199:                return AttributeTypeFactory.newAttributeType(columnName, type,
1200:                        nillable, 0, null, crs);
1201:            }
1202:
1203:            private PostgisAuthorityFactory getPostgisAuthorityFactory() {
1204:                if (paf == null) {
1205:                    paf = new PostgisAuthorityFactory(dataSource);
1206:                }
1207:
1208:                return paf;
1209:            }
1210:
1211:            /**
1212:             * Gets the sql geometry column name for this type.
1213:             *
1214:             * @param type DOCUMENT ME!
1215:             *
1216:             * @return DOCUMENT ME!
1217:             *
1218:             * @throws RuntimeException DOCUMENT ME!
1219:             *
1220:             * @task TODO: test this, I can just make sure it compiles.
1221:             */
1222:            private String getGeometrySQLTypeName(Class type) {
1223:                String res = (String) GEOM_CLASS_MAPPINGS.get(type);
1224:
1225:                if (res == null) {
1226:                    throw new RuntimeException("Unknown type name for class "
1227:                            + type + " please update GEOMETRY_MAPPINGS");
1228:                }
1229:
1230:                return res;
1231:            }
1232:
1233:            /**
1234:             * Creates a FeatureType in this instance of the PostgisDataStore. Since we
1235:             * don't yet know which attribute in the FeatureType is the primary key, we
1236:             * will create our own called "fid_tablename", which has its own sequence
1237:             * called "tablename_fid_seq". The user should not interact with this
1238:             * column, although its value will be the FID. This method currently assumes
1239:             * there are only 2 dimensions.
1240:             * 
1241:             * @throws IOException
1242:             *             if something goes horribly wrong or the table already exists
1243:             * @see org.geotools.data.DataStore#createSchema(org.geotools.feature.FeatureType)
1244:             */
1245:            public void createSchema(FeatureType featureType)
1246:                    throws IOException {
1247:                String tableName = featureType.getTypeName();
1248:
1249:                String lcTableName = tableName.toLowerCase();
1250:
1251:                AttributeType[] attributeType = featureType.getAttributeTypes();
1252:                String dbSchema = config.getDatabaseSchemaName();
1253:
1254:                PostgisSQLBuilder sqlb = createSQLBuilder();
1255:
1256:                //the featureType won't tell us who the primary key is, so we'll create
1257:                //our own "fid_tablename".  Later when we load the featureType, we will
1258:                //pretend we didn't see fid_tablename when we return the attributes.
1259:                String fidColumn = lcTableName + "_fid";
1260:
1261:                //make sure the fid column doesn't already exist
1262:                for (int i = 0; i < attributeType.length; i++) {
1263:                    if (attributeType[i].getName().equalsIgnoreCase(fidColumn)) {
1264:                        String message = "The featuretype cannot contain the column "
1265:                                + fidColumn
1266:                                + ", since this is used as the hidden FID column";
1267:                        throw new IOException(message);
1268:                    }
1269:                }
1270:
1271:                Connection con = this .getConnection(Transaction.AUTO_COMMIT);
1272:                Statement st = null;
1273:
1274:                boolean shouldExecute = !tablePresent(tableName, con);
1275:
1276:                try {
1277:                    con.setAutoCommit(false);
1278:                    st = con.createStatement();
1279:
1280:                    StringBuffer sql = new StringBuffer("CREATE TABLE ");
1281:                    sql.append(sqlb.encodeTableName(tableName));
1282:                    sql.append(" (");
1283:                    sql.append(sqlb.encodeColumnName(fidColumn));
1284:                    sql.append(" serial PRIMARY KEY,");
1285:                    sql.append(makeSqlCreate(attributeType));
1286:                    sql.append(");");
1287:
1288:                    String sqlStr = sql.toString();
1289:                    LOGGER.info(sqlStr);
1290:
1291:                    if (shouldExecute) {
1292:                        st.execute(sqlStr);
1293:                    }
1294:
1295:                    //fix from pr: it may be that table existed and then was dropped
1296:                    //without removing its geometry info from GEOMETRY_COLUMNS.
1297:                    //To support this, try to delete before inserting.
1298:                    //Preserving case for table names gives problems, 
1299:                    //so convert to lower case
1300:
1301:                    sql = new StringBuffer(
1302:                            "DELETE FROM GEOMETRY_COLUMNS WHERE f_table_catalog=''");
1303:                    sql.append(" AND f_table_schema = '");
1304:                    sql.append(dbSchema);
1305:                    sql.append("'");
1306:                    sql.append("AND f_table_name = '");
1307:                    sql.append(tableName);
1308:                    sql.append("';");
1309:
1310:                    //prints statement for later reuse
1311:                    sqlStr = sql.toString();
1312:                    LOGGER.info(sqlStr);
1313:
1314:                    if (shouldExecute) {
1315:                        st.execute(sqlStr);
1316:                    }
1317:
1318:                    //Ok, so Paolo Rizzi suggested that we get rid of our hand-adding
1319:                    //of geometry column information and use AddGeometryColumn instead
1320:                    //as it is better (this is in GEOT-379, he attached an extended
1321:                    //datastore that does postgis fixes).  But I am pretty positive 
1322:                    //the reason we are doing things this way is to preserve the order
1323:                    //of FeatureTypes.  I know this is fairly silly, from most 
1324:                    //information perspectives, but from another perspective it seems
1325:                    //to make sense - if you were transfering a featureType from one
1326:                    //data store to another then it should have the same order, right?
1327:                    //And order is important in WFS.  There are a few caveats though
1328:                    //for one I don't even know if things work right.  I imagine the
1329:                    //proper constraints that a AddGeometryColumn operation does are 
1330:                    //not set in our hand version, for one.  I would feel better about
1331:                    //ignoring the order and just doing things as we like if we had 
1332:                    //views in place, if users could add the schema, and then be able
1333:                    //to get it back in exactly the order they wanted.  So for now 
1334:                    //let's leave things as is, and maybe talk about it in an irc. -ch 
1335:                    for (int i = 0; i < attributeType.length; i++) {
1336:                        if (!(attributeType[i] instanceof  GeometryAttributeType)) {
1337:                            continue;
1338:                        }
1339:                        GeometryAttributeType geomAttribute = (GeometryAttributeType) attributeType[i];
1340:                        String columnName = attributeType[i].getName();
1341:
1342:                        CoordinateReferenceSystem refSys = geomAttribute
1343:                                .getCoordinateSystem();
1344:                        int SRID;
1345:
1346:                        if (refSys != null) {
1347:                            try {
1348:                                Set ident = refSys.getIdentifiers();
1349:                                if ((ident == null || ident.isEmpty())
1350:                                        && refSys == DefaultGeographicCRS.WGS84) {
1351:                                    SRID = 4326;
1352:                                } else {
1353:                                    String code = ((NamedIdentifier) ident
1354:                                            .toArray()[0]).getCode();
1355:                                    SRID = Integer.parseInt(code);
1356:                                }
1357:                            } catch (Exception e) {
1358:                                LOGGER.warning("SRID could not be determined");
1359:                                SRID = -1;
1360:                            }
1361:                        } else {
1362:                            SRID = -1;
1363:                        }
1364:
1365:                        //                DatabaseMetaData metaData = con.getMetaData();
1366:                        //                ResultSet rs = metaData.getCatalogs();
1367:                        //                rs.next();
1368:                        //
1369:                        //                //String dbName = rs.getString(1);
1370:                        //                rs.close();
1371:
1372:                        String typeName = null;
1373:
1374:                        //this construct seems unnecessary, since we already would
1375:                        //pass over if this wasn't a geometry...
1376:                        Class type = geomAttribute.getType();
1377:
1378:                        if (geomAttribute instanceof  GeometryAttributeType) {
1379:                            typeName = getGeometrySQLTypeName(type);
1380:                        } else {
1381:                            typeName = (String) CLASS_MAPPINGS.get(type);
1382:                        }
1383:
1384:                        if (typeName != null) {
1385:                            //                    statementSQL = new StringBuffer(
1386:                            //                        "SELECT AddGeometryColumn('" + dbSchema + "','"
1387:                            //                        + tableName + "','" + attributeType[i].getName()
1388:                            //                        + "','" + SRID + "','" + typeName + "',2);"
1389:                            //                    ); //assumes 2-D
1390:
1391:                            //add a row to the geometry_columns table
1392:                            sql = new StringBuffer(
1393:                                    "INSERT INTO GEOMETRY_COLUMNS VALUES (");
1394:                            sql.append("'','");
1395:                            sql.append(dbSchema);
1396:                            sql.append("','");
1397:                            sql.append(tableName);
1398:                            sql.append("','");
1399:                            sql.append(columnName);
1400:                            sql.append("',2,");
1401:                            sql.append(SRID);
1402:                            sql.append(",'");
1403:                            sql.append(typeName);
1404:                            sql.append("');");
1405:
1406:                            sqlStr = sql.toString();
1407:                            LOGGER.info(sqlStr);
1408:
1409:                            if (shouldExecute) {
1410:                                st.execute(sqlStr);
1411:                            }
1412:
1413:                            //add geometry constaints to the table
1414:                            if (SRID > -1) {
1415:                                sql = new StringBuffer("ALTER TABLE ");
1416:                                sql.append(sqlb.encodeTableName(tableName));
1417:                                sql.append(" ADD CONSTRAINT enforce_srid_");
1418:                                sql.append(columnName);
1419:                                sql.append(" CHECK (SRID(");
1420:                                sql.append(sqlb.encodeColumnName(columnName));
1421:                                sql.append(") = ");
1422:                                sql.append(SRID);
1423:                                sql.append(");");
1424:                                sqlStr = sql.toString();
1425:                                LOGGER.info(sqlStr);
1426:                                if (shouldExecute) {
1427:                                    st.execute(sqlStr);
1428:                                }
1429:                            }
1430:
1431:                            sql = new StringBuffer("ALTER TABLE ");
1432:                            sql.append(sqlb.encodeTableName(tableName));
1433:                            sql.append(" ADD CONSTRAINT enforce_dims_");
1434:                            sql.append(columnName);
1435:                            sql.append(" CHECK (ndims(");
1436:                            sql.append(sqlb.encodeColumnName(columnName));
1437:                            sql.append(") = 2);");
1438:                            sqlStr = sql.toString();
1439:                            LOGGER.info(sqlStr);
1440:                            if (shouldExecute) {
1441:                                st.execute(sqlStr);
1442:                            }
1443:
1444:                            if (!typeName.equals("GEOMETRY")) {
1445:                                sql = new StringBuffer("ALTER TABLE ");
1446:                                sql.append(sqlb.encodeTableName(tableName));
1447:                                sql.append(" ADD CONSTRAINT enforce_geotype_");
1448:                                sql.append(columnName);
1449:                                sql.append(" CHECK (geometrytype(");
1450:                                sql.append(sqlb.encodeColumnName(columnName));
1451:                                sql.append(") = '");
1452:                                sql.append(typeName);
1453:                                sql.append("'::text OR ");
1454:                                sql.append(sqlb.encodeColumnName(columnName));
1455:                                sql.append(" IS NULL);");
1456:                                sqlStr = sql.toString();
1457:                                LOGGER.info(sqlStr);
1458:                                if (shouldExecute) {
1459:                                    st.execute(sqlStr);
1460:                                }
1461:                            }
1462:
1463:                        } else {
1464:                            LOGGER.warning("Error: " + geomAttribute.getName()
1465:                                    + " unknown type!!!");
1466:                        }
1467:
1468:                        //also build a spatial index on each geometry column.
1469:                        sql = new StringBuffer("CREATE INDEX spatial_");
1470:                        sql.append(tableName);
1471:                        sql.append("_");
1472:                        sql.append(attributeType[i].getName().toLowerCase());
1473:                        sql.append(" ON ");
1474:                        sql.append(sqlb.encodeTableName(tableName));
1475:                        sql.append(" USING GIST (");
1476:                        sql.append(sqlb.encodeColumnName(attributeType[i]
1477:                                .getName()));
1478:                        sql.append(");");
1479:
1480:                        sqlStr = sql.toString();
1481:                        LOGGER.info(sqlStr);
1482:
1483:                        if (shouldExecute) {
1484:                            st.execute(sqlStr);
1485:                        }
1486:                    }
1487:
1488:                    con.commit();
1489:
1490:                } catch (SQLException e) {
1491:                    try {
1492:                        if (con != null) {
1493:                            con.rollback();
1494:                        }
1495:                    } catch (SQLException sqle) {
1496:                        throw new IOException(sqle.getMessage());
1497:                    }
1498:
1499:                    throw (IOException) new IOException(e.getMessage())
1500:                            .initCause(e);
1501:                } finally {
1502:                    try {
1503:                        if (st != null) {
1504:                            st.close();
1505:                        }
1506:                    } catch (SQLException e) {
1507:                        throw new IOException(e.getMessage());
1508:                    } finally {
1509:                        try {
1510:                            if (con != null) {
1511:                                con.setAutoCommit(true);
1512:                                con.close();
1513:                            }
1514:                        } catch (SQLException e) {
1515:                            throw new IOException(e.getMessage());
1516:                        }
1517:                    }
1518:                }
1519:
1520:                if (!shouldExecute) {
1521:                    throw new IOException("The table " + tableName
1522:                            + " already exists.");
1523:                }
1524:            }
1525:
1526:            //    /**
1527:            //     * Returns the sql type name given the SQL type code
1528:            //     *
1529:            //     * @param typeCode
1530:            //     *
1531:            //     *
1532:            //     * @throws RuntimeException DOCUMENT ME!
1533:            //     */
1534:            //    private String getSQLTypeName(int typeCode) {
1535:            //        Class typeClass = (Class) TYPE_MAPPINGS.get(new Integer(typeCode));
1536:            //
1537:            //        if (typeClass == null) {
1538:            //            throw new RuntimeException("Unknown type " + typeCode
1539:            //                + " please update TYPE_MAPPINGS");
1540:            //        }
1541:            //
1542:            //        String typeName = (String) CLASS_MAPPINGS.get(typeClass);
1543:            //
1544:            //        if (typeName == null) {
1545:            //            throw new RuntimeException("Unknown type name for class "
1546:            //                + typeClass.getName() + " please update CLASS_MAPPINGS");
1547:            //        }
1548:            //
1549:            //        return typeName;
1550:            //    }
1551:
1552:            private StringBuffer makeSqlCreate(AttributeType[] attributeType)
1553:                    throws IOException {
1554:                StringBuffer buf = new StringBuffer("");
1555:
1556:                for (int i = 0; i < attributeType.length; i++) {
1557:                    String typeName = null;
1558:                    typeName = (String) CLASS_MAPPINGS.get(attributeType[i]
1559:                            .getType());
1560:                    if (typeName == null)
1561:                        typeName = (String) GEOM_CLASS_MAPPINGS
1562:                                .get(attributeType[i].getType());
1563:
1564:                    if (typeName != null) {
1565:                        if (attributeType[i] instanceof  GeometryAttributeType) {
1566:                            typeName = "GEOMETRY";
1567:                        } else if (typeName.equals("VARCHAR")) {
1568:                            int length = -1;
1569:                            Filter f = attributeType[i].getRestriction();
1570:                            if (f != null
1571:                                    && f != Filter.EXCLUDE
1572:                                    && f != Filter.INCLUDE
1573:                                    && (f instanceof  PropertyIsLessThan || f instanceof  PropertyIsLessThanOrEqualTo)) {
1574:                                try {
1575:                                    CompareFilter cf = (CompareFilter) f;
1576:                                    if (cf.getLeftValue() instanceof  LengthFunction) {
1577:                                        length = Integer
1578:                                                .parseInt(((LiteralExpression) cf
1579:                                                        .getRightValue())
1580:                                                        .getLiteral()
1581:                                                        .toString());
1582:                                    } else {
1583:                                        if (cf.getRightValue() instanceof  LengthFunction) {
1584:                                            length = Integer
1585:                                                    .parseInt(((LiteralExpression) cf
1586:                                                            .getLeftValue())
1587:                                                            .getLiteral()
1588:                                                            .toString());
1589:                                        }
1590:                                    }
1591:                                } catch (NumberFormatException e) {
1592:                                    length = 256;
1593:                                }
1594:                            } else {
1595:                                length = 256;
1596:                            }
1597:
1598:                            if (length < 1) {
1599:                                LOGGER
1600:                                        .warning("FeatureType did not specify string length; defaulted to 256");
1601:                                length = 256;
1602:                            } else if (length > MAX_ALLOWED_VALUE) {
1603:                                length = MAX_ALLOWED_VALUE;
1604:                            }
1605:                            typeName = typeName + "(" + length + ")";
1606:                        }
1607:
1608:                        if (!attributeType[i].isNillable()) {
1609:                            typeName = typeName + " NOT NULL";
1610:                        }
1611:
1612:                        //TODO review!!! Is toString() always OK???
1613:                        Object defaultValue = attributeType[i]
1614:                                .createDefaultValue();
1615:
1616:                        if (defaultValue != null) {
1617:                            typeName = typeName + " DEFAULT '"
1618:                                    + defaultValue.toString() + "'";
1619:                        }
1620:
1621:                        buf.append(" \"" + attributeType[i].getName() + "\" "
1622:                                + typeName + ",");
1623:
1624:                    } else {
1625:                        String msg;
1626:                        if (attributeType[i] == null) {
1627:                            msg = "AttributeType was null!";
1628:                        } else {
1629:                            msg = "Type '" + attributeType[i].getType()
1630:                                    + "' not supported!";
1631:                        }
1632:                        throw (new IOException(msg));
1633:                    }
1634:                }
1635:
1636:                return buf.deleteCharAt(buf.length() - 1);
1637:            }
1638:
1639:            /**
1640:             * DOCUMENT ME!
1641:             *
1642:             * @param table
1643:             * @param con
1644:             *
1645:             *
1646:             * @throws IOException DOCUMENT ME!
1647:             * @throws DataSourceException DOCUMENT ME!
1648:             */
1649:            private boolean tablePresent(String table, Connection con)
1650:                    throws IOException {
1651:                final int TABLE_NAME_COL = 3;
1652:                Connection conn = null;
1653:                //List list = new ArrayList();
1654:
1655:                try {
1656:                    conn = getConnection(Transaction.AUTO_COMMIT);
1657:
1658:                    DatabaseMetaData meta = conn.getMetaData();
1659:                    String[] tableType = { "TABLE" };
1660:                    ResultSet tables = meta.getTables(null, config
1661:                            .getDatabaseSchemaName(), "%", tableType);
1662:
1663:                    while (tables.next()) {
1664:                        String tableName = tables.getString(TABLE_NAME_COL);
1665:
1666:                        if (allowTable(tableName) && (tableName != null)
1667:                                && (tableName.equalsIgnoreCase(table))) {
1668:                            return (true);
1669:                        }
1670:                    }
1671:
1672:                    return false;
1673:                } catch (SQLException sqlException) {
1674:                    JDBCUtils
1675:                            .close(conn, Transaction.AUTO_COMMIT, sqlException);
1676:                    conn = null;
1677:
1678:                    String message = "Error querying database for list of tables:"
1679:                            + sqlException.getMessage();
1680:                    throw new DataSourceException(message, sqlException);
1681:                } finally {
1682:                    JDBCUtils.close(conn, Transaction.AUTO_COMMIT, null);
1683:                }
1684:            }
1685:
1686:            /**
1687:             * DOCUMENT ME!
1688:             *
1689:             * @param query
1690:             *
1691:             *
1692:             * @throws IOException DOCUMENT ME!
1693:             */
1694:            /**
1695:             * Get propertyNames in a safe manner.
1696:             * 
1697:             * <p>
1698:             * Method wil figure out names from the schema for query.getTypeName(), if
1699:             * query getPropertyNames() is <code>null</code>, or
1700:             * query.retrieveAllProperties is <code>true</code>.
1701:             * </p>
1702:             *
1703:             * @param query
1704:             *
1705:             *
1706:             * @throws IOException
1707:             */
1708:            /*
1709:            private String[] propertyNames(Query query) throws IOException {
1710:                String[] names = query.getPropertyNames();
1711:
1712:                if ((names == null) || query.retrieveAllProperties()) {
1713:                    String typeName = query.getTypeName();
1714:                    FeatureType schema = getSchema(typeName);
1715:
1716:                    names = new String[schema.getAttributeCount()];
1717:
1718:                    for (int i = 0; i < schema.getAttributeCount(); i++) {
1719:                        names[i] = schema.getAttributeType(i).getName();
1720:                    }
1721:                }
1722:
1723:                return names;
1724:            }
1725:             */
1726:            /**
1727:             * @see org.geotools.data.DataStore#updateSchema(java.lang.String,
1728:             *      org.geotools.feature.FeatureType)
1729:             */
1730:            public void updateSchema(String typeName, FeatureType featureType)
1731:                    throws IOException {
1732:                throw new IOException(
1733:                        "PostgisDataStore.updateSchema not yet implemented");
1734:                //TODO: implement updateSchema
1735:            }
1736:
1737:            /**
1738:             * Default implementation based on getFeatureReader and getFeatureWriter.
1739:             * 
1740:             * <p>
1741:             * We should be able to optimize this to only get the RowSet once
1742:             * </p>
1743:             *
1744:             * @see org.geotools.data.DataStore#getFeatureSource(java.lang.String)
1745:             */
1746:            public FeatureSource getFeatureSource(String typeName)
1747:                    throws IOException {
1748:                if (!typeHandler.getFIDMapper(typeName).isVolatile()
1749:                        || allowWriteOnVolatileFIDs) {
1750:                    LOGGER.fine("get Feature source called on " + typeName);
1751:
1752:                    if (OPTIMIZE_MODE == OPTIMIZE_SQL) {
1753:                        LOGGER.fine("returning pg feature locking");
1754:
1755:                        return createFeatureLockingInternal(this ,
1756:                                getSchema(typeName));
1757:                    }
1758:
1759:                    // default
1760:                    if (getLockingManager() != null) {
1761:                        // Use default JDBCFeatureLocking that delegates all locking
1762:                        // the getLockingManager
1763:                        LOGGER.fine("returning jdbc feature locking");
1764:
1765:                        return new JDBCFeatureLocking(this , getSchema(typeName));
1766:                    } else {
1767:                        LOGGER
1768:                                .fine("returning jdbc feature store (lock manager is null)");
1769:
1770:                        // subclass should provide a FeatureLocking implementation
1771:                        // but for now we will simply forgo all locking
1772:                        return new JDBCFeatureStore(this , getSchema(typeName));
1773:                    }
1774:                } else {
1775:                    return new JDBCFeatureSource(this , getSchema(typeName));
1776:                }
1777:            }
1778:
1779:            public PostgisFeatureLocking createFeatureLockingInternal(
1780:                    PostgisDataStore ds, FeatureType type) throws IOException {
1781:
1782:                return new PostgisFeatureLocking(ds, type);
1783:            }
1784:
1785:            /**
1786:             * DOCUMENT ME!
1787:             *
1788:             * @param fReader
1789:             * @param queryData
1790:             *
1791:             *
1792:             * @throws IOException DOCUMENT ME!
1793:             */
1794:            protected JDBCFeatureWriter createFeatureWriter(
1795:                    FeatureReader fReader, QueryData queryData)
1796:                    throws IOException {
1797:                PostgisSQLBuilder sqlBuilder = (PostgisSQLBuilder) getSqlBuilder(fReader
1798:                        .getFeatureType().getTypeName());
1799:                PostgisFeatureWriter postgisFeatureWriter = new PostgisFeatureWriter(
1800:                        fReader, queryData, WKBEnabled, byteaWKB, sqlBuilder);
1801:                return postgisFeatureWriter;
1802:            }
1803:
1804:            /**
1805:             * Retrieve a FeatureWriter over entire dataset.
1806:             * 
1807:             * <p>
1808:             * Quick notes: This FeatureWriter is often used to add new content, or
1809:             * perform summary calculations over the entire dataset.
1810:             * </p>
1811:             * 
1812:             * <p>
1813:             * Subclass may wish to implement an optimized featureWriter for these
1814:             * operations.
1815:             * </p>
1816:             * 
1817:             * <p>
1818:             * It should provide Feature for next() even when hasNext() is
1819:             * <code>false</code>.
1820:             * </p>
1821:             * 
1822:             * <p>
1823:             * Subclasses are responsible for checking with the lockingManger unless
1824:             * they are providing their own locking support.
1825:             * </p>
1826:             *
1827:             * @param typeName
1828:             * @param transaction
1829:             *
1830:             *
1831:             * @throws IOException
1832:             *
1833:             * @see org.geotools.data.DataStore#getFeatureWriter(java.lang.String,
1834:             *      boolean, org.geotools.data.Transaction)
1835:             */
1836:            public FeatureWriter getFeatureWriter(String typeName,
1837:                    Transaction transaction) throws IOException {
1838:                return getFeatureWriter(typeName, Filter.INCLUDE, transaction);
1839:            }
1840:
1841:            /*
1842:             * (non-Javadoc)
1843:             *
1844:             * @see org.geotools.data.DataStore#getFeatureWriterAppend(java.lang.String,
1845:             *      org.geotools.data.Transaction)
1846:             */
1847:
1848:            /**
1849:             * Retrieve a FeatureWriter for creating new content.
1850:             * 
1851:             * <p>
1852:             * Subclass may wish to implement an optimized featureWriter for this
1853:             * operation. One based on prepared statements is a possibility, as we do
1854:             * not require a ResultSet.
1855:             * </p>
1856:             * 
1857:             * <p>
1858:             * To allow new content the FeatureWriter should provide Feature for next()
1859:             * even when hasNext() is <code>false</code>.
1860:             * </p>
1861:             * 
1862:             * <p>
1863:             * Subclasses are responsible for checking with the lockingManger unless
1864:             * they are providing their own locking support.
1865:             * </p>
1866:             *
1867:             * @param typeName
1868:             * @param transaction
1869:             *
1870:             *
1871:             * @throws IOException
1872:             *
1873:             * @see org.geotools.data.DataStore#getFeatureWriter(java.lang.String,
1874:             *      boolean, org.geotools.data.Transaction)
1875:             */
1876:            public FeatureWriter getFeatureWriterAppend(String typeName,
1877:                    Transaction transaction) throws IOException {
1878:                FeatureWriter writer = getFeatureWriter(typeName,
1879:                        Filter.EXCLUDE, transaction);
1880:
1881:                while (writer.hasNext()) {
1882:                    writer.next(); // this would be a use for skip then :-)
1883:                }
1884:
1885:                return writer;
1886:            }
1887:
1888:            int getSRID(String typeName, String geomColName) throws IOException {
1889:                return typeHandler.getFeatureTypeInfo(typeName).getSRID(
1890:                        geomColName);
1891:            }
1892:
1893:            /**
1894:             * @see org.geotools.data.jdbc.JDBCDataStore#getGeometryAttributeIO(org.geotools.feature.AttributeType)
1895:             */
1896:            protected AttributeIO getGeometryAttributeIO(AttributeType type,
1897:                    QueryData queryData) {
1898:                // grab the crs if available
1899:                GeometryAttributeType geometryType = (GeometryAttributeType) type;
1900:                CoordinateReferenceSystem crs = null;
1901:                if (geometryType != null)
1902:                    crs = geometryType.getCoordinateSystem();
1903:
1904:                Hints hints = queryData != null ? queryData.getHints()
1905:                        : GeoTools.getDefaultHints();
1906:                int D = (crs == null || Boolean.TRUE.equals(queryData
1907:                        .getHints().get(Hints.FEATURE_2D))) ? 2 : crs
1908:                        .getCoordinateSystem().getDimension();
1909:                if (WKBEnabled) {
1910:                    return new PgWKBAttributeIO(isByteaEnabled(), hints);
1911:                } else {
1912:                    if (D == 3) {
1913:                        return new EWKTAttributeIO();
1914:                    } else {
1915:                        return new WKTAttributeIO();
1916:                    }
1917:                }
1918:            }
1919:
1920:            protected int getResultSetType(boolean forWrite) {
1921:                return ResultSet.TYPE_FORWARD_ONLY;
1922:            }
1923:
1924:            protected int getConcurrency(boolean forWrite) {
1925:                return ResultSet.CONCUR_READ_ONLY;
1926:            }
1927:
1928:            /**
1929:             * Returns true if the WKB format is used to transfer geometries, false
1930:             * otherwise
1931:             *
1932:             */
1933:            public boolean isWKBEnabled() {
1934:                return WKBEnabled;
1935:            }
1936:
1937:            /**
1938:             * If turned on, WKB will be used to transfer geometry data instead of  WKT
1939:             *
1940:             * @param enabled
1941:             */
1942:            public void setWKBEnabled(boolean enabled) {
1943:                WKBEnabled = enabled;
1944:            }
1945:
1946:            /**
1947:             * Sets this postgis instance to use a less strict but faster bounding box
1948:             * query.  Setting this to <tt>true</tt> will have PostGIS issue bounding
1949:             * box queries against the envelope of the geometry, so some may be
1950:             * <i>slighty</i> wrong, but will perform much faster.  The  intersects
1951:             * function can still be used to obtain the exact query.
1952:             *
1953:             * @param isLooseBbox <tt>true</tt> if this should have a loose Bbox.
1954:             */
1955:            public void setLooseBbox(boolean isLooseBbox) {
1956:                this .looseBbox = isLooseBbox;
1957:            }
1958:
1959:            /**
1960:             * Whether the bounding boxes issued against this postgis datastore are on
1961:             * the envelope of the geometry or the actual geometry.
1962:             *
1963:             * @return <tt>true</tt> if the bounding box is 'loose', against the
1964:             *         envelope instead of the actual geometry.
1965:             */
1966:            public boolean isLooseBbox() {
1967:                return looseBbox;
1968:            }
1969:
1970:            /**
1971:             * Returns true if the data store is using the bytea function to fasten WKB
1972:             * data transfer, false otherwise
1973:             *
1974:             */
1975:            public boolean isByteaEnabled() {
1976:                return byteaEnabled;
1977:            }
1978:
1979:            public void setByteaWKB(boolean byteaWKB) {
1980:                this .byteaWKB = byteaWKB;
1981:            }
1982:
1983:            public boolean isByteaWKB() {
1984:                return byteaWKB;
1985:            }
1986:
1987:            /**
1988:             * Enables the use of bytea function for WKB data transfer (will improve
1989:             * performance).  Note this function need not be set by the programmer, as
1990:             * the datastore will use it to optimize performance whenever it can (when
1991:             * postGIS is 0.7.2 or later)
1992:             *
1993:             * @param byteaEnabled
1994:             */
1995:            public void setByteaEnabled(boolean byteaEnabled) {
1996:                this .byteaEnabled = byteaEnabled;
1997:            }
1998:
1999:            /**
2000:             * Enables the use of the 'estimated_extent' function for bounds computation.
2001:             * <p>
2002:             * Beware that this function is an approximation and is dependent on the 
2003:             * degree to with the data in the actual bounds follows a uniform distribution.
2004:             * </p>
2005:             */
2006:            public void setEstimatedExtent(boolean estimatedExtent) {
2007:                this .estimatedExtent = estimatedExtent;
2008:
2009:                //also make sure optimize mode is set properly
2010:                if (estimatedExtent) {
2011:                    LOGGER.info("Setting OPTIMIZE_MODE to 'SQL'");
2012:                    setOptimizeMode(OPTIMIZE_SQL);
2013:                }
2014:            }
2015:
2016:            /**
2017:             * @see {@link #setEstimatedExtent(boolean)}.
2018:             */
2019:            public boolean isEstimatedExtent() {
2020:                return estimatedExtent;
2021:            }
2022:
2023:            /**
2024:             * Sets the optimization mode for the datastore.
2025:             * 
2026:             * @param mode One of {@link #OPTIMIZE_SAFE},{@link #OPTIMIZE_SQL}.
2027:             */
2028:            public void setOptimizeMode(int mode) {
2029:                OPTIMIZE_MODE = mode;
2030:            }
2031:
2032:            public int getOptimizeMode() {
2033:                return OPTIMIZE_MODE;
2034:            }
2035:
2036:            public FeatureType getSchema(String arg0) throws IOException {
2037:                return super .getSchema(arg0);
2038:            }
2039:
2040:            /**
2041:             * Obtains the postgis datastore connection pool.
2042:             *  
2043:             * @return ConnectionPool
2044:             */
2045:            public DataSource getDataSource() {
2046:                return dataSource;
2047:            }
2048:
2049:            /**
2050:             * Obtains database specific information, such as version, supported
2051:             * functions, etc.
2052:             */
2053:            public PostgisDBInfo getDBInfo() {
2054:                if (dbInfo == null) {
2055:                    Connection conn;
2056:                    try {
2057:                        conn = getConnection(Transaction.AUTO_COMMIT);
2058:                        dbInfo = new PostgisDBInfo(conn);
2059:                    } catch (IOException e1) {
2060:                        LOGGER.log(Level.SEVERE,
2061:                                "Could not obtain DBInfo object", e1);
2062:                    }
2063:                }
2064:                return dbInfo;
2065:            }
2066:
2067:            /**
2068:             * The hints supported by this datastore depending on the configuration
2069:             */
2070:            private static final Set BASE_HINTS = Collections
2071:                    .unmodifiableSet(new HashSet(Arrays
2072:                            .asList(new Object[] { Hints.FEATURE_DETACHED })));
2073:            private static final Set WKB_HINTS = Collections
2074:                    .unmodifiableSet(new HashSet(Arrays.asList(new Object[] {
2075:                            Hints.FEATURE_DETACHED,
2076:                            Hints.JTS_COORDINATE_SEQUENCE_FACTORY,
2077:                            Hints.JTS_GEOMETRY_FACTORY })));
2078:
2079:            public Set getSupportedHints() {
2080:                if (isWKBEnabled()) {
2081:                    return WKB_HINTS;
2082:                } else {
2083:                    return BASE_HINTS;
2084:                }
2085:            }
2086:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.