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, null);
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