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.renderkit;
21
22 import org.apache.myfaces.tobago.component.ClientBehaviors;
23 import org.apache.myfaces.tobago.context.TobagoContext;
24 import org.apache.myfaces.tobago.internal.behavior.EventBehavior;
25 import org.apache.myfaces.tobago.internal.component.AbstractUICommand;
26 import org.apache.myfaces.tobago.internal.component.AbstractUIEvent;
27 import org.apache.myfaces.tobago.internal.component.AbstractUIReload;
28 import org.apache.myfaces.tobago.internal.renderkit.Collapse;
29 import org.apache.myfaces.tobago.internal.renderkit.Command;
30 import org.apache.myfaces.tobago.internal.renderkit.CommandMap;
31 import org.apache.myfaces.tobago.internal.util.HtmlRendererUtils;
32 import org.apache.myfaces.tobago.internal.util.RenderUtils;
33 import org.apache.myfaces.tobago.internal.webapp.TobagoResponseWriterWrapper;
34 import org.apache.myfaces.tobago.renderkit.html.CustomAttributes;
35 import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
36 import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
37 import org.apache.myfaces.tobago.util.ComponentUtils;
38 import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 import javax.faces.component.EditableValueHolder;
43 import javax.faces.component.UIComponent;
44 import javax.faces.component.ValueHolder;
45 import javax.faces.component.behavior.AjaxBehavior;
46 import javax.faces.component.behavior.ClientBehavior;
47 import javax.faces.component.behavior.ClientBehaviorBase;
48 import javax.faces.component.behavior.ClientBehaviorContext;
49 import javax.faces.component.behavior.ClientBehaviorHolder;
50 import javax.faces.context.FacesContext;
51 import javax.faces.context.ResponseWriter;
52 import javax.faces.convert.Converter;
53 import javax.faces.convert.ConverterException;
54 import javax.faces.render.ClientBehaviorRenderer;
55 import javax.faces.render.Renderer;
56 import java.io.IOException;
57 import java.lang.invoke.MethodHandles;
58 import java.util.List;
59 import java.util.Map;
60
61 public abstract class RendererBase<T extends UIComponent> extends Renderer {
62
63 private static final Logger LOG = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
64
65 private static final String FOCUS_KEY = HtmlRendererUtils.class.getName() + ".FocusId";
66
67
68
69 @Override
70 public final void encodeBegin(FacesContext context, UIComponent component) throws IOException {
71 this.encodeBeginInternal(context, (T) component);
72 }
73
74 public void encodeBeginInternal(FacesContext context, T component) throws IOException {
75 super.encodeBegin(context, component);
76 }
77
78 @Override
79 public final void encodeChildren(FacesContext context, UIComponent component) throws IOException {
80 this.encodeChildrenInternal(context, (T) component);
81 }
82
83 public void encodeChildrenInternal(FacesContext context, T component) throws IOException {
84 super.encodeChildren(context, component);
85 }
86
87 @Override
88 public final void encodeEnd(FacesContext context, UIComponent component) throws IOException {
89 this.encodeEndInternal(context, (T) component);
90 }
91
92 public void encodeEndInternal(FacesContext context, T component) throws IOException {
93 super.encodeEnd(context, component);
94 }
95
96 @Override
97 public final void decode(FacesContext context, UIComponent component) {
98 this.decodeInternal(context, (T) component);
99 }
100
101 public void decodeInternal(FacesContext context, T component) {
102 super.decode(context, component);
103 }
104
105 @Override
106 public Object getConvertedValue(
107 final FacesContext facesContext, final UIComponent component, final Object submittedValue)
108 throws ConverterException {
109 return getConvertedValueInternal(facesContext, (T) component, submittedValue);
110 }
111
112 public Object getConvertedValueInternal(
113 final FacesContext context, final T component, final Object submittedValue)
114 throws ConverterException {
115 if (!(submittedValue instanceof String)) {
116 return submittedValue;
117 }
118 final Converter converter = ComponentUtils.getConverter(context, component, submittedValue);
119 if (converter != null) {
120 return converter.getAsObject(context, component, (String) submittedValue);
121 } else {
122 return submittedValue;
123 }
124 }
125
126
127
128 protected String getCurrentValue(final FacesContext facesContext, final T component) {
129
130 if (component instanceof ValueHolder) {
131 final ValueHolder valueHolder = (ValueHolder) component;
132 if (valueHolder instanceof EditableValueHolder) {
133 final EditableValueHolder editableValueHolder = (EditableValueHolder) component;
134 final Object submittedValue = editableValueHolder.getSubmittedValue();
135 if (submittedValue != null || !editableValueHolder.isValid()) {
136 return (String) submittedValue;
137 }
138 }
139 String currentValue = null;
140 final Object result = ((ValueHolder) component).getValue();
141 if (result != null) {
142 currentValue = ComponentUtils.getFormattedValue(facesContext, component, result);
143 }
144 return currentValue;
145 } else {
146 return null;
147 }
148 }
149
150 public static void renderFocus(
151 final String clientId, final boolean focus, final boolean error, final FacesContext facesContext,
152 final TobagoResponseWriter writer) throws IOException {
153 final Map<String, Object> requestMap = facesContext.getExternalContext().getRequestMap();
154 if (!requestMap.containsKey(FOCUS_KEY)
155 && (clientId.equals(TobagoContext.getInstance(facesContext).getFocusId()) || focus || error)) {
156 requestMap.put(FOCUS_KEY, Boolean.TRUE);
157 writer.writeAttribute(HtmlAttributes.AUTOFOCUS, true);
158 }
159 }
160
161 protected TobagoResponseWriter getResponseWriter(final FacesContext facesContext) {
162 final ResponseWriter writer = facesContext.getResponseWriter();
163 if (writer instanceof TobagoResponseWriter) {
164 return (TobagoResponseWriter) writer;
165 } else {
166 return new TobagoResponseWriterWrapper(writer);
167 }
168 }
169
170 protected void insideBegin(final FacesContext facesContext, final HtmlElements inside) {
171 facesContext.getAttributes().put(inside, Boolean.TRUE);
172 }
173
174 protected void insideEnd(final FacesContext facesContext, final HtmlElements inside) {
175 facesContext.getAttributes().remove(inside);
176 }
177
178 protected boolean isInside(final FacesContext facesContext, final HtmlElements inside) {
179 return facesContext.getAttributes().get(inside) != null;
180 }
181
182
183
184
185 public void encodeReload(FacesContext facesContext, AbstractUIReload reload) throws IOException {
186 final TobagoResponseWriter writer = getResponseWriter(facesContext);
187 writer.write("{\"reload\":{\"frequency\":" + reload.getFrequency() + "}}");
188 }
189
190
191
192
193
194
195 protected void encodeBehavior(
196 final TobagoResponseWriter writer, final FacesContext facesContext, final ClientBehaviorHolder holder)
197 throws IOException {
198 if (holder != null) {
199 final CommandMap behaviorCommands = getBehaviorCommands(facesContext, holder);
200 encodeBehavior(writer, behaviorCommands);
201 }
202 }
203
204
205
206
207
208
209 protected void encodeBehavior(
210 final TobagoResponseWriter writer, final CommandMap behaviorCommands)
211 throws IOException {
212 if (behaviorCommands != null) {
213 final Command click = behaviorCommands.getClick();
214 if (click != null) {
215 encodeBehavior(writer, ClientBehaviors.click, click);
216 }
217 final Map<ClientBehaviors, Command> other = behaviorCommands.getOther();
218 if (other != null) {
219 for (Map.Entry<ClientBehaviors, Command> entry : other.entrySet()) {
220 encodeBehavior(writer, entry.getKey(), entry.getValue());
221 }
222 }
223 }
224 }
225
226 private void encodeBehavior(
227 final TobagoResponseWriter writer, final ClientBehaviors behaviors, final Command command)
228 throws IOException {
229 writer.startElement(HtmlElements.TOBAGO_BEHAVIOR);
230 writer.writeAttribute(CustomAttributes.EVENT, behaviors.name(), false);
231 writer.writeAttribute(HtmlAttributes.ACTION, command.getAction(), false);
232 writer.writeAttribute(CustomAttributes.EXECUTE, command.getExecute(), false);
233 writer.writeAttribute(CustomAttributes.RENDER, command.getRender(), false);
234 writer.writeAttribute(CustomAttributes.OMIT, command.getOmit());
235 writer.writeAttribute(CustomAttributes.CONFIRMATION, command.getConfirmation(), true);
236 writer.writeAttribute(CustomAttributes.DECOUPLED,
237 command.getTransition() != null ? command.getTransition() : false);
238 final Collapse collapse = command.getCollapse();
239 if (collapse != null) {
240 writer.writeAttribute(CustomAttributes.COLLAPSE_ACTION, collapse.getAction().name(), false);
241 writer.writeAttribute(CustomAttributes.COLLAPSE_TARGET, collapse.getFor(), false);
242 }
243 writer.writeAttribute(CustomAttributes.DELAY, command.getDelay());
244 writer.writeAttribute(CustomAttributes.FOCUS_ID, command.getFocus(), false);
245 writer.writeAttribute(HtmlAttributes.TARGET, command.getTarget(), true);
246
247
248 writer.endElement(HtmlElements.TOBAGO_BEHAVIOR);
249 }
250
251 protected CommandMap getBehaviorCommands(
252 final FacesContext facesContext, final ClientBehaviorHolder clientBehaviorHolder) {
253 CommandMap commandMap = null;
254
255 for (final Map.Entry<String, List<ClientBehavior>> entry : clientBehaviorHolder.getClientBehaviors().entrySet()) {
256 final String eventName = entry.getKey();
257 final ClientBehaviorContext clientBehaviorContext
258 = getClientBehaviorContext(facesContext, clientBehaviorHolder, eventName);
259
260 for (final ClientBehavior clientBehavior : entry.getValue()) {
261 if (clientBehavior instanceof EventBehavior) {
262 final EventBehavior eventBehavior = (EventBehavior) clientBehavior;
263 final AbstractUIEvent abstractUIEvent
264 = RenderUtils.getAbstractUIEvent((UIComponent) clientBehaviorHolder, eventBehavior);
265
266 if (abstractUIEvent != null && abstractUIEvent.isRendered() && !abstractUIEvent.isDisabled()) {
267 for (List<ClientBehavior> children : abstractUIEvent.getClientBehaviors().values()) {
268 for (ClientBehavior child : children) {
269 final CommandMap childMap = getCommandMap(facesContext, clientBehaviorContext, child);
270 commandMap = CommandMap.merge(commandMap, childMap);
271 }
272 }
273 }
274 }
275
276 final CommandMap map = getCommandMap(facesContext, clientBehaviorContext, clientBehavior);
277 commandMap = CommandMap.merge(commandMap, map);
278 }
279 }
280
281
282 if ((commandMap == null || commandMap.isEmpty()) && clientBehaviorHolder instanceof AbstractUICommand) {
283 if (commandMap == null) {
284 commandMap = new CommandMap();
285 }
286 commandMap.addCommand(ClientBehaviors.click, new Command(facesContext, (AbstractUICommand) clientBehaviorHolder));
287 }
288
289 return commandMap;
290 }
291
292 private static ClientBehaviorContext getClientBehaviorContext(
293 final FacesContext facesContext, final ClientBehaviorHolder clientBehaviorHolder, final String eventName) {
294 final UIComponent component = (UIComponent) clientBehaviorHolder;
295 return ClientBehaviorContext.createClientBehaviorContext(facesContext, component, eventName,
296 component.getClientId(facesContext), null);
297 }
298
299 private static CommandMap getCommandMap(
300 final FacesContext facesContext, final ClientBehaviorContext clientBehaviorContext,
301 final ClientBehavior clientBehavior) {
302 if (clientBehavior instanceof ClientBehaviorBase) {
303 String type = ((ClientBehaviorBase) clientBehavior).getRendererType();
304
305
306 if (type.equals(AjaxBehavior.BEHAVIOR_ID)) {
307 type = "org.apache.myfaces.tobago.behavior.Ajax";
308 }
309 final ClientBehaviorRenderer renderer = facesContext.getRenderKit().getClientBehaviorRenderer(type);
310 final String dummy = renderer.getScript(clientBehaviorContext, clientBehavior);
311 if (dummy != null) {
312 return CommandMap.restoreCommandMap(facesContext);
313 }
314 } else {
315 LOG.warn("Ignoring: '{}'", clientBehavior);
316 }
317 return null;
318 }
319
320 protected void decodeClientBehaviors(final FacesContext facesContext, final T component) {
321 if (component instanceof ClientBehaviorHolder) {
322 final ClientBehaviorHolder clientBehaviorHolder = (ClientBehaviorHolder) component;
323 final Map<String, List<ClientBehavior>> clientBehaviors = clientBehaviorHolder.getClientBehaviors();
324 if (clientBehaviors != null && !clientBehaviors.isEmpty()) {
325 final Map<String, String> paramMap = facesContext.getExternalContext().getRequestParameterMap();
326 final String behaviorEventName = paramMap.get("javax.faces.behavior.event");
327 if (behaviorEventName != null) {
328 final List<ClientBehavior> clientBehaviorList = clientBehaviors.get(behaviorEventName);
329 if (clientBehaviorList != null && !clientBehaviorList.isEmpty()) {
330 final String clientId = paramMap.get("javax.faces.source");
331 if (component.getClientId(facesContext).equals(clientId)) {
332 for (final ClientBehavior clientBehavior : clientBehaviorList) {
333 clientBehavior.decode(facesContext, component);
334 }
335 }
336 }
337 }
338 }
339 }
340 }
341
342 }