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.toggle;
20  
21  import java.io.IOException;
22  import java.lang.reflect.Method;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  
27  import javax.faces.application.ResourceDependency;
28  import javax.faces.component.UIComponent;
29  import javax.faces.component.UIViewRoot;
30  import javax.faces.component.behavior.ClientBehavior;
31  import javax.faces.context.FacesContext;
32  import javax.faces.context.ResponseWriter;
33  
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
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.HtmlGroupRendererBase;
39  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
40  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.ResourceUtils;
41  
42  /**
43   * 
44   * @JSFRenderer
45   *   renderKitId = "HTML_BASIC" 
46   *   family = "javax.faces.Panel"
47   *   type = "org.apache.myfaces.TogglePanel"
48   * 
49   */
50  @ResourceDependency(library="oam.custom.toggle", name="MyFacesToggleLink.js")
51  public class TogglePanelRenderer extends HtmlGroupRendererBase {
52  
53      private static Log log = LogFactory.getLog(TogglePanelRenderer.class);
54  
55      private static final String LAYOUT_BLOCK_VALUE = "block";
56  
57      @Override
58      public void decode(FacesContext context, UIComponent component)
59      {
60          super.decode(context, component);
61          
62          HtmlRendererUtils.decodeClientBehaviors(context, component);
63      }
64  
65      public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
66          RendererUtils.checkParamValidity(context, component, TogglePanel.class);
67          //addToggleLinkJavascript(context);
68          
69          TogglePanel togglePanel = (TogglePanel) component;
70          // render the hidden input field
71          ResponseWriter writer = context.getResponseWriter();
72          
73          Map<String, List<ClientBehavior>> behaviors = null;
74          behaviors = togglePanel.getClientBehaviors();
75          if (!behaviors.isEmpty())
76          {
77              ResourceUtils.renderDefaultJsfJsInlineIfNecessary(context, writer);
78          }
79          
80          boolean toggleMode = togglePanel.isToggled();
81          toggleVisibility(togglePanel.getChildren(), toggleMode);
82  
83          String hiddenFieldId = getHiddenFieldId(context, togglePanel);
84  
85          writer.startElement(HTML.INPUT_ELEM, component);
86          writer.writeAttribute(HTML.TYPE_ATTR, HTML.INPUT_TYPE_HIDDEN, null);
87          writer.writeAttribute(HTML.ID_ATTR, hiddenFieldId, null);
88          writer.writeAttribute(HTML.NAME_ATTR, hiddenFieldId, null);
89  
90          writer.writeAttribute(HTML.VALUE_ATTR, toggleMode ? "1" : "", null);
91  
92          writer.endElement(HTML.INPUT_ELEM);
93  
94          //super.encodeEnd(context, togglePanel);
95          boolean span = false;
96  
97          // will be SPAN or DIV, depending on the layout attribute value
98          String layoutElement = HTML.SPAN_ELEM;
99  
100         // if layout is 'block', render DIV instead SPAN
101         String layout = togglePanel.getLayout();
102         if (layout != null && layout.equals(LAYOUT_BLOCK_VALUE))
103         {
104             layoutElement = HTML.DIV_ELEM;
105         }
106 
107         if (behaviors != null && !behaviors.isEmpty())
108         {
109             //Render element and id to make javascript work
110             span = true;
111             writer.startElement(layoutElement, component);
112             writer.writeAttribute(HTML.ID_ATTR, component.getClientId(context),null);
113             HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.UNIVERSAL_ATTRIBUTES);
114             HtmlRendererUtils.renderBehaviorizedEventHandlers(context, writer, component, behaviors);
115         }
116         else
117         {
118             //No behaviors, do it as usual
119             if(component.getId()!=null && !component.getId().startsWith(UIViewRoot.UNIQUE_ID_PREFIX))
120             {
121                 span = true;
122 
123                 writer.startElement(layoutElement, component);
124 
125                 HtmlRendererUtils.writeIdIfNecessary(writer, component, context);
126 
127                 HtmlRendererUtils.renderHTMLAttributes(writer, component, HTML.COMMON_PASSTROUGH_ATTRIBUTES);
128             }
129             else
130             {
131                 span=HtmlRendererUtils.renderHTMLAttributesWithOptionalStartElement(writer,
132                                                                                  component,
133                                                                                  layoutElement,
134                                                                                  HTML.COMMON_PASSTROUGH_ATTRIBUTES);
135             }
136         }
137 
138         RendererUtils.renderChildren(context, component);
139         if (span)
140         {
141             writer.endElement(layoutElement);
142         }
143     }
144 
145     private void toggleVisibility(List children, boolean toggleMode) {
146         for(Iterator it = children.iterator(); it.hasNext(); ) {
147             UIComponent component = (UIComponent) it.next();
148             setComponentVisibility( component, toggleMode );
149         }
150     }
151 
152     // checks if this component has getStyle/setStyle methods
153     public static boolean hasStyleAttribute(UIComponent component) {
154         Method[] methods = component.getClass().getMethods();
155 
156         for (int i = 0; i < methods.length; i++) {
157             Method method = methods[i];
158             if (method.getName().equals("getStyle")) {
159                 return true;
160             }
161         }
162         return false;
163     }
164 
165     // hides component by appending 'display:none' to the 'style' attribute
166     public static void setComponentVisibility(UIComponent component, boolean toggleMode) {
167         FacesContext context = FacesContext.getCurrentInstance();
168 
169         if (!hasStyleAttribute(component)) {
170             log.info("style attribute expected, not found for component " + component.getClientId(context));
171             return;
172         }
173 
174         try {
175             Class c = component.getClass();
176             Method getStyle = c.getMethod("getStyle", new Class[] {});
177             Method setStyle = c.getMethod("setStyle", new Class[] { String.class });
178 
179             String style = (String) getStyle.invoke(component, new Object[] {});
180             
181             boolean display = toggleMode != isHiddenWhenToggled(component);
182             if( display ){
183                 if (style == null || style.length() == 0) {
184                     return;
185                 } else {
186                     int index = style.indexOf(";display:none;");
187                     if (index == -1)
188                         return;
189 
190                     if (index == 0) {
191                         style = null;
192                     } else {
193                         style = style.substring(0, index);
194                     }
195                 }
196             }else{ // hide
197                 if (style == null) {
198                     style = ";display:none;";
199                 } else if (style.indexOf("display:none;") == -1) {
200                     style = style.concat(";display:none;");
201                 }
202             }
203 
204             setStyle.invoke(component, new Object[] { style });
205         } catch (Throwable e) {
206             log.error("unable to set style attribute on component " + component.getClientId(context));
207         }
208     }
209 
210     public static boolean isHiddenWhenToggled(UIComponent component){
211         return component instanceof ToggleLink || component instanceof ToggleGroup;
212     }
213 
214     private String getHiddenFieldId(FacesContext context, TogglePanel togglePanel){
215         return togglePanel.getClientId(context) + "_hidden";
216     }
217 
218     static public String getToggleJavascriptFunctionName(FacesContext context, TogglePanel togglePanel) {
219         return "MyFacesToggleLinkUtils.toggle";
220     }
221     
222     /*
223     public void addToggleLinkJavascript(FacesContext context)throws IOException {
224         AddResource addResource = AddResourceFactory.getInstance(context);
225         
226         addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, AbstractTogglePanel.class, "MyFacesToggleLink.js");
227     }*/
228 }