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  
20  package org.apache.myfaces.tobago.renderkit.util;
21  
22  import org.apache.myfaces.tobago.context.ResourceManagerUtils;
23  import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
24  import org.apache.myfaces.tobago.internal.component.AbstractUIData;
25  import org.apache.myfaces.tobago.internal.util.StringUtils;
26  import org.apache.myfaces.tobago.model.ExpandedState;
27  import org.apache.myfaces.tobago.model.SelectedState;
28  import org.apache.myfaces.tobago.model.TreePath;
29  import org.apache.myfaces.tobago.util.ComponentUtils;
30  
31  import javax.faces.application.Application;
32  import javax.faces.application.ViewHandler;
33  import javax.faces.component.EditableValueHolder;
34  import javax.faces.component.UIComponent;
35  import javax.faces.component.UIParameter;
36  import javax.faces.component.ValueHolder;
37  import javax.faces.component.behavior.ClientBehavior;
38  import javax.faces.component.behavior.ClientBehaviorBase;
39  import javax.faces.component.behavior.ClientBehaviorContext;
40  import javax.faces.component.behavior.ClientBehaviorHolder;
41  import javax.faces.context.ExternalContext;
42  import javax.faces.context.FacesContext;
43  import javax.faces.render.ClientBehaviorRenderer;
44  import java.io.IOException;
45  import java.net.URLDecoder;
46  import java.util.List;
47  import java.util.Map;
48  
49  import org.slf4j.Logger;
50  import org.slf4j.LoggerFactory;
51  
52  public final class RenderUtils {
53  
54    private static final Logger LOG = LoggerFactory.getLogger(RenderUtils.class);
55  
56    private RenderUtils() {
57      // to prevent instantiation
58    }
59  
60    public static boolean contains(final Object[] list, final Object value) {
61      if (list == null) {
62        return false;
63      }
64      for (final Object aList : list) {
65        if (aList != null && aList.equals(value)) {
66          return true;
67        }
68      }
69      return false;
70    }
71  
72    public static void encodeChildren(final FacesContext facesContext, final UIComponent panel) throws IOException {
73      for (final UIComponent child : panel.getChildren()) {
74        encode(facesContext, child);
75      }
76    }
77  
78    public static void encode(final FacesContext facesContext, final UIComponent component) throws IOException {
79      encode(facesContext, component, null);
80    }
81  
82    public static void encode(
83        final FacesContext facesContext, final UIComponent component,
84        final List<? extends Class<? extends UIComponent>> only)
85        throws IOException {
86  
87      if (only != null && !matchFilter(component, only)) {
88        return;
89      }
90  
91      if (component.isRendered()) {
92        if (LOG.isDebugEnabled()) {
93          LOG.debug("rendering " + component.getRendererType() + " " + component);
94        }
95        component.encodeBegin(facesContext);
96        if (component.getRendersChildren()) {
97          component.encodeChildren(facesContext);
98        } else {
99          for (final UIComponent child : component.getChildren()) {
100           encode(facesContext, child, only);
101         }
102       }
103       component.encodeEnd(facesContext);
104     }
105   }
106 
107   private static boolean matchFilter(
108       final UIComponent component, final List<? extends Class<? extends UIComponent>> only) {
109     for (final Class<? extends UIComponent> clazz : only) {
110       if (clazz.isAssignableFrom(component.getClass())) {
111         return true;
112       }
113     }
114     return false;
115   }
116 
117   public static String currentValue(final UIComponent component) {
118     String currentValue = null;
119     if (component instanceof ValueHolder) {
120       Object value;
121       if (component instanceof EditableValueHolder) {
122         value = ((EditableValueHolder) component).getSubmittedValue();
123         if (value != null) {
124           return (String) value;
125         }
126       }
127 
128       value = ((ValueHolder) component).getValue();
129       if (value != null) {
130         currentValue = ComponentUtils.getFormattedValue(FacesContext.getCurrentInstance(), component, value);
131       }
132     }
133     return currentValue;
134   }
135 
136   public static void decodedStateOfTreeData(final FacesContext facesContext, final AbstractUIData data) {
137 
138     if (!data.isTreeModel()) {
139       return;
140     }
141 
142     // selected
143     final List<Integer> selectedIndices = decodeIndices(facesContext, data, AbstractUIData.SUFFIX_SELECTED);
144 
145     // expanded
146     final List<Integer> expandedIndices = decodeIndices(facesContext, data, AbstractUIData.SUFFIX_EXPANDED);
147 
148     final int last = data.isRowsUnlimited() ? Integer.MAX_VALUE : data.getFirst() + data.getRows();
149     for (int rowIndex = data.getFirst(); rowIndex < last; rowIndex++) {
150       data.setRowIndex(rowIndex);
151       if (!data.isRowAvailable()) {
152         break;
153       }
154 
155       final TreePath path = data.getPath();
156 
157       // selected
158       if (selectedIndices != null) {
159         final SelectedState selectedState = data.getSelectedState();
160         final boolean oldSelected = selectedState.isSelected(path);
161         final boolean newSelected = selectedIndices.contains(rowIndex);
162         if (newSelected != oldSelected) {
163           if (newSelected) {
164             selectedState.select(path);
165           } else {
166             selectedState.unselect(path);
167           }
168         }
169       }
170 
171       // expanded
172       if (expandedIndices != null) {
173         final ExpandedState expandedState = data.getExpandedState();
174         final boolean oldExpanded = expandedState.isExpanded(path);
175         final boolean newExpanded = expandedIndices.contains(rowIndex);
176         if (newExpanded != oldExpanded) {
177           if (newExpanded) {
178             expandedState.expand(path);
179           } else {
180             expandedState.collapse(path);
181           }
182         }
183       }
184 
185     }
186     data.setRowIndex(-1);
187   }
188 
189   private static List<Integer> decodeIndices(
190       final FacesContext facesContext, final AbstractUIData data, final String suffix) {
191     String string = null;
192     final String key = data.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + suffix;
193     try {
194       string = facesContext.getExternalContext().getRequestParameterMap().get(key);
195       if (string != null) {
196         return StringUtils.parseIntegerList(string);
197       }
198     } catch (final Exception e) {
199       // should not happen
200       LOG.warn("Can't parse " + suffix + ": '" + string + "' from parameter '" + key + "'", e);
201     }
202     return null;
203   }
204 
205   public static String generateUrl(final FacesContext facesContext, final AbstractUICommand component) {
206 
207     final Application application = facesContext.getApplication();
208     final ViewHandler viewHandler = application.getViewHandler();
209     final ExternalContext externalContext = facesContext.getExternalContext();
210 
211     String url = null;
212 
213     if (component.getResource() != null) {
214       final boolean jsfResource = component.isJsfResource();
215       url = ResourceManagerUtils.getPageWithoutContextPath(facesContext, component.getResource());
216       if (url != null) {
217         if (jsfResource) {
218           url = viewHandler.getActionURL(facesContext, url);
219           url = externalContext.encodeActionURL(url);
220         } else {
221           url = viewHandler.getResourceURL(facesContext, url);
222           url = externalContext.encodeResourceURL(url);
223         }
224       } else {
225         url = "";
226       }
227     } else if (component.getLink() != null) {
228 
229       final String link = component.getLink();
230       if (link.startsWith("/")) { // internal absolute link
231         url = externalContext.encodeResourceURL(externalContext.getRequestContextPath() + link);
232       } else if (StringUtils.isUrl(link)) { // external link
233         url = link;
234       } else { // internal relative link
235         url = externalContext.encodeResourceURL(link);
236       }
237 
238       final StringBuilder builder = new StringBuilder(url);
239       boolean firstParameter = !url.contains("?");
240       for (final UIComponent child : component.getChildren()) {
241         if (child instanceof UIParameter) {
242           final UIParameter parameter = (UIParameter) child;
243           if (firstParameter) {
244             builder.append("?");
245             firstParameter = false;
246           } else {
247             builder.append("&");
248           }
249           builder.append(parameter.getName());
250           builder.append("=");
251           final Object value = parameter.getValue();
252           // TODO encoding
253           builder.append(value != null ? URLDecoder.decode(value.toString()) : null);
254         }
255       }
256       url = builder.toString();
257     }
258 
259     return url;
260   }
261 
262   public static String getBehaviorCommands(final FacesContext facesContext, final ClientBehaviorHolder holder) {
263     final Map<String, List<ClientBehavior>> behaviors = holder.getClientBehaviors();
264     for (Map.Entry<String, List<ClientBehavior>> behavior : behaviors.entrySet()) {
265       final String key = behavior.getKey();
266       final ClientBehaviorContext context = ClientBehaviorContext.createClientBehaviorContext(
267           facesContext, (UIComponent) holder, key, ((UIComponent) holder).getClientId(facesContext), null);
268       for (ClientBehavior clientBehavior : behavior.getValue()) {
269         if (clientBehavior instanceof ClientBehaviorBase) {
270           final String type = ((ClientBehaviorBase) clientBehavior).getRendererType();
271           final ClientBehaviorRenderer clientBehaviorRenderer
272               = facesContext.getRenderKit().getClientBehaviorRenderer(type);
273           return clientBehaviorRenderer.getScript(context, clientBehavior);
274         } else {
275           LOG.warn("Ignoring: '{}'", clientBehavior);
276         }
277       }
278     }
279     return null;
280   }
281 
282   public static void decodeClientBehaviors(final FacesContext facesContext, final UIComponent component) {
283     if (component instanceof ClientBehaviorHolder) {
284       final ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) component;
285       final Map<String, List<ClientBehavior>> clientBehaviors = clientBehaviorHolder.getClientBehaviors();
286       if (clientBehaviors != null && !clientBehaviors.isEmpty()) {
287         final Map<String, String> paramMap = facesContext.getExternalContext().getRequestParameterMap();
288         final String behaviorEventName = paramMap.get("javax.faces.behavior.event");
289         if (behaviorEventName != null) {
290           final List<ClientBehavior> clientBehaviorList = clientBehaviors.get(behaviorEventName);
291           if (clientBehaviorList != null && !clientBehaviorList.isEmpty()) {
292             final String clientId = paramMap.get("javax.faces.source");
293             if (component.getClientId(facesContext).equals(clientId)) {
294               for (ClientBehavior clientBehavior : clientBehaviorList) {
295                 clientBehavior.decode(facesContext, component);
296               }
297             }
298           }
299         }
300       }
301     }
302   }
303 }