/* * Copyright 2003-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package jetbrains.mps.openapi.editor.cells.traversal; import jetbrains.mps.openapi.editor.cells.EditorCell; import jetbrains.mps.openapi.editor.cells.EditorCell_Collection; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.util.TreeIterator; import java.util.NoSuchElementException; /** * Traverses a (sub)tree of cells in preorder, starting from an arbitrary cell and traversing the rest of the subtree from that point, possibly going outside * the subtree of the starting node. If the subtree to traverse is {@code null}, the entire tree is traversed. * <p> * The direction in which the children of a collection are visited is given as a constructor parameter. However, the parent cell is always visited before its * children (preorder traversal). * <p> * After reaching the end of a subtree, the next subtree is found by going towards the root of the whole tree until a cell is reached that has a next sibling. * This next sibling is the root of the next subtree to traverse. */ class CellTreeIterator implements TreeIterator<EditorCell> { @Nullable private final EditorCell myRoot; private final Direction myDirection; private EditorCell myCurrentSubtree; private CellSubtreeIterator myCurrentIterator; CellTreeIterator(@Nullable EditorCell root, @NotNull EditorCell start, Direction direction) { myRoot = root; myDirection = direction; setSubtree(start); } @Override public void skipChildren() { myCurrentIterator.skipChildren(); } @Override public boolean hasNext() { return myCurrentIterator.hasNext() || hasNextSubtree(); } @Override public EditorCell next() { if (myCurrentIterator.hasNext()) { return myCurrentIterator.next(); } EditorCell nextSubtree = findNextSubtree(); if (nextSubtree == null) { throw new NoSuchElementException("attempt to move past the end of an iterator"); } setSubtree(nextSubtree); return myCurrentIterator.next(); } private void setSubtree(@NotNull EditorCell root) { myCurrentSubtree = root; myCurrentIterator = new CellSubtreeIterator(myCurrentSubtree, myDirection); } private boolean hasNextSubtree() { return findNextSubtree() != null; } @Nullable private EditorCell findNextSubtree() { EditorCell root = myCurrentSubtree; EditorCell_Collection parent = root.getParent(); while (parent != null && root != myRoot && myDirection.getLastCell(parent) == root) { root = parent; parent = parent.getParent(); } if (parent == null || root == myRoot) { return null; } EditorCell result = myDirection.getNextSibling(root); assert result != null : "next/prev sibling should be non-null for a cell that is not the last/first cell"; return result; } @Override public void remove() { myCurrentIterator.remove(); } }