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