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
;
82 private TreeState(List
<List
<PathElement
>> expandedPaths
, final List
<List
<PathElement
>> selectedPaths
) {
83 myExpandedPaths
= expandedPaths
;
84 mySelectedPaths
= selectedPaths
;
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
);
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
);
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
);
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
);
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
));
190 result
.add(new PathElement("", "", 0, userObject
));
200 private static String
getDescriptorKey(final NodeDescriptor nodeDescriptor
) {
201 if (nodeDescriptor
instanceof AbstractTreeNode
) {
203 if (nodeDescriptor
instanceof NodeDescriptorProvidingKey
) {
204 value
= ((NodeDescriptorProvidingKey
)nodeDescriptor
).getKey();
207 value
= ((AbstractTreeNode
)nodeDescriptor
).getValue();
210 if (value
instanceof NavigationItem
) {
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() {
234 _applyExpanded(tree
, root
);
239 private void _applyExpanded(TreeFacade tree
, Object root
) {
240 if (!(root
instanceof DefaultMutableTreeNode
)) {
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
;
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;
321 if (!pathElement
.matchedWithByObject(userObject
)) return false;
324 tree
.expand(treeNode
).doWhenDone(new Runnable() {
326 if (positionInPath
== path
.size() - 1) {
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
) {
344 private static void applySelectedTo(final List
<PathElement
> path
,
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
) {
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
) {
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() {