package org.eclipse.text.edits;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.Region;
public final class MoveSourceEdit extends TextEdit {
private MoveTargetEdit fTarget;
private ISourceModifier fModifier;
private String fSourceContent;
private MultiTextEdit fSourceRoot;
public MoveSourceEdit(int offset, int length) {
super(offset, length);
}
public MoveSourceEdit(int offset, int length, MoveTargetEdit target) {
this(offset, length);
setTargetEdit(target);
}
private MoveSourceEdit(MoveSourceEdit other) {
super(other);
if (other.fModifier != null)
fModifier= other.fModifier.copy();
}
public MoveTargetEdit getTargetEdit() {
return fTarget;
}
public void setTargetEdit(MoveTargetEdit edit) {
fTarget= edit;
fTarget.setSourceEdit(this);
}
public ISourceModifier getSourceModifier() {
return fModifier;
}
public void setSourceModifier(ISourceModifier modifier) {
fModifier= modifier;
}
String getContent() {
if (fSourceContent == null)
return "";
return fSourceContent;
}
MultiTextEdit getSourceRoot() {
return fSourceRoot;
}
void clearContent() {
fSourceContent= null;
fSourceRoot= null;
}
@Override
protected TextEdit doCopy() {
return new MoveSourceEdit(this);
}
@Override
protected void postProcessCopy(TextEditCopier copier) {
if (fTarget != null) {
MoveSourceEdit source= (MoveSourceEdit)copier.getCopy(this);
MoveTargetEdit target= (MoveTargetEdit)copier.getCopy(fTarget);
if (source != null && target != null)
source.setTargetEdit(target);
}
}
@Override
protected void accept0(TextEditVisitor visitor) {
boolean visitChildren= visitor.visit(this);
if (visitChildren) {
acceptChildren(visitor);
}
}
@Override
int traverseConsistencyCheck(TextEditProcessor processor, IDocument document, List<List<TextEdit>> sourceEdits) {
int result= super.traverseConsistencyCheck(processor, document, sourceEdits);
if (fSourceContent == null) {
if (sourceEdits.size() <= result) {
List<TextEdit> list= new ArrayList<>();
list.add(this);
for (int i= sourceEdits.size(); i < result; i++)
sourceEdits.add(null);
sourceEdits.add(list);
} else {
List<TextEdit> list= sourceEdits.get(result);
if (list == null) {
list= new ArrayList<>();
sourceEdits.add(result, list);
}
list.add(this);
}
}
return result;
}
@Override
void performConsistencyCheck(TextEditProcessor processor, IDocument document) throws MalformedTreeException {
if (fTarget == null)
throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.no_target"));
if (fTarget.getSourceEdit() != this)
throw new MalformedTreeException(getParent(), this, TextEditMessages.getString("MoveSourceEdit.different_source"));
}
@Override
void traverseSourceComputation(TextEditProcessor processor, IDocument document) {
performSourceComputation(processor, document);
}
@Override
void performSourceComputation(TextEditProcessor processor, IDocument document) {
try {
TextEdit[] children= removeChildren();
if (children.length > 0) {
String content= document.get(getOffset(), getLength());
EditDocument subDocument= new EditDocument(content);
fSourceRoot= new MultiTextEdit(getOffset(), getLength());
fSourceRoot.addChildren(children);
fSourceRoot.internalMoveTree(-getOffset());
int processingStyle= getStyle(processor);
TextEditProcessor subProcessor= TextEditProcessor.createSourceComputationProcessor(subDocument, fSourceRoot, processingStyle);
subProcessor.performEdits();
if (needsTransformation())
applyTransformation(subDocument, processingStyle);
fSourceContent= subDocument.get();
} else {
fSourceContent= document.get(getOffset(), getLength());
if (needsTransformation()) {
EditDocument subDocument= new EditDocument(fSourceContent);
applyTransformation(subDocument, getStyle(processor));
fSourceContent= subDocument.get();
}
}
} catch (BadLocationException cannotHappen) {
Assert.isTrue(false);
}
}
private int getStyle(TextEditProcessor processor) {
if ((processor.getStyle() & TextEdit.UPDATE_REGIONS) != 0)
return TextEdit.UPDATE_REGIONS;
return TextEdit.NONE;
}
@Override
int performDocumentUpdating(IDocument document) throws BadLocationException {
document.replace(getOffset(), getLength(), "");
fDelta= -getLength();
return fDelta;
}
@Override
boolean deleteChildren() {
return false;
}
private boolean needsTransformation() {
return fModifier != null;
}
private void applyTransformation(IDocument document, int style) throws MalformedTreeException {
if ((style & TextEdit.UPDATE_REGIONS) != 0 && fSourceRoot != null) {
Map<TextEdit, TextEdit> editMap= new HashMap<>();
TextEdit newEdit= createEdit(editMap);
List<ReplaceEdit> replaces= new ArrayList<>(Arrays.asList(fModifier.getModifications(document.get())));
insertEdits(newEdit, replaces);
try {
newEdit.apply(document, style);
} catch (BadLocationException cannotHappen) {
Assert.isTrue(false);
}
restorePositions(editMap);
} else {
MultiTextEdit newEdit= new MultiTextEdit(0, document.getLength());
TextEdit[] replaces= fModifier.getModifications(document.get());
for (TextEdit replace : replaces) {
newEdit.addChild(replace);
}
try {
newEdit.apply(document, style);
} catch (BadLocationException cannotHappen) {
Assert.isTrue(false);
}
}
}
private TextEdit createEdit(Map<TextEdit, TextEdit> editMap) {
MultiTextEdit result= new MultiTextEdit(0, fSourceRoot.getLength());
editMap.put(result, fSourceRoot);
createEdit(fSourceRoot, result, editMap);
return result;
}
private static void createEdit(TextEdit source, TextEdit target, Map<TextEdit, TextEdit> editMap) {
TextEdit[] children= source.getChildren();
for (TextEdit child : children) {
if (child.isDeleted())
continue;
RangeMarker marker= new RangeMarker(child.getOffset(), child.getLength());
target.addChild(marker);
editMap.put(marker, child);
createEdit(child, marker, editMap);
}
}
private void insertEdits(TextEdit root, List<ReplaceEdit> edits) {
while(!edits.isEmpty()) {
ReplaceEdit edit= edits.remove(0);
insert(root, edit, edits);
}
}
private static void insert(TextEdit parent, ReplaceEdit edit, List<ReplaceEdit> edits) {
if (!parent.hasChildren()) {
parent.addChild(edit);
return;
}
TextEdit[] children= parent.getChildren();
int removed= 0;
for (int i= 0; i < children.length; i++) {
TextEdit child= children[i];
if (child.covers(edit)) {
insert(child, edit, edits);
return;
} else if (edit.covers(child)) {
parent.removeChild(i - removed++);
edit.addChild(child);
} else {
IRegion intersect= intersect(edit, child);
if (intersect != null) {
ReplaceEdit[] splits= splitEdit(edit, intersect);
insert(child, splits[0], edits);
edits.add(splits[1]);
return;
}
}
}
parent.addChild(edit);
}
public static IRegion intersect(TextEdit op1, TextEdit op2) {
int offset1= op1.getOffset();
int length1= op1.getLength();
int end1= offset1 + length1 - 1;
int offset2= op2.getOffset();
if (end1 < offset2)
return null;
int length2= op2.getLength();
int end2= offset2 + length2 - 1;
if (end2 < offset1)
return null;
int end= Math.min(end1, end2);
if (offset1 < offset2) {
return new Region(offset2, end - offset2 + 1);
}
return new Region(offset1, end - offset1 + 1);
}
private static ReplaceEdit[] splitEdit(ReplaceEdit edit, IRegion intersect) {
if (edit.getOffset() != intersect.getOffset())
return splitIntersectRight(edit, intersect);
return splitIntersectLeft(edit, intersect);
}
private static ReplaceEdit[] splitIntersectRight(ReplaceEdit edit, IRegion intersect) {
ReplaceEdit[] result= new ReplaceEdit[2];
result[0]= new ReplaceEdit(intersect.getOffset(), intersect.getLength(), "");
result[1]= new ReplaceEdit(
edit.getOffset(),
intersect.getOffset() - edit.getOffset(),
edit.getText());
return result;
}
private static ReplaceEdit[] splitIntersectLeft(ReplaceEdit edit, IRegion intersect) {
ReplaceEdit[] result= new ReplaceEdit[2];
result[0]= new ReplaceEdit(intersect.getOffset(), intersect.getLength(), edit.getText());
result[1]= new ReplaceEdit(
intersect.getOffset() + intersect.getLength(),
edit.getLength() - intersect.getLength(),
"");
return result;
}
private static void restorePositions(Map<TextEdit, TextEdit> editMap) {
for (Entry<TextEdit, TextEdit> entry: editMap.entrySet()) {
TextEdit marker = entry.getKey();
TextEdit edit= entry.getValue();
if (marker.isDeleted()) {
edit.markAsDeleted();
} else {
edit.adjustOffset(marker.getOffset() - edit.getOffset());
edit.adjustLength(marker.getLength() - edit.getLength());
}
}
}
}