1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.myfaces.tobago.internal.util;
21
22 import org.apache.myfaces.tobago.component.ClientBehaviors;
23 import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
24 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
25 import org.apache.myfaces.tobago.internal.component.AbstractUICommandBase;
26 import org.apache.myfaces.tobago.internal.component.AbstractUIData;
27 import org.apache.myfaces.tobago.internal.component.AbstractUIEvent;
28 import org.apache.myfaces.tobago.internal.component.AbstractUITreeNodeBase;
29 import org.apache.myfaces.tobago.internal.renderkit.Command;
30 import org.apache.myfaces.tobago.internal.renderkit.CommandMap;
31 import org.apache.myfaces.tobago.model.ExpandedState;
32 import org.apache.myfaces.tobago.model.SelectedState;
33 import org.apache.myfaces.tobago.model.TreePath;
34 import org.apache.myfaces.tobago.util.ComponentUtils;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 import javax.faces.application.ViewHandler;
39 import javax.faces.component.EditableValueHolder;
40 import javax.faces.component.UIComponent;
41 import javax.faces.component.UIPanel;
42 import javax.faces.component.UIParameter;
43 import javax.faces.component.ValueHolder;
44 import javax.faces.component.behavior.AjaxBehavior;
45 import javax.faces.component.behavior.ClientBehavior;
46 import javax.faces.component.behavior.ClientBehaviorBase;
47 import javax.faces.component.behavior.ClientBehaviorContext;
48 import javax.faces.component.behavior.ClientBehaviorHolder;
49 import javax.faces.context.ExternalContext;
50 import javax.faces.context.FacesContext;
51 import javax.faces.render.ClientBehaviorRenderer;
52 import java.io.IOException;
53 import java.io.UnsupportedEncodingException;
54 import java.lang.invoke.MethodHandles;
55 import java.net.URLEncoder;
56 import java.util.Collections;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Objects;
60
61 public final class RenderUtils {
62
63 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
64
65 private RenderUtils() {
66
67 }
68
69
70
71
72 @Deprecated
73 public static boolean contains(final Object[] list, final Object value) {
74 return ArrayUtils.contains(list, value);
75 }
76
77
78
79
80 @Deprecated
81 public static void encodeChildren(final FacesContext facesContext, final UIComponent panel) throws IOException {
82 for (final UIComponent child : panel.getChildren()) {
83 child.encodeAll(facesContext);
84 }
85 }
86
87
88
89
90 @Deprecated
91 public static void encode(final FacesContext facesContext, final UIComponent component) throws IOException {
92 component.encodeAll(facesContext);
93 }
94
95
96
97
98 @Deprecated
99 public static void encode(
100 final FacesContext facesContext, final UIComponent component,
101 final List<? extends Class<? extends UIComponent>> only)
102 throws IOException {
103
104 if (only != null && !matchFilter(component, only)) {
105 return;
106 }
107
108 if (component.isRendered()) {
109 if (LOG.isDebugEnabled()) {
110 LOG.debug("rendering " + component.getRendererType() + " " + component);
111 }
112 component.encodeBegin(facesContext);
113 if (component.getRendersChildren()) {
114 component.encodeChildren(facesContext);
115 } else {
116 for (final UIComponent child : component.getChildren()) {
117 encode(facesContext, child, only);
118 }
119 }
120 component.encodeEnd(facesContext);
121 }
122 }
123
124
125
126
127 @Deprecated
128 private static boolean matchFilter(
129 final UIComponent component, final List<? extends Class<? extends UIComponent>> only) {
130 for (final Class<? extends UIComponent> clazz : only) {
131 if (clazz.isAssignableFrom(component.getClass())) {
132 return true;
133 }
134 }
135 return false;
136 }
137
138 public static String currentValue(final UIComponent component) {
139 String currentValue = null;
140 if (component instanceof ValueHolder) {
141 Object value;
142 if (component instanceof EditableValueHolder) {
143 value = ((EditableValueHolder) component).getSubmittedValue();
144 if (value != null) {
145 return (String) value;
146 }
147 }
148
149 value = ((ValueHolder) component).getValue();
150 if (value != null) {
151 currentValue = ComponentUtils.getFormattedValue(FacesContext.getCurrentInstance(), component, value);
152 }
153 }
154 return currentValue;
155 }
156
157 public static void decodedStateOfTreeData(final FacesContext facesContext, final AbstractUIData data) {
158
159 if (!data.isTreeModel()) {
160 return;
161 }
162
163
164 final List<Integer> selectedIndices = decodeIndices(facesContext, data, AbstractUIData.SUFFIX_SELECTED);
165
166
167 final List<Integer> expandedIndices = decodeIndices(facesContext, data, AbstractUIData.SUFFIX_EXPANDED);
168
169 final int last = data.isRowsUnlimited() ? Integer.MAX_VALUE : data.getFirst() + data.getRows();
170 for (int rowIndex = data.getFirst(); rowIndex < last; rowIndex++) {
171 data.setRowIndex(rowIndex);
172 if (!data.isRowAvailable()) {
173 break;
174 }
175
176
177 boolean skip = false;
178 for (final UIComponent uiComponent : data.getChildren()) {
179 if (uiComponent instanceof AbstractUITreeNodeBase && !uiComponent.isRendered()) {
180 skip = true;
181 break;
182 }
183 }
184 if (skip) {
185 continue;
186 }
187
188 final TreePath path = data.getPath();
189
190
191 if (selectedIndices != null) {
192 final SelectedState selectedState = data.getSelectedState();
193 final boolean oldSelected = selectedState.isSelected(path);
194 final boolean newSelected = selectedIndices.contains(rowIndex);
195 if (newSelected != oldSelected) {
196 if (newSelected) {
197 selectedState.select(path);
198 } else {
199 selectedState.unselect(path);
200 }
201 }
202 }
203
204
205 if (expandedIndices != null) {
206 final ExpandedState expandedState = data.getExpandedState();
207 final boolean oldExpanded = expandedState.isExpanded(path);
208 final boolean newExpanded = expandedIndices.contains(rowIndex);
209 if (newExpanded != oldExpanded) {
210 if (newExpanded) {
211 expandedState.expand(path);
212 } else {
213 expandedState.collapse(path);
214 }
215 }
216 }
217
218 }
219 data.setRowIndex(-1);
220 }
221
222 private static List<Integer> decodeIndices(
223 final FacesContext facesContext, final AbstractUIData data, final String suffix) {
224 String string = null;
225 final String key = data.getClientId(facesContext) + ComponentUtils.SUB_SEPARATOR + suffix;
226 try {
227 string = facesContext.getExternalContext().getRequestParameterMap().get(key);
228 return JsonUtils.decodeIntegerArray(string);
229 } catch (final Exception e) {
230
231 LOG.warn("Can't parse " + suffix + ": '" + string + "' from parameter '" + key + "'", e);
232 }
233 return null;
234 }
235
236 public static String generateUrl(final FacesContext facesContext, final AbstractUICommandBase component) {
237
238 final ExternalContext externalContext = facesContext.getExternalContext();
239 final String outcome = component.getOutcome();
240 final String link = component.getLink();
241
242 String url = null;
243
244 if (outcome != null) {
245 final ViewHandler viewHandler = facesContext.getApplication().getViewHandler();
246 url = viewHandler.getBookmarkableURL(
247 facesContext,
248 outcome,
249 null,
250 true);
251 } else if (link != null) {
252 if (StringUtils.isUrl(link)) {
253 url = link;
254 } else {
255 url = externalContext.encodeResourceURL(link);
256 }
257 }
258
259 if (link != null || outcome != null) {
260 final String characterEncoding = facesContext.getResponseWriter().getCharacterEncoding();
261 final StringBuilder builder = new StringBuilder(url);
262 boolean firstParameter = !url.contains("?");
263 for (final UIComponent child : component.getChildren()) {
264 if (child instanceof UIParameter) {
265 final UIParameter parameter = (UIParameter) child;
266 if (firstParameter) {
267 builder.append("?");
268 firstParameter = false;
269 } else {
270 builder.append("&");
271 }
272 appendUrlEncoded(builder, parameter.getName(), characterEncoding);
273 builder.append("=");
274 appendUrlEncoded(builder, parameter.getValue(), characterEncoding);
275 }
276 }
277
278 final String fragment = component.getFragment();
279 if (StringUtils.isNotBlank(fragment)) {
280 builder.append("#");
281 appendUrlEncoded(builder, fragment.trim(), characterEncoding);
282 }
283
284 url = builder.toString();
285 }
286
287 return url;
288 }
289
290 private static void appendUrlEncoded(
291 final StringBuilder builder, final Object value, final String characterEncoding) {
292
293 if (value != null) {
294 try {
295 final String encode = URLEncoder.encode(value.toString(), characterEncoding);
296
297 builder.append(encode.replace("+", "%20"));
298 } catch (final UnsupportedEncodingException e) {
299 LOG.error("string='" + value + "'", e);
300 }
301 }
302 }
303
304
305
306
307 @Deprecated
308 public static CommandMap getBehaviorCommands(final FacesContext facesContext,
309 final ClientBehaviorHolder clientBehaviorHolder) {
310 CommandMap commandMap = null;
311
312 for (final Map.Entry<String, List<ClientBehavior>> entry : clientBehaviorHolder.getClientBehaviors().entrySet()) {
313 final String eventName = entry.getKey();
314 final ClientBehaviorContext clientBehaviorContext
315 = getClientBehaviorContext(facesContext, clientBehaviorHolder, eventName);
316
317 for (final ClientBehavior clientBehavior : entry.getValue()) {
318 if (clientBehavior instanceof EventBehavior) {
319 final EventBehavior eventBehavior = (EventBehavior) clientBehavior;
320 final AbstractUIEvent abstractUIEvent = getAbstractUIEvent((UIComponent) clientBehaviorHolder, eventBehavior);
321
322 if (abstractUIEvent != null && abstractUIEvent.isRendered() && !abstractUIEvent.isDisabled()) {
323 for (List<ClientBehavior> children : abstractUIEvent.getClientBehaviors().values()) {
324 for (ClientBehavior child : children) {
325 final CommandMap childMap = getCommandMap(facesContext, clientBehaviorContext, child);
326 commandMap = CommandMap.merge(commandMap, childMap);
327 }
328 }
329 }
330 }
331
332 final CommandMap map = getCommandMap(facesContext, clientBehaviorContext, clientBehavior);
333 commandMap = CommandMap.merge(commandMap, map);
334 }
335 }
336
337
338 if ((commandMap == null || commandMap.isEmpty()) && clientBehaviorHolder instanceof AbstractUICommand) {
339 if (commandMap == null) {
340 commandMap = new CommandMap();
341 }
342 commandMap.addCommand(ClientBehaviors.click, new Command(facesContext, (AbstractUICommand) clientBehaviorHolder));
343 }
344
345 return commandMap;
346 }
347
348
349
350
351 @Deprecated
352 private static ClientBehaviorContext getClientBehaviorContext(final FacesContext facesContext,
353 final ClientBehaviorHolder clientBehaviorHolder, final String eventName) {
354 UIComponent component = (UIComponent) clientBehaviorHolder;
355 return ClientBehaviorContext.createClientBehaviorContext(facesContext, component, eventName,
356 component.getClientId(facesContext), null);
357 }
358
359 public static AbstractUIEvent getAbstractUIEvent(final UIComponent parent,
360 final EventBehavior eventBehavior) {
361 return (AbstractUIEvent) parent.getChildren().stream()
362 .filter(child -> child instanceof AbstractUIEvent)
363 .filter(child -> Objects.equals(child.getId(), eventBehavior.getFor()))
364 .findFirst().orElse(null);
365 }
366
367
368
369
370 @Deprecated
371 private static CommandMap getCommandMap(final FacesContext facesContext,
372 final ClientBehaviorContext clientBehaviorContext, final ClientBehavior clientBehavior) {
373 if (clientBehavior instanceof ClientBehaviorBase) {
374 String type = ((ClientBehaviorBase) clientBehavior).getRendererType();
375
376
377 if (type.equals(AjaxBehavior.BEHAVIOR_ID)) {
378 type = "org.apache.myfaces.tobago.behavior.Ajax";
379 }
380 final ClientBehaviorRenderer renderer = facesContext.getRenderKit().getClientBehaviorRenderer(type);
381 final String dummy = renderer.getScript(clientBehaviorContext, clientBehavior);
382 if (dummy != null) {
383 return CommandMap.restoreCommandMap(facesContext);
384 }
385 } else {
386 LOG.warn("Ignoring: '{}'", clientBehavior);
387 }
388 return null;
389 }
390
391 public static void decodeClientBehaviors(final FacesContext facesContext, final UIComponent component) {
392 if (component instanceof ClientBehaviorHolder) {
393 final ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) component;
394 final Map<String, List<ClientBehavior>> clientBehaviors = clientBehaviorHolder.getClientBehaviors();
395 if (clientBehaviors != null && !clientBehaviors.isEmpty()) {
396 final Map<String, String> paramMap = facesContext.getExternalContext().getRequestParameterMap();
397 final String behaviorEventName = paramMap.get("javax.faces.behavior.event");
398 if (behaviorEventName != null) {
399 final List<ClientBehavior> clientBehaviorList = clientBehaviors.get(behaviorEventName);
400 if (clientBehaviorList != null && !clientBehaviorList.isEmpty()) {
401 final String clientId = paramMap.get("javax.faces.source");
402 if (component.getClientId(facesContext).equals(clientId)) {
403 for (final ClientBehavior clientBehavior : clientBehaviorList) {
404 clientBehavior.decode(facesContext, component);
405 }
406 }
407 }
408 }
409 }
410 }
411 }
412
413 public static List<UIComponent> getFacetChildren(UIComponent facet) {
414 if (facet instanceof UIPanel) {
415 return facet.getChildren();
416 } else {
417 return Collections.singletonList(facet);
418 }
419 }
420 }