View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.tobago.model;
21  
22  import javax.swing.tree.TreeNode;
23  import java.io.Serializable;
24  import java.util.ArrayList;
25  import java.util.Collections;
26  import java.util.HashSet;
27  import java.util.Set;
28  
29  /**
30   * Manages the expanded state of an tree.
31   *
32   * @since 2.0.0
33   */
34  public class ExpandedState implements Serializable {
35  
36    private int defaultExpandedLevels;
37    private Set<TreePath> expandedSet;
38    private Set<TreePath> collapsedSet;
39  
40    /**
41     * Creates a new state object to store which nodes of a tree are expanded and collapsed in a view.
42     *
43     * @param defaultExpandedLevels All nodes up to this level are expanded by default.
44     */
45    public ExpandedState(final int defaultExpandedLevels) {
46      this.defaultExpandedLevels = defaultExpandedLevels;
47      this.expandedSet = new HashSet<>();
48      this.collapsedSet = new HashSet<>();
49    }
50  
51    /**
52     * Checks if a node is expanded.
53     *
54     * @param node The node to check.
55     * @return Is the node expanded?
56     */
57    public boolean isExpanded(final TreeNode node) {
58      final TreePath path = new TreePath(node);
59      return isExpanded(path);
60    }
61  
62    /**
63     * Checks if a node is expanded.
64     *
65     * @param path The path of the node to check.
66     * @return Is the node behind this path is expanded?
67     */
68    public boolean isExpanded(final TreePath path) {
69      if (expandedSet.contains(path)) {
70        return true;
71      }
72      if (collapsedSet.contains(path)) {
73        return false;
74      }
75      return path.getLength() < defaultExpandedLevels;
76    }
77  
78    /**
79     * Expands a single node.
80     *
81     * @param node The node to expand. Also the parents will be expanded since Tobago 3.0.0.
82     */
83    public void expand(final TreeNode node) {
84      expand(node, true);
85    }
86  
87    /**
88     * Expands a single node.
89     *
90     * @param node The node to expand.
91     * @param parents Should the parents also be expanded?
92     */
93    public void expand(final TreeNode node, final boolean parents) {
94      final TreePath path = new TreePath(node);
95      expand(path, parents);
96    }
97  
98    /**
99     * Expands a single node.
100    *
101    * @param path The path of the node to expand. Also the parents will be expanded since Tobago 3.0.0.
102    */
103   public void expand(final TreePath path) {
104     expand(path, true);
105   }
106 
107   /**
108    * Expands a single node.
109    *
110    * @param path The path of the node to expand.
111    * @param parents Should the parents also be expanded?
112    */
113   public void expand(final TreePath path, final boolean parents) {
114     if (path.getLength() >= defaultExpandedLevels) {
115       expandedSet.add(path);
116     } else {
117       collapsedSet.remove(path);
118     }
119     if (parents && !path.isRoot()) {
120       expand(path.getParent(), true);
121     }
122   }
123 
124   /**
125    * Expands all nodes that level are lower or equals the parameter level.
126    *
127    * @param level The level to expand.
128    */
129   public void expand(final int level) {
130     final ArrayList<TreePath> toRemove = new ArrayList<>();
131     if (level > defaultExpandedLevels) {
132       defaultExpandedLevels = level;
133       for (final TreePath treePath : expandedSet) {
134         if (treePath.getLength() < defaultExpandedLevels) {
135           toRemove.add(treePath);
136         }
137       }
138       expandedSet.removeAll(toRemove);
139       collapsedSet.clear();
140     } else {
141       for (final TreePath treePath : collapsedSet) {
142         if (treePath.getLength() < level) {
143           toRemove.add(treePath);
144         }
145       }
146       collapsedSet.removeAll(toRemove);
147     }
148   }
149 
150   /**
151    * Expands a nodes of the tree.
152    */
153   public void expandAll() {
154     defaultExpandedLevels = Integer.MAX_VALUE;
155     expandedSet.clear();
156     collapsedSet.clear();
157   }
158 
159   /**
160    * Collapses a single node.
161    *
162    * @param node The node to collapse.
163    */
164   public void collapse(final TreeNode node) {
165     final TreePath path = new TreePath(node);
166     collapse(path);
167   }
168 
169   /**
170    * Collapses a single node.
171    *
172    * @param path The path of the node to collapse.
173    */
174   public void collapse(final TreePath path) {
175     if (path.getLength() < defaultExpandedLevels) {
176       collapsedSet.add(path);
177     } else {
178       expandedSet.remove(path);
179     }
180   }
181 
182   /**
183    * Collapses all nodes that level are higher or equals the parameter level.
184    *
185    * @param level The level to expand.
186    */
187   public void collapse(final int level) {
188     int count = level;
189     // to use a symmetric algorithm like in expand
190     count--;
191 
192     final ArrayList<TreePath> toRemove = new ArrayList<>();
193     if (count < defaultExpandedLevels) {
194       defaultExpandedLevels = count;
195       for (final TreePath treePath : collapsedSet) {
196         if (treePath.getLength() >= defaultExpandedLevels) {
197           toRemove.add(treePath);
198         }
199       }
200       collapsedSet.removeAll(toRemove);
201       expandedSet.clear();
202     } else {
203       for (final TreePath treePath : expandedSet) {
204         if (treePath.getLength() >= count) {
205           toRemove.add(treePath);
206         }
207       }
208       expandedSet.removeAll(toRemove);
209     }
210   }
211 
212   /**
213    * Collapses a nodes of the tree.
214    */
215   public void collapseAll() {
216     defaultExpandedLevels = 0;
217     expandedSet.clear();
218     collapsedSet.clear();
219   }
220 
221   /**
222    * Collapses a nodes of the tree. The root node will be expanded.
223    */
224   public void collapseAllButRoot() {
225     defaultExpandedLevels = 1;
226     expandedSet.clear();
227     collapsedSet.clear();
228   }
229 
230   /**
231    * Resets the state to the defaults. After this call, the nodes with level smaller than defaultExpandedLevels
232    * are expanded, the other ones are collapsed.
233    */
234   public void reset() {
235     expandedSet.clear();
236     collapsedSet.clear();
237   }
238 
239   /**
240    * @return A unmodifiable set of paths of the expanded nodes.
241    */
242   public Set<TreePath> getExpandedSet() {
243     return Collections.unmodifiableSet(expandedSet);
244   }
245 
246   /**
247    * @return A unmodifiable set of paths of the collapsed nodes.
248    */
249   public Set<TreePath> getCollapsedSet() {
250     return Collections.unmodifiableSet(collapsedSet);
251   }
252 }