/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OpenFlexo is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.diff.merge;
import java.util.Vector;
import org.openflexo.diff.ComputeDiff.AdditionChange;
import org.openflexo.diff.ComputeDiff.DiffChange;
import org.openflexo.diff.ComputeDiff.ModificationChange;
import org.openflexo.diff.ComputeDiff.RemovalChange;
import org.openflexo.diff.DelimitingMethod;
import org.openflexo.diff.DiffSource;
public class MergeChange {
public enum MergeChangeSource {
Left, Right, Conflict
}
public enum MergeChangeType {
Addition, Removal, Modification
}
public enum MergeChangeAction {
AutomaticMergeResolving, KeepChange, IgnoreChange, ChooseLeft, ChooseRight, ChooseBothLeftFirst, ChooseBothRightFirst, ChooseNone, CustomEditing, Undecided
}
public enum ChangeCategory {
LEFT_ADDITION, LEFT_MODIFICATION, LEFT_REMOVAL, SMART_CONFLICT_RESOLVED, SMART_CONFLICT_UNRESOLVED, CUSTOM_EDITING_RESOLVED, CUSTOM_EDITING_UNRESOLVED, ADD_CONFLICT_RESOLVED, ADD_CONFLICT_UNRESOLVED, CONFLICT_RESOLVED, CONFLICT_UNRESOLVED, DEL_CONFLICT_RESOLVED, DEL_CONFLICT_UNRESOLVED, RIGHT_ADDITION, RIGHT_MODIFICATION, RIGHT_REMOVAL
}
public static final ChangeCategory[] CONFLICT_RESOLVED_CATEGORIES = new ChangeCategory[] { ChangeCategory.SMART_CONFLICT_RESOLVED,
ChangeCategory.CUSTOM_EDITING_RESOLVED, ChangeCategory.ADD_CONFLICT_RESOLVED, ChangeCategory.CONFLICT_RESOLVED,
ChangeCategory.DEL_CONFLICT_RESOLVED };
public static final ChangeCategory[] CONFLICT_UNRESOLVED_CATEGORIES = new ChangeCategory[] { ChangeCategory.SMART_CONFLICT_UNRESOLVED,
ChangeCategory.CUSTOM_EDITING_UNRESOLVED, ChangeCategory.ADD_CONFLICT_UNRESOLVED, ChangeCategory.CONFLICT_UNRESOLVED,
ChangeCategory.DEL_CONFLICT_UNRESOLVED };
public static final ChangeCategory[] LEFT_CATEGORIES = new ChangeCategory[] { ChangeCategory.LEFT_ADDITION,
ChangeCategory.LEFT_MODIFICATION, ChangeCategory.LEFT_REMOVAL };
public static final ChangeCategory[] RIGHT_CATEGORIES = new ChangeCategory[] { ChangeCategory.RIGHT_ADDITION,
ChangeCategory.RIGHT_MODIFICATION, ChangeCategory.RIGHT_REMOVAL };
protected int first0, last0;
protected int first1, last1;
protected int first2, last2;
protected int firstMergeIndex, lastMergeIndex;
protected String addedString;
protected String removedString;
private MergeChangeType mergeChangeType;
private MergeChangeSource mergeChangeSource;
private MergeChangeAction mergeChangeAction;
private MergeChangeResult customHandEdition = null;
private MergeChangeResult automaticMergeResult = null;
protected static MergeChange makeRightMergeChange(DiffChange aChange, int leftToOriginal) {
MergeChange returned = new MergeChange();
returned.first0 = aChange.getFirst0() - leftToOriginal;
returned.last0 = aChange.getLast0() - leftToOriginal;
returned.first1 = aChange.getFirst0();
returned.last1 = aChange.getLast0();
returned.first2 = aChange.getFirst1();
returned.last2 = aChange.getLast1();
if (aChange instanceof ModificationChange) {
returned.mergeChangeType = MergeChangeType.Modification;
}
if (aChange instanceof AdditionChange) {
returned.mergeChangeType = MergeChangeType.Addition;
}
if (aChange instanceof RemovalChange) {
returned.mergeChangeType = MergeChangeType.Removal;
}
returned.mergeChangeSource = MergeChangeSource.Right;
returned.mergeChangeAction = MergeChangeAction.KeepChange;
return returned;
}
protected static MergeChange makeLeftMergeChange(DiffChange aChange, int originalToRight) {
MergeChange returned = new MergeChange();
returned.first0 = aChange.getFirst0();
returned.last0 = aChange.getLast0();
returned.first1 = aChange.getFirst1();
returned.last1 = aChange.getLast1();
returned.first2 = aChange.getFirst1() - originalToRight;
returned.last2 = aChange.getLast1() - originalToRight;
if (aChange instanceof ModificationChange) {
returned.mergeChangeType = MergeChangeType.Modification;
}
if (aChange instanceof AdditionChange) {
returned.mergeChangeType = MergeChangeType.Removal;
}
if (aChange instanceof RemovalChange) {
returned.mergeChangeType = MergeChangeType.Addition;
}
returned.mergeChangeSource = MergeChangeSource.Left;
returned.mergeChangeAction = MergeChangeAction.KeepChange;
return returned;
}
protected static MergeChange makeMergeChange(Vector<MergeChange> changes, MergeChangeAction defaultAction) {
MergeChange returned = new MergeChange();
for (MergeChange change : changes) {
if (returned.getFirst0() == -1 || returned.getFirst0() >= change.getFirst0() && change.getFirst0() <= change.getLast0()) {
returned.setFirst0(change.getFirst0());
}
if (returned.getFirst1() == -1 || returned.getFirst1() >= change.getFirst1() && change.getFirst1() <= change.getLast1()) {
returned.setFirst1(change.getFirst1());
}
if (returned.getFirst2() == -1 || returned.getFirst2() >= change.getFirst2() && change.getFirst2() <= change.getLast2()) {
returned.setFirst2(change.getFirst2());
}
if (returned.getLast0() == -1 || returned.getLast0() <= change.getLast0() && change.getFirst0() <= change.getLast0()) {
returned.setLast0(change.getLast0());
}
if (returned.getLast1() == -1 || returned.getLast1() <= change.getLast1() && change.getFirst1() <= change.getLast1()) {
returned.setLast1(change.getLast1());
}
if (returned.getLast2() == -1 || returned.getLast2() <= change.getLast2() && change.getFirst2() <= change.getLast2()) {
returned.setLast2(change.getLast2());
}
}
returned.mergeChangeSource = MergeChangeSource.Conflict;
returned.mergeChangeAction = defaultAction;
return returned;
}
protected static MergeChange makeLeftMergeChange(Vector<DiffChange> changes, int originalToRight, Vector<DiffChange> oppositeChanges,
int leftToOriginal, MergeChangeAction defaultAction) {
Vector<MergeChange> changesToMerge = new Vector<MergeChange>();
for (DiffChange diffChange : changes) {
MergeChange change = makeLeftMergeChange(diffChange, originalToRight);
changesToMerge.add(change);
}
MergeChange returned = makeMergeChange(changesToMerge, defaultAction);
changesToMerge = new Vector<MergeChange>();
for (DiffChange diffChange : oppositeChanges) {
MergeChange change = makeRightMergeChange(diffChange, leftToOriginal);
changesToMerge.add(change);
}
MergeChange oppositeMerge = makeMergeChange(changesToMerge, defaultAction);
if (oppositeMerge.getFirst1() >= returned.getFirst1() && oppositeMerge.getLast1() <= returned.getLast1()) {
int d = oppositeMerge.getLast2() - oppositeMerge.getFirst2() - oppositeMerge.getLast1() + oppositeMerge.getFirst1();
if (Merge.debug) {
System.out.println("Including case from left d=" + d);
}
returned.setLast2(returned.getLast2() + d);
returned.delta = d;
}
return returned;
}
protected int delta = 0;
protected static MergeChange makeRightMergeChange(Vector<DiffChange> changes, int leftToOriginal, Vector<DiffChange> oppositeChanges,
int originalToRight, MergeChangeAction defaultAction) {
Vector<MergeChange> changesToMerge = new Vector<MergeChange>();
for (DiffChange diffChange : changes) {
MergeChange change = makeRightMergeChange(diffChange, leftToOriginal);
changesToMerge.add(change);
}
MergeChange returned = makeMergeChange(changesToMerge, defaultAction);
changesToMerge = new Vector<MergeChange>();
for (DiffChange diffChange : oppositeChanges) {
MergeChange change = makeLeftMergeChange(diffChange, originalToRight);
changesToMerge.add(change);
}
MergeChange oppositeMerge = makeMergeChange(changesToMerge, defaultAction);
if (oppositeMerge.getFirst1() >= returned.getFirst1() && oppositeMerge.getLast1() <= returned.getLast1()) {
int d = oppositeMerge.getLast0() - oppositeMerge.getFirst0() - oppositeMerge.getLast1() + oppositeMerge.getFirst1();
if (Merge.debug) {
System.out.println("Including case from right d=" + d);
}
returned.setLast0(returned.getLast0() + d);
returned.delta = d;
}
return returned;
}
protected MergeChange() {
first0 = -1;
last0 = -1;
first1 = -1;
last1 = -1;
first2 = -1;
last2 = -1;
}
public int getFirst0() {
return first0;
}
public int getFirst2() {
return first2;
}
public int getLast0() {
return last0;
}
public int getLast2() {
return last2;
}
private String _debug;
public String toNiceString() {
return getMergeChangeTypeAsString() + " " + getMergeChangeSourceAsString() + " " + first0 + "," + last0 + "/" + first1 + ","
+ last1 + "/" + first2 + "," + last2;
}
public String toDebugString() {
return toNiceString() + " " + getDebug();
}
@Override
public String toString() {
if (Merge.debug) {
return toDebugString();
}
return toNiceString();
}
private String getMergeChangeTypeAsString() {
if (getMergeChangeType() == MergeChangeType.Addition) {
return "ADDITION";
} else if (getMergeChangeType() == MergeChangeType.Removal) {
return "REMOVAL";
} else if (getMergeChangeType() == MergeChangeType.Modification) {
return "MODIFICATION";
}
return "???";
}
private String getMergeChangeSourceAsString() {
if (getMergeChangeSource() == MergeChangeSource.Left) {
return "LEFT";
} else if (getMergeChangeSource() == MergeChangeSource.Right) {
return "RIGHT";
} else if (getMergeChangeSource() == MergeChangeSource.Conflict) {
return "CONFLICT";
}
return "???";
}
public MergeChangeSource getMergeChangeSource() {
return mergeChangeSource;
}
public MergeChangeType getMergeChangeType() {
return mergeChangeType;
}
public void setMergeChangeSource(MergeChangeSource mergeChangeSource) {
this.mergeChangeSource = mergeChangeSource;
}
public void setMergeChangeType(MergeChangeType mergeChangeType) {
this.mergeChangeType = mergeChangeType;
}
public void setFirst0(int first0) {
this.first0 = first0;
}
public void setFirst2(int first1) {
this.first2 = first1;
}
public void setLast0(int last0) {
this.last0 = last0;
}
public void setLast2(int last1) {
this.last2 = last1;
}
public int getFirst1() {
return first1;
}
public void setFirst1(int first1) {
this.first1 = first1;
}
public int getLast1() {
return last1;
}
public void setLast1(int last1) {
this.last1 = last1;
}
public String getDebug() {
return _debug;
}
public void setDebug(String debug) {
_debug = debug;
}
private Merge _merge;
public String getOriginalText() {
StringBuffer sb = new StringBuffer();
for (String line : getOriginalTextLines()) {
sb.append(line + "\n");
}
return sb.toString();
}
private String _tokenizedLeftText = null;
public String getTokenizedLeftText() {
if (_tokenizedLeftText == null) {
StringBuffer sb = new StringBuffer();
for (String line : getLeftTextLines()) {
sb.append(line + "\n");
}
_tokenizedLeftText = sb.toString();
}
return _tokenizedLeftText;
}
private String _tokenizedRightText = null;
public String getTokenizedRightText() {
if (_tokenizedRightText == null) {
StringBuffer sb = new StringBuffer();
for (String line : getRightTextLines()) {
sb.append(line + "\n");
}
_tokenizedRightText = sb.toString();
}
return _tokenizedRightText;
}
public String[] getOriginalTextLines() {
String[] returned = new String[getLast1() - getFirst1() + 1];
;
for (int i = getFirst1(); i <= getLast1(); i++) {
returned[i - getFirst1()] = _merge.getOriginalSource().tokenValueAt(i);
}
return returned;
}
public String[] getLeftTextLines() {
String[] returned = new String[getLast0() - getFirst0() + 1];
;
for (int i = getFirst0(); i <= getLast0(); i++) {
returned[i - getFirst0()] = _merge.getLeftSource().tokenValueAt(i);
}
return returned;
}
public String[] getRightTextLines() {
String[] returned = new String[getLast2() - getFirst2() + 1];
;
for (int i = getFirst2(); i <= getLast2(); i++) {
returned[i - getFirst2()] = _merge.getRightSource().tokenValueAt(i);
}
return returned;
}
public class MergeChangeResult {
public String merge;
public int tokensNb;
private String[] _significativeTokens;
public MergeChangeResult(String merge, int tokensNb) {
this.merge = merge;
this.tokensNb = tokensNb;
}
public String[] getSignificativeTokens() {
if (_significativeTokens == null) {
_significativeTokens = new DiffSource(merge, getDelimitingMethod()).getSignificativeTokens();
}
return _significativeTokens;
}
}
private String _leftText = null;
public String getLeftText() {
if (_leftText == null) {
_leftText = _merge.getLeftSource().extractText(getFirst0(), getLast0());
}
return _leftText;
}
private String _rightText = null;
public String getRightText() {
if (_rightText == null) {
_rightText = _merge.getRightSource().extractText(getFirst2(), getLast2());
}
return _rightText;
}
public MergeChangeResult getMergeChangeResult() {
if (mergeChangeSource == MergeChangeSource.Left) {
if (mergeChangeAction == MergeChangeAction.KeepChange) {
return new MergeChangeResult(getLeftText(), getLast0() - getFirst0() + 1);
} else if (mergeChangeAction == MergeChangeAction.IgnoreChange) {
return new MergeChangeResult(getRightText(), getLast2() - getFirst2() + 1);
} else {
System.err.println("Inconsistent data in Merge " + this + " action:" + mergeChangeAction);
return null;
}
} else if (mergeChangeSource == MergeChangeSource.Right) {
if (mergeChangeAction == MergeChangeAction.KeepChange) {
return new MergeChangeResult(getRightText(), getLast2() - getFirst2() + 1);
} else if (mergeChangeAction == MergeChangeAction.IgnoreChange) {
return new MergeChangeResult(getLeftText(), getLast0() - getFirst0() + 1);
} else {
System.err.println("Inconsistent data in Merge " + this + " action:" + mergeChangeAction);
return null;
}
} else if (mergeChangeSource == MergeChangeSource.Conflict) {
if (mergeChangeAction == MergeChangeAction.ChooseLeft) {
return new MergeChangeResult(getLeftText(), getLast0() - getFirst0() + 1);
} else if (mergeChangeAction == MergeChangeAction.ChooseRight) {
return new MergeChangeResult(getRightText(), getLast2() - getFirst2() + 1);
} else if (mergeChangeAction == MergeChangeAction.ChooseNone) {
return new MergeChangeResult("", 0);
} else if (mergeChangeAction == MergeChangeAction.ChooseBothLeftFirst) {
return new MergeChangeResult(getLeftText() + getRightText(), getLast0() - getFirst0() + 1 + getLast2() - getFirst2() + 1);
} else if (mergeChangeAction == MergeChangeAction.ChooseBothRightFirst) {
return new MergeChangeResult(getRightText() + getLeftText(), getLast0() - getFirst0() + 1 + getLast2() - getFirst2() + 1);
} else if (mergeChangeAction == MergeChangeAction.CustomEditing) {
if (customHandEdition != null) {
return customHandEdition;
}
return new MergeChangeResult("", 0);
} else if (mergeChangeAction == MergeChangeAction.AutomaticMergeResolving) {
if (getAutomaticResolvedMerge() != null) {
return getAutomaticResolvedMerge();
}
// If automerge failed, take left
return new MergeChangeResult(getLeftText(), getLast0() - getFirst0() + 1);
// If automerge failed, choose none
// return new MergeChangeResult("",0);
// If automerge failed, take both, left first
/*return new MergeChangeResult(
getLeftText()
+getRightText(),
getLast0()-getFirst0()+1+getLast2()-getFirst2()+1); */
} else if (mergeChangeAction == MergeChangeAction.Undecided) {
return new MergeChangeResult("", 0);
} else {
System.err.println("Inconsistent data in Merge " + this + " action:" + mergeChangeAction);
return null;
}
}
return new MergeChangeResult("", 0);
}
public Merge getMerge() {
return _merge;
}
public void setMerge(Merge merge) {
_merge = merge;
}
public int getFirstMergeIndex() {
return firstMergeIndex;
}
public void setFirstMergeIndex(int firstMerge) {
this.firstMergeIndex = firstMerge;
}
public int getLastMergeIndex() {
return lastMergeIndex;
}
public void setLastMergeIndex(int lastMerge) {
this.lastMergeIndex = lastMerge;
}
public MergeChangeAction getMergeChangeAction() {
return mergeChangeAction;
}
public void setMergeChangeAction(MergeChangeAction mergeChangeAction) {
this.mergeChangeAction = mergeChangeAction;
triedToAutomaticallyResolve = false;
getMerge().notifyMergeChange(this);
}
public MergeChangeResult getCustomHandEdition() {
return customHandEdition;
}
public void setCustomHandEdition(String customHandEdition) {
DiffSource diffSource = new DiffSource(customHandEdition, getDelimitingMethod());
this.customHandEdition = new MergeChangeResult(customHandEdition, diffSource.tokensCount());
}
public DelimitingMethod getDelimitingMethod() {
return getMerge().getDelimitingMethod();
}
private boolean triedToAutomaticallyResolve = false;
private void tryToAutomaticallyResolve() {
if (!triedToAutomaticallyResolve && getMerge().getDocumentType().getAutomaticMergeResolvingModel() != null) {
getMerge().getDocumentType().getAutomaticMergeResolvingModel().resolve(this);
triedToAutomaticallyResolve = true;
/*String mergeResult = "Yes";
//= getMerge().getDocumentType().getAutomaticMergeResolvingModel().resolve(this);
if (mergeResult != null) {
DiffSource diffSource = new DiffSource(mergeResult,getDelimitingMethod());
automaticMergeResult = new MergeChangeResult(mergeResult,diffSource.tokensCount());
}
else {
automaticMergeResult = null;
}*/
}
}
public boolean isResolved() {
if (mergeChangeSource == MergeChangeSource.Left || mergeChangeSource == MergeChangeSource.Right) {
return true;
}
// else conflict
if (mergeChangeAction == MergeChangeAction.Undecided) {
return false;
}
if (mergeChangeAction == MergeChangeAction.AutomaticMergeResolving) {
tryToAutomaticallyResolve();
return automaticMergeResult != null;
}
if (mergeChangeAction == MergeChangeAction.CustomEditing) {
return customHandEdition != null;
}
return true;
}
public MergeChangeResult getAutomaticResolvedMerge() {
tryToAutomaticallyResolve();
return automaticMergeResult;
}
protected void setAutomaticResolvedMerge(String mergeResult) {
DiffSource diffSource = new DiffSource(mergeResult, getDelimitingMethod());
automaticMergeResult = new MergeChangeResult(mergeResult, diffSource.tokensCount());
}
private DetailedMerge _detailedMerge;
public DetailedMerge getDetailedMerge() {
if (_detailedMerge == null) {
_detailedMerge = new DetailedMerge(this);
}
return _detailedMerge;
}
private String _automaticMergeReason = null;
public String getAutomaticMergeReason() {
return _automaticMergeReason;
}
protected void setAutomaticMergeReason(String automaticMergeReason) {
_automaticMergeReason = automaticMergeReason;
}
public ChangeCategory category() {
if (getMergeChangeSource() == MergeChange.MergeChangeSource.Left) {
if (getMergeChangeType() == MergeChange.MergeChangeType.Addition) {
return ChangeCategory.LEFT_ADDITION;
} else if (getMergeChangeType() == MergeChange.MergeChangeType.Modification) {
return ChangeCategory.LEFT_MODIFICATION;
} else if (getMergeChangeType() == MergeChange.MergeChangeType.Removal) {
return ChangeCategory.LEFT_REMOVAL;
}
} else if (getMergeChangeSource() == MergeChange.MergeChangeSource.Conflict) {
if (getMergeChangeAction() == MergeChangeAction.AutomaticMergeResolving) {
if (isResolved()) {
return ChangeCategory.SMART_CONFLICT_RESOLVED;
} else {
return ChangeCategory.SMART_CONFLICT_UNRESOLVED;
}
} else if (getMergeChangeAction() == MergeChangeAction.CustomEditing) {
if (isResolved()) {
return ChangeCategory.CUSTOM_EDITING_RESOLVED;
} else {
return ChangeCategory.CUSTOM_EDITING_UNRESOLVED;
}
}
if (getMergeChangeType() == MergeChange.MergeChangeType.Addition) {
if (isResolved()) {
return ChangeCategory.ADD_CONFLICT_RESOLVED;
} else {
return ChangeCategory.ADD_CONFLICT_UNRESOLVED;
}
} else if (getMergeChangeType() == MergeChange.MergeChangeType.Modification) {
if (isResolved()) {
return ChangeCategory.CONFLICT_RESOLVED;
} else {
return ChangeCategory.CONFLICT_UNRESOLVED;
}
} else if (getMergeChangeType() == MergeChange.MergeChangeType.Removal) {
if (isResolved()) {
return ChangeCategory.DEL_CONFLICT_RESOLVED;
} else {
return ChangeCategory.DEL_CONFLICT_UNRESOLVED;
}
}
} else if (getMergeChangeSource() == MergeChange.MergeChangeSource.Right) {
if (getMergeChangeType() == MergeChange.MergeChangeType.Addition) {
return ChangeCategory.RIGHT_ADDITION;
} else if (getMergeChangeType() == MergeChange.MergeChangeType.Modification) {
return ChangeCategory.RIGHT_MODIFICATION;
} else if (getMergeChangeType() == MergeChange.MergeChangeType.Removal) {
return ChangeCategory.RIGHT_REMOVAL;
}
}
return null;
}
}