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