[Schmitzm-commits] r305 - trunk/src/org/geotools/data/shapefile

scm-commit@wald.intevation.org scm-commit at wald.intevation.org
Wed Aug 19 14:06:23 CEST 2009


Author: alfonx
Date: 2009-08-19 14:06:21 +0200 (Wed, 19 Aug 2009)
New Revision: 305

Added:
   trunk/src/org/geotools/data/shapefile/ShapefileDataStoreFactory.java
Log:
* Schmitzm/GP/AV-BUgFIX: ShapefileDataStoreFactory has been patched, so that offfline atlases can be started from folders that contain special characters. 
The class is now part of schmitzm.

Added: trunk/src/org/geotools/data/shapefile/ShapefileDataStoreFactory.java
===================================================================
--- trunk/src/org/geotools/data/shapefile/ShapefileDataStoreFactory.java	2009-08-19 12:03:12 UTC (rev 304)
+++ trunk/src/org/geotools/data/shapefile/ShapefileDataStoreFactory.java	2009-08-19 12:06:21 UTC (rev 305)
@@ -0,0 +1,636 @@
+/**
+ * THIS FILE IS PATCHED yb Stefan A. Krüger
+ * 
+ * See bug http://wald.intevation.org/tracker/index.php?func=detail&aid=1006&group_id=37&atid=260
+ * 
+ * The use of US-ASCII makes it impossible to have DISK versions of the 
+ * atlas start from a directory containing special characters.
+ */
+
+/*
+ *    GeoTools - OpenSource mapping toolkit
+ *    http://geotools.org
+ *    (C) 2002-2006, Geotools Project Managment Committee (PMC)
+ *
+ *    This library is free software; you can redistribute it and/or
+ *    modify it under the terms of the GNU Lesser General Public
+ *    License as published by the Free Software Foundation; either
+ *    version 2.1 of the License, or (at your option) any later version.
+ *
+ *    This library is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *    Lesser General Public License for more details.
+ */
+package org.geotools.data.shapefile;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.geotools.data.DataSourceException;
+import org.geotools.data.DataStore;
+import org.geotools.data.FileDataStoreFactorySpi;
+import org.geotools.data.shapefile.indexed.IndexedShapefileDataStore;
+
+import com.vividsolutions.jts.geom.Geometry;
+
+/**
+ * Implementation of the DataStore service provider interface for Shapefiles.
+ * <p>
+ * The specific implementation of ShapefileDataStore created by this class is
+ * not specified. For more information on the connection parameters please
+ * review the following public Param constants.
+ * <ul>
+ * <li>{@link URLP}
+ * <li>{@link NAMESPACEP}
+ * <li>{@link CREATE_SPATIAL_INDEX}
+ * <li>{@link MEMORY_MAPPED}
+ * <li>{@link DBFCHARSET}
+ * </ul>
+ * 
+ * @author Chris Holmes, TOPP
+ * @source $URL:
+ *         http://svn.geotools.org/geotools/trunk/gt/modules/plugin/shapefile
+ *         /src/main/java/org/geotools/data/shapefile/ShapefileDataStoreFactory.
+ *         java $
+ * @version $Id: ShapefileDataStoreFactory.java 29603 2008-03-11 22:49:05Z
+ *          jgarnett $
+ */
+public class ShapefileDataStoreFactory implements FileDataStoreFactorySpi {
+
+	protected static final Logger LOGGER = org.geotools.util.logging.Logging
+			.getLogger("org.geotools.data.shapefile");
+	/**
+	 * url to the .shp file.
+	 */
+	public static final Param URLP = new Param("url", URL.class,
+			"url to a .shp file");
+
+	/**
+	 * Optional - uri of the FeatureType's namespace
+	 */
+	public static final Param NAMESPACEP = new Param("namespace", URI.class,
+			"uri to a the namespace", false); // not required
+
+	/**
+	 * Optional - enable/disable the use of memory-mapped io
+	 */
+	public static final Param MEMORY_MAPPED = new Param("memory mapped buffer",
+			Boolean.class, "enable/disable the use of memory-mapped io", false);
+
+	/**
+	 * Optional - Enable/disable the automatic creation of spatial index
+	 */
+	public static final Param CREATE_SPATIAL_INDEX = new Param(
+			"create spatial index", Boolean.class,
+			"enable/disable the automatic creation of spatial index", false);
+
+	/**
+	 * Optional - character used to decode strings from the DBF file
+	 */
+	public static final Param DBFCHARSET = new Param("charset", Charset.class,
+			"character used to decode strings from the DBF file", false,
+			Charset.forName("ISO-8859-1")) {
+		/*
+		 * This is an example of a non simple Param type where a custom parse
+		 * method is required.
+		 * 
+		 * @see
+		 * org.geotools.data.DataStoreFactorySpi.Param#parse(java.lang.String)
+		 */
+		public Object parse(String text) throws IOException {
+			return Charset.forName(text);
+		}
+
+		public String text(Object value) {
+			return ((Charset) value).name();
+		}
+	};
+
+	/**
+	 * It is the users responsibility to prevent the creation of duplicate
+	 * DataStore instances (they should hold the results in a Map, Repository or
+	 * Catalog as described in the documenation).
+	 * <p>
+	 * However in the real world we are patching the problem, this Map is used
+	 * to store previously created DataStores so we can hand them out again.
+	 * There are two problems with this approach:
+	 * <ul>
+	 * <li>The ShapefileDataStoreFactory may be created more than once with
+	 * different Hints, for example when used to read a shapefile into alternate
+	 * Feature or Geometry representations.
+	 * <li>It flys in the face of a DataStore lifecycle, the JDBC DataStore
+	 * implementations have taken to support a dispose operation.
+	 * </ul>
+	 * As such I expect this "feature" to disappear, I am keeping it now only so
+	 * we can produce good warnings.
+	 */
+	private static Map liveStores = Collections.synchronizedMap(new HashMap());
+
+	/**
+	 * Takes a map of parameters which describes how to access a DataStore and
+	 * determines if it can be read by the ShapefileDataStore or
+	 * IndexedShapefileDataStore implementations.
+	 * 
+	 * @param params
+	 *            A map of parameters describing the location of a datastore.
+	 *            Files should be pointed to by a 'url' param.
+	 * 
+	 * @return true iff params contains a url param which points to a file
+	 *         ending in shp
+	 */
+	public boolean canProcess(Map params) {
+		boolean accept = false;
+		if (params.containsKey(URLP.key)) {
+			try {
+				URL url = (URL) URLP.lookUp(params);
+				accept = canProcess(url);
+			} catch (IOException ioe) {
+				// yes, I am eating this - since it is my job to return a
+				// true/false
+			}
+		}
+		return accept;
+	}
+
+	/**
+	 * Returns an instance of DataStore iff the resource pointed to the Map of
+	 * paramers can be handled as a shapefile.
+	 * <p>
+	 * The specific implementation of ShapefileDataStore returned is not
+	 * specified, and depends on the parameters given. For more information
+	 * please review the public static Param instances available for this class.
+	 * </p>
+	 * <ul>
+	 * <li>{@link URLP}
+	 * <li>{@link NAMESPACEP}
+	 * <li>{@link CREATE_SPATIAL_INDEX}
+	 * <li>{@link MEMORY_MAPPED}
+	 * <li>{@link DBFCHARSET}
+	 * </ul>
+	 * 
+	 * @param params
+	 *            A param list with information on the location of a restore.
+	 *            For shapefiles this should contain a 'url' param which points
+	 *            to a file which ends in shp.
+	 * 
+	 * @return DataStore A ShapefileDatastore
+	 * @throws IOException
+	 *             If a connection error (such as the file not existing occurs)
+	 * @throws DataSourceException
+	 *             Thrown if the datastore which is created cannot be attached
+	 *             to the restore specified in params.
+	 */
+	public DataStore createDataStore(Map params) throws IOException {
+		DataStore ds = null;
+		synchronized (liveStores) {
+			if (!liveStores.containsKey(params)) {
+				URL url = null;
+				try {
+					ds = createDataStoreInstance(params);
+					liveStores.put(params, ds);
+				} catch (MalformedURLException mue) {
+					throw new DataSourceException(
+							"Unable to attatch datastore to " + url, mue);
+				}
+			} else {
+				ds = (DataStore) liveStores.get(params);
+			}
+		}
+		return ds;
+	}
+
+	/**
+	 * Creates a new DataStore - for a file that does not exist yet.
+	 * <p>
+	 * This method has different logic than createDataStore. It is willing to be
+	 * memory mapped, and generate an index for a local file that does not exist
+	 * yet.
+	 * 
+	 */
+	public DataStore createNewDataStore(Map params) throws IOException {
+		DataStore ds = null;
+		synchronized (liveStores) {
+			if (!liveStores.containsKey(params)) {
+				URL url = null;
+				try {
+					ds = createNewShapefile(params);
+					liveStores.put(params, ds);
+				} catch (MalformedURLException mue) {
+					throw new DataSourceException(
+							"Unable to attatch datastore to " + url, mue);
+				}
+			} else {
+				ds = (DataStore) liveStores.get(params);
+			}
+		}
+		return ds;
+	}
+
+	/**
+	 * Will create a new shapefile baed on the provided parameters.
+	 * 
+	 * @param params
+	 *            Map of parameters
+	 * @throws IOException
+	 *             If the filename is not valid.
+	 * @throws UnsupportedOperationException
+	 */
+	DataStore createNewShapefile(Map params) throws IOException {
+		URL url = (URL) URLP.lookUp(params);
+		Boolean isMemoryMapped = (Boolean) MEMORY_MAPPED.lookUp(params);
+		URI namespace = (URI) NAMESPACEP.lookUp(params);
+		Charset dbfCharset = (Charset) DBFCHARSET.lookUp(params);
+		Boolean isCreateSpatialIndex = (Boolean) CREATE_SPATIAL_INDEX
+				.lookUp(params);
+
+		if (isCreateSpatialIndex == null) {
+			// should not be needed as default is TRUE
+			assert (true);
+			isCreateSpatialIndex = Boolean.TRUE;
+		}
+		if (dbfCharset == null) {
+			assert (true);
+			// this should not happen as Charset.forName("ISO-8859-1") was used
+			// as the param default?
+			dbfCharset = Charset.forName("ISO-8859-1");
+		}
+		if (isMemoryMapped == null) {
+			assert (true);
+			// this should not happen as false was the default
+			isMemoryMapped = Boolean.FALSE;
+		}
+		URL shpUrl = toShpURL(url);
+
+		boolean isLocal = shpUrl.getProtocol().equalsIgnoreCase("file");
+		if (!isLocal || new File(shpUrl.getFile()).exists()) {
+			LOGGER.warning("File already exists: " + shpUrl);
+		}
+		boolean useMemoryMappedBuffer = isLocal
+				&& isMemoryMapped.booleanValue();
+		boolean createIndex = isCreateSpatialIndex.booleanValue() && isLocal;
+
+		try {
+			if (createIndex) {
+				return new IndexedShapefileDataStore(url, namespace,
+						useMemoryMappedBuffer, true,
+						IndexedShapefileDataStore.TREE_QIX, dbfCharset);
+			} else {
+				return new ShapefileDataStore(url, namespace,
+						useMemoryMappedBuffer, dbfCharset);
+			}
+		} catch (MalformedURLException mue) {
+			throw new DataSourceException(
+					"Url for shapefile malformed: " + url, mue);
+		}
+	}
+
+	/**
+	 * Will create the correct implementation of ShapefileDataStore for the
+	 * provided parameters.
+	 * 
+	 * @param params
+	 *            Map of parameters
+	 * @throws IOException
+	 *             If the specified shapefle could not be accessed
+	 * @throws UnsupportedOperationException
+	 */
+	DataStore createDataStoreInstance(Map params) throws IOException {
+		URL url = (URL) URLP.lookUp(params);
+		Boolean isMemoryMapped = (Boolean) MEMORY_MAPPED.lookUp(params);
+		URI namespace = (URI) NAMESPACEP.lookUp(params);
+		Charset dbfCharset = (Charset) DBFCHARSET.lookUp(params);
+		Boolean isCreateSpatialIndex = (Boolean) CREATE_SPATIAL_INDEX
+				.lookUp(params);
+
+		if (isCreateSpatialIndex == null) {
+			// should not be needed as default is TRUE
+			isCreateSpatialIndex = Boolean.TRUE;
+		}
+		if (dbfCharset == null) {
+			// this should not happen as Charset.forName("ISO-8859-1") was used
+			// as the param default?
+			dbfCharset = Charset.forName("ISO-8859-1");
+		}
+		if (isMemoryMapped == null) {
+			isMemoryMapped = Boolean.FALSE;
+		}
+
+		URL shpUrl = toShpURL(url);
+		boolean isLocal = shpUrl.getProtocol().equalsIgnoreCase("file");
+		if (isLocal && !new File(shpUrl.getFile()).exists()) {
+			throw new FileNotFoundException("Shapefile not found:"
+					+ shpUrl.getFile());
+		}
+		boolean useMemoryMappedBuffer = isLocal
+				&& new File(shpUrl.getFile()).exists()
+				&& isMemoryMapped.booleanValue();
+		boolean createIndex = isCreateSpatialIndex.booleanValue() && isLocal;
+		byte treeIndex = IndexedShapefileDataStore.TREE_NONE;
+		if (isLocal) {
+			if (createIndex) {
+				treeIndex = IndexedShapefileDataStore.TREE_QIX; // default
+			} else {
+				// lets check and see if any index file is avaialble
+				if (new File(toQixURL(url).getFile()).exists()) {
+					treeIndex = IndexedShapefileDataStore.TREE_QIX;
+				} else if (new File(toFixURL(url).getFile()).exists()) {
+					treeIndex = IndexedShapefileDataStore.TREE_GRX;
+				}
+			}
+		}
+
+		try {
+			if (createIndex) {
+				return new IndexedShapefileDataStore(url, namespace,
+						useMemoryMappedBuffer, createIndex,
+						IndexedShapefileDataStore.TREE_QIX, dbfCharset);
+			} else if (treeIndex != IndexedShapefileDataStore.TREE_NONE) {
+				return new IndexedShapefileDataStore(url, namespace,
+						useMemoryMappedBuffer, false, treeIndex, dbfCharset);
+			} else {
+				return new ShapefileDataStore(url, namespace,
+						useMemoryMappedBuffer, dbfCharset);
+			}
+		} catch (MalformedURLException mue) {
+			throw new DataSourceException(
+					"Url for shapefile malformed: " + url, mue);
+		}
+	}
+
+	public String getDisplayName() {
+		return "Shapefile";
+	}
+
+	/**
+	 * Describes the type of data the datastore returned by this factory works
+	 * with.
+	 * 
+	 * @return String a human readable description of the type of restore
+	 *         supported by this datastore.
+	 */
+	public String getDescription() {
+		return "ESRI(tm) Shapefiles (*.shp)";
+	}
+
+	// public DataSourceMetadataEnity createMetadata( Map params )
+	// throws IOException {
+	//        
+	// URL url = (URL) URLP.lookUp(params);
+	// Boolean mm = (Boolean) MEMORY_MAPPED.lookUp(params);
+	//        
+	// String server;
+	// String name;
+	// if( url.getProtocol().equals("file")){
+	// server = "localhost";
+	// name = url.getPath();
+	// }
+	// else {
+	// server = url.getHost()+":"+url.getPort();
+	// name = url.getFile();
+	// }
+	// return new DataSourceMetadataEnity( server, name,
+	// "Shapefile access for "+url );
+	// }
+	/**
+	 * Test to see if this datastore is available, if it has all the appropriate
+	 * libraries to construct a datastore.
+	 * 
+	 * This datastore just checks for the ShapefileDataStore,
+	 * IndexedShapefileDataStore and Geometry implementations.
+	 * 
+	 * @return <tt>true</tt> if and only if this factory is available to create
+	 *         DataStores.
+	 */
+	public boolean isAvailable() {
+		try {
+			ShapefileDataStore.class.getName();
+			IndexedShapefileDataStore.class.getName();
+			Geometry.class.getName();
+		} catch (Exception e) {
+			return false;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Describe parameters.
+	 * 
+	 * 
+	 * @see org.geotools.data.DataStoreFactorySpi#getParametersInfo()
+	 */
+	public Param[] getParametersInfo() {
+		return new Param[] { URLP, NAMESPACEP, CREATE_SPATIAL_INDEX,
+				DBFCHARSET, MEMORY_MAPPED };
+	}
+
+	/**
+	 * @see org.geotools.data.dir.FileDataStoreFactorySpi#getFileExtensions()
+	 */
+	public String[] getFileExtensions() {
+		return new String[] { ".shp", };
+	}
+
+	/**
+	 * @see org.geotools.data.dir.FileDataStoreFactorySpi#canProcess(java.net.URL)
+	 */
+	public boolean canProcess(URL f) {
+		return f.getFile().toUpperCase().endsWith("SHP");
+	}
+
+	/**
+	 * We may need to create a new datastore if the provided file does not
+	 * exist.
+	 * 
+	 * @see org.geotools.data.dir.FileDataStoreFactorySpi#createDataStore(java.net.URL)
+	 */
+	public DataStore createDataStore(URL url) throws IOException {
+		Map params = new HashMap();
+		params.put(URLP.key, url);
+
+		boolean isLocal = url.getProtocol().equalsIgnoreCase("file");
+		if (isLocal && !(new File(url.getFile()).exists())) {
+			return createNewDataStore(params);
+		} else {
+			return createDataStore(params);
+		}
+	}
+
+	/**
+	 * @see org.geotools.data.dir.FileDataStoreFactorySpi#createDataStore(java.net.URL)
+	 */
+	public DataStore createDataStore(URL url, boolean memorymapped)
+			throws IOException {
+		Map params = new HashMap();
+		params.put(URLP.key, url);
+		params.put(MEMORY_MAPPED.key, new Boolean(memorymapped));
+		return createDataStore(params);
+	}
+
+	/**
+	 * @see org.geotools.data.dir.FileDataStoreFactorySpi#getTypeName(java.net.URL)
+	 */
+	public String getTypeName(URL url) throws IOException {
+		DataStore ds = createDataStore(url);
+		String[] names = ds.getTypeNames(); // should be exactly one
+		return ((names == null || names.length == 0) ? null : names[0]);
+	}
+
+	/**
+	 * Returns the implementation hints. The default implementation returns an
+	 * empty map.
+	 * <p>
+	 * When we have FeatureFactory, GeometryFactory and so on hooked up this map
+	 * will return Hints we paid attention too when we were constructed.
+	 * 
+	 * @return An empty map.
+	 */
+	public Map getImplementationHints() {
+		return Collections.EMPTY_MAP;
+	}
+
+	/**
+	 * Convert a URL to a String that is suitable for manipulation of its
+	 * extension (generally the last three characters of the file).
+	 * 
+	 * This uses URL.toExternalForm() in order to preserve valuable information
+	 * about the URL's Authority.
+	 * 
+	 * @param url
+	 *            the url to convert to a String. Must not be null.
+	 * @return a String representation of the URL
+	 * @throws MalformedURLException
+	 *             if the url is invalid
+	 */
+	public static String toFilename(URL url) throws MalformedURLException {
+		if (url == null) {
+			throw new NullPointerException("A shapefile URL is required");
+		}
+		try {
+			/*
+			 * The use of the four parameter URL constructor is discouraged as
+			 * it throws away valuable information about the authority that the
+			 * URL is hosted by. It is better if we use the full URL String to
+			 * reconstruct the other URLs.
+			 */
+
+			
+			System.err.println("Path in ShapefileDataStorefactory is applied!!!");
+			/**
+			 * TODO ACHTUNG PATCH!!!
+			 * 
+			 * See bug
+			 * http://wald.intevation.org/tracker/index.php?func=detail&aid
+			 * =1006&group_id=37&atid=260
+			 * 
+			 * The use of US-ASCII makes it impossible to have DISK versions of
+			 * the atlas start from a directory containing special characters.
+			 */
+
+			return java.net.URLDecoder.decode(url.toExternalForm(), Charset
+					.defaultCharset().toString());
+			// return java.net.URLDecoder.decode(url.toExternalForm(),
+			// "US-ASCII");
+
+		} catch (java.io.UnsupportedEncodingException use) {
+			throw new java.net.MalformedURLException("Unable to decode " + url
+					+ " cause " + use.getMessage());
+		}
+	}
+
+	/**
+	 * Convert the URL to a string that includes the provided extension (this
+	 * method respects the case of the original file extension.
+	 * 
+	 * This implementation calls toFileName( URL ) and then knocks off the known
+	 * extensions (ie "shp", "dbf", "shx") if present and makes use of the
+	 * provided extension.
+	 * 
+	 * 
+	 * @param url
+	 * @param ext
+	 * @return
+	 */
+	public static String toFilename(URL url, String ext)
+			throws java.net.MalformedURLException {
+		String filename = toFilename(url);
+		if (ext != null && !ext.startsWith(".")) {
+			ext += ".";
+		}
+
+		if (filename.endsWith(".shp") || filename.endsWith(".dbf")
+				|| filename.endsWith(".shx")) {
+			filename = filename.substring(0, filename.length() - 4);
+			if (ext != null) {
+				filename += ext.toLowerCase();
+			}
+		} else if (filename.endsWith(".SHP") || filename.endsWith(".DBF")
+				|| filename.endsWith(".SHX")) {
+			filename = filename.substring(0, filename.length() - 4);
+			if (ext != null) {
+				filename += ext.toUpperCase();
+			}
+		} else if (ext != null) {
+			filename += ext.toLowerCase();
+		}
+		return filename;
+	}
+
+	/** Figure out the URL for the "fix" file */
+	public static URL toFixURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".fix");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "qix" file */
+	public static URL toQixURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".qix");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "grx" file */
+	public static URL toGrxURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".grx");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "prj" file */
+	public static URL toXmlURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".shp.xml");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "prj" file */
+	public static URL toPrjURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".prj");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "shx" file */
+	public static URL toShxURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".shx");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "dbf" file */
+	public static URL toDbfURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".dbf");
+		return new URL(filename);
+	}
+
+	/** Figure out the URL for the "shp" file */
+	public static URL toShpURL(URL url) throws java.net.MalformedURLException {
+		String filename = toFilename(url, ".shp");
+		return new URL(filename);
+	}
+}


Property changes on: trunk/src/org/geotools/data/shapefile/ShapefileDataStoreFactory.java
___________________________________________________________________
Name: svn:mime-type
   + text/plain
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native



More information about the Schmitzm-commits mailing list