6299c27c6ea698f2a449264f14b2eab660303903
[fedora-idea.git] / platform / platform-api / src / com / intellij / ide / util / treeView / TreeState.java
blob6299c27c6ea698f2a449264f14b2eab660303903
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.ide.util.treeView;
18 import com.intellij.navigation.NavigationItem;
19 import com.intellij.openapi.util.*;
20 import com.intellij.util.ui.tree.TreeUtil;
21 import org.jdom.Element;
22 import org.jetbrains.annotations.NonNls;
23 import org.jetbrains.annotations.NotNull;
25 import javax.swing.*;
26 import javax.swing.tree.DefaultMutableTreeNode;
27 import javax.swing.tree.TreeNode;
28 import javax.swing.tree.TreePath;
29 import java.util.ArrayList;
30 import java.util.List;
32 public class TreeState implements JDOMExternalizable {
33 @NonNls private static final String PATH = "PATH";
34 @NonNls private static final String PATH_ELEMENT = "PATH_ELEMENT";
35 @NonNls private static final String USER_OBJECT = "USER_OBJECT";
37 static class PathElement implements JDOMExternalizable {
38 public String myItemId;
39 public String myItemType;
41 private final int myItemIndex;
42 private Object myUserObject;
44 public PathElement(final String itemId, final String itemType, final int itemIndex, Object userObject) {
45 myItemId = itemId;
46 myItemType = itemType;
48 myItemIndex = itemIndex;
49 myUserObject = userObject;
52 public PathElement() {
53 myItemIndex = -1;
54 myUserObject = null;
57 public boolean matchedWith(NodeDescriptor nodeDescriptor) {
58 return Comparing.equal(myItemId, getDescriptorKey(nodeDescriptor)) &&
59 Comparing.equal(myItemType, getDescriptorType(nodeDescriptor));
62 public boolean matchedWithByObject(Object object) {
63 return myUserObject != null && myUserObject.equals(object);
66 public void readExternal(Element element) throws InvalidDataException {
67 DefaultJDOMExternalizer.readExternal(this, element);
68 myUserObject = element.getAttributeValue(USER_OBJECT);
71 public void writeExternal(Element element) throws WriteExternalException {
72 DefaultJDOMExternalizer.writeExternal(this, element);
73 if (myUserObject instanceof String){
74 element.setAttribute(USER_OBJECT, (String)myUserObject);
79 private final List<List<PathElement>> myExpandedPaths;
80 private final List<List<PathElement>> mySelectedPaths;
82 private TreeState(List<List<PathElement>> expandedPaths, final List<List<PathElement>> selectedPaths) {
83 myExpandedPaths = expandedPaths;
84 mySelectedPaths = selectedPaths;
87 public TreeState() {
88 this(new ArrayList<List<PathElement>>(), new ArrayList<List<PathElement>>());
91 public void readExternal(Element element) throws InvalidDataException {
92 myExpandedPaths.clear();
93 final List paths = element.getChildren(PATH);
94 for (final Object path : paths) {
95 Element xmlPathElement = (Element)path;
96 myExpandedPaths.add(readPath(xmlPathElement));
100 private static List<PathElement> readPath(final Element xmlPathElement) throws InvalidDataException {
101 final ArrayList<PathElement> result = new ArrayList<PathElement>();
102 final List elements = xmlPathElement.getChildren(PATH_ELEMENT);
103 for (final Object element : elements) {
104 Element xmlPathElementElement = (Element)element;
105 final PathElement pathElement = new PathElement();
106 pathElement.readExternal(xmlPathElementElement);
107 result.add(pathElement);
109 return result;
112 public static TreeState createOn(JTree tree, final DefaultMutableTreeNode treeNode) {
113 return new TreeState(createExpandedPaths(tree, treeNode), createSelectedPaths(tree, treeNode));
116 public void writeExternal(Element element) throws WriteExternalException {
117 for (List<PathElement> path : myExpandedPaths) {
118 final Element pathElement = new Element(PATH);
119 writeExternal(pathElement, path);
120 element.addContent(pathElement);
124 private static void writeExternal(final Element pathXmlElement, final List<PathElement> path) throws WriteExternalException {
125 for (final PathElement aPath : path) {
126 final Element pathXmlElementElement = new Element(PATH_ELEMENT);
127 aPath.writeExternal(pathXmlElementElement);
128 pathXmlElement.addContent(pathXmlElementElement);
132 public static TreeState createOn(@NotNull JTree tree) {
133 return new TreeState(createPaths(tree), new ArrayList<List<PathElement>>());
137 private static List<List<PathElement>> createPaths(final JTree tree) {
138 final ArrayList<List<PathElement>> result = new ArrayList<List<PathElement>>();
139 final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree);
140 for (final TreePath expandedPath : expandedPaths) {
141 final List<PathElement> path = createPath(expandedPath);
142 if (path != null) {
143 result.add(path);
146 return result;
149 private static List<List<PathElement>> createExpandedPaths(JTree tree, final DefaultMutableTreeNode treeNode) {
150 final ArrayList<List<PathElement>> result = new ArrayList<List<PathElement>>();
151 final List<TreePath> expandedPaths = TreeUtil.collectExpandedPaths(tree, new TreePath(treeNode.getPath()));
152 for (final TreePath expandedPath : expandedPaths) {
153 final List<PathElement> path = createPath(expandedPath);
154 if (path != null) {
155 result.add(path);
158 return result;
161 private static List<List<PathElement>> createSelectedPaths(JTree tree, final DefaultMutableTreeNode treeNode) {
162 final ArrayList<List<PathElement>> result = new ArrayList<List<PathElement>>();
163 final List<TreePath> selectedPaths
164 = TreeUtil.collectSelectedPaths(tree, new TreePath(treeNode.getPath()));
165 for (final TreePath expandedPath : selectedPaths) {
166 final List<PathElement> path = createPath(expandedPath);
167 if (path != null) {
168 result.add(path);
171 return result;
174 private static List<PathElement> createPath(final TreePath treePath) {
175 final ArrayList<PathElement> result = new ArrayList<PathElement>();
176 for (int i = 0; i < treePath.getPathCount(); i++) {
177 final Object pathComponent = treePath.getPathComponent(i);
178 if (pathComponent instanceof DefaultMutableTreeNode) {
179 final DefaultMutableTreeNode node = (DefaultMutableTreeNode)pathComponent;
180 final TreeNode parent = node.getParent();
182 final Object userObject = node.getUserObject();
183 if (userObject instanceof NodeDescriptor) {
184 final NodeDescriptor nodeDescriptor = (NodeDescriptor)userObject;
185 //nodeDescriptor.update();
186 final int childIndex = parent != null ? parent.getIndex(node) : 0;
187 result.add(new PathElement(getDescriptorKey(nodeDescriptor), getDescriptorType(nodeDescriptor), childIndex, nodeDescriptor));
189 else {
190 result.add(new PathElement("", "", 0, userObject));
193 else {
194 return null;
197 return result;
200 private static String getDescriptorKey(final NodeDescriptor nodeDescriptor) {
201 if (nodeDescriptor instanceof AbstractTreeNode) {
202 Object value;
203 if (nodeDescriptor instanceof NodeDescriptorProvidingKey) {
204 value = ((NodeDescriptorProvidingKey)nodeDescriptor).getKey();
206 else {
207 value = ((AbstractTreeNode)nodeDescriptor).getValue();
210 if (value instanceof NavigationItem) {
211 try {
212 final String name = ((NavigationItem)value).getName();
213 return name != null ? name : value.toString();
215 catch (Exception e) {
216 //ignore for invalid psi element
220 return nodeDescriptor.toString();
223 private static String getDescriptorType(final NodeDescriptor nodeDescriptor) {
224 return nodeDescriptor.getClass().getName();
227 public void applyTo(JTree tree) {
228 applyExpanded(getFacade(tree), tree.getModel().getRoot());
231 private void applyExpanded(final TreeFacade tree, final Object root) {
232 tree.getIntialized().doWhenDone(new Runnable() {
233 public void run() {
234 _applyExpanded(tree, root);
239 private void _applyExpanded(TreeFacade tree, Object root) {
240 if (!(root instanceof DefaultMutableTreeNode)) {
241 return;
243 final DefaultMutableTreeNode nodeRoot = (DefaultMutableTreeNode)root;
244 final TreeNode[] nodePath = nodeRoot.getPath();
245 if (nodePath.length > 0) {
246 for (final List<PathElement> path : myExpandedPaths) {
247 applyTo(nodePath.length - 1,path, root, tree);
252 public void applyTo(final JTree tree, final DefaultMutableTreeNode node) {
253 applyExpanded(getFacade(tree), node);
254 if (tree.getSelectionCount() == 0) {
255 applySelected(tree, node);
259 private void applySelected(final JTree tree, final DefaultMutableTreeNode node) {
260 TreeUtil.unselect(tree, node);
261 List<TreePath> selectionPaths = new ArrayList<TreePath>();
262 for (List<PathElement> pathElements : mySelectedPaths) {
263 applySelectedTo(pathElements, tree.getModel().getRoot(), tree, selectionPaths);
266 if (selectionPaths.size() > 1) {
267 for (TreePath path : selectionPaths) {
268 tree.addSelectionPath(path);
274 private static DefaultMutableTreeNode findMatchedChild(DefaultMutableTreeNode parent, PathElement pathElement) {
276 for (int j = 0; j < parent.getChildCount(); j++) {
277 final TreeNode child = parent.getChildAt(j);
278 if (!(child instanceof DefaultMutableTreeNode)) continue;
279 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
280 final Object userObject = childNode.getUserObject();
281 if (pathElement.matchedWithByObject(userObject)) return childNode;
284 for (int j = 0; j < parent.getChildCount(); j++) {
285 final TreeNode child = parent.getChildAt(j);
286 if (!(child instanceof DefaultMutableTreeNode)) continue;
287 final DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)child;
288 final Object userObject = childNode.getUserObject();
289 if (!(userObject instanceof NodeDescriptor)) continue;
290 final NodeDescriptor nodeDescriptor = (NodeDescriptor)userObject;
291 if (pathElement.matchedWith(nodeDescriptor)) return childNode;
294 if (parent.getChildCount() > 0) {
295 int index = pathElement.myItemIndex;
296 if (index >= parent.getChildCount()) {
297 index = parent.getChildCount()-1;
299 final TreeNode child = parent.getChildAt(index);
300 if (child instanceof DefaultMutableTreeNode) {
301 return (DefaultMutableTreeNode) child;
305 return null;
309 private static boolean applyTo(final int positionInPath, final List<PathElement> path, final Object root, final TreeFacade tree) {
310 if (!(root instanceof DefaultMutableTreeNode)) return false;
312 final DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)root;
314 final Object userObject = treeNode.getUserObject();
315 final PathElement pathElement = path.get(positionInPath);
317 if (userObject instanceof NodeDescriptor) {
318 if (!pathElement.matchedWith((NodeDescriptor)userObject)) return false;
320 else {
321 if (!pathElement.matchedWithByObject(userObject)) return false;
324 tree.expand(treeNode).doWhenDone(new Runnable() {
325 public void run() {
326 if (positionInPath == path.size() - 1) {
327 return;
330 for (int j = 0; j < treeNode.getChildCount(); j++) {
331 final TreeNode child = treeNode.getChildAt(j);
332 final boolean resultFromChild = applyTo(positionInPath + 1, path, child, tree);
333 if (resultFromChild) {
334 break;
341 return true;
344 private static void applySelectedTo(final List<PathElement> path,
345 Object root,
346 JTree tree,
347 final List<TreePath> outSelectionPaths) {
349 for (int i = 1; i < path.size(); i++) {
350 if (!(root instanceof DefaultMutableTreeNode)) return;
352 root = findMatchedChild((DefaultMutableTreeNode)root, path.get(i));
355 if (!(root instanceof DefaultMutableTreeNode)) return;
357 final TreePath pathInNewTree = new TreePath(((DefaultMutableTreeNode) root).getPath());
358 TreeUtil.selectPath(tree, pathInNewTree);
359 outSelectionPaths.add(pathInNewTree);
362 interface TreeFacade {
363 ActionCallback getIntialized();
364 ActionCallback expand(DefaultMutableTreeNode node);
367 private static TreeFacade getFacade(JTree tree) {
368 final AbstractTreeBuilder builder = AbstractTreeBuilder.getBuilderFor(tree);
369 return builder != null ? new BuilderFacade(builder) : new JTreeFacade(tree);
372 static class JTreeFacade implements TreeFacade {
374 private JTree myTree;
376 JTreeFacade(JTree tree) {
377 myTree = tree;
380 public ActionCallback expand(DefaultMutableTreeNode node) {
381 myTree.expandPath(new TreePath(node.getPath()));
382 return new ActionCallback.Done();
385 public ActionCallback getIntialized() {
386 return new ActionCallback.Done();
390 static class BuilderFacade implements TreeFacade {
392 private AbstractTreeBuilder myBuilder;
394 BuilderFacade(AbstractTreeBuilder builder) {
395 myBuilder = builder;
398 public ActionCallback getIntialized() {
399 return myBuilder.getIntialized();
402 public ActionCallback expand(DefaultMutableTreeNode node) {
403 final Object userObject = node.getUserObject();
404 if (!(userObject instanceof NodeDescriptor)) return new ActionCallback.Rejected();
406 NodeDescriptor desc = (NodeDescriptor)userObject;
408 final Object element = myBuilder.getTreeStructureElement(desc);
410 final ActionCallback result = new ActionCallback();
412 myBuilder.expand(element, new Runnable() {
413 public void run() {
414 result.setDone();
418 return result;