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
;
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
) {
46 myItemType
= itemType
;
48 myItemIndex
= itemIndex
;
49 myUserObject
= userObject
;
52 public PathElement() {
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
;
81 private boolean myScrollToSelection
;
83 private TreeState(List
<List
<PathElement
>> expandedPaths
, final List
<List
<PathElement
>> selectedPaths
) {
84 myExpandedPaths
= expandedPaths
;
85 mySelectedPaths
= selectedPaths
;
86 myScrollToSelection
= true;
90 this(new ArrayList
<List
<PathElement
>>(), new ArrayList
<List
<PathElement
>>());
93 public void readExternal(Element element
) throws InvalidDataException
{
94 myExpandedPaths
.clear();
95 final List paths
= element
.getChildren(PATH
);
96 for (final Object path
: paths
) {
97 Element xmlPathElement
= (Element
)path
;
98 myExpandedPaths
.add(readPath(xmlPathElement
));
102 private static List
<PathElement
> readPath(final Element xmlPathElement
) throws InvalidDataException
{
103 final ArrayList
<PathElement
> result
= new ArrayList
<PathElement
>();
104 final List elements
= xmlPathElement
.getChildren(PATH_ELEMENT
);
105 for (final Object element
: elements
) {
106 Element xmlPathElementElement
= (Element
)element
;
107 final PathElement pathElement
= new PathElement();
108 pathElement
.readExternal(xmlPathElementElement
);
109 result
.add(pathElement
);
114 public static TreeState
createOn(JTree tree
, final DefaultMutableTreeNode treeNode
) {
115 return new TreeState(createExpandedPaths(tree
, treeNode
), createSelectedPaths(tree
, treeNode
));
118 public void writeExternal(Element element
) throws WriteExternalException
{
119 for (List
<PathElement
> path
: myExpandedPaths
) {
120 final Element pathElement
= new Element(PATH
);
121 writeExternal(pathElement
, path
);
122 element
.addContent(pathElement
);
126 private static void writeExternal(final Element pathXmlElement
, final List
<PathElement
> path
) throws WriteExternalException
{
127 for (final PathElement aPath
: path
) {
128 final Element pathXmlElementElement
= new Element(PATH_ELEMENT
);
129 aPath
.writeExternal(pathXmlElementElement
);
130 pathXmlElement
.addContent(pathXmlElementElement
);
134 public static TreeState
createOn(@NotNull JTree tree
) {
135 return new TreeState(createPaths(tree
), new ArrayList
<List
<PathElement
>>());
139 private static List
<List
<PathElement
>> createPaths(final JTree tree
) {
140 final ArrayList
<List
<PathElement
>> result
= new ArrayList
<List
<PathElement
>>();
141 final List
<TreePath
> expandedPaths
= TreeUtil
.collectExpandedPaths(tree
);
142 for (final TreePath expandedPath
: expandedPaths
) {
143 final List
<PathElement
> path
= createPath(expandedPath
);
151 private static List
<List
<PathElement
>> createExpandedPaths(JTree tree
, final DefaultMutableTreeNode treeNode
) {
152 final ArrayList
<List
<PathElement
>> result
= new ArrayList
<List
<PathElement
>>();
153 final List
<TreePath
> expandedPaths
= TreeUtil
.collectExpandedPaths(tree
, new TreePath(treeNode
.getPath()));
154 for (final TreePath expandedPath
: expandedPaths
) {
155 final List
<PathElement
> path
= createPath(expandedPath
);
163 private static List
<List
<PathElement
>> createSelectedPaths(JTree tree
, final DefaultMutableTreeNode treeNode
) {
164 final ArrayList
<List
<PathElement
>> result
= new ArrayList
<List
<PathElement
>>();
165 final List
<TreePath
> selectedPaths
166 = TreeUtil
.collectSelectedPaths(tree
, new TreePath(treeNode
.getPath()));
167 for (final TreePath expandedPath
: selectedPaths
) {
168 final List
<PathElement
> path
= createPath(expandedPath
);
176 private static List
<PathElement
> createPath(final TreePath treePath
) {
177 final ArrayList
<PathElement
> result
= new ArrayList
<PathElement
>();
178 for (int i
= 0; i
< treePath
.getPathCount(); i
++) {
179 final Object pathComponent
= treePath
.getPathComponent(i
);
180 if (pathComponent
instanceof DefaultMutableTreeNode
) {
181 final DefaultMutableTreeNode node
= (DefaultMutableTreeNode
)pathComponent
;
182 final TreeNode parent
= node
.getParent();
184 final Object userObject
= node
.getUserObject();
185 if (userObject
instanceof NodeDescriptor
) {
186 final NodeDescriptor nodeDescriptor
= (NodeDescriptor
)userObject
;
187 //nodeDescriptor.update();
188 final int childIndex
= parent
!= null ? parent
.getIndex(node
) : 0;
189 result
.add(new PathElement(getDescriptorKey(nodeDescriptor
), getDescriptorType(nodeDescriptor
), childIndex
, nodeDescriptor
));
192 result
.add(new PathElement("", "", 0, userObject
));
202 private static String
getDescriptorKey(final NodeDescriptor nodeDescriptor
) {
203 if (nodeDescriptor
instanceof AbstractTreeNode
) {
205 if (nodeDescriptor
instanceof NodeDescriptorProvidingKey
) {
206 value
= ((NodeDescriptorProvidingKey
)nodeDescriptor
).getKey();
209 value
= ((AbstractTreeNode
)nodeDescriptor
).getValue();
212 if (value
instanceof NavigationItem
) {
214 final String name
= ((NavigationItem
)value
).getName();
215 return name
!= null ? name
: value
.toString();
217 catch (Exception e
) {
218 //ignore for invalid psi element
222 return nodeDescriptor
.toString();
225 private static String
getDescriptorType(final NodeDescriptor nodeDescriptor
) {
226 return nodeDescriptor
.getClass().getName();
229 public void applyTo(JTree tree
) {
230 applyExpanded(getFacade(tree
), tree
.getModel().getRoot());
233 private void applyExpanded(final TreeFacade tree
, final Object root
) {
234 tree
.getIntialized().doWhenDone(new Runnable() {
236 _applyExpanded(tree
, root
);
241 private void _applyExpanded(TreeFacade tree
, Object root
) {
242 if (!(root
instanceof DefaultMutableTreeNode
)) {
245 final DefaultMutableTreeNode nodeRoot
= (DefaultMutableTreeNode
)root
;
246 final TreeNode
[] nodePath
= nodeRoot
.getPath();
247 if (nodePath
.length
> 0) {
248 for (final List
<PathElement
> path
: myExpandedPaths
) {
249 applyTo(nodePath
.length
- 1,path
, root
, tree
);
254 public void applyTo(final JTree tree
, final DefaultMutableTreeNode node
) {
255 applyExpanded(getFacade(tree
), node
);
256 if (tree
.getSelectionCount() == 0) {
257 applySelected(tree
, node
);
262 private void applySelected(final JTree tree
, final DefaultMutableTreeNode node
) {
263 TreeUtil
.unselect(tree
, node
);
264 List
<TreePath
> selectionPaths
= new ArrayList
<TreePath
>();
265 for (List
<PathElement
> pathElements
: mySelectedPaths
) {
266 applySelectedTo(pathElements
, tree
.getModel().getRoot(), tree
, selectionPaths
, myScrollToSelection
);
269 if (selectionPaths
.size() > 1) {
270 for (TreePath path
: selectionPaths
) {
271 tree
.addSelectionPath(path
);
277 private static DefaultMutableTreeNode
findMatchedChild(DefaultMutableTreeNode parent
, PathElement pathElement
) {
279 for (int j
= 0; j
< parent
.getChildCount(); j
++) {
280 final TreeNode child
= parent
.getChildAt(j
);
281 if (!(child
instanceof DefaultMutableTreeNode
)) continue;
282 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)child
;
283 final Object userObject
= childNode
.getUserObject();
284 if (pathElement
.matchedWithByObject(userObject
)) return childNode
;
287 for (int j
= 0; j
< parent
.getChildCount(); j
++) {
288 final TreeNode child
= parent
.getChildAt(j
);
289 if (!(child
instanceof DefaultMutableTreeNode
)) continue;
290 final DefaultMutableTreeNode childNode
= (DefaultMutableTreeNode
)child
;
291 final Object userObject
= childNode
.getUserObject();
292 if (!(userObject
instanceof NodeDescriptor
)) continue;
293 final NodeDescriptor nodeDescriptor
= (NodeDescriptor
)userObject
;
294 if (pathElement
.matchedWith(nodeDescriptor
)) return childNode
;
297 if (parent
.getChildCount() > 0) {
298 int index
= pathElement
.myItemIndex
;
299 if (index
>= parent
.getChildCount()) {
300 index
= parent
.getChildCount()-1;
302 final TreeNode child
= parent
.getChildAt(index
);
303 if (child
instanceof DefaultMutableTreeNode
) {
304 return (DefaultMutableTreeNode
) child
;
312 private static boolean applyTo(final int positionInPath
, final List
<PathElement
> path
, final Object root
, final TreeFacade tree
) {
313 if (!(root
instanceof DefaultMutableTreeNode
)) return false;
315 final DefaultMutableTreeNode treeNode
= (DefaultMutableTreeNode
)root
;
317 final Object userObject
= treeNode
.getUserObject();
318 final PathElement pathElement
= path
.get(positionInPath
);
320 if (userObject
instanceof NodeDescriptor
) {
321 if (!pathElement
.matchedWith((NodeDescriptor
)userObject
)) return false;
324 if (!pathElement
.matchedWithByObject(userObject
)) return false;
327 tree
.expand(treeNode
).doWhenDone(new Runnable() {
329 if (positionInPath
== path
.size() - 1) {
333 for (int j
= 0; j
< treeNode
.getChildCount(); j
++) {
334 final TreeNode child
= treeNode
.getChildAt(j
);
335 final boolean resultFromChild
= applyTo(positionInPath
+ 1, path
, child
, tree
);
336 if (resultFromChild
) {
347 private static void applySelectedTo(final List
<PathElement
> path
,
350 final List
<TreePath
> outSelectionPaths
, final boolean scrollToSelection
) {
352 for (int i
= 1; i
< path
.size(); i
++) {
353 if (!(root
instanceof DefaultMutableTreeNode
)) return;
355 root
= findMatchedChild((DefaultMutableTreeNode
)root
, path
.get(i
));
358 if (!(root
instanceof DefaultMutableTreeNode
)) return;
360 final TreePath pathInNewTree
= new TreePath(((DefaultMutableTreeNode
) root
).getPath());
361 if (scrollToSelection
) {
362 TreeUtil
.selectPath(tree
, pathInNewTree
);
364 tree
.setSelectionPath(pathInNewTree
);
366 outSelectionPaths
.add(pathInNewTree
);
369 interface TreeFacade
{
370 ActionCallback
getIntialized();
371 ActionCallback
expand(DefaultMutableTreeNode node
);
374 private static TreeFacade
getFacade(JTree tree
) {
375 final AbstractTreeBuilder builder
= AbstractTreeBuilder
.getBuilderFor(tree
);
376 return builder
!= null ?
new BuilderFacade(builder
) : new JTreeFacade(tree
);
379 static class JTreeFacade
implements TreeFacade
{
381 private JTree myTree
;
383 JTreeFacade(JTree tree
) {
387 public ActionCallback
expand(DefaultMutableTreeNode node
) {
388 myTree
.expandPath(new TreePath(node
.getPath()));
389 return new ActionCallback
.Done();
392 public ActionCallback
getIntialized() {
393 return new ActionCallback
.Done();
397 static class BuilderFacade
implements TreeFacade
{
399 private AbstractTreeBuilder myBuilder
;
401 BuilderFacade(AbstractTreeBuilder builder
) {
405 public ActionCallback
getIntialized() {
406 return myBuilder
.getIntialized();
409 public ActionCallback
expand(DefaultMutableTreeNode node
) {
410 final Object userObject
= node
.getUserObject();
411 if (!(userObject
instanceof NodeDescriptor
)) return new ActionCallback
.Rejected();
413 NodeDescriptor desc
= (NodeDescriptor
)userObject
;
415 final Object element
= myBuilder
.getTreeStructureElement(desc
);
417 final ActionCallback result
= new ActionCallback();
419 myBuilder
.expand(element
, new Runnable() {
429 public void setScrollToSelection(boolean scrollToSelection
) {
430 myScrollToSelection
= scrollToSelection
;