From c81422564d5792d2799edc202e198c65d5cdfd9f Mon Sep 17 00:00:00 2001 From: Sergey Malenkov Date: Mon, 17 Jul 2017 03:58:39 +0300 Subject: [PATCH] TreeUtil with non-recursive methods to convert TreePath --- .../intellij/ui/tree/AbstractTreeWalker.java | 9 +- .../src/com/intellij/ui/tree/TreeUtil.java | 129 +++++++++++++++ .../com/intellij/ui/tree/TreeUtilTest.java | 149 ++++++++++++++++++ 3 files changed, 280 insertions(+), 7 deletions(-) create mode 100644 platform/platform-impl/src/com/intellij/ui/tree/TreeUtil.java create mode 100644 platform/platform-tests/testSrc/com/intellij/ui/tree/TreeUtilTest.java diff --git a/platform/platform-impl/src/com/intellij/ui/tree/AbstractTreeWalker.java b/platform/platform-impl/src/com/intellij/ui/tree/AbstractTreeWalker.java index 7d603501f13a..02464796775a 100644 --- a/platform/platform-impl/src/com/intellij/ui/tree/AbstractTreeWalker.java +++ b/platform/platform-impl/src/com/intellij/ui/tree/AbstractTreeWalker.java @@ -116,11 +116,7 @@ abstract class AbstractTreeWalker { TreePath result = null; if (node != null) { try { - Object component = converter.apply(node); - TreePath path = parent != null - ? parent.pathByAddingChild(component) - : new TreePath(component); - + TreePath path = TreeUtil.createTreePath(parent, converter.apply(node)); switch (visitor.accept(path)) { case CONTINUE: update(null, State.REQUESTED); @@ -175,8 +171,7 @@ abstract class AbstractTreeWalker { current = path.getParentPath(); } else { - Object component = converter.apply(node); - TreePath path = this.current.pathByAddingChild(component); + TreePath path = TreeUtil.createTreePath(current, converter.apply(node)); switch (visitor.accept(path)) { case CONTINUE: update(State.STARTED, State.REQUESTED); diff --git a/platform/platform-impl/src/com/intellij/ui/tree/TreeUtil.java b/platform/platform-impl/src/com/intellij/ui/tree/TreeUtil.java new file mode 100644 index 000000000000..7c482341e49c --- /dev/null +++ b/platform/platform-impl/src/com/intellij/ui/tree/TreeUtil.java @@ -0,0 +1,129 @@ +/* + * Copyright 2000-2017 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 com.intellij.ui.tree; + +import org.jetbrains.annotations.NotNull; + +import javax.swing.tree.TreePath; +import java.lang.reflect.Array; +import java.util.function.Function; + +public class TreeUtil { + /** + * @param parent the parent path or {@code null} to indicate the root + * @param component the last path component + * @return a tree path with all the parent components plus the given component + */ + @NotNull + public static TreePath createTreePath(TreePath parent, @NotNull Object component) { + return parent != null + ? parent.pathByAddingChild(component) + : new TreePath(component); + } + + /** + * @param path a tree path to convert + * @return an array with the string representations of path components or {@code null} + * if the specified path is wrong + * or a path component is {@code null} + * or its string representation is {@code null} + */ + public static String[] convertTreePathToStrings(@NotNull TreePath path) { + return convertTreePathToArray(path, Object::toString, String.class); + } + + /** + * @param path a tree path to convert + * @return an array with the same path components or {@code null} + * if the specified path is wrong + * or a path component is {@code null} + */ + public static Object[] convertTreePathToArray(@NotNull TreePath path) { + return convertTreePathToArray(path, Function.identity(), Object.class); + } + + /** + * @param path a tree path to convert + * @param function a function to convert path components + * @return an array with the converted path components or {@code null} + * if the specified path is wrong + * or a path component is {@code null} + * or a path component is converted to {@code null} + */ + public static Object[] convertTreePathToArray(@NotNull TreePath path, @NotNull Function converter) { + return convertTreePathToArray(path, converter, Object.class); + } + + /** + * @param path a tree path to convert + * @param function a function to convert path components + * @param type a type of components of the new array + * @return an array of the specified type with the converted path components or {@code null} + * if the specified path is wrong + * or a path component is {@code null} + * or a path component is converted to {@code null} + */ + public static T[] convertTreePathToArray(@NotNull TreePath path, @NotNull Function converter, @NotNull Class type) { + int count = path.getPathCount(); + if (count <= 0) return null; + //noinspection unchecked + T[] array = (T[])Array.newInstance(type, count); + while (path != null && count > 0) { + Object component = path.getLastPathComponent(); + if (component == null) return null; + T object = convert(component, converter); + if (object == null) return null; + array[--count] = object; + path = path.getParentPath(); + } + return path != null || count > 0 ? null : array; + } + + /** + * @param array an array of path components to convert + * @return a tree path with the same path components or {@code null} + * if the specified array is empty + * or a path component is {@code null} + * or a path component is converted to {@code null} + */ + public static TreePath convertArrayToTreePath(@NotNull T... array) { + return convertArrayToTreePath(array, Function.identity()); + } + + /** + * @param array an array of path components to convert + * @param function a function to convert path components + * @return a tree path with the converted path components or {@code null} + * if the specified array is empty + * or a path component is {@code null} + * or a path component is converted to {@code null} + */ + public static TreePath convertArrayToTreePath(@NotNull T[] array, @NotNull Function converter) { + int count = array.length; + if (count <= 0) return null; + TreePath path = null; + for (T object : array) { + Object component = convert(object, converter); + if (component == null) return null; + path = createTreePath(path, component); + } + return path; + } + + private static O convert(I object, @NotNull Function converter) { + return object == null ? null : converter.apply(object); + } +} diff --git a/platform/platform-tests/testSrc/com/intellij/ui/tree/TreeUtilTest.java b/platform/platform-tests/testSrc/com/intellij/ui/tree/TreeUtilTest.java new file mode 100644 index 000000000000..0fbe5f1006c9 --- /dev/null +++ b/platform/platform-tests/testSrc/com/intellij/ui/tree/TreeUtilTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2000-2017 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 com.intellij.ui.tree; + +import org.junit.Assert; +import org.junit.Test; + +import javax.swing.tree.TreePath; + +public class TreeUtilTest { + @Test + public void createTreePath() { + Object root = new Object(); + TreePath parent = TreeUtil.createTreePath(null, root); + Assert.assertNotNull(parent); + Assert.assertNull(parent.getParentPath()); + Assert.assertSame(root, parent.getLastPathComponent()); + + Object node = new Object(); + TreePath path = TreeUtil.createTreePath(parent, node); + Assert.assertNotNull(path); + Assert.assertNotNull(path.getParentPath()); + Assert.assertNotSame(path, parent); + Assert.assertSame(parent, path.getParentPath()); + Assert.assertSame(node, path.getLastPathComponent()); + } + + @Test + public void convertTreePathToStrings() { + String[] strings = TreeUtil.convertTreePathToStrings(new TreePath(new Object[]{2, 1, 0})); + Assert.assertNotNull(strings); + Assert.assertEquals(3, strings.length); + Assert.assertEquals("2", strings[0]); + Assert.assertEquals("1", strings[1]); + Assert.assertEquals("0", strings[2]); + } + + @Test + public void convertTreePathToArrayWrongConverter() { + Assert.assertNull(TreeUtil.convertTreePathToArray(new TreePath(new Object[]{2, 1, 0}), component -> null)); + } + + @Test + public void convertTreePathToArrayWrongPathCount() { + Assert.assertNull(TreeUtil.convertTreePathToArray(new TreePath() { + })); + } + + @Test + public void convertTreePathToArrayWrongPathComponent() { + Assert.assertNull(TreeUtil.convertTreePathToArray(new TreePath(new Object[]{2, 1, 0}) { + @Override + public Object getLastPathComponent() { + return null; + } + })); + } + + @Test + public void convertTreePathToArrayDeep() { + convertTreePathToArrayDeep(1); + convertTreePathToArrayDeep(10); + convertTreePathToArrayDeep(100); + convertTreePathToArrayDeep(1000); + convertTreePathToArrayDeep(10000); + convertTreePathToArrayDeep(100000); + convertTreePathToArrayDeep(1000000); + } + + private static void convertTreePathToArrayDeep(int count) { + TreePath path = null; + for (int i = 0; i < count; i++) path = TreeUtil.createTreePath(path, i); + Assert.assertEquals(count, path.getPathCount()); + Assert.assertEquals(count - 1, path.getLastPathComponent()); + Object[] array = TreeUtil.convertTreePathToArray(path); + Assert.assertEquals(count, array.length); + } + + @Test + public void convertArrayToTreePath() { + TreePath path = TreeUtil.convertArrayToTreePath("2", "1", "0"); + Assert.assertNotNull(path); + Assert.assertEquals("0", path.getLastPathComponent()); + Assert.assertNotNull(path.getParentPath()); + Assert.assertEquals("1", path.getParentPath().getLastPathComponent()); + Assert.assertNotNull(path.getParentPath().getParentPath()); + Assert.assertEquals("2", path.getParentPath().getParentPath().getLastPathComponent()); + Assert.assertNull(path.getParentPath().getParentPath().getParentPath()); + } + + @Test + public void convertArrayToTreePathConverter() { + TreePath path = TreeUtil.convertArrayToTreePath(new Object[]{2, 1, 0}, Object::toString); + Assert.assertNotNull(path); + Assert.assertEquals("0", path.getLastPathComponent()); + Assert.assertNotNull(path.getParentPath()); + Assert.assertEquals("1", path.getParentPath().getLastPathComponent()); + Assert.assertNotNull(path.getParentPath().getParentPath()); + Assert.assertEquals("2", path.getParentPath().getParentPath().getLastPathComponent()); + Assert.assertNull(path.getParentPath().getParentPath().getParentPath()); + } + + @Test + public void convertArrayToTreePathEmptyArray() { + Assert.assertNull(TreeUtil.convertArrayToTreePath()); + } + + @Test + public void convertArrayToTreePathWrongComponent() { + Assert.assertNull(TreeUtil.convertArrayToTreePath("2", null, "0")); + } + + @Test + public void convertArrayToTreePathWrongConverter() { + Assert.assertNull(TreeUtil.convertArrayToTreePath(new String[]{"2", "1", "0"}, component -> null)); + } + + @Test + public void convertArrayToTreePathDeep() { + convertArrayToTreePathDeep(1); + convertArrayToTreePathDeep(10); + convertArrayToTreePathDeep(100); + convertArrayToTreePathDeep(1000); + convertArrayToTreePathDeep(10000); + convertArrayToTreePathDeep(100000); + convertArrayToTreePathDeep(1000000); + } + + private static void convertArrayToTreePathDeep(int count) { + Object[] array = new Object[count]; + for (int i = 0; i < count; i++) array[i] = i; + TreePath path = TreeUtil.convertArrayToTreePath(array); + Assert.assertEquals(count, path.getPathCount()); + Assert.assertEquals(count - 1, path.getLastPathComponent()); + } +}