use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.
the class ApplyPatchOp method applyPatch.
private void applyPatch(Patch patch) {
final WorkingTree workTree = workingTree();
final StagingDatabase indexDb = stagingDatabase();
if (reverse) {
patch = patch.reversed();
}
List<FeatureInfo> removed = patch.getRemovedFeatures();
for (FeatureInfo feature : removed) {
workTree.delete(NodeRef.parentPath(feature.getPath()), NodeRef.nodeFromPath(feature.getPath()));
}
List<FeatureInfo> added = patch.getAddedFeatures();
for (FeatureInfo feature : added) {
workTree.insert(NodeRef.parentPath(feature.getPath()), feature.getFeature());
}
List<FeatureDiff> diffs = patch.getModifiedFeatures();
for (FeatureDiff diff : diffs) {
String path = diff.getPath();
DepthSearch depthSearch = new DepthSearch(indexDb);
Optional<NodeRef> noderef = depthSearch.find(workTree.getTree(), path);
RevFeatureType oldRevFeatureType = command(RevObjectParse.class).setObjectId(noderef.get().getMetadataId()).call(RevFeatureType.class).get();
String refSpec = Ref.WORK_HEAD + ":" + path;
RevFeature feature = command(RevObjectParse.class).setRefSpec(refSpec).call(RevFeature.class).get();
RevFeatureType newRevFeatureType = getFeatureType(diff, feature, oldRevFeatureType);
ImmutableList<Optional<Object>> values = feature.getValues();
ImmutableList<PropertyDescriptor> oldDescriptors = oldRevFeatureType.sortedDescriptors();
ImmutableList<PropertyDescriptor> newDescriptors = newRevFeatureType.sortedDescriptors();
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder((SimpleFeatureType) newRevFeatureType.type());
Map<Name, Optional<?>> attrs = Maps.newHashMap();
for (int i = 0; i < oldDescriptors.size(); i++) {
PropertyDescriptor descriptor = oldDescriptors.get(i);
if (newDescriptors.contains(descriptor)) {
Optional<Object> value = values.get(i);
attrs.put(descriptor.getName(), value);
}
}
Set<Entry<PropertyDescriptor, AttributeDiff>> featureDiffs = diff.getDiffs().entrySet();
for (Iterator<Entry<PropertyDescriptor, AttributeDiff>> iterator = featureDiffs.iterator(); iterator.hasNext(); ) {
Entry<PropertyDescriptor, AttributeDiff> entry = iterator.next();
if (!entry.getValue().getType().equals(TYPE.REMOVED)) {
Optional<?> oldValue = attrs.get(entry.getKey().getName());
attrs.put(entry.getKey().getName(), entry.getValue().applyOn(oldValue));
}
}
Set<Entry<Name, Optional<?>>> entries = attrs.entrySet();
for (Iterator<Entry<Name, Optional<?>>> iterator = entries.iterator(); iterator.hasNext(); ) {
Entry<Name, Optional<?>> entry = iterator.next();
featureBuilder.set(entry.getKey(), entry.getValue().orNull());
}
SimpleFeature featureToInsert = featureBuilder.buildFeature(NodeRef.nodeFromPath(path));
workTree.insert(NodeRef.parentPath(path), featureToInsert);
}
ImmutableList<FeatureTypeDiff> alteredTrees = patch.getAlteredTrees();
for (FeatureTypeDiff diff : alteredTrees) {
Optional<RevFeatureType> featureType;
if (diff.getOldFeatureType().isNull()) {
featureType = patch.getFeatureTypeFromId(diff.getNewFeatureType());
workTree.createTypeTree(diff.getPath(), featureType.get().type());
} else if (diff.getNewFeatureType().isNull()) {
workTree.delete(diff.getPath());
} else {
featureType = patch.getFeatureTypeFromId(diff.getNewFeatureType());
workTree.updateTypeTree(diff.getPath(), featureType.get().type());
}
}
}
use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.
the class Patch method addAddedFeature.
/**
* Adds a feature to the list of newly added ones
*
* @param path the path of the added feature
* @param feature the feature
* @param featureType the feature type of the added feature
*/
public void addAddedFeature(String path, Feature feature, RevFeatureType featureType) {
addedFeatures.add(new FeatureInfo(feature, featureType, path));
addFeatureType(featureType);
}
use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.
the class Patch method addRemovedFeature.
/**
* Adds a feature to the list of removed ones
*
* @param path the path of the removed feature
* @param feature the feature
* @param featureType the feature type of the removed feature
*/
public void addRemovedFeature(String path, Feature feature, RevFeatureType featureType) {
removedFeatures.add(new FeatureInfo(feature, featureType, path));
addFeatureType(featureType);
}
use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.
the class ResponseWriter method writeMerged.
/**
* Writes the response for a set of merged features while also supplying the geometry.
*
* @param geogig - a CommandLocator to call commands from
* @param features - a FeatureInfo iterator to build the response from
* @throws XMLStreamException
*/
public void writeMerged(final Context geogig, Iterator<FeatureInfo> features) throws XMLStreamException {
Iterator<GeometryChange> changeIterator = Iterators.transform(features, new Function<FeatureInfo, GeometryChange>() {
@Override
public GeometryChange apply(FeatureInfo input) {
GeometryChange change = null;
RevFeature revFeature = RevFeatureBuilder.build(input.getFeature());
RevFeatureType featureType = input.getFeatureType();
Collection<PropertyDescriptor> attribs = featureType.type().getDescriptors();
String crsCode = null;
for (PropertyDescriptor attrib : attribs) {
PropertyType attrType = attrib.getType();
if (attrType instanceof GeometryType) {
GeometryType gt = (GeometryType) attrType;
CoordinateReferenceSystem crs = gt.getCoordinateReferenceSystem();
if (crs != null) {
try {
crsCode = CRS.lookupIdentifier(Citations.EPSG, crs, false);
} catch (FactoryException e) {
crsCode = null;
}
if (crsCode != null) {
crsCode = "EPSG:" + crsCode;
}
}
break;
}
}
FeatureBuilder builder = new FeatureBuilder(featureType);
GeogigSimpleFeature simpleFeature = (GeogigSimpleFeature) builder.build(revFeature.getId().toString(), revFeature);
change = new GeometryChange(simpleFeature, ChangeType.MODIFIED, input.getPath(), crsCode);
return change;
}
});
while (changeIterator.hasNext()) {
GeometryChange next = changeIterator.next();
if (next != null) {
GeogigSimpleFeature feature = next.getFeature();
out.writeStartElement("Feature");
writeElement("change", "MERGED");
writeElement("id", next.getPath());
List<Object> attributes = feature.getAttributes();
for (Object attribute : attributes) {
if (attribute instanceof Geometry) {
writeElement("geometry", ((Geometry) attribute).toText());
break;
}
}
if (next.getCRS() != null) {
writeElement("crs", next.getCRS());
}
out.writeEndElement();
}
}
}
use of org.locationtech.geogig.api.FeatureInfo in project GeoGig by boundlessgeo.
the class MergeOp method _call.
/**
* Executes the merge operation.
*
* @return always {@code true}
*/
@Override
protected MergeReport _call() throws RuntimeException {
Preconditions.checkArgument(commits.size() > 0, "No commits specified for merge.");
Preconditions.checkArgument(!(ours && theirs), "Cannot use both --ours and --theirs.");
final Optional<Ref> currHead = command(RefParse.class).setName(Ref.HEAD).call();
Preconditions.checkState(currHead.isPresent(), "Repository has no HEAD, can't rebase.");
Ref headRef = currHead.get();
ObjectId oursId = headRef.getObjectId();
// Preconditions.checkState(currHead.get() instanceof SymRef,
// "Can't rebase from detached HEAD");
// SymRef headRef = (SymRef) currHead.get();
// final String currentBranch = headRef.getTarget();
getProgressListener().started();
boolean fastForward = true;
boolean changed = false;
Optional<MergeScenarioReport> mergeScenario = Optional.absent();
List<CommitAncestorPair> pairs = Lists.newArrayList();
boolean hasConflictsOrAutomerge;
List<RevCommit> revCommits = Lists.newArrayList();
if (!ObjectId.NULL.equals(headRef.getObjectId())) {
revCommits.add(repository().getCommit(headRef.getObjectId()));
}
for (ObjectId commitId : commits) {
revCommits.add(repository().getCommit(commitId));
}
hasConflictsOrAutomerge = command(CheckMergeScenarioOp.class).setCommits(revCommits).call().booleanValue();
if (hasConflictsOrAutomerge && !theirs) {
Preconditions.checkState(commits.size() < 2, "Conflicted merge.\nCannot merge more than two commits when conflicts exist" + " or features have been modified in several histories");
RevCommit headCommit = repository().getCommit(headRef.getObjectId());
ObjectId commitId = commits.get(0);
Preconditions.checkArgument(!ObjectId.NULL.equals(commitId), "Cannot merge a NULL commit.");
Preconditions.checkArgument(repository().commitExists(commitId), "Not a valid commit: " + commitId.toString());
final RevCommit targetCommit = repository().getCommit(commitId);
Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).call();
pairs.add(new CommitAncestorPair(commitId, ancestorCommit.get()));
mergeScenario = Optional.of(command(ReportMergeScenarioOp.class).setMergeIntoCommit(headCommit).setToMergeCommit(targetCommit).call());
List<FeatureInfo> merged = mergeScenario.get().getMerged();
for (FeatureInfo feature : merged) {
this.workingTree().insert(NodeRef.parentPath(feature.getPath()), feature.getFeature());
Iterator<DiffEntry> unstaged = workingTree().getUnstaged(null);
index().stage(getProgressListener(), unstaged, 0);
changed = true;
fastForward = false;
}
List<DiffEntry> unconflicting = mergeScenario.get().getUnconflicted();
if (!unconflicting.isEmpty()) {
index().stage(getProgressListener(), unconflicting.iterator(), 0);
changed = true;
fastForward = false;
}
workingTree().updateWorkHead(index().getTree().getId());
List<Conflict> conflicts = mergeScenario.get().getConflicts();
if (!ours && !conflicts.isEmpty()) {
// In case we use the "ours" strategy, we do nothing. We ignore conflicting
// changes and leave the current elements
command(UpdateRef.class).setName(Ref.MERGE_HEAD).setNewValue(commitId).call();
command(UpdateRef.class).setName(Ref.ORIG_HEAD).setNewValue(headCommit.getId()).call();
command(ConflictsWriteOp.class).setConflicts(conflicts).call();
StringBuilder msg = new StringBuilder();
Optional<Ref> ref = command(ResolveBranchId.class).setObjectId(commitId).call();
if (ref.isPresent()) {
msg.append("Merge branch " + ref.get().getName());
} else {
msg.append("Merge commit '" + commitId.toString() + "'. ");
}
msg.append("\n\nConflicts:\n");
for (Conflict conflict : mergeScenario.get().getConflicts()) {
msg.append("\t" + conflict.getPath() + "\n");
}
command(SaveMergeCommitMessageOp.class).setMessage(msg.toString()).call();
StringBuilder sb = new StringBuilder();
for (Conflict conflict : conflicts) {
sb.append("CONFLICT: Merge conflict in " + conflict.getPath() + "\n");
}
sb.append("Automatic merge failed. Fix conflicts and then commit the result.\n");
throw new MergeConflictsException(sb.toString(), headCommit.getId(), commitId);
}
} else {
Preconditions.checkState(!hasConflictsOrAutomerge || commits.size() < 2, "Conflicted merge.\nCannot merge more than two commits when conflicts exist" + " or features have been modified in several histories");
for (ObjectId commitId : commits) {
ProgressListener subProgress = subProgress(100.f / commits.size());
Preconditions.checkArgument(!ObjectId.NULL.equals(commitId), "Cannot merge a NULL commit.");
Preconditions.checkArgument(repository().commitExists(commitId), "Not a valid commit: " + commitId.toString());
subProgress.started();
if (ObjectId.NULL.equals(headRef.getObjectId())) {
// Fast-forward
if (headRef instanceof SymRef) {
final String currentBranch = ((SymRef) headRef).getTarget();
command(UpdateRef.class).setName(currentBranch).setNewValue(commitId).call();
headRef = (SymRef) command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call().get();
} else {
headRef = command(UpdateRef.class).setName(headRef.getName()).setNewValue(commitId).call().get();
}
workingTree().updateWorkHead(commitId);
index().updateStageHead(commitId);
subProgress.complete();
changed = true;
continue;
}
RevCommit headCommit = repository().getCommit(headRef.getObjectId());
final RevCommit targetCommit = repository().getCommit(commitId);
Optional<ObjectId> ancestorCommit = command(FindCommonAncestor.class).setLeft(headCommit).setRight(targetCommit).call();
pairs.add(new CommitAncestorPair(commitId, ancestorCommit.get()));
subProgress.setProgress(10.f);
Preconditions.checkState(ancestorCommit.isPresent(), "No ancestor commit could be found.");
if (commits.size() == 1) {
mergeScenario = Optional.of(command(ReportMergeScenarioOp.class).setMergeIntoCommit(headCommit).setToMergeCommit(targetCommit).call());
if (ancestorCommit.get().equals(headCommit.getId())) {
// Fast-forward
if (headRef instanceof SymRef) {
final String currentBranch = ((SymRef) headRef).getTarget();
command(UpdateRef.class).setName(currentBranch).setNewValue(commitId).call();
headRef = (SymRef) command(UpdateSymRef.class).setName(Ref.HEAD).setNewValue(currentBranch).call().get();
} else {
headRef = command(UpdateRef.class).setName(headRef.getName()).setNewValue(commitId).call().get();
}
workingTree().updateWorkHead(commitId);
index().updateStageHead(commitId);
subProgress.complete();
changed = true;
continue;
} else if (ancestorCommit.get().equals(commitId)) {
continue;
}
}
// get changes
Iterator<DiffEntry> diff = command(DiffTree.class).setOldTree(ancestorCommit.get()).setNewTree(targetCommit.getId()).setReportTrees(true).call();
// stage changes
index().stage(new SubProgressListener(subProgress, 100.f), diff, 0);
changed = true;
fastForward = false;
workingTree().updateWorkHead(index().getTree().getId());
subProgress.complete();
}
}
if (!changed) {
throw new NothingToCommitException("The branch has already been merged.");
}
RevCommit mergeCommit = commit(fastForward);
MergeReport result = new MergeReport(mergeCommit, mergeScenario, oursId, pairs);
return result;
}
Aggregations