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