package com.revolsys.gis.esri.gdb.file; import java.io.File; import java.net.URI; import java.nio.file.Path; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicInteger; import java.util.regex.Pattern; import com.revolsys.collection.map.Maps; import com.revolsys.io.FileUtil; import com.revolsys.io.IoFactoryRegistry; import com.revolsys.io.file.Paths; import com.revolsys.record.io.FileRecordStoreFactory; import com.revolsys.record.io.RecordStoreRecordAndGeometryWriterFactory; import com.revolsys.record.schema.RecordStore; import com.revolsys.util.Property; import com.revolsys.util.UrlUtil; public class FileGdbRecordStoreFactory implements FileRecordStoreFactory { public static final String DESCRIPTION = "ESRI File Geodatabase"; private static final Map<String, AtomicInteger> COUNTS = new HashMap<>(); private static final List<String> FILE_NAME_EXTENSIONS = Arrays.asList("gdb"); private static final Map<String, FileGdbRecordStore> RECORD_STORES = new HashMap<>(); private static final List<Pattern> URL_PATTERNS = Arrays.asList( Pattern.compile("[^(file:)].*.gdb/?"), Pattern.compile("file:(/(//)?)?.*.gdb/?"), Pattern.compile("folderconnection:/(//)?.*.gdb/?")); static { final RecordStoreRecordAndGeometryWriterFactory writerFactory = new RecordStoreRecordAndGeometryWriterFactory( DESCRIPTION, "application/x-esri-gdb", true, true, "gdb"); IoFactoryRegistry.addFactory(writerFactory); } public static FileGdbRecordStore newRecordStore(final File file) { if (file == null) { return null; } else { synchronized (COUNTS) { final String fileName = FileUtil.getCanonicalPath(file); final AtomicInteger count = Maps.get(COUNTS, fileName, new AtomicInteger()); count.incrementAndGet(); FileGdbRecordStore recordStore = RECORD_STORES.get(fileName); if (recordStore == null || recordStore.isClosed()) { recordStore = new FileGdbRecordStore(file); RECORD_STORES.put(fileName, recordStore); } return recordStore; } } } public static FileGdbRecordStore newRecordStore(final Path path) { if (path == null) { return null; } else { final File file = path.toFile(); return newRecordStore(file); } } /** * Release the record store for the file. Decrements the count of references to the file. If * the count <=0 then the record store will be removed. * * @param fileName * @return True if the record store has no references and was released. False otherwise */ static boolean release(final FileGdbRecordStore recordStore) { synchronized (COUNTS) { final String fileName = recordStore.getFileName(); final FileGdbRecordStore currentRecordStore = RECORD_STORES.get(fileName); if (currentRecordStore == recordStore) { final AtomicInteger countHolder = Maps.get(COUNTS, fileName, new AtomicInteger()); final int count = countHolder.decrementAndGet(); if (count <= 0) { COUNTS.remove(fileName); RECORD_STORES.remove(fileName); return true; } } else { return !recordStore.isClosed(); } } return false; } @Override public boolean canOpenPath(final Path path) { if (FileRecordStoreFactory.super.canOpenPath(path)) { try { // FGDB must be a file not inside a zip file path.toFile(); } catch (final UnsupportedOperationException e) { return false; } if (Paths.exists(Paths.getPath(path, "timestamps"))) { return true; } } return false; } @Override public String getName() { return DESCRIPTION; } @Override public List<String> getRecordStoreFileExtensions() { return FILE_NAME_EXTENSIONS; } @Override public Class<? extends RecordStore> getRecordStoreInterfaceClass( final Map<String, ? extends Object> connectionProperties) { return RecordStore.class; } @Override public List<Pattern> getUrlPatterns() { return URL_PATTERNS; } @Override public boolean isAvailable() { return true; } @Override public FileGdbRecordStore newRecordStore( final Map<String, ? extends Object> connectionProperties) { final Map<String, Object> properties = new LinkedHashMap<>(connectionProperties); final String url = (String)properties.remove("url"); final File file = FileUtil.getUrlFile(url); final FileGdbRecordStore recordStore = newRecordStore(file); RecordStore.setConnectionProperties(recordStore, properties); return recordStore; } @Override public Map<String, Object> parseUrl(final String url) { final Map<String, Object> parameters = new LinkedHashMap<>(); try { final URI uri = UrlUtil.getUri(url); final File file = FileUtil.getFile(uri); if (file != null) { parameters.put("recordStoreType", getName()); parameters.put("file", file); } } catch (final Throwable e) { } return parameters; } @Override public String toUrl(final Map<String, Object> urlParameters) { final String file = Maps.getString(urlParameters, "file"); if (Property.hasValue(file)) { try { return FileUtil.toUrlString(file); } catch (final Throwable e) { return null; } } return null; } }