/* 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:
* Johnathan Garrett (LMN Solutions) - initial implementation
*/
package org.locationtech.geogig.remote;
import java.util.Iterator;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RepositoryFilter;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.RevObject;
import org.locationtech.geogig.api.RevObject.TYPE;
import org.locationtech.geogig.api.plumbing.RevObjectParse;
import org.locationtech.geogig.api.plumbing.diff.DiffEntry;
import org.locationtech.geogig.repository.Repository;
import com.google.common.collect.AbstractIterator;
/**
* An implementation of a {@link DiffEntry} iterator that filters entries based on a provided
* {@link RepositoryFilter}.
*/
public abstract class FilteredDiffIterator extends AbstractIterator<DiffEntry> {
protected boolean filtered = false;
private Iterator<DiffEntry> source;
private Repository sourceRepo;
private RepositoryFilter repoFilter;
public final boolean wasFiltered() {
return filtered;
}
/**
* @return {@code true} if a side effect of consuming this iterator is that the objects it
* refers to are automatically added to the local objects database
*/
public abstract boolean isAutoIngesting();
/**
* Constructs a new {@code FilteredDiffIterator}.
*
* @param source the unfiltered iterator
* @param sourceRepo the repository where objects are stored
* @param repoFilter the filter to use
*/
public FilteredDiffIterator(Iterator<DiffEntry> source, Repository sourceRepo,
RepositoryFilter repoFilter) {
this.source = source;
this.sourceRepo = sourceRepo;
this.repoFilter = repoFilter;
filtered = false;
}
/**
* Compute the next {@link DiffEntry} that matches our {@link RepositoryFilter}.
*/
protected DiffEntry computeNext() {
while (source.hasNext()) {
DiffEntry input = source.next();
// HACK: ignore diff entries reporting a change to a tree, the feature changes will come
// next and the new tree is built from them. I'm not totally sure this is the best way
// to handle this situation but the unit tests are written expecting this behavior
// probably as a side effect of a bug in TreeDiffEntryIterator (used to be called by
// DiffTree) that doesn't report tree changes even is setReportTrees(true) was set.
if (input.isChange() && input.getOldObject().getType().equals(TYPE.TREE)) {
continue;
}
NodeRef oldObject = filter(input.getOldObject());
NodeRef newObject;
if (oldObject != null) {
newObject = input.getNewObject();
if (newObject != null) {
// we are tracking this object, but we still need to process the new object
RevObject object = sourceRepo.command(RevObjectParse.class)
.setObjectId(newObject.getNode().getObjectId()).call().get();
RevObject metadata = null;
if (newObject.getMetadataId() != ObjectId.NULL) {
metadata = sourceRepo.command(RevObjectParse.class)
.setObjectId(newObject.getMetadataId()).call().get();
}
processObject(object);
processObject(metadata);
}
} else {
newObject = filter(input.getNewObject());
}
if (oldObject == null && newObject == null) {
filtered = true;
continue;
}
return new DiffEntry(oldObject, newObject);
}
return endOfData();
}
private NodeRef filter(NodeRef node) {
if (node == null) {
return null;
}
RevObject object = sourceRepo.objectDatabase().get(node.objectId());
RevObject metadata = null;
if (!node.getMetadataId().isNull()) {
metadata = sourceRepo.objectDatabase().get(node.getMetadataId());
}
if (node.getType() == TYPE.FEATURE) {
if (trackingObject(object.getId())) {
// We are already tracking this object, continue to do so
return node;
}
RevFeatureType revFeatureType = (RevFeatureType) metadata;
if (!repoFilter.filterObject(revFeatureType, node.getParentPath(), object)) {
return null;
}
}
processObject(object);
processObject(metadata);
return node;
}
/**
* An overridable method for hinting that the given object should be tracked, regardless of
* whether or not it matches the filter.
*
* @param objectId the id of the object
* @return true if the object should be tracked, false if it should only be tracked if it
* matches the filter
*/
protected boolean trackingObject(ObjectId objectId) {
return true;
}
/**
* An overridable method to process all objects that match the filter.
*
* @param object the object to process
*/
protected void processObject(RevObject object) {
}
}