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.component;
21
22 import org.apache.myfaces.tobago.ajax.AjaxUtils;
23 import org.apache.myfaces.tobago.compat.InvokeOnComponent;
24 import org.apache.myfaces.tobago.context.ClientProperties;
25 import org.apache.myfaces.tobago.internal.ajax.AjaxInternalUtils;
26 import org.apache.myfaces.tobago.internal.ajax.AjaxResponseRenderer;
27 import org.apache.myfaces.tobago.internal.component.AbstractUIPage;
28 import org.apache.myfaces.tobago.internal.util.FacesContextUtils;
29 import org.apache.myfaces.tobago.util.ApplyRequestValuesCallback;
30 import org.apache.myfaces.tobago.util.ComponentUtils;
31 import org.apache.myfaces.tobago.util.FacesVersion;
32 import org.apache.myfaces.tobago.util.ProcessValidationsCallback;
33 import org.apache.myfaces.tobago.util.TobagoCallback;
34 import org.apache.myfaces.tobago.util.UpdateModelValuesCallback;
35 import org.apache.myfaces.tobago.util.VariableResolverUtils;
36 import org.slf4j.Logger;
37 import org.slf4j.LoggerFactory;
38
39 import javax.faces.FacesException;
40 import javax.faces.component.ContextCallback;
41 import javax.faces.component.UIComponent;
42 import javax.faces.context.FacesContext;
43 import javax.faces.event.AbortProcessingException;
44 import javax.faces.event.FacesEvent;
45 import javax.faces.event.PhaseId;
46 import java.io.IOException;
47 import java.util.ArrayList;
48 import java.util.ConcurrentModificationException;
49 import java.util.List;
50 import java.util.ListIterator;
51 import java.util.Locale;
52 import java.util.Map;
53
54 public class UIViewRoot extends javax.faces.component.UIViewRoot implements InvokeOnComponent {
55
56 private static final Logger LOG = LoggerFactory.getLogger(UIViewRoot.class);
57
58 private static final TobagoCallback APPLY_REQUEST_VALUES_CALLBACK = new ApplyRequestValuesCallback();
59 private static final ContextCallback PROCESS_VALIDATION_CALLBACK = new ProcessValidationsCallback();
60 private static final ContextCallback UPDATE_MODEL_VALUES_CALLBACK = new UpdateModelValuesCallback();
61
62 private List<FacesEvent> events;
63
64
65
66
67 @Override
68 public void setLocale(Locale locale) {
69 super.setLocale(locale);
70 ClientProperties clientProperties = VariableResolverUtils.resolveClientProperties(getFacesContext());
71 clientProperties.setLocale(locale);
72 clientProperties.updateUserAgent(getFacesContext());
73 }
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114 public void broadcastEventsForPhase(FacesContext context, PhaseId phaseId) {
115 broadcastForPhase(phaseId);
116 if (context.getRenderResponse() || context.getResponseComplete()) {
117 clearEvents();
118 }
119 }
120
121
122
123
124
125
126
127
128
129
130
131
132
133 public void queueEvent(FacesEvent event) {
134 if (event == null) {
135 throw new NullPointerException("event");
136 }
137 if (events == null) {
138 events = new ArrayList<FacesEvent>();
139 }
140 events.add(event);
141 }
142
143
144 private void broadcastForPhase(PhaseId phaseId) {
145 if (events == null) {
146 return;
147 }
148
149 boolean abort = false;
150
151 int phaseIdOrdinal = phaseId.getOrdinal();
152 for (ListIterator<FacesEvent> listiterator = events.listIterator(); listiterator.hasNext();) {
153 FacesEvent event = listiterator.next();
154 int ordinal = event.getPhaseId().getOrdinal();
155 if (ordinal == PhaseId.ANY_PHASE.getOrdinal() || ordinal == phaseIdOrdinal) {
156 UIComponent source = event.getComponent();
157 try {
158 source.broadcast(event);
159 } catch (FacesException e) {
160 Throwable fe = e;
161 while (fe != null) {
162 if (fe instanceof AbortProcessingException) {
163 if (LOG.isTraceEnabled()) {
164 LOG.trace("AbortProcessingException caught!");
165 }
166
167
168
169 abort = true;
170 break;
171 }
172 fe = fe.getCause();
173 }
174 if (!abort) {
175 throw e;
176 } else {
177 break;
178 }
179 } finally {
180
181 try {
182 listiterator.remove();
183 } catch (ConcurrentModificationException cme) {
184 int eventIndex = listiterator.previousIndex();
185 events.remove(eventIndex);
186
187 }
188 }
189 }
190 }
191
192 if (abort) {
193
194 clearEvents();
195 }
196 }
197
198
199 private void clearEvents() {
200 events = null;
201 }
202
203
204 @Override
205 public void processDecodes(FacesContext context) {
206 if (context == null) {
207 throw new NullPointerException("context");
208 }
209 Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.parseAndStoreComponents(context);
210 if (ajaxComponents != null) {
211
212 AbstractUIPage page = ComponentUtils.findPage(context);
213 page.decode(context);
214 page.markSubmittedForm(context);
215 FacesContextUtils.setAjax(context, true);
216
217
218
219 decodeActionComponent(context, page, ajaxComponents);
220
221
222 for (Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
223 FacesContextUtils.setAjaxComponentId(context, entry.getKey());
224 invokeOnComponent(context, entry.getKey(), APPLY_REQUEST_VALUES_CALLBACK);
225 }
226 } else {
227 super.processDecodes(context);
228 }
229 broadcastForPhase(PhaseId.APPLY_REQUEST_VALUES);
230 if (context.getRenderResponse() || context.getResponseComplete()) {
231 clearEvents();
232 }
233 }
234
235 private void decodeActionComponent(FacesContext facesContext, AbstractUIPage page, Map<String,
236 UIComponent> ajaxComponents) {
237 String actionId = page.getActionId();
238 UIComponent actionComponent = null;
239 if (actionId != null) {
240 actionComponent = findComponent(actionId);
241 if (actionComponent == null && FacesVersion.supports20() && FacesVersion.isMyfaces()) {
242 String bugActionId = actionId.replaceAll(":\\d+:", ":");
243 try {
244 actionComponent = findComponent(bugActionId);
245
246 } catch (Exception e) {
247
248 }
249 }
250 }
251 if (actionComponent == null) {
252 return;
253 }
254 for (UIComponent ajaxComponent : ajaxComponents.values()) {
255 UIComponent component = actionComponent;
256 while (component != null) {
257 if (component == ajaxComponent) {
258 return;
259 }
260 component = component.getParent();
261 }
262 }
263 invokeOnComponent(facesContext, actionId, APPLY_REQUEST_VALUES_CALLBACK);
264 }
265
266
267 @Override
268 public void processValidators(FacesContext context) {
269 if (context == null) {
270 throw new NullPointerException("context");
271 }
272
273 Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.getAjaxComponents(context);
274 if (ajaxComponents != null) {
275 for (Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
276 FacesContextUtils.setAjaxComponentId(context, entry.getKey());
277 invokeOnComponent(context, entry.getKey(), PROCESS_VALIDATION_CALLBACK);
278 }
279 } else {
280 super.processValidators(context);
281 }
282 broadcastForPhase(PhaseId.PROCESS_VALIDATIONS);
283 if (context.getRenderResponse() || context.getResponseComplete()) {
284 clearEvents();
285 }
286 }
287
288 @Override
289 public void processUpdates(FacesContext context) {
290 if (context == null) {
291 throw new NullPointerException("context");
292 }
293 Map<String, UIComponent> ajaxComponents = AjaxInternalUtils.getAjaxComponents(context);
294 if (ajaxComponents != null) {
295 for (Map.Entry<String, UIComponent> entry : ajaxComponents.entrySet()) {
296 invokeOnComponent(context, entry.getKey(), UPDATE_MODEL_VALUES_CALLBACK);
297 }
298 } else {
299 super.processUpdates(context);
300 }
301 broadcastForPhase(PhaseId.UPDATE_MODEL_VALUES);
302 if (context.getRenderResponse() || context.getResponseComplete()) {
303 clearEvents();
304 }
305 }
306
307 @Override
308 public void processApplication(FacesContext context) {
309 if (context == null) {
310 throw new NullPointerException("context");
311 }
312 broadcastForPhase(PhaseId.INVOKE_APPLICATION);
313 if (context.getRenderResponse() || context.getResponseComplete()) {
314 clearEvents();
315 }
316 }
317
318
319
320 @Override
321 public boolean getRendersChildren() {
322 if (AjaxUtils.isAjaxRequest(FacesContext.getCurrentInstance())) {
323 return true;
324 } else {
325 return super.getRendersChildren();
326 }
327 }
328
329 @Override
330 public void encodeChildren(FacesContext context) throws IOException {
331 if (AjaxUtils.isAjaxRequest(context)) {
332 new AjaxResponseRenderer().renderResponse(context);
333
334 } else {
335 super.encodeChildren(context);
336 }
337 }
338
339 @Override
340 public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback)
341 throws FacesException {
342 return ComponentUtils.invokeOnComponent(context, this, clientId, callback);
343 }
344 }