/* * Copyright 2003-2013 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; import jetbrains.mps.openapi.editor.cells.traversal.CellTreeIterable; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.util.Condition; import java.util.ArrayList; import java.util.Iterator; import java.util.List; /** * Semen Alperovich * 03 05, 2013 */ public class CellTraversalUtil { // no one can instantiate this class. private CellTraversalUtil() { } public static EditorCell getNextSibling(@NotNull EditorCell cell) { return getSibling(cell, true); } public static EditorCell getPrevSibling(@NotNull EditorCell cell) { return getSibling(cell, false); } private static EditorCell getSibling(@NotNull EditorCell cell, boolean forward) { final EditorCell_Collection parent = cell.getParent(); if (parent == null) { return null; } Iterator<EditorCell> iterator = parent.iterator(cell, forward); if (iterator.hasNext()) { return iterator.next(); } return null; } public static EditorCell getNextLeaf(@NotNull EditorCell cell) { EditorCell next = getNextSibling(cell); if (next != null) { return getFirstLeaf(next); } EditorCell_Collection parent = cell.getParent(); if (parent != null) { return getNextLeaf(parent); } return null; } /** * Compares two cells. * Cell which is first is the editor is lesser. * <p/> * Comparing cells must have common parent. * Check getCommonParent(firstCell, secondCell) != null * * @param firstCell a first cell to be compared. * @param secondCell a second cell to be compared. * @return -1, zero, or 1 as the first cell * is less than, equal to, or greater than the second cell. * @throws java.lang.IllegalArgumentException if the first cell and * the second cell don't have common parent. */ public static int compare(@NotNull EditorCell firstCell, @NotNull EditorCell secondCell) { if (firstCell.equals(secondCell)) { return 0; } List<EditorCell> firstCellAndParents = new ArrayList<>(); EditorCell parent = firstCell; while (parent != null) { if (parent.equals(secondCell)) { return 1; } firstCellAndParents.add(parent); parent = parent.getParent(); } EditorCell_Collection commonParent = secondCell.getParent(); EditorCell secondChild = secondCell; while (commonParent != null && !firstCellAndParents.contains(commonParent)) { secondChild = commonParent; commonParent = commonParent.getParent(); } if (commonParent == null) { throw new IllegalArgumentException(firstCell.toString() + " and " + secondCell.toString() + " don't have common parent"); } if (commonParent.equals(firstCell)) { return -1; } EditorCell firstChild = firstCellAndParents.get(firstCellAndParents.indexOf(commonParent) - 1); if (findInNextSiblings(firstChild, secondChild)) { return -1; } if (findInNextSiblings(secondChild, firstChild)){ return 1; } for (EditorCell cell : commonParent) { if (cell == firstChild) { return -1; } if (cell == secondChild) { return 1; } } return 0; } private static boolean findInNextSiblings(EditorCell firstChild, EditorCell secondChild) { EditorCell sibling = firstChild.getNextSibling(); while (sibling != null) { if (sibling.equals(secondChild)) { return true; } sibling = sibling.getNextSibling(); } return false; } public static EditorCell getNextLeaf(@NotNull EditorCell cell, @NotNull Condition<EditorCell> condition) { EditorCell current = getNextLeaf(cell); while (current != null) { if (condition.met(current)) { return current; } current = getNextLeaf(current); } return null; } public static EditorCell getPrevLeaf(@NotNull EditorCell cell) { EditorCell prev = getPrevSibling(cell); if (prev != null) { return getLastLeaf(prev); } EditorCell_Collection parent = cell.getParent(); if (parent != null) { return getPrevLeaf(parent); } return null; } public static EditorCell getPrevLeaf(@NotNull EditorCell cell, @NotNull Condition<EditorCell> condition) { EditorCell current = getPrevLeaf(cell); while (current != null) { if (condition.met(current)) { return current; } current = getPrevLeaf(current); } return null; } @NotNull public static EditorCell getFirstLeaf(@NotNull EditorCell cell) { if (cell instanceof EditorCell_Collection) { return ((EditorCell_Collection) cell).isEmpty() ? cell : getFirstLeaf(((EditorCell_Collection) cell).firstCell()); } else { return cell; } } @NotNull public static EditorCell getLastLeaf(@NotNull EditorCell cell) { if (cell instanceof EditorCell_Collection) { return ((EditorCell_Collection) cell).isEmpty() ? cell : getLastLeaf(((EditorCell_Collection) cell).lastCell()); } else { return cell; } } public static EditorCell getCommonParent(@NotNull EditorCell firstCell, @NotNull EditorCell secondCell) { List<EditorCell> firstParents = getParents(firstCell, true); List<EditorCell> secondParents = getParents(secondCell, true); for (EditorCell cell : firstParents) { if (secondParents.contains(cell)) { return cell; } } return null; } public static List<EditorCell> getParents(@NotNull EditorCell cell, boolean includeThis) { List<EditorCell> parents = new ArrayList<>(); EditorCell tempCell = includeThis ? cell : cell.getParent(); while (tempCell != null) { parents.add(tempCell); tempCell = tempCell.getParent(); } return parents; } public static boolean isAncestor(@NotNull EditorCell ancestor, @NotNull EditorCell child) { EditorCell_Collection parent = child.getParent(); while (parent != null) { if (parent.equals(ancestor)) { return true; } parent = parent.getParent(); } return false; } public static boolean isAncestorOrEquals(@NotNull EditorCell ancestor, @NotNull EditorCell child) { return ancestor.equals(child) || isAncestor(ancestor, child); } public static EditorCell_Collection getFoldedParent(@NotNull EditorCell cell) { for (EditorCell_Collection parent = cell.getParent(); parent != null; parent = parent.getParent()) { if (parent.isCollapsed()) { return parent; } } return null; } /** * Returns a {@link CellTreeIterable} that iterates over the subtree of {@code root}, in preorder, starting with {@code start}. * * @param root the root of the subtree to iterate, {@code null} to iterate the whole tree that {@code start} is part of. * @param start the first node to visit * @param forward {@code true} to visit children of a cell from first to last, {@code false} for the reverse order. * @return a new instance of {@link CellTreeIterable} */ public static CellTreeIterable iterateTree(@Nullable EditorCell root, @NotNull EditorCell start, boolean forward) { return new CellTreeIterable(root, start, forward); } @NotNull public static EditorCell getContainingBigCell(@NotNull EditorCell cell) { while (!cell.isBig() && cell.getParent() != null) { cell = cell.getParent(); } return cell; } }