/* 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: * Victor Olaya (Boundless) - initial implementation */ package org.locationtech.geogig.api.plumbing.diff; import org.locationtech.geogig.storage.FieldType; import org.locationtech.geogig.storage.text.TextValueSerializer; import com.google.common.base.Objects; import com.google.common.base.Optional; import com.google.common.base.Preconditions; import com.vividsolutions.jts.geom.Geometry; /** * An implementation of AttributeDiff to be used with attributes containing geometries * */ public class GeometryAttributeDiff implements AttributeDiff { private TYPE type; private Optional<Geometry> oldGeometry; private Optional<Geometry> newGeometry; private LCSGeometryDiffImpl diff; public GeometryAttributeDiff(Optional<Geometry> oldGeom, Optional<Geometry> newGeom) { Preconditions.checkArgument(oldGeom != null || newGeom != null); oldGeometry = oldGeom; newGeometry = newGeom; if (newGeom == null || !newGeom.isPresent()) { type = TYPE.REMOVED; } else if (oldGeom == null || !oldGeom.isPresent()) { type = TYPE.ADDED; } else if (oldGeom.equals(newGeom)) { type = TYPE.NO_CHANGE; diff = new LCSGeometryDiffImpl(oldGeom, newGeom); } else { type = TYPE.MODIFIED; diff = new LCSGeometryDiffImpl(oldGeom, newGeom); } } public GeometryAttributeDiff(LCSGeometryDiffImpl diff) { type = TYPE.MODIFIED; this.diff = diff; } public GeometryAttributeDiff(String s) { String[] tokens = s.split("\t"); if (tokens[0].equals("M")) { type = TYPE.MODIFIED; diff = new LCSGeometryDiffImpl(s.substring(s.indexOf("\t") + 1)); } else if (tokens[0].equals("A")) { Preconditions.checkArgument(tokens.length == 3); type = TYPE.ADDED; newGeometry = Optional.fromNullable((Geometry) TextValueSerializer.fromString( FieldType.forBinding(Geometry.class), tokens[1])); } else if (tokens[0].equals("R")) { Preconditions.checkArgument(tokens.length == 3); type = TYPE.REMOVED; oldGeometry = Optional.fromNullable((Geometry) TextValueSerializer.fromString( FieldType.forBinding(Geometry.class), tokens[1])); } else { throw new IllegalArgumentException("Wrong difference definition:" + s); } } @Override public Optional<?> getOldValue() { return oldGeometry; } @Override public Optional<?> getNewValue() { return newGeometry; } @Override public TYPE getType() { return type; } @Override public AttributeDiff reversed() { if (type == TYPE.MODIFIED) { return new GeometryAttributeDiff(this.diff.reversed()); } else { return new GeometryAttributeDiff(oldGeometry, newGeometry); } } @SuppressWarnings("unchecked") @Override public Optional<?> applyOn(Optional<?> obj) { Preconditions.checkState(canBeAppliedOn(obj)); switch (type) { case ADDED: return newGeometry; case REMOVED: return null; case MODIFIED: default: return diff.applyOn((Optional<Geometry>) obj); } } @SuppressWarnings("unchecked") @Override public boolean canBeAppliedOn(Optional<?> obj) { switch (type) { case ADDED: return obj == null; case REMOVED: return obj.equals(oldGeometry); case MODIFIED: default: return diff.canBeAppliedOn((Optional<Geometry>) obj); } } public String toString() { switch (type) { case ADDED: return "[MISSING] -> " + TextValueSerializer.asString(Optional.fromNullable((Object) newGeometry .orNull())); case REMOVED: return TextValueSerializer .asString(Optional.fromNullable((Object) oldGeometry.orNull())) + " -> [MISSING]"; case MODIFIED: default: return diff.toString(); } } @Override public String asText() { switch (type) { case ADDED: return type.name().toCharArray()[0] + "\t" + TextValueSerializer.asString(Optional.fromNullable((Object) newGeometry .orNull())); case REMOVED: return type.name().toCharArray()[0] + "\t" + TextValueSerializer.asString(Optional.fromNullable((Object) oldGeometry .orNull())); case MODIFIED: default: return type.name().toCharArray()[0] + "\t" + diff.asText(); } } @Override public boolean equals(Object o) { if (!(o instanceof GeometryAttributeDiff)) { return false; } GeometryAttributeDiff d = (GeometryAttributeDiff) o; if (oldGeometry == null && newGeometry == null) { return Objects.equal(oldGeometry, oldGeometry) && Objects.equal(newGeometry, d.newGeometry) && Objects.equal(type, d.type); } else { return diff.equals(d.diff); } } /** * Returns the difference corresponding to the case of a modified attributed. If the attribute * is of type ADDED or REMOVED, this method will return null */ public LCSGeometryDiffImpl getDiff() { return diff; } @Override public boolean conflicts(AttributeDiff ad) { if (!(ad instanceof GeometryAttributeDiff)) { return true; } GeometryAttributeDiff gad = (GeometryAttributeDiff) ad; if (TYPE.REMOVED.equals(ad.getType()) && TYPE.REMOVED.equals(getType())) { return false; } if (TYPE.MODIFIED.equals(ad.getType()) && TYPE.MODIFIED.equals(getType())) { if (gad.diff.equals(diff)) { return false; } else { return !gad.canBeAppliedOn(newGeometry); } } if (TYPE.ADDED.equals(ad.getType()) && TYPE.ADDED.equals(getType())) { return !gad.newGeometry.equals(newGeometry); } return true; } }