extract node iterators; separate merging of nodes with text
Signed-off-by: Florian Thienel <florian@thienel.org>
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/MergeNodesWithTextIterator.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/MergeNodesWithTextIterator.java
new file mode 100644
index 0000000..a8f6dc9
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/MergeNodesWithTextIterator.java
@@ -0,0 +1,123 @@
+package org.eclipse.vex.core.internal.dom;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.eclipse.core.runtime.Assert;
+
+public class MergeNodesWithTextIterator implements Iterator<Node> {
+
+ private final Parent parent;
+ private final Iterator<Node> nodes;
+ private final Content content;
+ private final ContentRange contentRange;
+
+ private int textCursor;
+ private Node currentChild;
+ private ContentRange nextTextGap;
+
+ public MergeNodesWithTextIterator(final Parent parent, final Iterable<Node> nodes, final Content content, final ContentRange contentRange) {
+ this.parent = parent;
+ this.nodes = nodes.iterator();
+ this.content = content;
+ this.contentRange = contentRange.intersection(parent.getRange());
+ initialize();
+ }
+
+ private void initialize() {
+ currentChild = null;
+ nextTextGap = contentRange;
+ textCursor = contentRange.getStartOffset();
+ nextStep();
+ }
+
+ private void nextStep() {
+ while (nodes.hasNext()) {
+ currentChild = nodes.next();
+ if (!currentChild.isAssociated()) {
+ nextTextGap = contentRange;
+ return;
+ } else if (currentChild.isInRange(contentRange)) {
+ nextTextGap = currentChild.getRange();
+ textCursor = findNextTextStart(textCursor, nextTextGap.getStartOffset());
+ return;
+ } else if (contentRange.contains(currentChild.getStartOffset())) {
+ nextTextGap = contentRange.intersection(currentChild.getRange());
+ textCursor = findNextTextStart(textCursor, nextTextGap.getStartOffset());
+ currentChild = null; // we can bail out here because we are behind the trimmed range now
+ return;
+ } else if (contentRange.contains(currentChild.getEndOffset())) {
+ textCursor = currentChild.getEndOffset() + 1;
+ }
+ }
+
+ currentChild = null;
+ nextTextGap = new ContentRange(contentRange.getEndOffset(), contentRange.getEndOffset());
+ textCursor = findNextTextStart(textCursor, contentRange.getEndOffset());
+ }
+
+ private int findNextTextStart(int currentOffset, final int maximumOffset) {
+ while (currentOffset < maximumOffset && content.isTagMarker(currentOffset)) {
+ currentOffset++;
+ }
+ return currentOffset;
+ }
+
+ private int findNextTextEnd(int currentOffset, final int minimumOffset) {
+ while (currentOffset > minimumOffset && content.isTagMarker(currentOffset)) {
+ currentOffset--;
+ }
+ return currentOffset;
+ }
+
+ public boolean hasNext() {
+ return hasMoreChildrenInRange() || hasMoreText();
+ }
+
+ private boolean hasMoreChildrenInRange() {
+ return currentChild != null;
+ }
+
+ private boolean hasMoreText() {
+ return textCursor < nextTextGap.getStartOffset();
+ }
+
+ public Node next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+
+ if (currentChild != null && !currentChild.isAssociated()) {
+ return nextChild();
+ }
+
+ final int textStart = findNextTextStart(textCursor, nextTextGap.getStartOffset());
+ final int textEnd = findNextTextEnd(nextTextGap.getStartOffset(), textStart);
+ textCursor = nextTextGap.getEndOffset() + 1;
+
+ if (textStart < textEnd) {
+ return nextText(textStart, textEnd);
+ }
+ if (textStart == textEnd && !content.isTagMarker(textStart)) {
+ return nextText(textStart, textEnd);
+ }
+
+ Assert.isNotNull(currentChild, "No text and no node make Vex go crazy!");
+
+ return nextChild();
+ }
+
+ private Node nextChild() {
+ final Node child = currentChild;
+ nextStep();
+ return child;
+ }
+
+ private Node nextText(final int textStart, final int textEnd) {
+ return new Text(parent, content, new ContentRange(textStart, textEnd));
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove node.");
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/NodesInContentRangeIterator.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/NodesInContentRangeIterator.java
new file mode 100644
index 0000000..ebd6acc
--- /dev/null
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/NodesInContentRangeIterator.java
@@ -0,0 +1,48 @@
+package org.eclipse.vex.core.internal.dom;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+public class NodesInContentRangeIterator implements Iterator<Node> {
+
+ private final Iterator<Node> nodes;
+ private final ContentRange contentRange;
+
+ private Node currentNode;
+
+ public NodesInContentRangeIterator(final Iterable<Node> nodes, final ContentRange contentRange) {
+ this.contentRange = contentRange;
+ this.nodes = nodes.iterator();
+ nextStep();
+ }
+
+ private void nextStep() {
+ while (nodes.hasNext()) {
+ currentNode = nodes.next();
+ if (!currentNode.isAssociated()) {
+ return;
+ }
+ if (contentRange.contains(currentNode.getRange())) {
+ return;
+ }
+ }
+ currentNode = null;
+ }
+
+ public boolean hasNext() {
+ return currentNode != null;
+ }
+
+ public Node next() {
+ if (!hasNext()) {
+ throw new NoSuchElementException();
+ }
+ final Node next = currentNode;
+ nextStep();
+ return next;
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException("Cannot remove node.");
+ }
+}
\ No newline at end of file
diff --git a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Parent.java b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Parent.java
index 37aa332..4af572b 100644
--- a/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Parent.java
+++ b/org.eclipse.vex.core/src/org/eclipse/vex/core/internal/dom/Parent.java
@@ -14,7 +14,6 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
-import java.util.NoSuchElementException;
import org.eclipse.core.runtime.Assert;
@@ -91,7 +90,10 @@
return new Axis(this) {
@Override
public Iterator<Node> iterator(final Node sourceNode, final Axis axis) {
- return new ChildrenAndText(getContentRange(), shouldIncludeText());
+ if (shouldIncludeText()) {
+ return new MergeNodesWithTextIterator(Parent.this, children, getContent(), getContentRange());
+ }
+ return new NodesInContentRangeIterator(children, getContentRange());
}
};
}
@@ -126,121 +128,4 @@
return this;
}
- private class ChildrenAndText implements Iterator<Node> {
-
- private final ContentRange trimmedRange;
- private final boolean includeText;
- private final Iterator<Node> childIterator;
-
- private int textCursor;
- private Node currentChild;
- private ContentRange nextTextGap;
-
- public ChildrenAndText(final ContentRange range, final boolean includeText) {
- this.includeText = includeText;
- trimmedRange = range.intersection(getRange());
- childIterator = children.iterator();
- initialize();
- }
-
- private void initialize() {
- currentChild = null;
- nextTextGap = trimmedRange;
- textCursor = trimmedRange.getStartOffset();
- nextStep();
- }
-
- private void nextStep() {
- while (childIterator.hasNext()) {
- currentChild = childIterator.next();
- if (!currentChild.isAssociated()) {
- nextTextGap = trimmedRange;
- return;
- } else if (currentChild.isInRange(trimmedRange)) {
- nextTextGap = currentChild.getRange();
- textCursor = findNextTextStart(textCursor, nextTextGap.getStartOffset());
- return;
- } else if (trimmedRange.contains(currentChild.getStartOffset())) {
- nextTextGap = trimmedRange.intersection(currentChild.getRange());
- textCursor = findNextTextStart(textCursor, nextTextGap.getStartOffset());
- currentChild = null; // we can bail out here because we are behind the trimmed range now
- return;
- } else if (trimmedRange.contains(currentChild.getEndOffset())) {
- textCursor = currentChild.getEndOffset() + 1;
- }
- }
-
- currentChild = null;
- nextTextGap = new ContentRange(trimmedRange.getEndOffset(), trimmedRange.getEndOffset());
- textCursor = findNextTextStart(textCursor, trimmedRange.getEndOffset());
- }
-
- private int findNextTextStart(int currentOffset, final int maximumOffset) {
- while (currentOffset < maximumOffset && getContent().isTagMarker(currentOffset)) {
- currentOffset++;
- }
- return currentOffset;
- }
-
- private int findNextTextEnd(int currentOffset, final int minimumOffset) {
- while (currentOffset > minimumOffset && getContent().isTagMarker(currentOffset)) {
- currentOffset--;
- }
- return currentOffset;
- }
-
- public boolean hasNext() {
- return hasMoreChildrenInRange() || hasMoreText();
- }
-
- private boolean hasMoreChildrenInRange() {
- return currentChild != null;
- }
-
- private boolean hasMoreText() {
- return includeText && textCursor < nextTextGap.getStartOffset();
- }
-
- public Node next() {
- if (!hasNext()) {
- throw new NoSuchElementException();
- }
-
- if (currentChild != null && !currentChild.isAssociated()) {
- return nextChild();
- }
-
- if (includeText) {
- final int textStart = findNextTextStart(textCursor, nextTextGap.getStartOffset());
- final int textEnd = findNextTextEnd(nextTextGap.getStartOffset(), textStart);
- textCursor = nextTextGap.getEndOffset() + 1;
-
- if (textStart < textEnd) {
- return nextText(textStart, textEnd);
- }
- if (textStart == textEnd && !getContent().isTagMarker(textStart)) {
- return nextText(textStart, textEnd);
- }
- }
-
- Assert.isNotNull(currentChild, "No text and no child makes Homer go crazy!");
-
- return nextChild();
- }
-
- private Node nextChild() {
- final Node child = currentChild;
- nextStep();
- return child;
- }
-
- private Node nextText(final int textStart, final int textEnd) {
- return new Text(Parent.this, getContent(), new ContentRange(textStart, textEnd));
- }
-
- public void remove() {
- throw new UnsupportedOperationException("Cannot remove children.");
- }
- }
-
}