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