/* Copyright (c) 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.rest.repository; import static org.locationtech.geogig.rest.repository.RESTUtils.getGeogig; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.Reader; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import org.locationtech.geogig.api.GeoGIG; import org.locationtech.geogig.api.ObjectId; import org.locationtech.geogig.api.RepositoryFilter; import org.locationtech.geogig.api.RevCommit; import org.locationtech.geogig.api.plumbing.diff.DiffEntry; import org.locationtech.geogig.api.porcelain.DiffOp; import org.locationtech.geogig.remote.BinaryPackedChanges; import org.locationtech.geogig.remote.FilteredDiffIterator; import org.locationtech.geogig.repository.Repository; import org.restlet.Context; import org.restlet.Finder; import org.restlet.data.MediaType; import org.restlet.data.Request; import org.restlet.data.Response; import org.restlet.resource.OutputRepresentation; import org.restlet.resource.Representation; import org.restlet.resource.Resource; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.base.Throwables; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; /** * Gets a set of changes that match a provided filter from a particular commit. */ public class FilteredChangesResource extends Finder { private static final Logger LOGGER = LoggerFactory.getLogger(FilteredChangesResource.class); @Override public Resource findTarget(Request request, Response response) { return new ChangesResource(getContext(), request, response); } private static class ChangesResource extends Resource { public ChangesResource(// Context context, // Request request, // Response response) // { super(context, request, response); } @Override public boolean allowPost() { return true; } @Override public void post(Representation entity) { try { final InputStream inStream; try { inStream = entity.getStream(); } catch (IOException e) { throw Throwables.propagate(e); } final Reader body = new InputStreamReader(inStream); final JsonParser parser = new JsonParser(); final JsonElement messageJson = parser.parse(body); final Set<ObjectId> tracked = new HashSet<ObjectId>(); RepositoryFilter filter = new RepositoryFilter(); ObjectId commitId = ObjectId.NULL; if (messageJson.isJsonObject()) { final JsonObject message = messageJson.getAsJsonObject(); final JsonArray trackedArray; if (message.has("tracked") && message.get("tracked").isJsonArray()) { trackedArray = message.get("tracked").getAsJsonArray(); } else { trackedArray = new JsonArray(); } if (message.has("commitId") && message.get("commitId").isJsonPrimitive()) { commitId = ObjectId.valueOf(message.get("commitId").getAsJsonPrimitive() .getAsString()); } else { commitId = ObjectId.NULL; } for (final JsonElement e : trackedArray) { if (e.isJsonPrimitive()) { tracked.add(ObjectId.valueOf(e.getAsJsonPrimitive().getAsString())); } } if (message.has("filter") && message.get("filter").isJsonArray()) { JsonArray filterArray = message.get("filter").getAsJsonArray(); for (final JsonElement e : filterArray) { if (e.isJsonObject()) { JsonObject filterObject = e.getAsJsonObject(); String featureType = null; String filterType = null; String filterText = null; if (filterObject.has("featurepath") && filterObject.get("featurepath").isJsonPrimitive()) { featureType = filterObject.get("featurepath") .getAsJsonPrimitive().getAsString(); } if (filterObject.has("type") && filterObject.get("type").isJsonPrimitive()) { filterType = filterObject.get("type").getAsJsonPrimitive() .getAsString(); } if (filterObject.has("filter") && filterObject.get("filter").isJsonPrimitive()) { filterText = filterObject.get("filter").getAsJsonPrimitive() .getAsString(); } if (featureType != null && filterType != null && filterText != null) { filter.addFilter(featureType, filterType, filterText); } } } } } final GeoGIG ggit = getGeogig(getRequest()).get(); final Repository repository = ggit.getRepository(); RevCommit commit = repository.getCommit(commitId); ObjectId parent = ObjectId.NULL; if (commit.getParentIds().size() > 0) { parent = commit.getParentIds().get(0); } Iterator<DiffEntry> changes = ggit.command(DiffOp.class) .setNewVersion(commit.getId()).setOldVersion(parent).setReportTrees(true) .call(); FilteredDiffIterator filteredChanges = new FilteredDiffIterator(changes, repository, filter) { @Override protected boolean trackingObject(ObjectId objectId) { return tracked.contains(objectId); } @Override public boolean isAutoIngesting() { return false; } }; getResponse().setEntity( new FilteredDiffIteratorRepresentation(new BinaryPackedChanges(repository), filteredChanges)); } catch (Exception e) { throw new RuntimeException(e); } } private static final MediaType PACKED_OBJECTS = new MediaType("application/x-geogig-packed"); private class FilteredDiffIteratorRepresentation extends OutputRepresentation { private final BinaryPackedChanges packer; private final FilteredDiffIterator changes; public FilteredDiffIteratorRepresentation(BinaryPackedChanges packer, FilteredDiffIterator changes) { super(PACKED_OBJECTS); this.changes = changes; this.packer = packer; } @Override public void write(OutputStream out) throws IOException { LOGGER.debug("Writing objects to remote..."); packer.write(out, changes); // signal the end of changes out.write(2); if (changes.wasFiltered()) { out.write(1); } else { out.write(0); } } } } }