/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2008, Open Source Geospatial Foundation (OSGeo) * * 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; * version 2.1 of the License. * * 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.indexed.attribute; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.io.RandomAccessFile; import java.net.MalformedURLException; import java.net.URL; import java.nio.channels.FileChannel; import java.text.DecimalFormat; import org.geotools.data.DataUtilities; /** * <P> * Class to manage a summary for attribute indexes. * </P> * <P> * It's just map that associates attribute name with index file. Using the name * instead of its position on dbf permits to abstract from position, so * attribute order can change with no influence on indexes. * </P> * * @author Manuele Ventoruzzo * * * @source $URL$ */ public class AttributeIndexSummary { public static final String SUMMARY_EXT = ".ids"; public static final String INDEX_EXT = ".i"; public static final DecimalFormat SUFFIX = new DecimalFormat("00"); public static final int DEFAULT_CACHE_SIZE = 134217728; // 128MB /** Url of summary file */ protected URL summaryURL = null; protected String filename = null; protected int cacheSize; /** * Creates an IndexSummary * * @param shapefileUrl * url of shapefile for wich indexes are related to */ public AttributeIndexSummary(URL shpURL) throws MalformedURLException, IOException { this(shpURL,DEFAULT_CACHE_SIZE); } /** * Creates an IndexSummary * * @param shapefileUrl * url of shapefile for wich indexes are related to * @param cacheSize * maximum amount of memory to be used for index creation */ public AttributeIndexSummary(URL shpURL, int cacheSize) throws MalformedURLException, IOException { try { filename = java.net.URLDecoder.decode(shpURL.toString(), "US-ASCII"); filename = filename.substring(0, filename.lastIndexOf(".shp")); } catch (java.io.UnsupportedEncodingException use) { throw new java.net.MalformedURLException("Unable to decode " + shpURL + " cause " + use.getMessage()); } int indexslash = filename.lastIndexOf(File.pathSeparator); if (indexslash == -1) { indexslash = 0; } summaryURL = new URL(filename + SUMMARY_EXT); // create summary file (if it doesn't exist) empty DataUtilities.urlToFile(summaryURL).createNewFile(); this.cacheSize = cacheSize; } /** * Index creation. Adds attribute name to summary and invokes attribute * index creation. * * @param attribute */ public void createIndex(String attribute) throws FileNotFoundException, IOException { URL url = getIndexURL(attribute); if (url==null) { addIndex(attribute); url = getIndexURL(attribute); } synchronized (this) { // now invokes AttributeIndexWriter to create the index File f = DataUtilities.urlToFile(url); if (f.exists()) { if (!f.delete()) throw new IOException("File index cannot be deleted. Probably it's locked."); } RandomAccessFile raf = new RandomAccessFile(f, "rw"); FileChannel writeChannel = raf.getChannel(); AttributeIndexWriter indexWriter = new AttributeIndexWriter(attribute, writeChannel, getDBFChannel(),cacheSize); indexWriter.buildIndex(); } } /** * Returns the index for specified attribute * * @param attribute * attribute to search for * @return Index reader or null if such attribute doesn't have an index */ public AttributeIndexReader getIndex(String attribute) throws FileNotFoundException, IOException { URL url = getIndexURL(attribute); if (url == null) return null; File f = DataUtilities.urlToFile(url); if (!f.exists()) return null; RandomAccessFile raf = new RandomAccessFile(DataUtilities.urlToFile(url), "r"); return new AttributeIndexReader(attribute, raf.getChannel()); } public boolean hasIndex(String attribute) throws FileNotFoundException, IOException { URL url = getIndexURL(attribute); if (url == null) return false; return DataUtilities.urlToFile(url).exists(); } /** * Tests whether an index for this attribute exists. * * @param attribute * @return */ public boolean existsIndex(String attribute) throws FileNotFoundException, IOException { URL url = getIndexURL(attribute); if (url == null) return false; File f = DataUtilities.urlToFile(url); return f.exists(); } /** * Returns the index URL for specified attribute * * @param attribute * attribute to search for * @return URL to index file or null if such attribute doesn't have an index */ protected URL getIndexURL(String attribute) throws FileNotFoundException, IOException { File f = DataUtilities.urlToFile(summaryURL); BufferedReader in = new BufferedReader(new FileReader(f)); int count = 0; while (in.ready()) { String s = in.readLine(); count++; if (s.equals(attribute)) { // index name: filename + number of row in index summary + // extension return new URL(filename+INDEX_EXT+SUFFIX.format(count)); } } return null; // index not found } protected synchronized void addIndex(String attribute) throws FileNotFoundException, IOException { File f = DataUtilities.urlToFile(summaryURL); PrintWriter out = new PrintWriter(new FileWriter(f,true)); out.println(attribute); out.flush(); out.close(); } protected FileChannel getDBFChannel() throws FileNotFoundException, MalformedURLException { URL url = new URL(filename+".dbf"); File f = DataUtilities.urlToFile(url); if (!f.exists()) url = new URL(filename+".DBF"); f = DataUtilities.urlToFile(url); if (!f.exists()) throw new FileNotFoundException("DBF file not found"); RandomAccessFile raf = new RandomAccessFile(f, "r"); return raf.getChannel(); } }