mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-06-09 10:46:02 +02:00
First implementation.
This commit is contained in:
parent
7b1d81201d
commit
d213e4b109
1 changed files with 379 additions and 2 deletions
|
@ -12,6 +12,26 @@ package org.eclipse.cdt.internal.core.model;
|
|||
|
||||
import org.eclipse.cdt.core.model.ICElement;
|
||||
|
||||
/**
|
||||
* A C element delta biulder creates a C element delta on a C element between
|
||||
* the version of the C element at the time the comparator was created and the
|
||||
* current version of the C element.
|
||||
*
|
||||
* It performs this operation by locally caching the contents of
|
||||
* the C element when it is created. When the method buildDeltas() is called, it
|
||||
* creates a delta over the cached contents and the new contents.
|
||||
*
|
||||
* This class is similar to the JDT CElementDeltaBuilder class.
|
||||
*/
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.cdt.core.model.ICElement;
|
||||
import org.eclipse.cdt.core.model.ICElementDelta;
|
||||
import org.eclipse.cdt.core.model.IParent;
|
||||
|
||||
/**
|
||||
* A C element delta biulder creates a C element delta on a C element between
|
||||
* the version of the C element at the time the comparator was created and the
|
||||
|
@ -26,12 +46,369 @@ import org.eclipse.cdt.core.model.ICElement;
|
|||
|
||||
public class CElementDeltaBuilder {
|
||||
|
||||
/**
|
||||
* The c element handle
|
||||
*/
|
||||
ICElement cElement;
|
||||
|
||||
/**
|
||||
* The maximum depth in the C element children we should look into
|
||||
*/
|
||||
int maxDepth = Integer.MAX_VALUE;
|
||||
|
||||
/**
|
||||
* The old handle to info relationships
|
||||
*/
|
||||
Map infos;
|
||||
|
||||
/**
|
||||
* The old position info
|
||||
*/
|
||||
Map oldPositions;
|
||||
|
||||
/**
|
||||
* The new position info
|
||||
*/
|
||||
Map newPositions;
|
||||
|
||||
/**
|
||||
* Change delta
|
||||
*/
|
||||
CElementDelta delta;
|
||||
|
||||
public CElementDeltaBuilder(ICElement cElement) {
|
||||
/**
|
||||
* List of added elements
|
||||
*/
|
||||
ArrayList added;
|
||||
|
||||
/**
|
||||
* List of removed elements
|
||||
*/
|
||||
ArrayList removed;
|
||||
|
||||
/**
|
||||
* Doubly linked list item
|
||||
*/
|
||||
class ListItem {
|
||||
public ICElement previous;
|
||||
public ICElement next;
|
||||
|
||||
public ListItem(ICElement previous, ICElement next) {
|
||||
this.previous = previous;
|
||||
this.next = next;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Creates a C element comparator on a C element
|
||||
* looking as deep as necessary.
|
||||
*/
|
||||
public CElementDeltaBuilder(ICElement CElement) {
|
||||
this.cElement = CElement;
|
||||
this.initialize();
|
||||
this.recordElementInfo(CElement,0);
|
||||
}
|
||||
/**
|
||||
* Creates a C element comparator on a C element
|
||||
* looking only 'maxDepth' levels deep.
|
||||
*/
|
||||
public CElementDeltaBuilder(ICElement cElement, int maxDepth) {
|
||||
this.cElement = cElement;
|
||||
this.maxDepth = maxDepth;
|
||||
this.initialize();
|
||||
this.recordElementInfo(cElement,0);
|
||||
}
|
||||
/**
|
||||
* Repairs the positioning information
|
||||
* after an element has been added
|
||||
*/
|
||||
private void added(ICElement element) {
|
||||
this.added.add(element);
|
||||
ListItem current = this.getNewPosition(element);
|
||||
ListItem previous = null, next = null;
|
||||
if (current.previous != null)
|
||||
previous = this.getNewPosition(current.previous);
|
||||
if (current.next != null)
|
||||
next = this.getNewPosition(current.next);
|
||||
if (previous != null)
|
||||
previous.next = current.next;
|
||||
if (next != null)
|
||||
next.previous = current.previous;
|
||||
}
|
||||
/**
|
||||
* Builds the C element deltas between the old content of the translation unit
|
||||
* and its new content.
|
||||
*/
|
||||
public void buildDeltas() {
|
||||
this.recordNewPositions(this.cElement, 0);
|
||||
this.findAdditions(this.cElement, 0);
|
||||
this.findDeletions();
|
||||
this.findChangesInPositioning(this.cElement, 0);
|
||||
this.trimDelta(this.delta);
|
||||
}
|
||||
/**
|
||||
* Finds elements which have been added or changed.
|
||||
*/
|
||||
private void findAdditions(ICElement newElement, int depth) {
|
||||
CElementInfo oldInfo = this.getElementInfo(newElement);
|
||||
if (oldInfo == null && depth < this.maxDepth) {
|
||||
this.delta.added(newElement);
|
||||
added(newElement);
|
||||
} else {
|
||||
this.removeElementInfo(newElement);
|
||||
}
|
||||
|
||||
if (depth >= this.maxDepth) {
|
||||
// mark element as changed
|
||||
this.delta.changed(newElement, ICElementDelta.F_CONTENT);
|
||||
return;
|
||||
}
|
||||
|
||||
CElementInfo newInfo = null;
|
||||
newInfo = ((CElement)newElement).getElementInfo();
|
||||
|
||||
this.findContentChange(oldInfo, newInfo, newElement);
|
||||
|
||||
if (oldInfo != null && newElement instanceof IParent) {
|
||||
|
||||
ICElement[] children = newInfo.getChildren();
|
||||
if (children != null) {
|
||||
int length = children.length;
|
||||
for(int i = 0; i < length; i++) {
|
||||
this.findAdditions(children[i], depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Looks for changed positioning of elements.
|
||||
*/
|
||||
private void findChangesInPositioning(ICElement element, int depth) {
|
||||
if (depth >= this.maxDepth || this.added.contains(element) || this.removed.contains(element))
|
||||
return;
|
||||
|
||||
if (!isPositionedCorrectly(element)) {
|
||||
this.delta.removed(element);
|
||||
this.delta.added(element);
|
||||
}
|
||||
|
||||
if (element instanceof IParent) {
|
||||
CElementInfo info = null;
|
||||
info = ((CElement)element).getElementInfo();
|
||||
|
||||
ICElement[] children = info.getChildren();
|
||||
if (children != null) {
|
||||
int length = children.length;
|
||||
for(int i = 0; i < length; i++) {
|
||||
this.findChangesInPositioning(children[i], depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The elements are equivalent, but might have content changes.
|
||||
*/
|
||||
private void findContentChange(CElementInfo oldInfo, CElementInfo newInfo, ICElement newElement) {
|
||||
if (oldInfo instanceof SourceManipulationInfo && newInfo instanceof SourceManipulationInfo) {
|
||||
SourceManipulationInfo oldSourceInfo = (SourceManipulationInfo) oldInfo;
|
||||
SourceManipulationInfo newSourceInfo = (SourceManipulationInfo) newInfo;
|
||||
|
||||
if ((oldSourceInfo).getModifiers() != (newSourceInfo).getModifiers()) {
|
||||
this.delta.changed(newElement, ICElementDelta.F_MODIFIERS);
|
||||
}
|
||||
|
||||
// The element info should be able to tell if the contents are the same.
|
||||
if(!oldSourceInfo.hasSameContentsAs(newSourceInfo)){
|
||||
this.delta.changed(newElement, ICElementDelta.F_CONTENT);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Adds removed deltas for any handles left in the table
|
||||
*/
|
||||
private void findDeletions() {
|
||||
Iterator iter = this.infos.keySet().iterator();
|
||||
while(iter.hasNext()) {
|
||||
ICElement element = (ICElement)iter.next();
|
||||
this.delta.removed(element);
|
||||
this.removed(element);
|
||||
}
|
||||
}
|
||||
private CElementInfo getElementInfo(ICElement element) {
|
||||
return (CElementInfo)this.infos.get(element);
|
||||
}
|
||||
private ListItem getNewPosition(ICElement element) {
|
||||
return (ListItem)this.newPositions.get(element);
|
||||
}
|
||||
private ListItem getOldPosition(ICElement element) {
|
||||
return (ListItem)this.oldPositions.get(element);
|
||||
}
|
||||
private void initialize() {
|
||||
this.infos = new HashMap(20);
|
||||
this.oldPositions = new HashMap(20);
|
||||
this.newPositions = new HashMap(20);
|
||||
this.putOldPosition(this.cElement, new ListItem(null, null));
|
||||
this.putNewPosition(this.cElement, new ListItem(null, null));
|
||||
this.delta = new CElementDelta(cElement);
|
||||
|
||||
// if building a delta on a translation unit or below,
|
||||
// it's a fine grained delta
|
||||
if (cElement.getElementType() >= ICElement.C_FILE) {
|
||||
this.delta.fineGrained();
|
||||
}
|
||||
|
||||
this.added = new ArrayList(5);
|
||||
this.removed = new ArrayList(5);
|
||||
}
|
||||
/**
|
||||
* Inserts position information for the elements into the new or old positions table
|
||||
*/
|
||||
private void insertPositions(ICElement[] elements, boolean isNew) {
|
||||
int length = elements.length;
|
||||
ICElement previous = null, current = null, next = (length > 0) ? elements[0] : null;
|
||||
for(int i = 0; i < length; i++) {
|
||||
previous = current;
|
||||
current = next;
|
||||
next = (i + 1 < length) ? elements[i + 1] : null;
|
||||
if (isNew) {
|
||||
this.putNewPosition(current, new ListItem(previous, next));
|
||||
} else {
|
||||
this.putOldPosition(current, new ListItem(previous, next));
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns true if the given elements represent the an equivalent declaration.
|
||||
*
|
||||
* <p>NOTE: Since this comparison can be done with handle info only,
|
||||
* none of the internal calls need to use the locally cached contents
|
||||
* of the old translation unit.
|
||||
*/
|
||||
private boolean isIdentical(CElement e1, CElement e2) {
|
||||
if (e1 == null ^ e2 == null)
|
||||
return false;
|
||||
|
||||
if (e1 == null)
|
||||
return true;
|
||||
|
||||
return e1.isIdentical(e2);
|
||||
}
|
||||
/**
|
||||
* Answers true if the elements position has not changed.
|
||||
* Takes into account additions so that elements following
|
||||
* new elements will not appear out of place.
|
||||
*/
|
||||
private boolean isPositionedCorrectly(ICElement element) {
|
||||
ListItem oldListItem = this.getOldPosition(element);
|
||||
if (oldListItem == null)
|
||||
return false;
|
||||
ICElement oldPrevious = oldListItem.previous;
|
||||
ListItem newListItem = this.getNewPosition(element);
|
||||
if (newListItem == null)
|
||||
return false;
|
||||
ICElement newPrevious = newListItem.previous;
|
||||
if (oldPrevious == newPrevious)
|
||||
return true;
|
||||
ICElement lastNewPrevious = null;
|
||||
while(lastNewPrevious != newPrevious) {
|
||||
if (isIdentical((CElement)oldPrevious, (CElement)newPrevious))
|
||||
return true;
|
||||
lastNewPrevious = newPrevious;
|
||||
// if newPrevious is null at this time we should exit the loop.
|
||||
if (newPrevious == null) break;
|
||||
newPrevious = (this.getNewPosition(newPrevious)).previous;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private void putElementInfo(ICElement element, CElementInfo info) {
|
||||
this.infos.put(element, info);
|
||||
}
|
||||
private void putNewPosition(ICElement element, ListItem position) {
|
||||
this.newPositions.put(element, position);
|
||||
}
|
||||
private void putOldPosition(ICElement element, ListItem position) {
|
||||
this.oldPositions.put(element, position);
|
||||
}
|
||||
/**
|
||||
* Records this elements info, and attempts
|
||||
* to record the info for the children.
|
||||
*/
|
||||
private void recordElementInfo(ICElement element, int depth) {
|
||||
if (depth >= this.maxDepth) {
|
||||
return;
|
||||
}
|
||||
CElementInfo info = (CElementInfo)CModelManager.getDefault().getInfo(element);
|
||||
if (info == null) // no longer in the C model.
|
||||
return;
|
||||
this.putElementInfo(element, info);
|
||||
|
||||
if (element instanceof IParent) {
|
||||
ICElement[] children = info.getChildren();
|
||||
if (children != null) {
|
||||
insertPositions(children, false);
|
||||
for(int i = 0, length = children.length; i < length; i++)
|
||||
recordElementInfo(children[i], depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fills the newPositions hashtable with the new position information
|
||||
*/
|
||||
private void recordNewPositions(ICElement newElement, int depth) {
|
||||
if (depth < this.maxDepth && newElement instanceof IParent) {
|
||||
CElementInfo info = null;
|
||||
info = ((CElement)newElement).getElementInfo();
|
||||
|
||||
ICElement[] children = info.getChildren();
|
||||
if (children != null) {
|
||||
insertPositions(children, true);
|
||||
for(int i = 0, length = children.length; i < length; i++) {
|
||||
recordNewPositions(children[i], depth + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Repairs the positioning information
|
||||
* after an element has been removed
|
||||
*/
|
||||
private void removed(ICElement element) {
|
||||
this.removed.add(element);
|
||||
ListItem current = this.getOldPosition(element);
|
||||
ListItem previous = null, next = null;
|
||||
if (current.previous != null)
|
||||
previous = this.getOldPosition(current.previous);
|
||||
if (current.next != null)
|
||||
next = this.getOldPosition(current.next);
|
||||
if (previous != null)
|
||||
previous.next = current.next;
|
||||
if (next != null)
|
||||
next.previous = current.previous;
|
||||
|
||||
}
|
||||
private void removeElementInfo(ICElement element) {
|
||||
this.infos.remove(element);
|
||||
}
|
||||
public String toString() {
|
||||
StringBuffer buffer = new StringBuffer();
|
||||
buffer.append("Built delta:\n"); //$NON-NLS-1$
|
||||
buffer.append(this.delta.toString());
|
||||
return buffer.toString();
|
||||
}
|
||||
/**
|
||||
* Trims deletion deltas to only report the highest level of deletion
|
||||
*/
|
||||
private void trimDelta(CElementDelta delta) {
|
||||
if (delta.getKind() == ICElementDelta.REMOVED) {
|
||||
ICElementDelta[] children = delta.getAffectedChildren();
|
||||
for(int i = 0, length = children.length; i < length; i++) {
|
||||
delta.removeAffectedChild((CElementDelta)children[i]);
|
||||
}
|
||||
} else {
|
||||
ICElementDelta[] children = delta.getAffectedChildren();
|
||||
for(int i = 0, length = children.length; i < length; i++) {
|
||||
trimDelta((CElementDelta)children[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue