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