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.htmlnavmenu;
20  
21  import java.util.Iterator;
22  import java.util.List;
23  
24  import javax.faces.component.UIComponent;
25  import javax.faces.context.FacesContext;
26  import javax.faces.event.AbortProcessingException;
27  import javax.faces.event.ActionEvent;
28  import javax.faces.event.FacesEvent;
29  import javax.faces.event.PhaseId;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.myfaces.component.html.ext.HtmlCommandLink;
34  
35  /**
36   * Must be nested inside a panel_navigation action and renders a 
37   * clickable navigation item. 
38   * 
39   * This action is derived from the standard command_link action 
40   * and has equal attributes. (Replaces former "navigation_item" 
41   * tag.) 
42   * 
43   * Unless otherwise specified, all attributes accept static 
44   * values or EL expressions.
45   * 
46   * Many thanks to the guys from Swiss Federal Institute of Intellectual Property and Marc Bouquet
47   * for helping to develop this component.
48   *
49   * @JSFComponent
50   *   name = "t:commandNavigation2"
51   *   class = "org.apache.myfaces.custom.navmenu.htmlnavmenu.HtmlCommandNavigationItem"
52   *   tagClass = "org.apache.myfaces.custom.navmenu.htmlnavmenu.HtmlCommandNavigationItemTag"
53   * @since 1.1.7
54   * @author Manfred Geiler
55   * @author Thomas Spiegl
56   */
57  public abstract class AbstractHtmlCommandNavigationItem extends HtmlCommandLink {
58      private static final Log log = LogFactory.getLog(AbstractHtmlCommandNavigationItem.class);
59  
60      public static final String COMPONENT_TYPE = "org.apache.myfaces.HtmlCommandNavigationItem";
61      public static final String COMPONENT_FAMILY = "javax.faces.Command";
62      private static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.NavigationMenu";
63      
64      /**
65       * @JSFProperty
66       *   defaultValue="true" 
67       *   tagExcluded="true"
68       */
69      public boolean isImmediate() {
70          //always immediate
71          return true;
72      }
73  
74      public void setImmediate(Boolean immediate) {
75          if (log.isWarnEnabled()) log.warn("Immediate property of HtmlCommandNavigation cannot be set --> ignored.");
76      }
77  
78      /**
79       * Menu node is open.
80       * 
81       * @JSFProperty
82       *   localMethod="true"
83       *   setMethod="true"
84       *   defaultValue="false";
85       */
86      public abstract boolean isOpen();
87      
88      protected abstract boolean isSetOpen();
89      
90      protected abstract boolean isLocalOpen();
91      
92      public abstract void setOpen(boolean open);
93      
94      public Boolean getOpenDirectly() {
95          if (isSetOpen())
96              return Boolean.valueOf(isLocalOpen());
97          else
98              return null;
99      }
100     
101     public Boolean getActiveDirectly() {
102         if (isSetActive())
103             return Boolean.valueOf(isLocalActive());
104         else
105             return null;
106     }    
107 
108     /**
109      * Menu node is active.
110      * 
111      * @JSFProperty
112      *   localMethod="true"
113      *   setMethod="true"
114      *   defaultValue="false";
115      */
116     public abstract boolean isActive();
117     
118     protected abstract boolean isSetActive();
119     
120     protected abstract boolean isLocalActive();
121     
122     public abstract void setActive(boolean active);
123     
124     /**
125      * A comma separated list of viewIds for which this item 
126      * should be active.
127      * 
128      * @JSFProperty
129      */
130     public abstract String getActiveOnViewIds();
131     
132     /**
133      * The external link where to redirect when this is clicked.
134      * 
135      * @JSFProperty
136      */    
137     public abstract String getExternalLink();
138     
139     /**
140      * @return false, if this item is child of another HtmlCommandNavigation, which is closed
141      */
142     public boolean isRendered() {
143         if (! super.isRendered()) {
144             return false;
145         }
146 
147         HtmlPanelNavigationMenu parentPanelNavMenu = getParentPanelNavigation();
148         if (parentPanelNavMenu != null && parentPanelNavMenu.isRenderAll())
149             return true;
150 
151         UIComponent parent = getParent();
152         while (parent != null) {
153             if (parent instanceof AbstractHtmlCommandNavigationItem) {
154                 if (!((AbstractHtmlCommandNavigationItem) parent).isOpen()) {
155                     return false;
156                 }
157             }
158 
159             if (parent instanceof HtmlPanelNavigationMenu) {
160                 break;
161             }
162             else {
163                 parent = parent.getParent();
164             }
165         }
166 
167         return true;
168     }
169 
170     private HtmlPanelNavigationMenu getParentPanelNavigation() {
171         UIComponent parent = getParent();
172 
173         // search HtmlPanelNavigation
174         UIComponent p = parent;
175         while (p != null && !(p instanceof HtmlPanelNavigationMenu)) {
176             p = p.getParent();
177         }
178         // p is now the HtmlPanelNavigation
179         if (!(p instanceof HtmlPanelNavigationMenu)) {
180             log.error("HtmlCommandNavigation without parent HtmlPanelNavigation ?!");
181             return null;
182         }
183 
184         return (HtmlPanelNavigationMenu) p;
185     }
186 
187     public void toggleOpen() {
188         HtmlPanelNavigationMenu menu = getParentPanelNavigation();
189         if (isOpen() && menu != null && !menu.isExpandAll()) {
190             if (getChildCount() > 0) {
191                 //item is a menu group --> close item
192                 setOpen(false);
193             }
194         }
195         else {
196             UIComponent parent = getParent();
197 
198             //close all siblings
199             closeAllChildren(parent.getChildren().iterator(), this, true);
200 
201             //open all parents (to be sure) and search HtmlPanelNavigation
202             UIComponent p = parent;
203             AbstractHtmlCommandNavigationItem rootItem = null;
204             while (p != null && !(p instanceof HtmlPanelNavigationMenu)) {
205                 if (p instanceof AbstractHtmlCommandNavigationItem) {
206                     rootItem = (AbstractHtmlCommandNavigationItem) p;
207                     rootItem.setOpen(true);
208                 }
209                 p = p.getParent();
210             }
211             if (rootItem != null) {
212                 List children = menu.getChildren();
213                 for (int i = 0, sizei = children.size(); i < sizei; i++) {
214                     Object obj = children.get(i);
215                     if (obj != rootItem && obj instanceof AbstractHtmlCommandNavigationItem) {
216                         ((AbstractHtmlCommandNavigationItem)obj).setOpen(false);
217                     }
218                 }
219             }
220 
221             // p is now the HtmlPanelNavigation
222             if (!(p instanceof HtmlPanelNavigationMenu)) {
223                 log.error("HtmlCommandNavigation without parent HtmlPanelNavigation ?!");
224             }
225             else {
226                 if (!hasCommandNavigationChildren() || ((HtmlPanelNavigationMenu) p).isExpandAll()) {
227                     //item is an end node or Menu always expanded --> deactivate all other nodes, and then...
228 
229                     //deactivate all other items
230                     deactivateAllChildren(p.getChildren().iterator());
231                     //...activate this item
232                     setActive(true);
233                 }
234                 else {
235                     //open item
236                     setOpen(true);
237                 }
238             }
239         }
240     }
241 
242     private boolean hasCommandNavigationChildren() {
243         if (getChildCount() == 0) {
244             return false;
245         }
246         List list = getChildren();
247         for (int i = 0, sizei = list.size(); i < sizei; i++) {
248             if (list.get(i) instanceof AbstractHtmlCommandNavigationItem) {
249                 return true;
250             }
251         }
252         return false;
253     }
254 
255 
256     private static void deactivateAllChildren(Iterator children) {
257         while (children.hasNext()) {
258             UIComponent ni = (UIComponent) children.next();
259             if (ni instanceof AbstractHtmlCommandNavigationItem) {
260                 ((AbstractHtmlCommandNavigationItem) ni).setActive(false);
261                 if (ni.getChildCount() > 0) {
262                     deactivateAllChildren(ni.getChildren().iterator());
263                 }
264             }
265         }
266     }
267 
268     private static void closeAllChildren(Iterator children, AbstractHtmlCommandNavigationItem current, boolean resetActive) {
269         while (children.hasNext()) {
270             UIComponent ni = (UIComponent) children.next();
271             if (ni instanceof AbstractHtmlCommandNavigationItem) {
272                 ((AbstractHtmlCommandNavigationItem) ni).setOpen(false);
273                 if (resetActive)
274                     ((AbstractHtmlCommandNavigationItem) ni).setActive(false);
275                 if (ni.getChildCount() > 0) {
276                     closeAllChildren(ni.getChildren().iterator(), current, current != ni);
277                 }
278             }
279         }
280     }
281 
282     public String[] getActiveOnVieIds() {
283         String value = getActiveOnViewIds();
284         if (value == null)
285             return new String[]{};
286         return value.split(",");
287     }
288 
289     public void deactivateAll() {
290         UIComponent parent = this.getParent();
291         while (!(parent instanceof HtmlPanelNavigationMenu) && parent != null) {
292             parent = parent.getParent();
293         }
294         if (parent == null) {
295             throw new IllegalStateException("no PanelNavigationMenu!");
296         }
297 
298         HtmlPanelNavigationMenu root = (HtmlPanelNavigationMenu) parent;
299         for (Iterator it = root.getChildren().iterator(); it.hasNext();) {
300             Object o = it.next();
301             if (o instanceof AbstractHtmlCommandNavigationItem) {
302                 AbstractHtmlCommandNavigationItem navItem = (AbstractHtmlCommandNavigationItem) o;
303                 navItem.setActive(false);
304                 if (navItem.getChildCount() > 0) {
305                     navItem.deactivateChildren();
306                 }
307             }
308         }
309     }
310 
311     public void deactivateChildren() {
312         for (Iterator it = this.getChildren().iterator(); it.hasNext();) {
313             Object o = it.next();
314             if (o instanceof AbstractHtmlCommandNavigationItem) {
315                 AbstractHtmlCommandNavigationItem current = (AbstractHtmlCommandNavigationItem) o;
316                 current.setActive(false);
317                 if (current.getChildCount() > 0) {
318                     current.deactivateChildren();
319                 }
320             }
321         }
322     }
323 
324     public void broadcast(FacesEvent event) throws AbortProcessingException {
325         if (event instanceof ActionEvent) {
326             ActionEvent actionEvent = (ActionEvent) event;
327             if (actionEvent.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES) {
328                 AbstractHtmlCommandNavigationItem navItem = (AbstractHtmlCommandNavigationItem) actionEvent.getComponent();
329                 navItem.toggleOpen();
330                 FacesContext.getCurrentInstance().renderResponse();
331             }
332         }
333         super.broadcast(event);
334     }
335 }