001 /*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License. You may obtain a copy of the License at
009 *
010 * http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied. See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019
020 package org.apache.myfaces.tobago.component;
021
022 import org.apache.commons.logging.Log;
023 import org.apache.commons.logging.LogFactory;
024 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_COLUMNS;
025 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SELECTABLE;
026 import static org.apache.myfaces.tobago.TobagoConstants.FACET_LAYOUT_DEFAULT;
027 import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_GRID_LAYOUT;
028 import org.apache.myfaces.tobago.config.ThemeConfig;
029
030 import javax.faces.component.UIComponent;
031 import javax.faces.context.FacesContext;
032 import javax.swing.tree.DefaultMutableTreeNode;
033 import javax.swing.tree.TreeNode;
034 import java.io.IOException;
035 import java.util.ArrayList;
036 import java.util.Collections;
037 import java.util.Iterator;
038 import java.util.List;
039 import java.util.Map;
040 import java.util.Set;
041
042 /*
043 * User: weber
044 * Date: Mar 16, 2005
045 * Time: 12:33:08 PM
046 */
047 public class UITreeListbox extends UITreeOld implements LayoutProvider {
048
049 private static final Log LOG = LogFactory.getLog(UITreeListbox.class);
050
051 public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.TreeListbox";
052
053 public static final String BOXES_PREFIX = "boxes_";
054
055 private List<UITreeOldNode> selectionPath = null;
056 private List<UITreeOldNode> expandPath = null;
057
058 private boolean encodingChildren = false;
059
060 private List<UITreeListboxBox> boxes;
061
062
063 protected String nodeStateId(FacesContext facesContext, UITreeOldNode node) {
064 // this must do the same as nodeStateId() in tree.js
065 String clientId = node.getClientId(facesContext);
066 int last = clientId.lastIndexOf(':') + 1;
067 return clientId.substring(last);
068 }
069
070 public void encodeBegin(FacesContext facesContext)
071 throws IOException {
072 // TODO change this should be renamed to DimensionUtils.prepare!!!
073 UILayout.getLayout(this).layoutBegin(facesContext, this);
074 // debugStates(facesContext);
075 fixSelectionType();
076 super.encodeBegin(facesContext);
077 debugStates(facesContext);
078 createUIBoxes(facesContext);
079 }
080
081 @SuppressWarnings(value = "unchecked")
082 private void fixSelectionType() {
083 final Map attributes = getAttributes();
084 Object selectable = attributes.get(ATTR_SELECTABLE);
085 if ("single".equals(selectable)
086 || "singleLeafOnly".equals(selectable)
087 || "siblingLeafOnly".equals(selectable)) {
088 } else if (selectable == null) {
089 attributes.put(ATTR_SELECTABLE, "single");
090 } else {
091 // fix to single
092 LOG.warn("Illegal attributeValue selectable : " + selectable + " set to 'single'");
093 attributes.put(ATTR_SELECTABLE, "single");
094 }
095 }
096
097 private void debugStates(FacesContext facesContext) {
098 if (LOG.isDebugEnabled()) {
099 LOG.debug("#####################################################");
100 StringBuilder state = new StringBuilder("expandState : ;");
101 for (DefaultMutableTreeNode treeNode : getState().getExpandState()) {
102 state.append(nodeStateId(facesContext, findUITreeNode(getRoot(), treeNode)));
103 state.append(";");
104 }
105 LOG.debug(state);
106
107 state = new StringBuilder("selectState : ;");
108 for (DefaultMutableTreeNode treeNode : getState().getSelection()) {
109 state.append(nodeStateId(facesContext, findUITreeNode(getRoot(), treeNode)));
110 state.append(";");
111 }
112 LOG.debug(state);
113
114 state = new StringBuilder("selectionPath : ;");
115 for (UITreeOldNode treeNode : getSelectionPath()) {
116 state.append(nodeStateId(facesContext, treeNode));
117 state.append(";");
118 }
119 LOG.debug(state);
120
121 state = new StringBuilder("expandPath : ;");
122 for (UITreeOldNode treeNode : getExpandPath()) {
123 state.append(nodeStateId(facesContext, treeNode));
124 state.append(";");
125 }
126 LOG.debug(state);
127
128 LOG.debug("");
129 }
130
131 }
132
133 public void createSelectionPath() {
134 selectionPath = new ArrayList<UITreeOldNode>();
135 expandPath = new ArrayList<UITreeOldNode>();
136 if (isSelectableTree()) {
137 Iterator iterator = getState().getSelection().iterator();
138 if (iterator.hasNext()) {
139 TreeNode treeNode = (TreeNode) iterator.next();
140 UITreeOldNode selectedNode = findUITreeNode(getRoot(), treeNode);
141 if (selectedNode != null) {
142 UIComponent ancestor = selectedNode;
143 while (ancestor != null && ancestor instanceof UITreeOldNode) {
144 selectionPath.add(0, (UITreeOldNode) ancestor);
145 ancestor = ancestor.getParent();
146 }
147 }
148 }
149 }
150 Set<DefaultMutableTreeNode> expandState = getState().getExpandState();
151 if (selectionPath.isEmpty()) {
152 DefaultMutableTreeNode treeNode = getRoot().getTreeNode();
153 createExpandPath(treeNode, expandState);
154 selectionPath.addAll(expandPath);
155 } else {
156 for (UITreeOldNode node : selectionPath) {
157 if (!node.getTreeNode().isLeaf()) {
158 expandPath.add(node);
159 }
160 }
161 }
162 if (expandPath.isEmpty()) {
163 expandPath.add(getRoot());
164 }
165 expandState.clear();
166 for (UITreeOldNode uiTreeNode : expandPath) {
167 expandState.add((DefaultMutableTreeNode) uiTreeNode.getValue());
168 }
169
170 }
171
172 private boolean createExpandPath(DefaultMutableTreeNode node,
173 Set<DefaultMutableTreeNode> expandState) {
174 if (expandState.contains(node)) {
175 expandPath.add(findUITreeNode(getRoot(), node));
176 for (int i = 0; i < node.getChildCount(); i++) {
177 if (createExpandPath((DefaultMutableTreeNode) node.getChildAt(i), expandState)) {
178 break;
179 }
180 }
181 return true;
182 }
183 return false;
184 }
185
186 private void createUIBoxes(FacesContext facesContext) {
187 int depth = getRoot().getTreeNode().getDepth();
188 boxes = new ArrayList<UITreeListboxBox>(depth);
189 for (int i = 0; i < depth; i++) {
190 UITreeListboxBox box = (UITreeListboxBox) ComponentUtil.createComponent(
191 facesContext, UITreeListboxBox.COMPONENT_TYPE,
192 UITreeListboxBox.RENDERER_TYPE);
193 getFacets().put(BOXES_PREFIX + i, box);
194 box.setLevel(i);
195 box.setNodes(getNodes(i));
196 boxes.add(box);
197 }
198 }
199
200 private List<UITreeOldNode> getNodes(int level) {
201 List children;
202 if (level == 0) {
203 children = getRoot().getChildren();
204 } else if (selectionPath.size() > level) {
205 children = selectionPath.get(level).getChildren();
206 } else {
207 children = Collections.EMPTY_LIST;
208 }
209 List<UITreeOldNode> nodes = new ArrayList<UITreeOldNode>(children.size());
210 for (Object node : children) {
211 if (node instanceof UITreeOldNode) {
212 nodes.add((UITreeOldNode) node);
213 }
214 }
215 return nodes;
216 }
217
218 public void encodeChildren(FacesContext facesContext) throws IOException {
219 if (isRendered()) {
220 encodingChildren = true;
221 UILayout.getLayout(this).encodeChildrenOfComponent(facesContext, this);
222 encodingChildren = false;
223 }
224 }
225
226 public void encodeEnd(FacesContext facesContext) throws IOException {
227 super.encodeEnd(facesContext);
228 }
229
230 public int getChildCount() {
231 if (encodingChildren) {
232 return boxes != null ? boxes.size() : 0;
233 } else {
234 return super.getChildCount();
235 }
236 }
237
238 public List getChildren() {
239 if (encodingChildren) {
240 return boxes;
241 } else {
242 return super.getChildren();
243 }
244 }
245
246 public UITreeOldNode getSelectedNode(int level) {
247 UITreeOldNode selectedComponent = null;
248 if (selectionPath.size() > level + 1) {
249 selectedComponent = selectionPath.get(level + 1);
250 }
251 return selectedComponent;
252 }
253
254 public List<UITreeOldNode> getSelectionPath() {
255 return selectionPath;
256 }
257
258 public List<UITreeOldNode> getExpandPath() {
259 return expandPath;
260 }
261
262 public boolean isSelectedNode(DefaultMutableTreeNode treeNode) {
263 return getState().getSelection().contains(treeNode);
264 }
265
266 // --------------------------------------------------- Interface LayoutProvider
267
268 public UILayout provideLayout() {
269 UILayout layout = (UILayout) getFacet(FACET_LAYOUT_DEFAULT);
270 if (layout == null) {
271 layout = (UILayout) ComponentUtil.createComponent(
272 UIGridLayout.COMPONENT_TYPE,
273 RENDERER_TYPE_GRID_LAYOUT, null);
274
275 int depth = ((DefaultMutableTreeNode) getValue()).getDepth();
276 final int defaultColumnCount = ThemeConfig.getValue(
277 FacesContext.getCurrentInstance(), this, "defaultColumnCount");
278
279 if (defaultColumnCount < depth) {
280 depth = defaultColumnCount;
281 }
282
283 StringBuilder columns = new StringBuilder("1*");
284 for (int i = 1; i < depth; i++) {
285 columns.append(";1*");
286 }
287
288 layout.getAttributes().put(ATTR_COLUMNS, columns.toString());
289 getFacets().put(FACET_LAYOUT_DEFAULT, layout);
290 }
291
292 return layout;
293 }
294
295 }
296