/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2015, Geomatys * * 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.geotoolkit.data.gml; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import javax.xml.bind.JAXBException; import javax.xml.stream.XMLStreamException; import org.apache.sis.feature.FeatureExt; import org.apache.sis.internal.feature.AttributeConvention; import org.apache.sis.storage.DataStoreException; import org.geotoolkit.data.AbstractFeatureStore; import org.geotoolkit.data.FeatureReader; import org.geotoolkit.data.FeatureStoreFactory; import org.geotoolkit.data.FeatureStoreRuntimeException; import org.geotoolkit.data.FeatureWriter; import org.geotoolkit.data.query.Query; import org.geotoolkit.data.query.QueryCapabilities; import org.geotoolkit.factory.Hints; import org.geotoolkit.nio.IOUtilities; import org.geotoolkit.nio.PosixDirectoryFilter; import org.opengis.util.GenericName; import org.geotoolkit.feature.xml.jaxb.JAXBFeatureTypeReader; import org.geotoolkit.feature.xml.jaxp.JAXPStreamFeatureReader; import org.geotoolkit.feature.xml.jaxp.JAXPStreamFeatureWriter; import org.geotoolkit.parameter.Parameters; import org.geotoolkit.storage.DataFileStore; import org.geotoolkit.storage.DataStores; import org.opengis.feature.Feature; import org.opengis.feature.FeatureType; import org.opengis.filter.Filter; import org.opengis.filter.identity.FeatureId; import org.opengis.parameter.ParameterValueGroup; /** * GML feature store. * * @author Johann Sorel (Geomatys) */ public class GMLSparseFeatureStore extends AbstractFeatureStore implements DataFileStore { private final Path file; private FeatureType featureType; private final Map<String,String> schemaLocations = new HashMap<>(); private String gmlVersion = "3.2.1"; //all types private final Map<GenericName, Object> cache = new HashMap<>(); private Boolean longitudeFirst; public GMLSparseFeatureStore(final File f) throws MalformedURLException, DataStoreException{ this(f.toPath(),null,null); } @Deprecated public GMLSparseFeatureStore(final File f,String xsd, String typeName) throws MalformedURLException, DataStoreException{ this(toParameters(f.toPath(),xsd,typeName)); } public GMLSparseFeatureStore(final Path f,String xsd, String typeName) throws MalformedURLException, DataStoreException{ this(toParameters(f,xsd,typeName)); } public GMLSparseFeatureStore(final ParameterValueGroup params) throws DataStoreException { super(params); final URI uri = (URI) params.parameter(GMLFeatureStoreFactory.PATH.getName().toString()).getValue(); this.file = Paths.get(uri); this.longitudeFirst = (Boolean) params.parameter(GMLFeatureStoreFactory.LONGITUDE_FIRST.getName().toString()).getValue(); } private static ParameterValueGroup toParameters(final Path f,String xsd, String typeName) throws MalformedURLException{ final ParameterValueGroup params = GMLFeatureStoreFactory.PARAMETERS_DESCRIPTOR.createValue(); Parameters.getOrCreate(GMLFeatureStoreFactory.PATH, params).setValue(f.toUri()); Parameters.getOrCreate(GMLFeatureStoreFactory.SPARSE, params).setValue(true); if(xsd!=null) Parameters.getOrCreate(GMLFeatureStoreFactory.XSD, params).setValue(xsd); if(typeName!=null) Parameters.getOrCreate(GMLFeatureStoreFactory.XSD_TYPE_NAME, params).setValue(typeName); return params; } @Override public FeatureStoreFactory getFactory() { return (FeatureStoreFactory) DataStores.getFactoryById(GMLFeatureStoreFactory.NAME); } @Override public synchronized Set<GenericName> getNames() throws DataStoreException { if(featureType==null){ final String xsd = (String) parameters.parameter(GMLFeatureStoreFactory.XSD.getName().toString()).getValue(); final String xsdTypeName = (String) parameters.parameter(GMLFeatureStoreFactory.XSD_TYPE_NAME.getName().toString()).getValue(); if(xsd!=null){ //read types from XSD file final JAXBFeatureTypeReader reader = new JAXBFeatureTypeReader(); try{ for(FeatureType ft : reader.read(new URL(xsd))){ if(ft.getName().tip().toString().equalsIgnoreCase(xsdTypeName)){ featureType = ft; } } if(featureType==null){ throw new DataStoreException("Type for name "+xsdTypeName+" not found in xsd."); } schemaLocations.put(reader.getTargetNamespace(),xsd); }catch(MalformedURLException | JAXBException ex){ throw new DataStoreException(ex.getMessage(),ex); } }else{ //read type in the first gml file final JAXPStreamFeatureReader reader = new JAXPStreamFeatureReader(); reader.getProperties().put(JAXPStreamFeatureReader.LONGITUDE_FIRST, longitudeFirst); reader.setReadEmbeddedFeatureType(true); try { if (Files.isDirectory(file)) { try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(file, new PosixDirectoryFilter("*.gml", true))) { final Iterator<Path> gmlPaths = directoryStream.iterator(); // get first gml file only if (gmlPaths.hasNext()) { final Path gmlPath = gmlPaths.next(); FeatureReader ite = reader.readAsStream(gmlPath); featureType = ite.getFeatureType(); schemaLocations.putAll(reader.getSchemaLocations()); } } } } catch (IOException | XMLStreamException ex) { throw new DataStoreException(ex.getMessage(), ex); } finally { reader.dispose(); } } } return Collections.singleton(featureType.getName()); } @Override public FeatureType getFeatureType(String typeName) throws DataStoreException { typeCheck(typeName); return featureType; } @Override public List<FeatureType> getFeatureTypeHierarchy(String typeName) throws DataStoreException { return super.getFeatureTypeHierarchy(typeName); } @Override public QueryCapabilities getQueryCapabilities() { return GMLFeatureStore.CAPABILITIES; } @Override public void refreshMetaModel() { } @Override public Path[] getDataFiles() throws DataStoreException { return new Path[]{file}; } @Override public boolean isWritable(String typeName) throws DataStoreException { typeCheck(typeName); return true; } @Override public FeatureReader getFeatureReader(Query query) throws DataStoreException { typeCheck(query.getTypeName()); final ReadIterator ite = new ReadIterator(featureType, file); return handleRemaining(ite, query); } @Override public FeatureWriter getFeatureWriter(Query query) throws DataStoreException { typeCheck(query.getTypeName()); final WriterIterator ite = new WriterIterator(featureType, file); return handleRemaining(ite, query.getFilter()); } @Override public List<FeatureId> addFeatures(String groupName, Collection<? extends Feature> newFeatures, Hints hints) throws DataStoreException { return handleAddWithFeatureWriter(groupName, newFeatures, hints); } @Override public void updateFeatures(String groupName, Filter filter, Map<String, ? extends Object> values) throws DataStoreException { handleUpdateWithFeatureWriter(groupName, filter, values); } @Override public void removeFeatures(String groupName, Filter filter) throws DataStoreException { handleRemoveWithFeatureWriter(groupName, filter); } // TYPE CREATE/UPDATE NOT SUPPORTED //////////////////////////////////////// @Override public void createFeatureType(FeatureType featureType) throws DataStoreException { throw new DataStoreException("Writing not supported"); } @Override public void updateFeatureType(FeatureType featureType) throws DataStoreException { throw new DataStoreException("Writing not supported"); } @Override public void deleteFeatureType(String typeName) throws DataStoreException { throw new DataStoreException("Writing not supported"); } private class ReadIterator implements FeatureReader{ protected final FeatureType type; protected final JAXPStreamFeatureReader xmlReader; protected FeatureReader featureReader; protected Feature currentFeature = null; protected Path currentFile = null; protected Feature nextFeature = null; protected final List<Path> files = new LinkedList<>(); protected int index=-1; /** * @deprecated */ @Deprecated public ReadIterator(FeatureType type, File folder) throws DataStoreException { this(type, folder.toPath()); } public ReadIterator(FeatureType type, Path folder) throws DataStoreException { this.type = type; this.xmlReader = new JAXPStreamFeatureReader(type); this.xmlReader.getProperties().put(JAXPStreamFeatureReader.LONGITUDE_FIRST, longitudeFirst); try { this.files.addAll(IOUtilities.listChildren(folder, "*.gml")); } catch (IOException e) { throw new DataStoreException(e.getLocalizedMessage(), e); } } @Override public FeatureType getFeatureType() { return type; } @Override public Feature next() throws FeatureStoreRuntimeException { try { findNext(); } catch (IOException | XMLStreamException ex) { throw new FeatureStoreRuntimeException(ex); } currentFeature = nextFeature; if(currentFeature==null){ currentFile = null; throw new NoSuchElementException("No more features"); } currentFile = files.get(index); nextFeature = null; return currentFeature; } @Override public void remove() { throw new UnsupportedOperationException("No supported yet"); } @Override public boolean hasNext() throws FeatureStoreRuntimeException { try { findNext(); } catch (IOException | XMLStreamException ex) { throw new FeatureStoreRuntimeException(ex); } return nextFeature != null; } private void findNext() throws IOException, XMLStreamException{ if(nextFeature!=null) return; while(nextFeature==null){ if(featureReader==null){ //get the next file index++; if(index >= files.size()){ return; } xmlReader.reset(); featureReader = xmlReader.readAsStream(files.get(index)); } if(featureReader.hasNext()){ nextFeature = featureReader.next(); }else{ featureReader = null; } } } @Override public void close() { xmlReader.dispose(); } } private class WriterIterator extends ReadIterator implements FeatureWriter{ /** * @deprecated */ @Deprecated public WriterIterator(FeatureType type, File folder) throws DataStoreException { super(type, folder.toPath()); } public WriterIterator(FeatureType type, Path folder) throws DataStoreException { super(type, folder); } @Override public Feature next() throws FeatureStoreRuntimeException { if(hasNext()){ return super.next(); }else{ //append mode currentFeature = type.newInstance(); currentFeature.setPropertyValue(AttributeConvention.IDENTIFIER_PROPERTY.toString(), FeatureExt.createDefaultFeatureId()); currentFile = null; return currentFeature; } } @Override public void remove() throws FeatureStoreRuntimeException { if(currentFile==null){ throw new IllegalStateException("No current feature to remove."); } try { Files.delete(currentFile); } catch (IOException e) { throw new FeatureStoreRuntimeException("Unable to delete GML file "+currentFile.toAbsolutePath().toString(), e); } } @Override public void write() throws FeatureStoreRuntimeException { if(currentFeature==null){ throw new IllegalStateException("No current feature to write."); } if(currentFile==null){ //append mode currentFile = file.resolve(FeatureExt.getId(currentFeature).getID()+".gml"); } //write feature final JAXPStreamFeatureWriter writer = new JAXPStreamFeatureWriter(gmlVersion,"2.0.0",schemaLocations); try{ writer.write(currentFeature, currentFile); }catch(IOException | XMLStreamException | DataStoreException ex){ throw new FeatureStoreRuntimeException(ex.getMessage(),ex); } finally{ try { writer.dispose(); } catch (IOException | XMLStreamException ex) { throw new FeatureStoreRuntimeException(ex.getMessage(),ex); } } } } }