/**
* Copyright: Fabio Zadrozny
* License: EPL
*/
package org.python.pydev.shared_core.partitioner;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.rules.IToken;
import org.python.pydev.shared_core.string.FastStringBuffer;
import org.python.pydev.shared_core.structure.LinkedListWarningOnSlowOperations;
public final class SubRuleToken {
public IToken token;
public int offset;
public int len;
public SubRuleToken(IToken token, int offset, int len) {
Assert.isTrue(offset >= 0);
//System.out.println(token + " offset: " + offset + " len:" + len);
this.token = token;
this.offset = offset;
this.len = len;
}
public SubRuleToken createCopy() {
SubRuleToken copy = copyWithoutChildren();
if (this.children != null) {
for (SubRuleToken c : this.children) {
copy.addChild(c.createCopy());
}
}
return copy;
}
@Override
public String toString() {
FastStringBuffer ret = new FastStringBuffer("SubRuleToken[", 30)
.appendObject(token.getData())
.append(" offset: ")
.append(offset)
.append(" len: ")
.append(len);
if (children != null && children.size() > 0) {
ret.append(" children:\n")
.append(children.toString());
}
ret.append(']')
.toString();
return ret.toString();
}
public String toStringBetter() {
return toString(0);
}
public String toString(int level) {
String ident = new FastStringBuffer().appendN(" ", level + 1).toString();
String ident2 = new FastStringBuffer().appendN(" ", level).toString();
FastStringBuffer ret = new FastStringBuffer("SubRuleToken: ", 30)
.appendObject(token.getData())
.append(" offset: ")
.append(offset)
.append(" len: ")
.append(len);
if (children != null && children.size() > 0) {
ret.append(" children:[");
for (SubRuleToken subRuleToken : children) {
ret.append("\n");
ret.append(ident);
ret.append(subRuleToken.toString(level + 1));
}
ret.append("\n");
ret.append(ident2);
ret.append("]");
}
return ret.toString();
}
private LinkedList<SubRuleToken> children;
public void makeRelativeToOffset(int offset) {
this.offset -= offset;
if (this.children != null) {
for (SubRuleToken c : this.children) {
c.makeRelativeToOffset(offset);
}
}
}
public void addOffset(int offset) {
this.offset += offset;
if (this.children != null) {
for (SubRuleToken c : this.children) {
c.addOffset(offset);
}
}
}
public void flatten(LinkedList<SubRuleToken> lst) {
addSubRuleToken(lst, this.copyWithoutChildren(), true);
if (this.children != null) {
for (SubRuleToken c : this.children) {
c.flatten(lst);
}
}
}
private SubRuleToken copyWithoutChildren() {
return new SubRuleToken(token, offset, len);
}
public List<SubRuleToken> flatten() {
LinkedList<SubRuleToken> lst = new LinkedListWarningOnSlowOperations<>();
flatten(lst);
return lst;
}
public void addChildren(List<SubRuleToken> lst) {
if (lst == null) {
return;
}
if (this.children == null) {
this.children = new LinkedListWarningOnSlowOperations<>();
}
for (SubRuleToken subRuleToken : lst) {
addSubRuleToken(children, subRuleToken);
}
}
public void addChild(SubRuleToken subRuleToken) {
if (this.children == null) {
this.children = new LinkedListWarningOnSlowOperations<>();
}
addSubRuleToken(children, subRuleToken);
}
public static void addSubRuleToken(LinkedList<SubRuleToken> lst, SubRuleToken subRuleToken) {
addSubRuleToken(lst, subRuleToken, false);
}
/**
* Adds a sub rule token to a list with existing sub-rule tokens. It fixes existing sub-rule tokens
* so that they do not overlap. Note that list is always kept ordered by the offset/len.
*/
public static void addSubRuleToken(LinkedList<SubRuleToken> lst, SubRuleToken subRuleToken, boolean ignoreEmpty) {
if (ignoreEmpty) {
if (subRuleToken.token == null) {
return;
}
if (subRuleToken.token instanceof DummyToken) {
return;
}
Object data = subRuleToken.token.getData();
if (data == null || "".equals(data)) {
return;
}
}
for (ListIterator<SubRuleToken> it = lst.listIterator(lst.size()); it.hasPrevious();) {
SubRuleToken prev = it.previous();
if (prev.offset + prev.len <= subRuleToken.offset) {
//This should be 95% of our use-cases (always add a new one non-overlapping
//at the last position).
it.next();
it.add(subRuleToken);
return;
} else {
//Everything from now on is to add it in the proper place properly
//managing possible overlaps with existing regions.
if (prev.offset < subRuleToken.offset) {
int prevEndOffset = prev.offset + prev.len;
int newEndOffset = subRuleToken.offset + subRuleToken.len;
prev.len = subRuleToken.offset - prev.offset;
it.next();
it.add(subRuleToken);
if (prevEndOffset > newEndOffset) {
// We have to create a new as the newly added was in the middle of the existing one.
it.add(new SubRuleToken(prev.token, newEndOffset, prevEndOffset - newEndOffset));
}
return;
} else if (prev.offset == subRuleToken.offset) {
//Same starting offset, let's see if it has to be broken
if (prev.len <= subRuleToken.len) {
//Same len (or smaller): the new one overrides it.
it.remove();
it.add(subRuleToken);
return;
} else {
//The previous is larger than the new one. Let's change its
//starting offset/len and add the new one before it.
int newOffset = subRuleToken.offset + subRuleToken.len;
prev.len = prev.len - (newOffset - prev.offset);
prev.offset = newOffset;
it.add(subRuleToken);
return;
}
} else {
// The previous offset is higher than the newly added token
it.remove(); //Remove the previous and add it back later.
while (it.hasPrevious()) {
SubRuleToken beforePrevious = it.previous();
int beforePreviousEndOffset = beforePrevious.offset + beforePrevious.len;
if (beforePreviousEndOffset < subRuleToken.offset) {
//All ok (keep it).
it.next();
break;
} else {
if (beforePrevious.offset == subRuleToken.offset) {
if (beforePrevious.len <= subRuleToken.len) {
//No need to keep it at all: just remove it.
it.remove();
break;
} else {
//We need to keep it (but it'll appear after the one
//we're adding now).
it.remove();
it.add(subRuleToken);
beforePrevious.offset = subRuleToken.offset + subRuleToken.len;
beforePrevious.len = beforePreviousEndOffset - beforePrevious.offset;
subRuleToken = null;
it.add(beforePrevious);
break;
}
} else if (beforePrevious.offset < subRuleToken.offset) {
//Ok, we found one which is lower than the newly added token, so, let's
//fix it.
if (beforePreviousEndOffset > subRuleToken.offset + subRuleToken.len) {
//It's in the middle of this token.
beforePrevious.len = subRuleToken.offset - beforePrevious.offset;
it.next();
it.add(subRuleToken);
int newEndOffset = subRuleToken.offset + subRuleToken.len;
// We have to create a new as the newly added was in the middle of the existing one.
it.add(new SubRuleToken(beforePrevious.token, newEndOffset, beforePreviousEndOffset
- newEndOffset));
it.add(prev);
return;
} else {
if (subRuleToken.offset < beforePreviousEndOffset) {
beforePrevious.len = subRuleToken.offset - beforePrevious.offset;
}
it.next();
break;
}
} else {
if (beforePrevious.offset >= subRuleToken.offset + subRuleToken.len
|| beforePreviousEndOffset > subRuleToken.offset + subRuleToken.len) {
it.remove();
it.add(prev);
it.previous();
prev = beforePrevious;
continue;
} else {
it.remove();
continue;
}
}
}
}
if (subRuleToken != null) {
int newOffset = subRuleToken.offset + subRuleToken.len;
int prevEndOffset = prev.offset + prev.len;
if (prev.offset < newOffset) {
prev.len = prevEndOffset - newOffset;
prev.offset = newOffset;
}
it.add(subRuleToken);
}
if (prev.len > 0) {
it.add(prev);
}
return;
}
}
}
lst.add(subRuleToken);
}
public static void fillWithSubToken(IToken contentScope, IRegion contentRegion, LinkedList<SubRuleToken> lst) {
final int offset = contentRegion.getOffset();
final int len = contentRegion.getLength();
fillWithSubToken(contentScope, offset, len, lst);
}
public static void fillWithSubToken(IToken contentScope, final int offset, final int len,
LinkedList<SubRuleToken> lst) {
int lastOffset = offset;
int lastLen = 0;
for (ListIterator<SubRuleToken> it = lst.listIterator(); it.hasNext();) {
SubRuleToken next = it.next();
if (next.offset > lastOffset + lastLen) {
int off = lastOffset + lastLen;
int l = next.offset - (lastOffset + lastLen);
it.set(new SubRuleToken(contentScope, off, l));
it.add(next);
}
lastOffset = next.offset;
lastLen = next.len;
}
// To finish, check offset+len
if (offset + len > lastOffset + lastLen) {
int off = lastOffset + lastLen;
int l = (offset + len) - (lastOffset + lastLen);
lst.add(new SubRuleToken(contentScope, off, l));
}
}
public List<SubRuleToken> getChildren() {
return this.children;
}
public void fillWithTokensAtOffset(int offset, List<IToken> lst) {
if (offset >= this.offset && offset <= this.offset + this.len) {
lst.add(token);
if (this.children != null) {
for (SubRuleToken c : this.children) {
c.fillWithTokensAtOffset(offset, lst);
}
}
}
}
}