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.collapsiblepanel;
20  
21  import java.io.IOException;
22  import java.util.List;
23  import java.util.Map;
24  
25  import javax.faces.application.Application;
26  import javax.faces.application.ViewHandler;
27  import javax.faces.component.UICommand;
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.behavior.ClientBehavior;
30  import javax.faces.component.behavior.ClientBehaviorHolder;
31  import javax.faces.component.html.HtmlCommandLink;
32  import javax.faces.component.html.HtmlOutputText;
33  import javax.faces.context.FacesContext;
34  import javax.faces.context.ResponseWriter;
35  import javax.faces.convert.ConverterException;
36  
37  import org.apache.myfaces.renderkit.html.util.DummyFormUtils;
38  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
39  import org.apache.myfaces.shared_tomahawk.renderkit.html.HTML;
40  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRenderer;
41  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
42  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.ResourceUtils;
43  
44  /**
45   * @JSFRenderer
46   *   renderKitId = "HTML_BASIC" 
47   *   family = "javax.faces.Panel"
48   *   type = "org.apache.myfaces.CollapsiblePanel"
49   *   
50   * @author Kalle Korhonen (latest modification by $Author: lu4242 $)
51   * @version $Revision: 671709 $ $Date: 2008-06-25 22:12:59 -0500 (miƩ, 25 jun 2008) $
52   */
53  public class HtmlCollapsiblePanelRenderer extends HtmlRenderer {
54      //private static final Log log = LogFactory.getLog(HtmlCollapsiblePanel.class);
55      private static final String LINK_ID = "ToggleCollapsed".intern();
56      private static final String COLLAPSED_STATE_ID = "CollapsedState".intern();
57  
58      public boolean getRendersChildren() {
59          return true;
60      }
61  
62      public void encodeChildren(FacesContext facesContext, UIComponent uiComponent) throws IOException {
63          // RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlCollapsiblePanel.class);
64          ResponseWriter writer = facesContext.getResponseWriter();
65          HtmlCollapsiblePanel collapsiblePanel = (HtmlCollapsiblePanel) uiComponent;
66  
67          UIComponent headerComp = collapsiblePanel.getFacet("header");
68  
69          if (headerComp == null){
70              HtmlCommandLink link = getLink(facesContext, collapsiblePanel);
71              collapsiblePanel.getChildren().add(link);
72  
73              headerComp = link;
74          }
75  
76          //Render the current state - collapsed or not - of the panel.
77          HtmlRendererUtils.renderHiddenInputField(writer, collapsiblePanel.getClientId(facesContext) +
78              COLLAPSED_STATE_ID,
79                                                   collapsiblePanel.getSubmittedValue() != null ?
80                                                       collapsiblePanel.getSubmittedValue() : (collapsiblePanel.isCollapsed() + ""));
81  
82          // Always render the header - to be able toggle the collapsed state
83          RendererUtils.renderChild(facesContext, headerComp);
84          headerComp.setRendered(false);
85  
86          // conditionally render the rest of the children
87          if (!collapsiblePanel.isCollapsed()) {
88              HtmlRendererUtils.writePrettyLineSeparator(facesContext);
89              // TODO apply styles from the parent element to this DIV
90              writer.startElement(HTML.DIV_ELEM, uiComponent);
91              RendererUtils.renderChildren(facesContext, uiComponent);
92              writer.endElement(HTML.DIV_ELEM);
93              HtmlRendererUtils.writePrettyLineSeparator(facesContext);
94          }
95          else {
96              UIComponent component = collapsiblePanel.getFacet("closedContent");
97              if (component != null) {
98                  writer.startElement(HTML.DIV_ELEM, uiComponent);
99                  RendererUtils.renderChild(facesContext, component);
100                 writer.endElement(HTML.DIV_ELEM);
101                 HtmlRendererUtils.writePrettyLineSeparator(facesContext);
102             }
103         }
104 
105         headerComp.setRendered(true);
106     }
107 
108     public void encodeBegin(FacesContext facesContext, UIComponent uiComponent) throws IOException {
109         RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlCollapsiblePanel.class);
110         ResponseWriter writer = facesContext.getResponseWriter();
111 
112         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
113         writer.startElement(HTML.DIV_ELEM, uiComponent);
114 
115         Map<String, List<ClientBehavior>> behaviors = null;
116         if (uiComponent instanceof ClientBehaviorHolder)
117         {
118             behaviors = ((ClientBehaviorHolder) uiComponent).getClientBehaviors();
119             if (!behaviors.isEmpty())
120             {
121                 ResourceUtils.renderDefaultJsfJsInlineIfNecessary(facesContext, facesContext.getResponseWriter());
122             }
123         }
124         
125         if (behaviors != null && !behaviors.isEmpty())
126         {
127             writer.writeAttribute(HTML.ID_ATTR, uiComponent.getClientId(facesContext), null);
128             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.UNIVERSAL_ATTRIBUTES);
129             HtmlRendererUtils.renderBehaviorizedEventHandlers(facesContext, writer, uiComponent, behaviors);
130         }
131         else
132         {
133             HtmlRendererUtils.writeIdIfNecessary(writer, uiComponent, facesContext);
134             HtmlRendererUtils.renderHTMLAttributes(writer, uiComponent, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
135         }
136         
137         ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
138         String viewId = facesContext.getViewRoot().getViewId();
139         viewHandler.getActionURL(facesContext, viewId);
140 
141         facesContext.getApplication();
142     }
143 
144 
145     public void encodeEnd(FacesContext facesContext, UIComponent uiComponent) throws IOException {
146         //RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlCollapsiblePanel.class);
147         ResponseWriter writer = facesContext.getResponseWriter();
148         writer.endElement(HTML.DIV_ELEM);
149         HtmlRendererUtils.writePrettyLineSeparator(facesContext);
150     }
151 
152     public void decode(FacesContext facesContext, UIComponent uiComponent) {
153         RendererUtils.checkParamValidity(facesContext, uiComponent, HtmlCollapsiblePanel.class);
154         HtmlCollapsiblePanel collapsiblePanel = (HtmlCollapsiblePanel) uiComponent;
155 
156         Map reqParams = facesContext.getExternalContext().getRequestParameterMap();
157 
158         String togglingIndicated = (String) reqParams.get(HtmlRendererUtils
159                 .getHiddenCommandLinkFieldName(
160                 DummyFormUtils.findNestingForm(collapsiblePanel, facesContext)));
161         // if togglingIndicated is null this application could be running within the RI.
162         // The RI denotes link activation by adding a hidden field with the name
163         // and value of the link client ID.
164         if (togglingIndicated == null
165                 && reqParams.containsKey(collapsiblePanel.getClientId(facesContext) + LINK_ID)) {
166             togglingIndicated = collapsiblePanel.getClientId(facesContext) + LINK_ID;
167         }
168 
169         String reqValue = (String) reqParams.get(
170             collapsiblePanel.getClientId(facesContext) + COLLAPSED_STATE_ID);
171 
172         collapsiblePanel.setCurrentlyCollapsed(HtmlCollapsiblePanel.isCollapsed(reqValue));
173 
174         if ((collapsiblePanel.getClientId(facesContext) + LINK_ID).equals(togglingIndicated)) {
175             if (reqValue != null)
176             {
177                 collapsiblePanel.setSubmittedValue("" + !collapsiblePanel.isCurrentlyCollapsed());
178             }
179             else
180             {
181                 collapsiblePanel.setSubmittedValue("" + !collapsiblePanel.isCollapsed());
182             }
183             
184             UIComponent header = collapsiblePanel.getFacet("header");
185             
186             if (header != null)
187             {
188                 UICommand link = (UICommand)RendererUtils.findComponent(header,HtmlHeaderLink.class);
189                 
190                 if (link != null && link.isImmediate())
191                 {
192                     //In this case we need to update the model directly, because
193                     //PROCESS_VALIDATIONS and UPDATE_MODEL phase is not called
194                     //(immediate=true), but we need to reflect the change
195                     //on the collapsed value.
196                     //In this case, no ValueChangeEvent is fired,
197                     //because it is an immediate call.
198                     Object convertedValue = getConvertedValue(facesContext,collapsiblePanel,
199                             collapsiblePanel.getSubmittedValue());
200                     
201                     collapsiblePanel.setValue(convertedValue);
202                     collapsiblePanel.setSubmittedValue(null);
203                     collapsiblePanel.updateModel(facesContext);
204                 }
205             }
206         }
207         else {
208             if (reqValue != null)
209                 collapsiblePanel.setSubmittedValue("" + collapsiblePanel.isCurrentlyCollapsed());
210         }
211         
212         HtmlRendererUtils.decodeClientBehaviors(facesContext, uiComponent);
213     }
214 
215     protected HtmlCommandLink getLink(FacesContext facesContext, HtmlCollapsiblePanel collapsiblePanel)
216         throws IOException {
217         Application application = facesContext.getApplication();
218         HtmlCommandLink link = (HtmlCommandLink) application.createComponent(HtmlCommandLink.COMPONENT_TYPE);
219         link.setId(collapsiblePanel.getId() + LINK_ID);
220         link.setTransient(true);
221         link.setImmediate(true);
222 
223         List<UIComponent> children = link.getChildren();
224         // Create the indicator. You could later make this conditional and render optional images instead
225         HtmlOutputText uiText = (HtmlOutputText) application.createComponent(HtmlOutputText.COMPONENT_TYPE);
226         uiText.setTransient(true);
227         uiText.setValue(collapsiblePanel.isCollapsed() ? "&gt;" : "&#957;");
228         uiText.setEscape(false);
229         uiText.setStyleClass(collapsiblePanel.getIndicatorStyleClass());
230         uiText.setStyle(collapsiblePanel.getIndicatorStyle());
231         children.add(uiText);
232 
233         // Create the optional label
234         String label = collapsiblePanel.getTitle();
235         if (label != null) {
236             uiText = (HtmlOutputText) application.createComponent(HtmlOutputText.COMPONENT_TYPE);
237             uiText.setTransient(true);
238             uiText.setValue(" " + label);
239             uiText.setStyleClass(collapsiblePanel.getTitleStyleClass());
240             uiText.setStyle(collapsiblePanel.getTitleStyle());
241             children.add(uiText);
242         }
243         return link;
244     }
245 
246     public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
247         if (submittedValue instanceof String) {
248             return Boolean.valueOf((String) submittedValue);
249         }
250 
251         return super.getConvertedValue(context, component, submittedValue);
252     }
253 }