/* Copyright (c) 2013 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:
* Victor Olaya (Boundless) - initial implementation
*/
package org.locationtech.geogig.cli.porcelain;
import static org.fusesource.jansi.Ansi.Color.BLUE;
import static org.fusesource.jansi.Ansi.Color.GREEN;
import static org.fusesource.jansi.Ansi.Color.RED;
import static org.fusesource.jansi.Ansi.Color.YELLOW;
import static org.locationtech.geogig.api.plumbing.diff.DiffEntry.ChangeType.ADDED;
import static org.locationtech.geogig.api.plumbing.diff.DiffEntry.ChangeType.MODIFIED;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import jline.console.ConsoleReader;
import org.fusesource.jansi.Ansi;
import org.locationtech.geogig.api.GeoGIG;
import org.locationtech.geogig.api.NodeRef;
import org.locationtech.geogig.api.ObjectId;
import org.locationtech.geogig.api.RevFeature;
import org.locationtech.geogig.api.RevFeatureType;
import org.locationtech.geogig.api.RevObject;
import org.locationtech.geogig.api.plumbing.DiffFeature;
import org.locationtech.geogig.api.plumbing.RevObjectParse;
import org.locationtech.geogig.api.plumbing.diff.AttributeDiff;
import org.locationtech.geogig.api.plumbing.diff.DiffEntry;
import org.locationtech.geogig.api.plumbing.diff.DiffEntry.ChangeType;
import org.locationtech.geogig.api.plumbing.diff.FeatureDiff;
import org.locationtech.geogig.api.plumbing.diff.GeometryAttributeDiff;
import org.locationtech.geogig.api.plumbing.diff.LCSGeometryDiffImpl;
import org.locationtech.geogig.cli.AnsiDecorator;
import org.locationtech.geogig.storage.text.TextValueSerializer;
import org.opengis.feature.type.PropertyDescriptor;
import com.google.common.base.Optional;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableList;
interface DiffPrinter {
/**
* @param geogig
* @param console
* @param entry
* @throws IOException
*/
void print(GeoGIG geogig, ConsoleReader console, DiffEntry entry) throws IOException;
}
class SummaryDiffPrinter implements DiffPrinter {
@Override
public void print(GeoGIG geogig, ConsoleReader console, DiffEntry entry) throws IOException {
Ansi ansi = AnsiDecorator.newAnsi(console.getTerminal().isAnsiSupported());
final NodeRef newObject = entry.getNewObject();
final NodeRef oldObject = entry.getOldObject();
String oldMode = oldObject == null ? shortOid(ObjectId.NULL) : shortOid(oldObject
.getMetadataId());
String newMode = newObject == null ? shortOid(ObjectId.NULL) : shortOid(newObject
.getMetadataId());
String oldId = oldObject == null ? shortOid(ObjectId.NULL) : shortOid(oldObject.objectId());
String newId = newObject == null ? shortOid(ObjectId.NULL) : shortOid(newObject.objectId());
ansi.a(oldMode).a(" ");
ansi.a(newMode).a(" ");
ansi.a(oldId).a(" ");
ansi.a(newId).a(" ");
ansi.fg(entry.changeType() == ADDED ? GREEN : (entry.changeType() == MODIFIED ? YELLOW
: RED));
char type = entry.changeType().toString().charAt(0);
ansi.a(" ").a(type).reset();
ansi.a(" ").a(formatPath(entry));
console.println(ansi.toString());
}
private static String shortOid(ObjectId oid) {
return new StringBuilder(oid.toString().substring(0, 6)).append("...").toString();
}
private static String formatPath(DiffEntry entry) {
String path;
NodeRef oldObject = entry.getOldObject();
NodeRef newObject = entry.getNewObject();
if (oldObject == null) {
path = newObject.path();
} else if (newObject == null) {
path = oldObject.path();
} else {
if (oldObject.path().equals(newObject.path())) {
path = oldObject.path();
} else {
path = oldObject.path() + " -> " + newObject.path();
}
}
return path;
}
}
class FullDiffPrinter implements DiffPrinter {
SummaryDiffPrinter summaryPrinter = new SummaryDiffPrinter();
private boolean noGeom;
private boolean noHeader;
public FullDiffPrinter(boolean noGeom, boolean noHeader) {
this.noGeom = noGeom;
this.noHeader = noHeader;
}
@Override
public void print(GeoGIG geogig, ConsoleReader console, DiffEntry diffEntry) throws IOException {
if (!noHeader) {
summaryPrinter.print(geogig, console, diffEntry);
}
if (diffEntry.changeType() == ChangeType.MODIFIED) {
FeatureDiff diff = geogig.command(DiffFeature.class)
.setNewVersion(Suppliers.ofInstance(diffEntry.getNewObject()))
.setOldVersion(Suppliers.ofInstance(diffEntry.getOldObject())).call();
Map<PropertyDescriptor, AttributeDiff> diffs = diff.getDiffs();
Ansi ansi = AnsiDecorator.newAnsi(console.getTerminal().isAnsiSupported());
Set<Entry<PropertyDescriptor, AttributeDiff>> entries = diffs.entrySet();
Iterator<Entry<PropertyDescriptor, AttributeDiff>> iter = entries.iterator();
while (iter.hasNext()) {
Entry<PropertyDescriptor, AttributeDiff> entry = iter.next();
PropertyDescriptor pd = entry.getKey();
AttributeDiff ad = entry.getValue();
if (ad instanceof GeometryAttributeDiff
&& ad.getType() == org.locationtech.geogig.api.plumbing.diff.AttributeDiff.TYPE.MODIFIED
&& !noGeom) {
GeometryAttributeDiff gd = (GeometryAttributeDiff) ad;
ansi.fg(YELLOW);
ansi.a(pd.getName()).a(": ");
ansi.reset();
String text = gd.getDiff().getDiffCoordsString();
for (int i = 0; i < text.length(); i++) {
if (text.charAt(i) == '(') {
ansi.fg(GREEN);
ansi.a(text.charAt(i));
} else if (text.charAt(i) == '[') {
ansi.fg(RED);
ansi.a(text.charAt(i));
} else if (text.charAt(i) == ']' || text.charAt(i) == ')') {
ansi.a(text.charAt(i));
ansi.reset();
} else if (text.charAt(i) == LCSGeometryDiffImpl.INNER_RING_SEPARATOR
.charAt(0)
|| text.charAt(i) == LCSGeometryDiffImpl.SUBGEOM_SEPARATOR
.charAt(0)) {
ansi.fg(BLUE);
ansi.a(text.charAt(i));
ansi.reset();
} else {
ansi.a(text.charAt(i));
}
}
ansi.reset();
ansi.newline();
} else {
ansi.fg(ad.getType() == org.locationtech.geogig.api.plumbing.diff.AttributeDiff.TYPE.ADDED ? GREEN
: (ad.getType() == org.locationtech.geogig.api.plumbing.diff.AttributeDiff.TYPE.REMOVED ? RED
: YELLOW));
ansi.a(pd.getName()).a(": ").a(ad.toString());
ansi.reset();
ansi.newline();
}
}
console.println(ansi.toString());
} else if (diffEntry.changeType() == ChangeType.ADDED) {
NodeRef noderef = diffEntry.getNewObject();
RevFeatureType featureType = geogig.command(RevObjectParse.class)
.setObjectId(noderef.getMetadataId()).call(RevFeatureType.class).get();
Optional<RevObject> obj = geogig.command(RevObjectParse.class)
.setObjectId(noderef.objectId()).call();
RevFeature feature = (RevFeature) obj.get();
ImmutableList<Optional<Object>> values = feature.getValues();
int i = 0;
for (Optional<Object> value : values) {
console.println(featureType.sortedDescriptors().get(i).getName() + "\t"
+ TextValueSerializer.asString(value));
i++;
}
console.println();
}
}
}