diff options
Diffstat (limited to 'platform/structure-view-impl/src/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java')
-rw-r--r-- | platform/structure-view-impl/src/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java | 279 |
1 files changed, 279 insertions, 0 deletions
diff --git a/platform/structure-view-impl/src/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java b/platform/structure-view-impl/src/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java new file mode 100644 index 000000000000..f08fb99cf4bf --- /dev/null +++ b/platform/structure-view-impl/src/com/intellij/ide/util/treeView/smartTree/CachingChildrenTreeNode.java @@ -0,0 +1,279 @@ +/* + * Copyright 2000-2014 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.ide.util.treeView.smartTree; + +import com.intellij.ide.structureView.impl.StructureViewElementWrapper; +import com.intellij.ide.util.treeView.AbstractTreeNode; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.pom.Navigatable; +import gnu.trove.THashMap; +import org.jetbrains.annotations.NotNull; + +import java.util.*; + +public abstract class CachingChildrenTreeNode <Value> extends AbstractTreeNode<Value> { + private static final Logger LOG = Logger.getInstance("#com.intellij.ide.util.treeView.smartTree.CachingChildrenTreeNode"); + private List<CachingChildrenTreeNode> myChildren; + private List<CachingChildrenTreeNode> myOldChildren = null; + protected final TreeModel myTreeModel; + + public CachingChildrenTreeNode(Project project, Value value, TreeModel treeModel) { + super(project, + value instanceof StructureViewElementWrapper ? (Value) ((StructureViewElementWrapper) value).getWrappedElement() : value); + myTreeModel = treeModel; + } + + @Override + @NotNull + public Collection<AbstractTreeNode> getChildren() { + ensureChildrenAreInitialized(); + return new ArrayList<AbstractTreeNode>(myChildren); + } + + private void ensureChildrenAreInitialized() { + if (myChildren == null) { + myChildren = new ArrayList<CachingChildrenTreeNode>(); + rebuildSubtree(); + } + } + + protected void addSubElement(CachingChildrenTreeNode node) { + ensureChildrenAreInitialized(); + myChildren.add(node); + node.setParent(this); + } + + protected void setChildren(Collection<AbstractTreeNode> children) { + clearChildren(); + for (AbstractTreeNode node : children) { + myChildren.add((CachingChildrenTreeNode)node); + node.setParent(this); + } + } + + private static class CompositeComparator implements java.util.Comparator<CachingChildrenTreeNode> { + private final Sorter[] mySorters; + + public CompositeComparator(final Sorter[] sorters) { + mySorters = sorters; + } + + @Override + public int compare(final CachingChildrenTreeNode o1, final CachingChildrenTreeNode o2) { + final Object value1 = o1.getValue(); + final Object value2 = o2.getValue(); + for (Sorter sorter : mySorters) { + final int result = sorter.getComparator().compare(value1, value2); + if (result != 0) return result; + } + return 0; + } + } + + protected void sortChildren(Sorter[] sorters) { + if (myChildren == null) return; + + Collections.sort(myChildren, new CompositeComparator(sorters)); + + for (CachingChildrenTreeNode child : myChildren) { + if (child instanceof GroupWrapper) { + child.sortChildren(sorters); + } + } + } + + protected void filterChildren(@NotNull Filter[] filters) { + Collection<AbstractTreeNode> children = getChildren(); + for (Filter filter : filters) { + for (Iterator<AbstractTreeNode> eachNode = children.iterator(); eachNode.hasNext();) { + TreeElementWrapper eachChild = (TreeElementWrapper)eachNode.next(); + TreeElement value = eachChild.getValue(); + if (value == null || !filter.isVisible(value)) { + eachNode.remove(); + } + } + } + setChildren(children); + } + + protected void groupChildren(@NotNull Grouper[] groupers) { + for (Grouper grouper : groupers) { + groupElements(grouper); + } + Collection<AbstractTreeNode> children = getChildren(); + for (AbstractTreeNode child : children) { + if (child instanceof GroupWrapper) { + ((GroupWrapper)child).groupChildren(groupers); + } + } + } + + private void groupElements(Grouper grouper) { + ArrayList<AbstractTreeNode<TreeElement>> ungrouped = new ArrayList<AbstractTreeNode<TreeElement>>(); + Collection<AbstractTreeNode> children = getChildren(); + for (final AbstractTreeNode child : children) { + CachingChildrenTreeNode<TreeElement> node = (CachingChildrenTreeNode<TreeElement>)child; + if (node instanceof TreeElementWrapper) { + ungrouped.add(node); + } + } + + if (!ungrouped.isEmpty()) { + processUngrouped(ungrouped, grouper); + } + + Collection<AbstractTreeNode> result = new LinkedHashSet<AbstractTreeNode>(); + for (AbstractTreeNode child : children) { + AbstractTreeNode parent = child.getParent(); + if (parent != this) { + if (!result.contains(parent)) result.add(parent); + } + else { + result.add(child); + } + } + setChildren(result); + } + + private void processUngrouped(@NotNull List<AbstractTreeNode<TreeElement>> ungrouped, @NotNull Grouper grouper) { + Map<TreeElement,AbstractTreeNode> ungroupedObjects = collectValues(ungrouped); + Collection<Group> groups = grouper.group(this, ungroupedObjects.keySet()); + + Map<Group, GroupWrapper> groupNodes = createGroupNodes(groups); + + for (Group group : groups) { + if (group == null) { + LOG.error(grouper + " returned null group: "+groups); + } + GroupWrapper groupWrapper = groupNodes.get(group); + Collection<TreeElement> children = group.getChildren(); + for (TreeElement node : children) { + if (node == null) { + LOG.error(group + " returned null child: " + children); + } + CachingChildrenTreeNode child = createChildNode(node); + groupWrapper.addSubElement(child); + AbstractTreeNode abstractTreeNode = ungroupedObjects.get(node); + abstractTreeNode.setParent(groupWrapper); + } + } + } + + protected TreeElementWrapper createChildNode(@NotNull TreeElement child) { + return new TreeElementWrapper(getProject(), child, myTreeModel); + } + + private static Map<TreeElement, AbstractTreeNode> collectValues(List<AbstractTreeNode<TreeElement>> ungrouped) { + Map<TreeElement, AbstractTreeNode> objects = new LinkedHashMap<TreeElement, AbstractTreeNode>(); + for (final AbstractTreeNode<TreeElement> node : ungrouped) { + objects.put(node.getValue(), node); + } + return objects; + } + + private Map<Group, GroupWrapper> createGroupNodes(@NotNull Collection<Group> groups) { + Map<Group, GroupWrapper> result = new THashMap<Group, GroupWrapper>(); + for (Group group : groups) { + result.put(group, createGroupWrapper(getProject(), group, myTreeModel)); + } + return result; + } + + protected GroupWrapper createGroupWrapper(final Project project, @NotNull Group group, final TreeModel treeModel) { + return new GroupWrapper(project, group, treeModel); + } + + + private void rebuildSubtree() { + initChildren(); + performTreeActions(); + + synchronizeChildren(); + + } + + protected void synchronizeChildren() { + if (myOldChildren != null && myChildren != null) { + HashMap<Object, CachingChildrenTreeNode> oldValuesToChildrenMap = new HashMap<Object, CachingChildrenTreeNode>(); + for (CachingChildrenTreeNode oldChild : myOldChildren) { + final Object oldValue = oldChild instanceof TreeElementWrapper ? oldChild.getValue() : oldChild; + if (oldValue != null) { + oldValuesToChildrenMap.put(oldValue, oldChild); + } + } + + for (int i = 0; i < myChildren.size(); i++) { + CachingChildrenTreeNode newChild = myChildren.get(i); + final Object newValue = newChild instanceof TreeElementWrapper ? newChild.getValue() : newChild; + if (newValue != null) { + final CachingChildrenTreeNode oldChild = oldValuesToChildrenMap.get(newValue); + if (oldChild != null) { + oldChild.copyFromNewInstance(newChild); + oldChild.setValue(newChild.getValue()); + myChildren.set(i, oldChild); + } + } + } + + myOldChildren = null; + } + } + + protected abstract void copyFromNewInstance(@NotNull CachingChildrenTreeNode newInstance); + + protected abstract void performTreeActions(); + + protected abstract void initChildren(); + + @Override + public void navigate(final boolean requestFocus) { + ((Navigatable)getValue()).navigate(requestFocus); + } + + @Override + public boolean canNavigate() { + return getValue() instanceof Navigatable && ((Navigatable)getValue()).canNavigate(); + } + + @Override + public boolean canNavigateToSource() { + return getValue() instanceof Navigatable && ((Navigatable)getValue()).canNavigateToSource(); + } + + protected void clearChildren() { + if (myChildren != null) { + myChildren.clear(); + } else { + myChildren = new ArrayList<CachingChildrenTreeNode>(); + } + } + + public void rebuildChildren() { + if (myChildren != null) { + myOldChildren = myChildren; + for (final CachingChildrenTreeNode node : myChildren) { + node.rebuildChildren(); + } + myChildren = null; + } + } + + protected void resetChildren() { + myChildren = null; + } +} |