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 org.apache.myfaces.tobago.TobagoConstants;
025    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_TAB_INDEX;
026    import org.apache.myfaces.tobago.context.ResourceManagerUtil;
027    import org.apache.myfaces.tobago.model.TreeState;
028    import org.apache.myfaces.tobago.taglib.component.ToolBarTag;
029    import org.apache.myfaces.tobago.util.MessageFactory;
030    import org.apache.myfaces.tobago.util.StringUtils;
031    
032    import javax.faces.application.FacesMessage;
033    import javax.faces.component.ActionSource;
034    import javax.faces.component.NamingContainer;
035    import javax.faces.component.UICommand;
036    import javax.faces.component.UIComponent;
037    import javax.faces.component.UIPanel;
038    import javax.faces.context.FacesContext;
039    import javax.faces.el.MethodBinding;
040    import javax.faces.el.ValueBinding;
041    import javax.faces.event.AbortProcessingException;
042    import javax.faces.event.ActionListener;
043    import javax.faces.event.FacesEvent;
044    import javax.faces.validator.Validator;
045    import javax.faces.validator.ValidatorException;
046    import javax.swing.tree.DefaultMutableTreeNode;
047    import javax.swing.tree.TreeNode;
048    import java.io.IOException;
049    import java.io.Serializable;
050    import java.util.Iterator;
051    import java.util.Set;
052    
053    @Deprecated
054    public class UITreeOld extends javax.faces.component.UIInput implements NamingContainer, ActionSource {
055    
056      private static final Log LOG = LogFactory.getLog(UITreeOld.class);
057    
058      public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.TreeOld";
059      public static final String MESSAGE_NOT_LEAF = "tobago.tree.MESSAGE_NOT_LEAF";
060    
061      public static final String SEP = "-";
062      // TODO should moved to renderer
063      public static final String TREE_DIV = SEP + "div";
064      public static final String TREE_STATE = SEP + "treeState";
065      public static final String SELECT_STATE = SEP + "selectState";
066      public static final String MARKER = SEP + "marker";
067      public static final String SCROLL_POSITION = SEP + "scrollPosition";
068    
069      public static final String FACET_TREE_NODE_COMMAND = "treeNodeCommand";
070      public static final String PARAMETER_TREE_NODE_ID = "treeNodeId";
071    
072      public static final String COMMAND_PREFIX = "command";
073    
074      public static final String COMMAND_NEW = "new";
075      public static final String COMMAND_DELETE = "delete";
076      public static final String COMMAND_EDIT = "edit";
077      public static final String COMMAND_CUT = "cut";
078      public static final String COMMAND_COPY = "copy";
079      public static final String COMMAND_PASTE = "paste";
080      public static final String COMMAND_MOVE_UP = "moveUp";
081      public static final String COMMAND_MOVE_DOWN = "moveDown";
082    
083      private UITreeOld.Command[] treeCommands;
084    
085      private MethodBinding actionListenerBinding;
086      private TreeState treeState;
087    
088      private boolean showJunctions = true;
089      private boolean showJunctionsSet = false;
090      private boolean showIcons = true;
091      private boolean showIconsSet = false;
092      private boolean showRoot = true;
093      private boolean showRootSet = false;
094      private boolean showRootJunction = true;
095      private boolean showRootJunctionSet = false;
096    
097      private String mode;
098    
099      private Integer tabIndex;
100    
101      public UITreeOld() {
102        treeCommands = new UITreeOld.Command[]{
103            new UITreeOld.Command(COMMAND_NEW),
104            new UITreeOld.Command(COMMAND_DELETE),
105            new UITreeOld.Command(COMMAND_EDIT),
106            new UITreeOld.Command(COMMAND_CUT),
107            new UITreeOld.Command(COMMAND_COPY),
108            new UITreeOld.Command(COMMAND_PASTE),
109            new UITreeOld.Command(COMMAND_MOVE_UP),
110            new UITreeOld.Command(COMMAND_MOVE_DOWN),
111        };
112      }
113    
114    // ---------------------------- interface ActionSource
115    
116      public void broadcast(FacesEvent event) throws AbortProcessingException {
117        super.broadcast(event);
118    
119        MethodBinding binding = getActionListener();
120    
121        if (binding != null) {
122          FacesContext context = getFacesContext();
123          binding.invoke(context, new Object[]{event});
124        }
125      }
126    
127      public MethodBinding getAction() {
128        return null;
129      }
130    
131      public void setAction(MethodBinding methodBinding) {
132    
133      }
134    
135      public String getMode() {
136        if (mode != null) {
137          return mode;
138        }
139        ValueBinding vb = getValueBinding(TobagoConstants.ATTR_MODE);
140        if (vb != null) {
141          return (String) vb.getValue(getFacesContext());
142        } else {
143          return "tree";
144        }
145      }
146    
147      public void setMode(String mode) {
148        this.mode = mode;
149      }
150    
151      public MethodBinding getActionListener() {
152        return actionListenerBinding;
153      }
154    
155      public void setActionListener(MethodBinding actionListener) {
156        this.actionListenerBinding = actionListener;
157      }
158    
159      public void addActionListener(ActionListener actionListener) {
160        addFacesListener(actionListener);
161      }
162    
163      public ActionListener[] getActionListeners() {
164        return (ActionListener[]) getFacesListeners(ActionListener.class);
165      }
166    
167      public void removeActionListener(ActionListener actionListener) {
168        removeFacesListener(actionListener);
169      }
170    
171      public void encodeBegin(FacesContext facesContext)
172          throws IOException {
173        recreateTreeNodes();
174        if (ComponentUtil.getBooleanAttribute(this, TobagoConstants.ATTR_MUTABLE)
175            && getFacet("mutableToolbar") == null
176            && getFacet("defaultToolbar") == null) {
177          createDefaultToolbar(facesContext);
178        }
179        super.encodeBegin(facesContext);
180      }
181    
182      // TODO move this to renderkit
183      public void createDefaultToolbar(FacesContext facesContext) {
184    
185        UIComponent toolbar = ComponentUtil.createComponent(
186            facesContext, UIPanel.COMPONENT_TYPE,
187            TobagoConstants.RENDERER_TYPE_TOOL_BAR);
188        toolbar.getAttributes().put(TobagoConstants.ATTR_ICON_SIZE, ToolBarTag.ICON_SMALL);
189        toolbar.getAttributes().put(TobagoConstants.ATTR_LABEL_POSITION, ToolBarTag.LABEL_OFF);
190        ActionListener[] handlers = getActionListeners();
191    
192        if ((handlers == null || handlers.length == 0) && getActionListener() == null) {
193          LOG.error("No actionListener found in tree, so tree editing will not work!");
194        }
195    
196        UITreeOld.Command[] commands = getCommands();
197        for (int i = 0; i < commands.length; i++) {
198          UICommand command = (UICommand) ComponentUtil.createComponent(
199              facesContext, UICommand.COMPONENT_TYPE,
200              TobagoConstants.RENDERER_TYPE_LINK, commands[i].getCommand());
201          toolbar.getChildren().add(command);
202    
203          for (ActionListener listener : getActionListeners()) {
204            command.addActionListener(listener);
205          }
206          command.setActionListener(getActionListener());
207          command.getAttributes().put(
208              TobagoConstants.ATTR_IMAGE, "image/tobago.tree." + commands[i].getCommand() + ".gif");
209          String title = ResourceManagerUtil.getPropertyNotNull(facesContext, "tobago",
210              "tree" + StringUtils.firstToUpperCase(commands[i].getCommand()));
211          command.getAttributes().put(TobagoConstants.ATTR_TIP, title);
212    
213        }
214    
215        getFacets().put("defaultToolbar", toolbar);
216    
217      }
218    
219      private void recreateTreeNodes() {
220        UITreeOldNode root = getRoot();
221        // Delete all UIComponent childs, because moving of childen will not work
222        // in Mutable Tree.
223        // They may have invalid modelReferences.
224        try {
225          if (root != null) {
226            if (LOG.isDebugEnabled()) {
227              LOG.debug("removing root 1");
228            }
229            getChildren().remove(root);
230            if (LOG.isDebugEnabled()) {
231              LOG.debug("removing root 2");
232            }
233          }
234        } catch (Exception e) {
235          LOG.error("", e);
236        }
237    
238        try {
239          root = new UITreeOldNode(this, 0);
240          root.createTreeNodes();
241        } catch (Exception e) {
242          LOG.error(e, e);
243        }
244      }
245    
246      public UITreeOldNode getRoot() {
247        // find the UITreeOldNode in the childen.
248        for (Iterator i = getChildren().iterator(); i.hasNext();) {
249          UIComponent child = (UIComponent) i.next();
250          if (child instanceof UITreeOldNode) {
251            return (UITreeOldNode) child;
252          }
253        }
254        // in a new UITree isn't a root
255        return null;
256      }
257    
258      public void encodeChildren(FacesContext context)
259          throws IOException {
260    //     will be called from end.jsp
261      }
262    
263      public UITreeOldNode findUITreeNode(UITreeOldNode node, TreeNode treeNode) {
264        UITreeOldNode found = null;
265        if (node.getTreeNode().equals(treeNode)) {
266          return node;
267        } else {
268          for (Iterator iter = node.getChildren().iterator(); iter.hasNext();) {
269            UITreeOldNode uiTreeNode = (UITreeOldNode) iter.next();
270            found = findUITreeNode(uiTreeNode, treeNode);
271            if (found != null) {
272              break;
273            }
274          }
275        }
276        return found;
277      }
278    
279      public boolean getRendersChildren() {
280        return true;
281      }
282    
283      public boolean isSelectableTree() {
284        final Object selectable
285            = ComponentUtil.getAttribute(this, TobagoConstants.ATTR_SELECTABLE);
286        return selectable != null
287            && (selectable.equals("multi") || selectable.equals("multiLeafOnly")
288            || selectable.equals("single") || selectable.equals("singleLeafOnly")
289            || selectable.equals("sibling") || selectable.equals("siblingLeafOnly"));
290      }
291    
292      public void processDecodes(FacesContext facesContext) {
293    
294        if (!isRendered()) {
295          return;
296        }
297    
298        if (ComponentUtil.isOutputOnly(this)) {
299          setValid(true);
300        } else {
301          // in tree first decode node and than decode children
302    
303          decode(facesContext);
304    
305          for (Iterator i = getFacetsAndChildren(); i.hasNext();) {
306            UIComponent uiComponent = ((UIComponent) i.next());
307            uiComponent.processDecodes(facesContext);
308          }
309        }
310      }
311    
312      public void validate(FacesContext context) {
313        if (isRequired() && getState().getSelection().size() == 0) {
314          setValid(false);
315          FacesMessage facesMessage = MessageFactory.createFacesMessage(context,
316              UISelectOne.MESSAGE_VALUE_REQUIRED, FacesMessage.SEVERITY_ERROR);
317          context.addMessage(getClientId(context), facesMessage);
318        }
319    
320        String selectable = ComponentUtil.getStringAttribute(this,
321            TobagoConstants.ATTR_SELECTABLE);
322        if (selectable != null && selectable.endsWith("LeafOnly")) {
323    
324          Set<DefaultMutableTreeNode> selection = getState().getSelection();
325    
326          for (DefaultMutableTreeNode node : selection) {
327            if (!node.isLeaf()) {
328              setValid(false);
329              FacesMessage facesMessage = MessageFactory.createFacesMessage(
330                  context, MESSAGE_NOT_LEAF, FacesMessage.SEVERITY_ERROR);
331              context.addMessage(getClientId(context), facesMessage);
332              break; // don't continue iteration, no dublicate messages needed
333            }
334          }
335        }
336    
337    //  call all validators
338        if (getValidators() != null) {
339          for (Validator validator : getValidators()) {
340            try {
341              validator.validate(context, this, null);
342            } catch (ValidatorException ve) {
343              // If the validator throws an exception, we're
344              // invalid, and we need to add a message
345              setValid(false);
346              FacesMessage message = ve.getFacesMessage();
347              if (message != null) {
348                message.setSeverity(FacesMessage.SEVERITY_ERROR);
349                context.addMessage(getClientId(context), message);
350              }
351            }
352          }
353        }
354      }
355    
356      public void updateModel(FacesContext facesContext) {
357        // nothig to update for tree's
358        // TODO: updateing the model here and *NOT* in the decode phase
359      }
360    
361      public Object saveState(FacesContext context) {
362        Object[] state = new Object[8];
363        state[0] = super.saveState(context);
364        state[1] = saveAttachedState(context, actionListenerBinding);
365        state[2] = showJunctionsSet ? showJunctions : null;
366        state[3] = showIconsSet ? showIcons : null;
367        state[4] = showRootSet ? showRoot : null;
368        state[5] = showRootJunctionSet ? showRootJunction : null;
369        state[6] = mode;
370        state[7] = tabIndex;
371        return state;
372      }
373    
374      public void restoreState(FacesContext context, Object state) {
375        Object[] values = (Object[]) state;
376        super.restoreState(context, values[0]);
377        actionListenerBinding = (MethodBinding) restoreAttachedState(context, values[1]);
378        if (values[2] != null) {
379          showJunctions = (Boolean) values[2];
380          showJunctionsSet = true;
381        }
382        if (values[3] != null) {
383          showIcons = (Boolean) values[3];
384          showIconsSet = true;
385        }
386        if (values[4] != null) {
387          showRoot = (Boolean) values[4];
388          showRootSet = true;
389        }
390        if (values[5] != null) {
391          showRootJunction = (Boolean) values[5];
392          showRootJunctionSet = true;
393        }
394        mode = (String) values[6];
395        tabIndex = (Integer) values[7];
396      }
397    
398      public UITreeOld.Command[] getCommands() {
399        return treeCommands;
400      }
401    
402      public TreeState getState() {
403        if (treeState != null) {
404          return treeState;
405        }
406        ValueBinding valueBinding = getValueBinding(TobagoConstants.ATTR_STATE);
407        if (valueBinding != null) {
408          FacesContext facesContext = getFacesContext();
409          TreeState state = (TreeState) valueBinding.getValue(facesContext);
410          if (state == null) {
411            state = new TreeState();
412            valueBinding.setValue(facesContext, state);
413          }
414          return state;
415        } else {
416          treeState = new TreeState();
417          return treeState;
418        }
419      }
420    
421      public void setState(TreeState state) {
422        this.treeState = state;
423      }
424    
425      public boolean isShowJunctions() {
426        if (showJunctionsSet) {
427          return (showJunctions);
428        }
429        ValueBinding vb = getValueBinding(TobagoConstants.ATTR_SHOW_JUNCTIONS);
430        if (vb != null) {
431          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
432        } else {
433          return (this.showJunctions);
434        }
435      }
436    
437      public void setShowJunctions(boolean showJunctions) {
438        this.showJunctions = showJunctions;
439        this.showJunctionsSet = true;
440      }
441    
442      public boolean isShowIcons() {
443        if (showIconsSet) {
444          return (showIcons);
445        }
446        ValueBinding vb = getValueBinding(TobagoConstants.ATTR_SHOW_ICONS);
447        if (vb != null) {
448          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
449        } else {
450          return (this.showIcons);
451        }
452      }
453    
454      public void setShowIcons(boolean showIcons) {
455        this.showIcons = showIcons;
456        this.showIconsSet = true;
457      }
458    
459      public boolean isShowRoot() {
460        if (showRootSet) {
461          return (showRoot);
462        }
463        ValueBinding vb = getValueBinding(TobagoConstants.ATTR_SHOW_ROOT);
464        if (vb != null) {
465          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
466        } else {
467          return (this.showRoot);
468        }
469      }
470    
471      public void setShowRoot(boolean showRoot) {
472        this.showRoot = showRoot;
473        this.showRootSet = true;
474      }
475    
476      public boolean isShowRootJunction() {
477        if (showRootJunctionSet) {
478          return (showRootJunction);
479        }
480        ValueBinding vb = getValueBinding(TobagoConstants.ATTR_SHOW_ROOT_JUNCTION);
481        if (vb != null) {
482          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
483        } else {
484          return (this.showRootJunction);
485        }
486      }
487    
488      public void setShowRootJunction(boolean showRootJunction) {
489        this.showRootJunction = showRootJunction;
490        this.showRootJunctionSet = true;
491      }
492    
493      public static class Command implements Serializable {
494        private String command;
495    
496        public Command(String command) {
497          this.command = command;
498        }
499    
500        public String getCommand() {
501          return command;
502        }
503      }
504    
505      public Integer getTabIndex() {
506        if (tabIndex != null) {
507          return tabIndex;
508        }
509        ValueBinding vb = getValueBinding(ATTR_TAB_INDEX);
510        if (vb != null) {
511          Number number = (Number) vb.getValue(getFacesContext());
512          if (number != null) {
513            return Integer.valueOf(number.intValue());
514          }
515        }
516        return null;
517      }
518    
519      public void setTabIndex(Integer tabIndex) {
520        this.tabIndex = tabIndex;
521      }
522    }