/* Copyright (c) 2012-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: * Gabriel Roldan (Boundless) - initial implementation */ package org.locationtech.geogig.test.integration; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; import org.geotools.data.DataUtilities; import org.geotools.feature.NameImpl; import org.geotools.feature.SchemaException; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.geometry.jts.ReferencedEnvelope; import org.geotools.geometry.jts.WKTReader2; import org.geotools.referencing.CRS; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TemporaryFolder; import org.locationtech.geogig.api.Context; import org.locationtech.geogig.api.GeoGIG; import org.locationtech.geogig.api.GeogigTransaction; import org.locationtech.geogig.api.GlobalContextBuilder; import org.locationtech.geogig.api.Node; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.Platform; import org.locationtech.geogig.api.RevCommit; import org.locationtech.geogig.api.TestPlatform; import org.locationtech.geogig.api.porcelain.AddOp; import org.locationtech.geogig.api.porcelain.CommitOp; import org.locationtech.geogig.api.porcelain.ConfigOp; import org.locationtech.geogig.api.porcelain.ConfigOp.ConfigAction; import org.locationtech.geogig.repository.Repository; import org.locationtech.geogig.repository.WorkingTree; import org.opengis.feature.Feature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.feature.type.Name; import org.opengis.geometry.BoundingBox; import org.opengis.referencing.crs.CoordinateReferenceSystem; import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.vividsolutions.jts.io.ParseException; public abstract class RepositoryTestCase extends Assert { public static final String idL1 = "Lines.1"; public static final String idL2 = "Lines.2"; public static final String idL3 = "Lines.3"; public static final String idP1 = "Points.1"; public static final String idP2 = "Points.2"; public static final String idP3 = "Points.3"; public static final String idPG1 = "Polygon.1"; public static final String idPG2 = "Polygon.2"; public static final String idPG3 = "Polygon.3"; public static final String pointsNs = "http://geogig.points"; public static final String pointsName = "Points"; public static final String pointsTypeSpec = "sp:String,ip:Integer,pp:Point:srid=4326"; protected static final String modifiedPointsTypeSpec = "sp:String,ip:Integer,pp:Point:srid=4326,extra:String"; public static final Name pointsTypeName = new NameImpl("http://geogig.points", pointsName); protected SimpleFeatureType pointsType; protected SimpleFeatureType modifiedPointsType; protected Feature points1; protected Feature points1_modified; protected Feature points1B; protected Feature points1B_modified; protected Feature points2; protected Feature points3; public static final String linesNs = "http://geogig.lines"; public static final String linesName = "Lines"; public static final String linesTypeSpec = "sp:String,ip:Integer,pp:LineString:srid=4326"; public static final Name linesTypeName = new NameImpl("http://geogig.lines", linesName); public SimpleFeatureType linesType; public Feature lines1; public Feature lines2; public Feature lines3; public static final String polyNs = "http://geogig.polygon"; public static final String polyName = "Polygon"; public static final String polyTypeSpec = "sp:String,ip:Integer,pp:Polygon:srid=4326"; public static final Name polyTypeName = new NameImpl("http://geogig.polygon", polyName); public SimpleFeatureType polyType; public Feature poly1; public Feature poly2; public Feature poly3; protected GeoGIG geogig; protected Repository repo; // prevent recursion private boolean setup = false; protected File envHome; protected Context injector; @Rule public TemporaryFolder repositoryTempFolder = new TemporaryFolder(); @Before public final void setUp() throws Exception { if (setup) { throw new IllegalStateException("Are you calling super.setUp()!?"); } setup = true; doSetUp(); } protected final void doSetUp() throws IOException, SchemaException, ParseException, Exception { envHome = repositoryTempFolder.newFolder("repo"); injector = createInjector(); geogig = new GeoGIG(injector, envHome); repo = geogig.getOrCreateRepository(); repo.command(ConfigOp.class).setAction(ConfigAction.CONFIG_SET).setName("user.name") .setValue("Gabriel Roldan").call(); repo.command(ConfigOp.class).setAction(ConfigAction.CONFIG_SET).setName("user.email") .setValue("groldan@boundlessgeo.com").call(); pointsType = DataUtilities.createType(pointsNs, pointsName, pointsTypeSpec); modifiedPointsType = DataUtilities.createType(pointsNs, pointsName, modifiedPointsTypeSpec); points1 = feature(pointsType, idP1, "StringProp1_1", new Integer(1000), "POINT(1 1)"); points1_modified = feature(pointsType, idP1, "StringProp1_1a", new Integer(1001), "POINT(1 2)"); points1B = feature(modifiedPointsType, idP1, "StringProp1_1", new Integer(1000), "POINT(1 1)", "ExtraString"); points1B_modified = feature(modifiedPointsType, idP1, "StringProp1_1a", new Integer(1001), "POINT(1 2)", "ExtraStringB"); points2 = feature(pointsType, idP2, "StringProp1_2", new Integer(2000), "POINT(2 2)"); points3 = feature(pointsType, idP3, "StringProp1_3", new Integer(3000), "POINT(3 3)"); linesType = DataUtilities.createType(linesNs, linesName, linesTypeSpec); lines1 = feature(linesType, idL1, "StringProp2_1", new Integer(1000), "LINESTRING (1 1, 2 2)"); lines2 = feature(linesType, idL2, "StringProp2_2", new Integer(2000), "LINESTRING (3 3, 4 4)"); lines3 = feature(linesType, idL3, "StringProp2_3", new Integer(3000), "LINESTRING (5 5, 6 6)"); polyType = DataUtilities.createType(polyNs, polyName, polyTypeSpec); poly1 = feature(polyType, idPG1, "StringProp3_1", new Integer(1000), "POLYGON ((1 1, 2 2, 3 3, 4 4, 1 1))"); poly2 = feature(polyType, idPG2, "StringProp3_2", new Integer(2000), "POLYGON ((6 6, 7 7, 8 8, 9 9, 6 6))"); poly3 = feature(polyType, idPG3, "StringProp3_3", new Integer(3000), "POLYGON ((11 11, 12 12, 13 13, 14 14, 11 11))"); setUpInternal(); } protected Context createInjector() { Platform testPlatform = createPlatform(); GlobalContextBuilder.builder = new TestContextBuilder(testPlatform); return GlobalContextBuilder.builder.build(); } protected Platform createPlatform() { Platform testPlatform = new TestPlatform(envHome); return testPlatform; } @After public final void tearDown() throws Exception { setup = false; tearDownInternal(); if (repo != null) { repo.close(); } repo = null; injector = null; } /** * Called as the last step in {@link #setUp()} */ protected abstract void setUpInternal() throws Exception; /** * Called before {@link #tearDown()}, subclasses may override as appropriate */ protected void tearDownInternal() throws Exception { // } public Repository getRepository() { return repo; } public GeoGIG getGeogig() { return geogig; } protected Feature feature(SimpleFeatureType type, String id, Object... values) throws ParseException { SimpleFeatureBuilder builder = new SimpleFeatureBuilder(type); for (int i = 0; i < values.length; i++) { Object value = values[i]; if (type.getDescriptor(i) instanceof GeometryDescriptor) { if (value instanceof String) { value = new WKTReader2().read((String) value); } } builder.set(i, value); } return builder.buildFeature(id); } protected List<RevCommit> populate(boolean oneCommitPerFeature, Feature... features) throws Exception { return populate(oneCommitPerFeature, Arrays.asList(features)); } protected List<RevCommit> populate(boolean oneCommitPerFeature, List<Feature> features) throws Exception { List<RevCommit> commits = new ArrayList<RevCommit>(); for (Feature f : features) { insertAndAdd(f); if (oneCommitPerFeature) { RevCommit commit = geogig.command(CommitOp.class).call(); commits.add(commit); } } if (!oneCommitPerFeature) { RevCommit commit = geogig.command(CommitOp.class).call(); commits.add(commit); } return commits; } /** * Inserts the Feature to the index and stages it to be committed. */ public ObjectId insertAndAdd(Feature f) throws Exception { return insertAndAdd(null, f); } /** * Inserts the Feature to the index and stages it to be committed. */ public ObjectId insertAndAdd(GeogigTransaction transaction, Feature f) throws Exception { ObjectId objectId = insert(transaction, f); if (transaction != null) { transaction.command(AddOp.class).call(); } else { geogig.command(AddOp.class).call(); } return objectId; } /** * Inserts the feature to the index but does not stages it to be committed */ public ObjectId insert(Feature f) throws Exception { return insert(null, f); } /** * Inserts the feature to the index but does not stages it to be committed */ public ObjectId insert(GeogigTransaction transaction, Feature f) throws Exception { final WorkingTree workTree = (transaction != null ? transaction.workingTree() : repo .workingTree()); Name name = f.getType().getName(); String parentPath = name.getLocalPart(); Node ref = workTree.insert(parentPath, f); ObjectId objectId = ref.getObjectId(); return objectId; } public void insertAndAdd(Feature... features) throws Exception { insertAndAdd(null, features); } public void insertAndAdd(GeogigTransaction transaction, Feature... features) throws Exception { insert(transaction, features); geogig.command(AddOp.class).call(); } public void insert(Feature... features) throws Exception { insert(null, features); } public void insert(GeogigTransaction transaction, Feature... features) throws Exception { for (Feature f : features) { insert(transaction, f); } } /** * Deletes a feature from the index * * @param f * @return * @throws Exception */ public boolean deleteAndAdd(Feature f) throws Exception { return deleteAndAdd(null, f); } /** * Deletes a feature from the index * * @param f * @return * @throws Exception */ public boolean deleteAndAdd(GeogigTransaction transaction, Feature f) throws Exception { boolean existed = delete(transaction, f); if (existed) { if (transaction != null) { transaction.command(AddOp.class).call(); } else { geogig.command(AddOp.class).call(); } } return existed; } public boolean delete(Feature f) throws Exception { return delete(null, f); } public boolean delete(GeogigTransaction transaction, Feature f) throws Exception { final WorkingTree workTree = (transaction != null ? transaction.workingTree() : repo .workingTree()); Name name = f.getType().getName(); String localPart = name.getLocalPart(); String id = f.getIdentifier().getID(); boolean existed = workTree.delete(localPart, id); return existed; } public <E> List<E> toList(Iterator<E> logs) { List<E> logged = new ArrayList<E>(); Iterators.addAll(logged, logs); return logged; } public <E> List<E> toList(Iterable<E> logs) { List<E> logged = new ArrayList<E>(); Iterables.addAll(logged, logs); return logged; } /** * Computes the aggregated bounds of {@code features}, assuming all of them are in the same CRS */ public ReferencedEnvelope boundsOf(Feature... features) { ReferencedEnvelope bounds = null; for (int i = 0; i < features.length; i++) { Feature f = features[i]; if (bounds == null) { bounds = (ReferencedEnvelope) f.getBounds(); } else { bounds.include(f.getBounds()); } } return bounds; } /** * Computes the aggregated bounds of {@code features} in the {@code targetCrs} */ public ReferencedEnvelope boundsOf(CoordinateReferenceSystem targetCrs, Feature... features) throws Exception { ReferencedEnvelope bounds = new ReferencedEnvelope(targetCrs); for (int i = 0; i < features.length; i++) { Feature f = features[i]; BoundingBox fbounds = f.getBounds(); if (!CRS.equalsIgnoreMetadata(targetCrs, fbounds)) { fbounds = fbounds.toBounds(targetCrs); } bounds.include(fbounds); } return bounds; } }