/* Copyright (c) 2013-2014 Boundless and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Distribution License v1.0 * which accompanies this distribution, and is available at * https://www.eclipse.org/org/documents/edl-v10.html * * Contributors: * Juan Marin (Boundless) - initial implementation */ package org.locationtech.geogig.geotools.plumbing; import java.util.List; import javax.annotation.Nullable; import org.geotools.data.DataUtilities; import org.geotools.data.memory.MemoryDataStore; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.simple.SimpleFeatureStore; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.junit.Test; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.RevFeatureTypeImpl; import org.locationtech.geogig.api.porcelain.AddOp; import org.locationtech.geogig.api.porcelain.CommitOp; import org.locationtech.geogig.geotools.plumbing.GeoToolsOpException.StatusCode; import org.locationtech.geogig.test.integration.RepositoryTestCase; import org.opengis.feature.Feature; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import com.google.common.base.Function; import com.google.common.base.Optional; public class ExportOpTest extends RepositoryTestCase { @Override protected void setUpInternal() throws Exception { } @Test public void testExportFromWorkingTree() throws Exception { Feature[] points = new Feature[] { points1, points2, points3 }; for (Feature feature : points) { insert(feature); } MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName).call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(featureCollection.size(), points.length); SimpleFeatureIterator features = featureCollection.features(); assertTrue(collectionsAreEqual(features, points)); } @Test public void testExportFromHEAD() throws Exception { Feature[] points = new Feature[] { points1, points2, points3 }; for (Feature feature : points) { insert(feature); } geogig.command(AddOp.class).call(); geogig.command(CommitOp.class).setAll(true).call(); MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath("HEAD:" + pointsName) .call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(featureCollection.size(), points.length); SimpleFeatureIterator features = featureCollection.features(); assertTrue(collectionsAreEqual(features, points)); } @Test public void testExportingUsingFunction() throws Exception { // Testing export of points feature type into a simplified feature type that // does not contain the integer attribute. String simplifiedPointsName = "simplifiedPoints"; String simplifiedPointsTypeSpec = "sp:String,pp:Point:srid=4326"; SimpleFeatureType simplifiedPointsType = DataUtilities.createType(pointsNs, simplifiedPointsName, simplifiedPointsTypeSpec); Feature simplifiedPoints1 = feature(simplifiedPointsType, ((SimpleFeature) points1).getID(), ((SimpleFeature) points1).getAttribute(0), ((SimpleFeature) points1).getAttribute(2)); Feature simplifiedPoints2 = feature(simplifiedPointsType, ((SimpleFeature) points2).getID(), ((SimpleFeature) points2).getAttribute(0), ((SimpleFeature) points2).getAttribute(2)); Feature simplifiedPoints3 = feature(simplifiedPointsType, ((SimpleFeature) points3).getID(), ((SimpleFeature) points3).getAttribute(0), ((SimpleFeature) points3).getAttribute(2)); Feature[] simplifiedPoints = new Feature[] { simplifiedPoints1, simplifiedPoints2, simplifiedPoints3 }; final SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(simplifiedPointsType); Function<Feature, Optional<Feature>> function = new Function<Feature, Optional<Feature>>() { @Override @Nullable public Optional<Feature> apply(@Nullable Feature feature) { SimpleFeature simpleFeature = (SimpleFeature) feature; featureBuilder.add(simpleFeature.getAttribute(0)); featureBuilder.add(simpleFeature.getAttribute(2)); return Optional.of((Feature) featureBuilder.buildFeature(null)); } }; Feature[] points = new Feature[] { points1, points2, points3 }; for (Feature feature : points) { insert(feature); } MemoryDataStore dataStore = new MemoryDataStore(simplifiedPointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .setFeatureTypeConversionFunction(function).call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(featureCollection.size(), points.length); SimpleFeatureIterator features = featureCollection.features(); assertTrue(collectionsAreEqual(features, simplifiedPoints)); // check for exceptions when using a function that returns features with a wrong featuretype try { String wrongFeaturesName = "wrongFeatures"; String wrongFeaturesTypeSpec = "sp:String"; SimpleFeatureType wrongFeaturesType = DataUtilities.createType(pointsNs, wrongFeaturesName, wrongFeaturesTypeSpec); final SimpleFeatureBuilder wrongFeatureBuilder = new SimpleFeatureBuilder( wrongFeaturesType); Function<Feature, Optional<Feature>> wrongFunction = new Function<Feature, Optional<Feature>>() { @Override @Nullable public Optional<Feature> apply(@Nullable Feature feature) { SimpleFeature simpleFeature = (SimpleFeature) feature; wrongFeatureBuilder.add(simpleFeature.getAttribute(0)); return Optional.of((Feature) wrongFeatureBuilder.buildFeature(null)); } }; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .setFeatureTypeConversionFunction(wrongFunction).call(); fail(); } catch (GeoToolsOpException e) { assertEquals(e.statusCode, StatusCode.UNABLE_TO_ADD); } } private boolean collectionsAreEqual(SimpleFeatureIterator features, Feature[] points) { // features are not iterated in the same order as the original set, so // we just do pairwise comparison to check that all the original features // are represented in the exported feature store while (features.hasNext()) { boolean found = true; List<Object> attributesExported = features.next().getAttributes(); for (int i = 0; i < points.length; i++) { found = true; List<Object> attributesOriginal = ((SimpleFeature) points[i]).getAttributes(); for (int j = 0; j < attributesExported.size(); j++) { Object attributeExported = attributesExported.get(j); Object attributeOriginal = attributesOriginal.get(j); if (!attributeOriginal.equals(attributeExported)) { found = false; break; } } if (found) { break; } } if (!found) { return false; } } return true; } @Test public void testExportFromWrongFeatureType() throws Exception { MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; try { geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName).call(); fail(); } catch (IllegalArgumentException e) { assertTrue(true); } } @Test public void testExportFromTreeWithSeveralFeatureTypesUsingDefaultFeatureType() throws Exception { Feature[] points = new Feature[] { points2, points1B, points3 }; for (Feature feature : points) { insert(feature); } Feature[] expectedPoints = new Feature[] { points2, points3 }; MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .exportDefaultFeatureType().call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(featureCollection.size(), expectedPoints.length); SimpleFeatureIterator features = featureCollection.features(); assertTrue(collectionsAreEqual(features, expectedPoints)); } @Test public void testExportWithAlterUsingDefaultFeatureType() throws Exception { Feature[] points = new Feature[] { points2, points1B, points3 }; for (Feature feature : points) { insert(feature); } MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .setAlter(true).call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(featureCollection.size(), points.length); SimpleFeatureIterator features = featureCollection.features(); assertTrue(collectionsAreEqual(features, points)); } @Test public void testExportWithAlterUsingFeatureTypeId() throws Exception { Feature[] points = new Feature[] { points2, points1B, points3 }; for (Feature feature : points) { insert(feature); } MemoryDataStore dataStore = new MemoryDataStore(modifiedPointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .setAlter(true) .setFilterFeatureTypeId(RevFeatureTypeImpl.build(modifiedPointsType).getId()).call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(featureCollection.size(), points.length); SimpleFeatureIterator features = featureCollection.features(); while (features.hasNext()) { List<Object> attributes = features.next().getAttributes(); assertEquals(4, attributes.size()); } } @Test public void testExportFromTreeWithSeveralFeatureTypesUsingFeatureTypeId() throws Exception { Feature[] points = new Feature[] { points2, points1B, points3 }; for (Feature feature : points) { insert(feature); } Feature[] expectedPoints = new Feature[] { points1B }; MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .setFilterFeatureTypeId(RevFeatureTypeImpl.build(modifiedPointsType).getId()).call(); featureSource = dataStore.getFeatureSource(typeName); featureStore = (SimpleFeatureStore) featureSource; SimpleFeatureCollection featureCollection = featureStore.getFeatures(); assertEquals(expectedPoints.length, featureCollection.size()); SimpleFeatureIterator features = featureCollection.features(); assertTrue(collectionsAreEqual(features, expectedPoints)); } @Test public void testExportFromTreeWithSeveralFeatureTypesUsingNonexistantTypeId() throws Exception { Feature[] points = new Feature[] { points2, points1B, points3 }; for (Feature feature : points) { insert(feature); } MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; try { geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName) .setFilterFeatureTypeId(ObjectId.forString("fake")).call(); fail(); } catch (IllegalArgumentException e) { assertTrue(e.getMessage(), e.getMessage().contains("filter feature type")); } } @Test public void testExportFromTreeWithSeveralFeatureTypes() throws Exception { Feature[] points = new Feature[] { points2, points1B, points3 }; for (Feature feature : points) { insert(feature); } MemoryDataStore dataStore = new MemoryDataStore(pointsType); final String typeName = dataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName); SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; try { geogig.command(ExportOp.class).setFeatureStore(featureStore).setPath(pointsName).call(); fail(); } catch (GeoToolsOpException e) { assertEquals(GeoToolsOpException.StatusCode.MIXED_FEATURE_TYPES, e.statusCode); } } }