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