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