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 org.apache.commons.logging.Log;
22  import org.apache.commons.logging.LogFactory;
23  import org.apache.myfaces.custom.navmenu.NavigationMenuUtils;
24  import org.apache.myfaces.custom.navmenu.UINavigationMenuItem;
25  import org.apache.myfaces.shared_tomahawk.el.SimpleActionMethodBinding;
26  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
27  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
28  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
29  
30  import javax.faces.component.UIComponent;
31  import javax.faces.component.UIOutput;
32  import javax.faces.component.UIViewRoot;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  import javax.faces.el.MethodBinding;
36  import javax.faces.el.ValueBinding;
37  import javax.faces.event.ActionEvent;
38  import java.io.IOException;
39  import java.util.Iterator;
40  import java.util.List;
41  
42  /**
43   * @author Thomas Spiegl
44   * @author Manfred Geiler
45   */
46  class HtmlNavigationMenuRendererUtils {
47      private static final Log log = LogFactory.getLog(HtmlNavigationMenuRendererUtils.class);
48  
49      private static final Class[] ACTION_LISTENER_ARGS = {ActionEvent.class};
50  
51      private HtmlNavigationMenuRendererUtils() {
52      }
53  
54      public static void renderChildrenListLayout(FacesContext facesContext,
55                                                  ResponseWriter writer,
56                                                  HtmlPanelNavigationMenu panelNav,
57                                                  List children,
58                                                  int level) throws IOException {
59          for (Iterator it = children.iterator(); it.hasNext();) {
60              UIComponent child = (UIComponent) it.next();
61              if (!child.isRendered()) continue;
62  
63              if (child instanceof UINavigationMenuItem) {
64                  renderChildrenListLayout(facesContext, writer, panelNav, child.getChildren(), level);
65              }
66              if (child instanceof HtmlCommandNavigationItem) {
67                  //navigation item
68                  HtmlRendererUtils.writePrettyLineSeparator(facesContext);
69  
70                  HtmlCommandNavigationItem navItem = (HtmlCommandNavigationItem) child;
71  
72                  String externalLink = navItem.getExternalLink();
73  
74                  String style = HtmlNavigationMenuRendererUtils.getNavigationItemStyle(panelNav, navItem);
75                  String styleClass = HtmlNavigationMenuRendererUtils.getNavigationItemClass(panelNav, navItem);
76  
77                  writer.startElement(HTML.LI_ELEM, panelNav);
78                  HtmlNavigationMenuRendererUtils.writeStyleAttributes(writer, style, styleClass);
79  
80                  Object value = navItem.getValue();
81                  boolean renderAsOutputLink = externalLink != null && value != null;
82  
83                  if (!renderAsOutputLink) {
84                      //if there is an external link specified don't render the command nav item, its content
85                      //will be wrapped by a output link in the renderChildren() method
86                      if (externalLink == null) {
87                          navItem.setValue(null); // unset value, value must not be rendered
88                          navItem.encodeBegin(facesContext);
89                      }
90                      HtmlNavigationMenuRendererUtils.renderChildren(facesContext, navItem, panelNav);
91                      if (externalLink == null) {
92                          navItem.encodeEnd(facesContext);
93                          navItem.setValue(value); // restore value
94                      }
95                  }
96                  else {
97                      //there is an external link value and display value exists, so, just render its children
98                      renderChildren(facesContext, navItem, panelNav);
99                  }
100 
101                 if (hasCommandNavigationItemChildren(navItem)) {
102                     writer.startElement(HTML.UL_ELEM, panelNav);
103 
104                     if (panelNav.isRenderAll())
105                         HtmlNavigationMenuRendererUtils.writeStyleAttributes(writer, navItem.getStyle(), navItem.getStyleClass());
106 
107                     HtmlRendererUtils.renderHTMLAttributes(writer, panelNav, HTML.UL_PASSTHROUGH_ATTRIBUTES);
108                     renderChildrenListLayout(facesContext, writer, panelNav, child.getChildren(), level + 1);
109                     writer.endElement(HTML.UL_ELEM);
110                 }
111 
112                 writer.endElement(HTML.LI_ELEM);
113             }
114         }
115     }
116 
117     private static boolean hasCommandNavigationItemChildren(HtmlCommandNavigationItem item) {
118         List children = item.getChildren();
119         for (int i = 0, sizei = children.size(); i < sizei; i++) {
120             if (children.get(i) instanceof HtmlCommandNavigationItem) {
121                 return true;
122             }
123         }
124         return false;
125     }
126 
127     public static void renderChildrenTableLayout(FacesContext facesContext,
128                                                  ResponseWriter writer,
129                                                  HtmlPanelNavigationMenu panelNav,
130                                                  List children,
131                                                  int level) throws IOException {
132         for (Iterator it = children.iterator(); it.hasNext();) {
133             UIComponent child = (UIComponent) it.next();
134             if (!child.isRendered()) continue;
135             if (child instanceof HtmlCommandNavigationItem) {
136                 //navigation item
137                 HtmlRendererUtils.writePrettyLineSeparator(facesContext);
138 
139                 String style = getNavigationItemStyle(panelNav, (HtmlCommandNavigationItem) child);
140                 String styleClass = getNavigationItemClass(panelNav, (HtmlCommandNavigationItem) child);
141 
142                 writer.startElement(HTML.TR_ELEM, panelNav);
143                 writer.startElement(HTML.TD_ELEM, panelNav);
144                 writeStyleAttributes(writer, style, styleClass);
145 
146                 if (style != null || styleClass != null) {
147                     writer.startElement(HTML.SPAN_ELEM, panelNav);
148                     writeStyleAttributes(writer, style, styleClass);
149                 }
150                 indent(writer, level);
151                 child.encodeBegin(facesContext);
152 
153                 child.encodeEnd(facesContext);
154                 if (style != null || styleClass != null) {
155                     writer.endElement(HTML.SPAN_ELEM);
156                 }
157 
158                 writer.endElement(HTML.TD_ELEM);
159                 writer.endElement(HTML.TR_ELEM);
160 
161                 if (child.getChildCount() > 0) {
162                     renderChildrenTableLayout(facesContext, writer, panelNav, child.getChildren(), level + 1);
163                 }
164             }
165             else {
166                 //separator
167                 HtmlRendererUtils.writePrettyLineSeparator(facesContext);
168 
169                 String style = panelNav.getSeparatorStyle();
170                 String styleClass = panelNav.getSeparatorClass();
171 
172                 writer.startElement(HTML.TR_ELEM, panelNav);
173                 writer.startElement(HTML.TD_ELEM, panelNav);
174                 writeStyleAttributes(writer, style, styleClass);
175 
176                 if (style != null || styleClass != null) {
177                     writer.startElement(HTML.SPAN_ELEM, panelNav);
178                     writeStyleAttributes(writer, style, styleClass);
179                 }
180                 indent(writer, level);
181                 RendererUtils.renderChild(facesContext, child);
182                 if (style != null || styleClass != null) {
183                     writer.endElement(HTML.SPAN_ELEM);
184                 }
185 
186                 writer.endElement(HTML.TD_ELEM);
187                 writer.endElement(HTML.TR_ELEM);
188             }
189         }
190     }
191 
192     public static void indent(ResponseWriter writer, int level) throws IOException {
193         StringBuffer buf = new StringBuffer();
194         for (int i = 0; i < level; i++) {
195             buf.append("&#160;&#160;&#160;&#160;");
196         }
197         writer.write(buf.toString());
198     }
199 
200     public static String getNavigationItemStyle(HtmlPanelNavigationMenu navPanel, HtmlCommandNavigationItem navItem) {
201         if (navItem.isActive()) {
202             return navPanel.getActiveItemStyle();
203         }
204         else if (navItem.isOpen()) {
205             return navPanel.getOpenItemStyle();
206         }
207         else {
208             return navPanel.getItemStyle();
209         }
210     }
211 
212     public static String getNavigationItemClass(HtmlPanelNavigationMenu navPanel,
213                                                 HtmlCommandNavigationItem navItem) {
214         // MYFACES-117, if a styleClass is supplied for a HtmlCommandNavigationItem,
215         // panelNavigation active/open/normal styles for items will be overriden                       
216         if (navItem.getStyleClass() != null) {
217             return navItem.getStyleClass();
218         }
219         if (navItem.isActive()) {
220             return navPanel.getActiveItemClass();
221         }
222         else if (navItem.isOpen()) {
223             return navPanel.getOpenItemClass();
224         }
225         else {
226             return navPanel.getItemClass();
227         }
228     }
229 
230     public static void writeStyleAttributes(ResponseWriter writer,
231                                             String style,
232                                             String styleClass) throws IOException {
233         HtmlRendererUtils.renderHTMLAttribute(writer, HTML.STYLE_ATTR, HTML.STYLE_ATTR, style);
234         HtmlRendererUtils.renderHTMLAttribute(writer, HTML.STYLE_CLASS_ATTR, HTML.STYLE_CLASS_ATTR, styleClass);
235     }
236 
237     public static UIComponent getPanel(UIComponent link) {
238         UIComponent navPanel = link.getParent();
239         while (navPanel != null && !(navPanel instanceof HtmlPanelNavigationMenu)) {
240             navPanel = navPanel.getParent();
241         }
242         if (navPanel == null) {
243             throw new IllegalStateException("HtmlCommandNavigationItem not nested in HtmlPanelNavigation!?");
244         }
245         return navPanel;
246     }
247 
248     public static boolean isListLayout(HtmlPanelNavigationMenu panelNav) {
249         return !"Table".equalsIgnoreCase(panelNav.getLayout());
250     }
251 
252     public static void renderChildren(FacesContext facesContext, HtmlCommandNavigationItem component, HtmlPanelNavigationMenu parentPanelNav) throws IOException {
253         if (component.getChildCount() > 0) {
254             //if there is an external link value, wrapp the content with an output link
255             if (component.getExternalLink() != null) {
256                 ResponseWriter writer = facesContext.getResponseWriter();
257 
258                 writer.startElement(HTML.ANCHOR_ELEM, null);
259                 writer.writeAttribute(HTML.HREF_ATTR, component.getExternalLink(), null);
260                 if (component.getTarget() != null)
261                     writer.writeAttribute(HTML.TARGET_ATTR, component.getTarget(), null);
262 
263                 //the style attributes need to be taken from the parent panel nav, because the command panel navigation item
264                 //is not rendered in this case which would have render them
265                 String style = HtmlNavigationMenuRendererUtils.getNavigationItemStyle(parentPanelNav, component);
266                 String styleClass = HtmlNavigationMenuRendererUtils.getNavigationItemClass(parentPanelNav, component);
267                 HtmlNavigationMenuRendererUtils.writeStyleAttributes(writer, style, styleClass);
268             }
269 
270             for (Iterator it = component.getChildren().iterator(); it.hasNext();) {
271                 UIComponent child = (UIComponent) it.next();
272                 if (!(child instanceof HtmlCommandNavigationItem)) {
273                     RendererUtils.renderChild(facesContext, child);
274                 }
275             }
276 
277             //end wrapper output link
278             if (component.getExternalLink() != null) {
279                 ResponseWriter writer = facesContext.getResponseWriter();
280                 writer.endElement(HTML.ANCHOR_ELEM);
281             }
282         }
283     }
284 
285     public static void debugTree(Log log, FacesContext facesContext, List children, int level) {
286         for (Iterator it = children.iterator(); it.hasNext();) {
287             UIComponent child = (UIComponent) it.next();
288             if (child instanceof UINavigationMenuItem) {
289                 UINavigationMenuItem item = (UINavigationMenuItem) child;
290                 StringBuffer buf = new StringBuffer();
291                 for (int i = 0; i < level * 4; i++) buf.append(' ');
292                 log.debug(buf.toString() + "--> " + item.getItemLabel() + " id:" + item.getClientId(facesContext));
293                 debugTree(log, facesContext, child.getChildren(), level + 1);
294             }
295             else if (child instanceof HtmlCommandNavigationItem) {
296                 HtmlCommandNavigationItem item = (HtmlCommandNavigationItem) child;
297                 StringBuffer buf = new StringBuffer();
298                 for (int i = 0; i < level * 4; i++) buf.append(' ');
299                 String value;
300                 if (item.getChildren().size() > 0 && item.getChildren().get(0) instanceof UIOutput) {
301                     UIOutput uiOutput = (UIOutput) item.getChildren().get(0);
302                     value = uiOutput.getValue() != null ? uiOutput.getValue().toString() : "?";
303                 }
304                 else {
305                     value = item.getValue() != null ? item.getValue().toString() : "";
306                 }
307                 log.debug(buf.toString() + value + " id:" + item.getClientId(facesContext));
308                 debugTree(log, facesContext, child.getChildren(), level + 1);
309             }
310         }
311     }
312 
313     public static HtmlCommandNavigationItem findPreviousItem(UIViewRoot previousViewRoot, String clientId) {
314         HtmlCommandNavigationItem previousItem = null;
315         if (previousViewRoot != null) {
316             UIComponent previousComp = previousViewRoot.findComponent(clientId);
317             if (previousComp instanceof HtmlCommandNavigationItem) {
318                 previousItem = (HtmlCommandNavigationItem) previousComp;
319             }
320         }
321         return previousItem;
322     }
323 
324     public static MethodBinding getMethodBinding(FacesContext facesContext, String value, boolean actionListener) {
325         MethodBinding mb;
326         if (NavigationMenuUtils.isValueReference(value)) {
327             mb = facesContext.getApplication().createMethodBinding(value, actionListener ? ACTION_LISTENER_ARGS : null);
328         }
329         else {
330             if (actionListener) {
331                 log.error("Invalid actionListener value " + value + " (has to be ValueReference!)");
332                 mb = null;
333             }
334             else {
335                 mb = new SimpleActionMethodBinding(value);
336             }
337         }
338         return mb;
339     }
340 
341     public static void setAttributeValue(FacesContext facesContext, UIComponent comp, String attribute, String value) {
342         if (value == null)
343             return;
344         if (NavigationMenuUtils.isValueReference(value)) {
345             ValueBinding vb = facesContext.getApplication().createValueBinding(value);
346             comp.setValueBinding(attribute, vb);
347         }
348         else {
349             comp.getAttributes().put(attribute, value);
350         }
351     }
352    
353 }