View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.navmenu;
20  
21  import java.util.Iterator;
22  import java.util.StringTokenizer;
23  
24  import javax.faces.component.ActionSource;
25  import javax.faces.component.UIComponent;
26  import javax.faces.component.UISelectItem;
27  import javax.faces.context.FacesContext;
28  import javax.faces.el.EvaluationException;
29  import javax.faces.el.MethodBinding;
30  import javax.faces.el.ValueBinding;
31  import javax.faces.event.AbortProcessingException;
32  import javax.faces.event.ActionEvent;
33  import javax.faces.event.ActionListener;
34  import javax.faces.event.FacesEvent;
35  
36  import org.apache.myfaces.component.UserRoleAware;
37  import org.apache.myfaces.component.UserRoleUtils;
38  import org.apache.myfaces.custom.navmenu.htmlnavmenu.HtmlCommandNavigationItem;
39  import org.apache.myfaces.custom.navmenu.htmlnavmenu.HtmlPanelNavigationMenu;
40  
41  /**
42   * A menu item. Used by navigationMenu, jscookMenu. 
43   * 
44   * Unless otherwise specified, all attributes accept static values or EL expressions.
45   * 
46   * @JSFComponent
47   *   name = "t:navigationMenuItem"
48   *   bodyContent = "JSP"
49   *   class = "org.apache.myfaces.custom.navmenu.UINavigationMenuItem"
50   *   tagClass = "org.apache.myfaces.custom.navmenu.HtmlNavigationMenuItemTag"
51   * @since 1.1.7
52   * @author Thomas Spiegl (latest modification by $Author: lu4242 $)
53   * @version $Revision: 719425 $ $Date: 2008-11-20 18:41:15 -0500 (Thu, 20 Nov 2008) $
54   */
55  public abstract class AbstractUINavigationMenuItem extends UISelectItem implements
56      UserRoleAware, ActionSource {
57      private static final boolean DEFAULT_IMMEDIATE = true;
58  
59      public static final String COMPONENT_TYPE = "org.apache.myfaces.NavigationMenuItem";
60      public static final String COMPONENT_FAMILY = "javax.faces.SelectItem";
61  
62      public AbstractUINavigationMenuItem() {
63          super();
64      }
65  
66      public String getFamily() {
67          return COMPONENT_FAMILY;
68      }
69  
70      /**
71       * @JSFProperty 
72       */
73      public abstract String getIcon();
74  
75      /**
76       * @JSFProperty
77       *   defaultValue="false" 
78       */
79      public abstract boolean isSplit();
80  
81      /**
82       * @JSFProperty
83       *   defaultValue="false" 
84       *   tagExcluded = "true"
85       */
86      public abstract boolean isOpen();
87      
88      public abstract void setOpen(boolean open);
89  
90      public abstract void setActive(boolean active);
91  
92      /**
93       * @JSFProperty
94       *   defaultValue="false"
95       *   tagExcluded = "true"
96       */
97      public abstract boolean isActive();
98  
99      /**
100      * @JSFProperty
101      *   defaultValue="true" 
102      *   tagExcluded="true"
103      */
104     public abstract boolean isImmediate();
105 
106     /**
107      * @JSFProperty
108      *   tagExcluded = "true"
109      */
110     public abstract String getExternalLink();
111 
112     // Action Source
113     public abstract void setAction(MethodBinding action);
114 
115     /**
116      * Specifies the action to take when this command is invoked.
117      *
118      * If the value is an expression, it is expected to be a method 
119      * binding EL expression that identifies an action method. An action method
120      * accepts no parameters and has a String return value, called the action
121      * outcome, that identifies the next view displayed. The phase that this
122      * event is fired in can be controlled via the immediate attribute.
123      *
124      * If the value is a string literal, it is treated as a navigation outcome
125      * for the current view.  This is functionally equivalent to a reference to
126      * an action method that returns the string literal.
127      * 
128      * @JSFProperty
129      *   stateHolder = "true"
130      *   literalOnly = "true"
131      *   returnSignature="java.lang.String"
132      */
133     public abstract MethodBinding getAction();
134     
135     public abstract void setActionListener(MethodBinding actionListener);
136 
137     /**
138      * A method binding EL expression that identifies an action listener method
139      * to be invoked if this component is activated by the user. An action
140      * listener method accepts a parameter of type javax.faces.event.ActionEvent
141      * and returns void. The phase that this event is fired in can be controlled
142      * via the immediate attribute.
143      *  
144      * @JSFProperty
145      *   stateHolder = "true"
146      *   literalOnly = "true"
147      *   returnSignature="void"
148      *   methodSignature="javax.faces.event.ActionEvent"
149      */
150     public abstract MethodBinding getActionListener();
151 
152     public void addActionListener(ActionListener listener) {
153         addFacesListener(listener);
154     }
155 
156     public ActionListener[] getActionListeners() {
157         return (ActionListener[]) getFacesListeners(ActionListener.class);
158     }
159 
160     public void removeActionListener(ActionListener listener) {
161         removeFacesListener(listener);
162     }
163 
164     // Action Source
165 
166     /**
167      * 
168      * @JSFProperty 
169      */
170     public abstract String getTarget();
171 
172     /**
173      * When set instead of a Hyperlink a span tag is rendered in 
174      * the corresponding Component
175      * 
176      * @JSFProperty
177      *   defaultValue="false" 
178      */
179     public abstract boolean isDisabled();
180 
181     /**
182      * CSS-Style Attribute to render when disabled is true
183      * 
184      * @JSFProperty 
185      */
186     public abstract String getDisabledStyle();
187 
188     /**
189      * @see javax.faces.component.UIComponent#broadcast(javax.faces.event.FacesEvent)
190      */
191     public void broadcast(FacesEvent event) throws AbortProcessingException {
192         super.broadcast(event);
193 
194         if (event instanceof ActionEvent) {
195             FacesContext context = getFacesContext();
196 
197             MethodBinding actionListenerBinding = getActionListener();
198             if (actionListenerBinding != null) {
199                 try {
200                     actionListenerBinding.invoke(context,
201                                                  new Object[]{event});
202                 }
203                 catch (EvaluationException e) {
204                     Throwable cause = e.getCause();
205                     if (cause != null
206                         && cause instanceof AbortProcessingException) {
207                         throw (AbortProcessingException) cause;
208                     }
209                     else {
210                         throw e;
211                     }
212                 }
213             }
214 
215             ActionListener defaultActionListener = context.getApplication()
216                 .getActionListener();
217             if (defaultActionListener != null) {
218                 defaultActionListener.processAction((ActionEvent) event);
219             }
220         }
221     }
222 
223     /**
224      * CSS-Style Class to use when disabled is true
225      * 
226      * @JSFProperty 
227      */
228     public abstract String getDisabledStyleClass();
229 
230     /**
231      * @JSFProperty
232      *   localMethod="true"
233      *   tagExcluded = "true" 
234      */
235     public abstract String getActiveOnViewIds();
236     
237     protected abstract String getLocalActiveOnViewIds();
238     
239     public String getActiveOnViewIdsDirectly() {
240         return getLocalActiveOnViewIds();
241     }
242 
243     //START isRendered hack
244     //UISelectItem and UISelectItems does not use rendered 
245     //(inherited from UIComponentBase)
246     //but this components use it, so we need to implement it
247     //here (isInherited hack does not work because custom code 
248     //is added on isRendered, so the variable must be defined
249     //here).
250     
251     private static final boolean DEFAULT_RENDERED = true;
252     
253     private Boolean _rendered = null;
254     
255     /**
256      * A boolean value that indicates whether this component should be rendered.
257      * Default value: true.
258      * 
259      * @JSFProperty tagExcluded = "false"
260      */
261     public boolean isRendered() {
262         if (!UserRoleUtils.isVisibleOnUserRole(this))
263             return false;
264         //return super.isRendered();
265         if (_rendered != null) return _rendered.booleanValue();
266         ValueBinding vb = getValueBinding("rendered");
267         Boolean v = vb != null ? (Boolean)vb.getValue(getFacesContext()) : null;
268         return v != null ? v.booleanValue() : DEFAULT_RENDERED;
269     }
270     
271     public void setRendered(boolean rendered)
272     {
273         _rendered = Boolean.valueOf(rendered);
274     }
275     
276     public Object saveState(FacesContext facesContext)
277     {
278         Object[] values = new Object[2];
279         values[0] = super.saveState(facesContext);
280         values[1] = _rendered;
281         return values; 
282     }
283 
284     public void restoreState(FacesContext facesContext, Object state)
285     {
286         Object[] values = (Object[])state;
287         super.restoreState(facesContext,values[0]);
288         _rendered = (java.lang.Boolean) values[1];
289     }
290     //END isRendered hack
291     
292     public void toggleActive(FacesContext context) {
293         StringTokenizer tokenizer = new StringTokenizer(this.getActiveOnViewIdsDirectly(), ";");
294         while (tokenizer.hasMoreTokens()) {
295             String token = tokenizer.nextToken();
296             if (token.trim().equals(context.getViewRoot().getViewId())) {
297                 this.deactivateAll();
298                 this.setActive(true);
299                 openParents();
300             }
301             else {
302                 this.setActive(false);
303             }
304         }
305     }
306 
307     private void openParents() {
308         UIComponent comp = this;
309 
310         while ((comp = comp.getParent()) instanceof AbstractUINavigationMenuItem) {
311             AbstractUINavigationMenuItem parent = (AbstractUINavigationMenuItem) comp;
312             if (!parent.isOpen())
313                 parent.setOpen(true);
314             else
315                 return;
316         }
317     }
318 
319     public void deactivateAll() {
320         UIComponent parent = this.getParent();
321         while (!(parent instanceof HtmlPanelNavigationMenu) && parent != null) {
322             parent = parent.getParent();
323         }
324         if (parent == null) {
325             throw new IllegalStateException("no PanelNavigationMenu!");
326         }
327 
328         HtmlPanelNavigationMenu root = (HtmlPanelNavigationMenu) parent;
329         for (Iterator it = root.getChildren().iterator(); it.hasNext();) {
330             Object o = it.next();
331             if (o instanceof AbstractUINavigationMenuItem) {
332                 AbstractUINavigationMenuItem navItem = (AbstractUINavigationMenuItem) o;
333                 navItem.setActive(false);
334                 if (navItem.getChildCount() > 0) {
335                     navItem.deactivateChildren();
336                 }
337             }
338             if (o instanceof HtmlCommandNavigationItem) {
339                 HtmlCommandNavigationItem current = (HtmlCommandNavigationItem) o;
340                 current.setActive(false);
341                 if (current.getChildCount() > 0) {
342                     current.deactivateChildren();
343                 }
344             }
345         }
346     }
347 
348     public void deactivateChildren() {
349         for (Iterator it = this.getChildren().iterator(); it.hasNext();) {
350             Object o = it.next();
351             if (o instanceof AbstractUINavigationMenuItem) {
352                 AbstractUINavigationMenuItem current = (AbstractUINavigationMenuItem) o;
353                 current.setActive(false);
354                 if (current.getChildCount() > 0) {
355                     current.deactivateChildren();
356                 }
357             }
358         }
359     }
360 
361     public Boolean getActiveDirectly() {
362         return Boolean.valueOf(isActive());
363     }
364 }