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 062 protected String nodeStateId(FacesContext facesContext, UITreeOldNode node) { 063 // this must do the same as nodeStateId() in tree.js 064 String clientId = node.getClientId(facesContext); 065 int last = clientId.lastIndexOf(':') + 1; 066 return clientId.substring(last); 067 } 068 069 public void encodeBegin(FacesContext facesContext) 070 throws IOException { 071 // TODO change this should be renamed to DimensionUtils.prepare!!! 072 UILayout.getLayout(this).layoutBegin(facesContext, this); 073 // debugStates(facesContext); 074 fixSelectionType(); 075 super.encodeBegin(facesContext); 076 debugStates(facesContext); 077 createUIBoxes(facesContext); 078 } 079 080 @SuppressWarnings(value = "unchecked") 081 private void fixSelectionType() { 082 final Map attributes = getAttributes(); 083 Object selectable = attributes.get(ATTR_SELECTABLE); 084 if ("single".equals(selectable) 085 || "singleLeafOnly".equals(selectable) 086 || "siblingLeafOnly".equals(selectable)) { 087 } else if (selectable == null) { 088 attributes.put(ATTR_SELECTABLE, "single"); 089 } else { 090 // fix to single 091 LOG.warn("Illegal attributeValue selectable : " + selectable + " set to 'single'"); 092 attributes.put(ATTR_SELECTABLE, "single"); 093 } 094 } 095 096 private void debugStates(FacesContext facesContext) { 097 if (LOG.isDebugEnabled()) { 098 LOG.debug("#####################################################"); 099 StringBuilder state = new StringBuilder("expandState : ;"); 100 for (DefaultMutableTreeNode treeNode : getState().getExpandState()) { 101 state.append(nodeStateId(facesContext, findUITreeNode(getRoot(), treeNode))); 102 state.append(";"); 103 } 104 LOG.debug(state); 105 106 state = new StringBuilder("selectState : ;"); 107 for (DefaultMutableTreeNode treeNode : getState().getSelection()) { 108 state.append(nodeStateId(facesContext, findUITreeNode(getRoot(), treeNode))); 109 state.append(";"); 110 } 111 LOG.debug(state); 112 113 state = new StringBuilder("selectionPath : ;"); 114 for (UITreeOldNode treeNode : getSelectionPath()) { 115 state.append(nodeStateId(facesContext, treeNode)); 116 state.append(";"); 117 } 118 LOG.debug(state); 119 120 state = new StringBuilder("expandPath : ;"); 121 for (UITreeOldNode treeNode : getExpandPath()) { 122 state.append(nodeStateId(facesContext, treeNode)); 123 state.append(";"); 124 } 125 LOG.debug(state); 126 127 LOG.debug(""); 128 } 129 130 } 131 132 public void createSelectionPath() { 133 selectionPath = new ArrayList<UITreeOldNode>(); 134 expandPath = new ArrayList<UITreeOldNode>(); 135 if (isSelectableTree()) { 136 Iterator iterator = getState().getSelection().iterator(); 137 if (iterator.hasNext()) { 138 TreeNode treeNode = (TreeNode) iterator.next(); 139 UITreeOldNode selectedNode = findUITreeNode(getRoot(), treeNode); 140 if (selectedNode != null) { 141 UIComponent ancestor = selectedNode; 142 while (ancestor != null && ancestor instanceof UITreeOldNode) { 143 selectionPath.add(0, (UITreeOldNode) ancestor); 144 ancestor = ancestor.getParent(); 145 } 146 } 147 } 148 } 149 Set<DefaultMutableTreeNode> expandState = getState().getExpandState(); 150 if (selectionPath.isEmpty()) { 151 DefaultMutableTreeNode treeNode = getRoot().getTreeNode(); 152 createExpandPath(treeNode, expandState); 153 selectionPath.addAll(expandPath); 154 } else { 155 for (UITreeOldNode node : selectionPath) { 156 if (!node.getTreeNode().isLeaf()) { 157 expandPath.add(node); 158 } 159 } 160 } 161 if (expandPath.isEmpty()) { 162 expandPath.add(getRoot()); 163 } 164 expandState.clear(); 165 for (UITreeOldNode uiTreeNode : expandPath) { 166 expandState.add((DefaultMutableTreeNode) uiTreeNode.getValue()); 167 } 168 169 } 170 171 private boolean createExpandPath(DefaultMutableTreeNode node, 172 Set<DefaultMutableTreeNode> expandState) { 173 if (expandState.contains(node)) { 174 expandPath.add(findUITreeNode(getRoot(), node)); 175 for (int i = 0; i < node.getChildCount(); i++) { 176 if (createExpandPath((DefaultMutableTreeNode) node.getChildAt(i), expandState)) { 177 break; 178 } 179 } 180 return true; 181 } 182 return false; 183 } 184 185 private void createUIBoxes(FacesContext facesContext) { 186 int depth = getRoot().getTreeNode().getDepth(); 187 boxes = new ArrayList<UITreeListboxBox>(depth); 188 for (int i = 0; i < depth; i++) { 189 UITreeListboxBox box = (UITreeListboxBox) ComponentUtil.createComponent( 190 facesContext, UITreeListboxBox.COMPONENT_TYPE, 191 UITreeListboxBox.RENDERER_TYPE); 192 getFacets().put(BOXES_PREFIX + i, box); 193 box.setLevel(i); 194 box.setNodes(getNodes(i)); 195 boxes.add(box); 196 } 197 } 198 199 private List<UITreeOldNode> getNodes(int level) { 200 List children; 201 if (level == 0) { 202 children = getRoot().getChildren(); 203 } else if (selectionPath.size() > level) { 204 children = selectionPath.get(level).getChildren(); 205 } else { 206 children = Collections.EMPTY_LIST; 207 } 208 List<UITreeOldNode> nodes = new ArrayList<UITreeOldNode>(children.size()); 209 for (Object node : children) { 210 if (node instanceof UITreeOldNode) { 211 nodes.add((UITreeOldNode) node); 212 } 213 } 214 return nodes; 215 } 216 217 public void encodeChildren(FacesContext facesContext) throws IOException { 218 if (isRendered()) { 219 encodingChildren = true; 220 UILayout.getLayout(this).encodeChildrenOfComponent(facesContext, this); 221 encodingChildren = false; 222 } 223 } 224 225 public void encodeEnd(FacesContext facesContext) throws IOException { 226 super.encodeEnd(facesContext); 227 } 228 229 public int getChildCount() { 230 if (encodingChildren) { 231 return boxes != null ? boxes.size() : 0; 232 } else { 233 return super.getChildCount(); 234 } 235 } 236 237 public List getChildren() { 238 if (encodingChildren) { 239 return boxes; 240 } else { 241 return super.getChildren(); 242 } 243 } 244 245 public UITreeOldNode getSelectedNode(int level) { 246 UITreeOldNode selectedComponent = null; 247 if (selectionPath.size() > level + 1) { 248 selectedComponent = selectionPath.get(level + 1); 249 } 250 return selectedComponent; 251 } 252 253 public List<UITreeOldNode> getSelectionPath() { 254 return selectionPath; 255 } 256 257 public List<UITreeOldNode> getExpandPath() { 258 return expandPath; 259 } 260 261 public boolean isSelectedNode(DefaultMutableTreeNode treeNode) { 262 return getState().getSelection().contains(treeNode); 263 } 264 265 // --------------------------------------------------- Interface LayoutProvider 266 267 public UILayout provideLayout() { 268 UILayout layout = (UILayout) getFacet(FACET_LAYOUT_DEFAULT); 269 if (layout == null) { 270 layout = (UILayout) ComponentUtil.createComponent( 271 UIGridLayout.COMPONENT_TYPE, 272 RENDERER_TYPE_GRID_LAYOUT); 273 274 int depth = ((DefaultMutableTreeNode) getValue()).getDepth(); 275 final int defaultColumnCount = ThemeConfig.getValue( 276 FacesContext.getCurrentInstance(), this, "defaultColumnCount"); 277 278 if (defaultColumnCount < depth) { 279 depth = defaultColumnCount; 280 } 281 282 StringBuilder columns = new StringBuilder("1*"); 283 for (int i = 1; i < depth; i++) { 284 columns.append(";1*"); 285 } 286 287 layout.getAttributes().put(ATTR_COLUMNS, columns.toString()); 288 getFacets().put(FACET_LAYOUT_DEFAULT, layout); 289 } 290 291 return layout; 292 } 293 294 } 295