001 package org.apache.myfaces.tobago.component;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one or more
005 * contributor license agreements. See the NOTICE file distributed with
006 * this work for additional information regarding copyright ownership.
007 * The ASF licenses this file to You under the Apache License, Version 2.0
008 * (the "License"); you may not use this file except in compliance with
009 * the License. You may obtain a copy of the License at
010 *
011 * http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 import org.apache.commons.collections.KeyValue;
021 import org.apache.commons.collections.list.SetUniqueList;
022 import org.apache.commons.collections.set.ListOrderedSet;
023 import org.apache.commons.logging.Log;
024 import org.apache.commons.logging.LogFactory;
025 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_APPLICATION_ICON;
026 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_FOCUS_ID;
027 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_HEIGHT;
028 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STATE;
029 import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH;
030 import static org.apache.myfaces.tobago.TobagoConstants.SUBCOMPONENT_SEP;
031 import org.apache.myfaces.tobago.model.PageState;
032 import org.apache.myfaces.tobago.model.PageStateImpl;
033 import org.apache.myfaces.tobago.webapp.TobagoMultipartFormdataRequest;
034
035 import javax.faces.application.FacesMessage;
036 import javax.faces.component.UIComponent;
037 import javax.faces.context.FacesContext;
038 import javax.faces.el.ValueBinding;
039 import javax.servlet.ServletRequest;
040 import javax.servlet.http.HttpServletRequestWrapper;
041 import java.io.IOException;
042 import java.util.ArrayList;
043 import java.util.Iterator;
044 import java.util.List;
045 import java.util.Set;
046 import java.util.StringTokenizer;
047
048 public class UIPage extends UIForm {
049
050 private static final Log LOG = LogFactory.getLog(UIPage.class);
051
052 public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.Page";
053 public static final String ENCTYPE_KEY = UIPanel.class.getName() + ".enctype";
054
055 public static final String DEFAULT_STYLE = "style/style.css";
056
057 private static final int DEFAULT_WIDTH = 1024;
058
059 private static final int DEFAULT_HEIGHT = 768;
060
061 private String formId;
062
063 private String focusId;
064
065 private String actionId;
066
067 private String defaultActionId;
068
069 private List<KeyValue> postfields;
070
071 private SetUniqueList scriptFiles;
072
073 private Set<String> scriptBlocks;
074
075 private Set<String> styleFiles;
076
077 private Set<String> styleBlocks;
078
079 private Set<String> onloadScripts;
080
081 private Set<String> onunloadScripts;
082
083 private Set<String> onexitScripts;
084
085 private Set<String> onsubmitScripts;
086
087 private Set<UIPopup> popups;
088
089 private Integer width;
090
091 private Integer height;
092
093 private String applicationIcon;
094
095 @SuppressWarnings("unchecked")
096 public UIPage() {
097 scriptFiles = SetUniqueList.decorate(new ArrayList());
098 scriptBlocks = new ListOrderedSet();
099 styleFiles = new ListOrderedSet();
100 styleFiles.add(DEFAULT_STYLE);
101 styleBlocks = new ListOrderedSet();
102 onloadScripts = new ListOrderedSet();
103 onunloadScripts = new ListOrderedSet();
104 onexitScripts = new ListOrderedSet();
105 onsubmitScripts = new ListOrderedSet();
106 popups = new ListOrderedSet();
107 }
108
109 @Override
110 public void encodeBegin(FacesContext facesContext) throws IOException {
111 // TODO change this should be renamed to DimensionUtils.prepare!!!
112 UILayout.getLayout(this).layoutBegin(facesContext, this);
113 super.encodeBegin(facesContext);
114 }
115
116
117 @Override
118 public void encodeChildren(FacesContext context) throws IOException {
119 }
120
121 public String getFormId(FacesContext facesContext) {
122 if (formId == null) {
123 formId = getClientId(facesContext)
124 + SUBCOMPONENT_SEP + "form";
125 }
126 return formId;
127 }
128
129 @Override
130 public void processDecodes(FacesContext facesContext) {
131
132 checkTobagoRequest(facesContext);
133
134 decode(facesContext);
135
136 clearScriptsAndPopups();
137
138 markSubmittedForm(facesContext);
139
140 // invoke processDecodes() on children
141 for (Iterator kids = getFacetsAndChildren(); kids.hasNext();) {
142 UIComponent kid = (UIComponent) kids.next();
143 kid.processDecodes(facesContext);
144 }
145 }
146
147 public void markSubmittedForm(FacesContext facesContext) {
148 // find the form of the action command and set submitted to it and all
149 // children
150
151 // reset old submitted state
152 setSubmitted(false);
153
154 String currentActionId = getActionId();
155 if (LOG.isDebugEnabled()) {
156 LOG.debug("actionId = '" + currentActionId + "'");
157 }
158
159 UIComponent command = null;
160 try {
161 command = findComponent(currentActionId);
162 } catch (Exception e) {
163 // ignore
164 }
165
166 // TODO: remove this if block if prooven this never happens anymore
167 if (command == null
168 && currentActionId != null && currentActionId.matches(".*:\\d+:.*")) {
169 // If currentActionId component was inside a sheet the id contains the
170 // rowindex and is therefore not found here.
171 // We do not need the row here because we want just to find the
172 // related form, so removing the rowindex will help here.
173 currentActionId = currentActionId.replaceAll(":\\d+:", ":");
174 try {
175 command = findComponent(currentActionId);
176 LOG.info("command = \"" + command + "\"", new Exception());
177 } catch (Exception e) {
178 // ignore
179 }
180 }
181
182 if (LOG.isTraceEnabled()) {
183 LOG.trace(currentActionId);
184 LOG.trace(command);
185 LOG.trace(ComponentUtil.toString(facesContext.getViewRoot(), 0));
186 }
187
188 if (command != null) {
189 UIForm form = ComponentUtil.findForm(command);
190 form.setSubmitted(true);
191
192 if (LOG.isTraceEnabled()) {
193 LOG.trace(form);
194 LOG.trace(form.getClientId(facesContext));
195 }
196 } else {
197 if (LOG.isDebugEnabled()) {
198 LOG.debug("Illegal actionId! Rerender the view.");
199 }
200 facesContext.renderResponse();
201 }
202 }
203
204 private void clearScriptsAndPopups() {
205 // clear script Set's
206 getOnloadScripts().clear();
207 getOnunloadScripts().clear();
208 getOnexitScripts().clear();
209 getScriptBlocks().clear();
210 getPopups().clear();
211 }
212
213 private void checkTobagoRequest(FacesContext facesContext) {
214 // multipart/form-data must use TobagoMultipartFormdataRequest
215 String contentType = (String) facesContext.getExternalContext()
216 .getRequestHeaderMap().get("content-type");
217 if (contentType != null && contentType.startsWith("multipart/form-data")) {
218 Object request = facesContext.getExternalContext().getRequest();
219 boolean okay = false;
220 if (request instanceof TobagoMultipartFormdataRequest) {
221 okay = true;
222 } else if (request instanceof HttpServletRequestWrapper) {
223 ServletRequest wrappedRequest
224 = ((HttpServletRequestWrapper) request).getRequest();
225 if (wrappedRequest instanceof TobagoMultipartFormdataRequest) {
226 okay = true;
227 }
228 }
229 // TODO PortletRequest ??
230 if (!okay) {
231 LOG.error("Can't process multipart/form-data without TobagoRequest. "
232 + "Please check the web.xml and define a TobagoMultipartFormdataFilter. "
233 + "See documentation for <tc:file>");
234 facesContext.addMessage(null, new FacesMessage("An error has occured!"));
235 }
236 }
237 }
238
239 public List<KeyValue> getPostfields() {
240 if (postfields == null) {
241 postfields = new ArrayList<KeyValue>();
242 }
243 return postfields;
244 }
245
246 @Override
247 public void processUpdates(FacesContext context) {
248 super.processUpdates(context);
249 updatePageState(context);
250 }
251
252 public void updatePageState(FacesContext facesContext) {
253 PageState state = getPageState(facesContext);
254 decodePageState(facesContext, state);
255 }
256
257 @SuppressWarnings("unchecked")
258 private void decodePageState(FacesContext facesContext, PageState pageState) {
259 String name;
260 String value = null;
261 try {
262 name = getClientId(facesContext)
263 + SUBCOMPONENT_SEP + "form-clientDimension";
264 value = (String) facesContext.getExternalContext()
265 .getRequestParameterMap().get(name);
266 if (value != null) {
267 StringTokenizer tokenizer = new StringTokenizer(value, ";");
268 int width = Integer.parseInt(tokenizer.nextToken());
269 int height = Integer.parseInt(tokenizer.nextToken());
270 if (pageState != null) {
271 pageState.setClientWidth(width);
272 pageState.setClientHeight(height);
273 }
274 facesContext.getExternalContext().getRequestMap().put("tobago-page-clientDimension-width", width);
275 facesContext.getExternalContext().getRequestMap().put("tobago-page-clientDimension-height", height);
276 }
277 } catch (Exception e) {
278 LOG.error("Error in decoding state: value='" + value + "'", e);
279 }
280 }
281
282 public PageState getPageState(FacesContext facesContext) {
283 ValueBinding stateBinding = getValueBinding(ATTR_STATE);
284 if (stateBinding != null) {
285 PageState state = (PageState) stateBinding.getValue(facesContext);
286 if (state == null) {
287 state = new PageStateImpl();
288 stateBinding.setValue(facesContext, state);
289 }
290 return state;
291 } else {
292 return null;
293 }
294 }
295
296 // ///////////////////////////////////////////// bean getter + setter
297
298 public String getFocusId() {
299 if (focusId != null) {
300 return focusId;
301 }
302 ValueBinding vb = getValueBinding(ATTR_FOCUS_ID);
303 if (vb != null) {
304 return (String) vb.getValue(getFacesContext());
305 } else {
306 return null;
307 }
308 }
309
310 public void setFocusId(String focusId) {
311 this.focusId = focusId;
312 }
313
314 public String getActionId() {
315 return actionId;
316 }
317
318 public void setActionId(String actionId) {
319 this.actionId = actionId;
320 }
321
322 public String getDefaultActionId() {
323 return defaultActionId;
324 }
325
326 public void setDefaultActionId(String defaultActionId) {
327 this.defaultActionId = defaultActionId;
328 }
329
330 @SuppressWarnings("unchecked")
331 public List<String> getScriptFiles() {
332 return scriptFiles;
333 }
334
335 public Set<String> getScriptBlocks() {
336 return scriptBlocks;
337 }
338
339 public Set<String> getStyleFiles() {
340 return styleFiles;
341 }
342
343 public Set<String> getStyleBlocks() {
344 return styleBlocks;
345 }
346
347 public Set<String> getOnloadScripts() {
348 return onloadScripts;
349 }
350
351 public Set<String> getOnunloadScripts() {
352 return onunloadScripts;
353 }
354
355 public Set<String> getOnexitScripts() {
356 return onexitScripts;
357 }
358
359 public Set<String> getOnsubmitScripts() {
360 return onsubmitScripts;
361 }
362
363 public Set<UIPopup> getPopups() {
364 return popups;
365 }
366
367 public Integer getWidth() {
368 if (width != null) {
369 return width;
370 }
371 ValueBinding vb = getValueBinding(ATTR_WIDTH);
372 if (vb != null) {
373 return (Integer) vb.getValue(getFacesContext());
374 } else {
375 Integer requestWidth =
376 (Integer) FacesContext.getCurrentInstance().getExternalContext().
377 getRequestMap().get("tobago-page-clientDimension-width");
378 if (requestWidth != null) {
379 return requestWidth;
380 } else {
381 return DEFAULT_WIDTH;
382 }
383 }
384 }
385
386 public void setWidth(Integer width) {
387 this.width = width;
388 }
389
390 public Integer getHeight() {
391 if (height != null) {
392 return height;
393 }
394 ValueBinding vb = getValueBinding(ATTR_HEIGHT);
395 if (vb != null) {
396 return (Integer) vb.getValue(getFacesContext());
397 } else {
398 Integer requestHeight =
399 (Integer) FacesContext.getCurrentInstance().getExternalContext().
400 getRequestMap().get("tobago-page-clientDimension-height");
401 if (requestHeight != null) {
402 return requestHeight;
403 } else {
404 return DEFAULT_HEIGHT;
405 }
406 }
407 }
408
409 public void setHeight(Integer height) {
410 this.height = height;
411 }
412
413 public String getApplicationIcon() {
414 if (applicationIcon != null) {
415 return applicationIcon;
416 }
417 ValueBinding vb = getValueBinding(ATTR_APPLICATION_ICON);
418 if (vb != null) {
419 return (String) vb.getValue(getFacesContext());
420 } else {
421 return null;
422 }
423 }
424
425 public void setApplicationIcon(String applicationIcon) {
426 this.applicationIcon = applicationIcon;
427 }
428
429 public void restoreState(FacesContext context, Object state) {
430 Object[] values = (Object[]) state;
431 super.restoreState(context, values[0]);
432 this.width = (Integer) values[1];
433 this.height = (Integer) values[2];
434 this.focusId = (String) values[3];
435 this.applicationIcon = (String) values[4];
436 }
437
438 public Object saveState(FacesContext context) {
439 Object[] values = new Object[5];
440 values[0] = super.saveState(context);
441 values[1] = width;
442 values[2] = height;
443 values[3] = focusId;
444 values[4] = applicationIcon;
445 return values;
446 }
447 }