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