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.logging.Log;
021    import org.apache.commons.logging.LogFactory;
022    import org.apache.myfaces.tobago.ajax.api.AjaxComponent;
023    import org.apache.myfaces.tobago.ajax.api.AjaxPhaseListener;
024    import org.apache.myfaces.tobago.ajax.api.AjaxUtils;
025    import org.apache.myfaces.tobago.event.PageActionEvent;
026    import org.apache.myfaces.tobago.event.SheetStateChangeEvent;
027    import org.apache.myfaces.tobago.event.SheetStateChangeListener;
028    import org.apache.myfaces.tobago.event.SheetStateChangeSource;
029    import org.apache.myfaces.tobago.event.SortActionEvent;
030    import org.apache.myfaces.tobago.event.SortActionSource;
031    import org.apache.myfaces.tobago.model.SheetState;
032    import org.apache.myfaces.tobago.renderkit.LayoutInformationProvider;
033    import org.apache.myfaces.tobago.renderkit.LayoutableRendererBase;
034    import org.apache.myfaces.tobago.renderkit.SheetRendererWorkaround;
035    import org.apache.myfaces.tobago.util.LayoutInfo;
036    import org.apache.myfaces.tobago.util.LayoutUtil;
037    import org.apache.myfaces.tobago.util.StringUtils;
038    
039    import javax.faces.component.UIColumn;
040    import javax.faces.component.UIComponent;
041    import javax.faces.context.FacesContext;
042    import javax.faces.el.EvaluationException;
043    import javax.faces.el.MethodBinding;
044    import javax.faces.el.ValueBinding;
045    import javax.faces.event.AbortProcessingException;
046    import javax.faces.event.FacesEvent;
047    import javax.faces.event.PhaseId;
048    import javax.servlet.http.HttpServletResponse;
049    import java.io.IOException;
050    import java.util.ArrayList;
051    import java.util.Collections;
052    import java.util.List;
053    import java.util.Map;
054    
055    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_COLUMNS;
056    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_DIRECT_LINK_COUNT;
057    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_FIRST;
058    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_INNER_WIDTH;
059    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_LAYOUT_WIDTH;
060    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_ROWS;
061    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SELECTABLE;
062    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SELECTED_LIST_STRING;
063    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SHOW_DIRECT_LINKS;
064    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SHOW_HEADER;
065    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SHOW_PAGE_RANGE;
066    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_SHOW_ROW_RANGE;
067    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_STATE;
068    import static org.apache.myfaces.tobago.TobagoConstants.ATTR_WIDTH_LIST_STRING;
069    import static org.apache.myfaces.tobago.TobagoConstants.FACET_RELOAD;
070    import static org.apache.myfaces.tobago.TobagoConstants.RENDERER_TYPE_OUT;
071    
072    public class UIData extends javax.faces.component.UIData
073        implements SheetStateChangeSource, SortActionSource, AjaxComponent {
074    
075      private static final Log LOG = LogFactory.getLog(UIData.class);
076    
077      public static final String COMPONENT_TYPE = "org.apache.myfaces.tobago.Data";
078    
079      public static final String FACET_SORTER = "sorter";
080      public static final String SORTER_ID = "sorter";
081      public static final String ATTR_SCROLL_POSITION = "attrScrollPosition";
082    
083      public static final String NONE = "none";
084      public static final String SINGLE = "single";
085      public static final String SINGLE_OR_NONE = "singleOrNone";
086      public static final String MULTI = "multi";
087      public static final int DEFAULT_DIRECT_LINK_COUNT = 9;
088      public static final int DEFAULT_ROW_COUNT = 100;
089      private static final String DEFAULT_SELECTABLE = MULTI;
090    
091      private MethodBinding stateChangeListener;
092      private List<Integer> widthList;
093      private MethodBinding sortActionListener;
094      private SheetState sheetState;
095      private Boolean showHeader;
096      private String showRowRange;
097      private String showPageRange;
098      private String showDirectLinks;
099      private String columns;
100      private Integer directLinkCount;
101      private Integer rows;
102    
103      private String selectable;
104    
105      private transient LayoutTokens columnLayout;
106    
107      /**
108       * Remove the (by user) resized column widths. An application may provide a button to access it.
109       * Since 1.0.26.
110       */
111      public void resetColumnWidths() {
112        SheetState state = getSheetState(FacesContext.getCurrentInstance());
113        if (state != null) {
114          state.setColumnWidths(null);
115        }
116        getAttributes().remove(ATTR_WIDTH_LIST_STRING);
117      }
118    
119      public void encodeBegin(FacesContext facesContext) throws IOException {
120        UILayout.prepareDimension(facesContext, this);
121        SheetState state = getSheetState(facesContext);
122        if (state.getFirst() > -1 && state.getFirst() < getRowCount()) {
123          ValueBinding valueBinding = getValueBinding(ATTR_FIRST);
124          if (valueBinding != null) {
125            valueBinding.setValue(facesContext, state.getFirst());
126          } else {
127            setFirst(state.getFirst());
128          }
129        }
130        super.encodeBegin(facesContext);
131      }
132    
133      public void encodeEnd(FacesContext facesContext) throws IOException {
134        setupState(facesContext);
135        prepareDimensions(facesContext);
136        super.encodeEnd(facesContext);
137      }
138    
139      public void processDecodes(FacesContext context) {
140        final String ajaxId = (String) context.getExternalContext()
141            .getRequestParameterMap().get(AjaxPhaseListener.AJAX_COMPONENT_ID);
142        if (ajaxId !=null && ajaxId.equals(getClientId(context))) {
143          if (getFacet(FACET_RELOAD) != null && getFacet(FACET_RELOAD) instanceof UIReload
144              && getFacet(FACET_RELOAD).isRendered()
145              && ((UIReload) getFacet(FACET_RELOAD)).isImmediate()
146              && ajaxId.equals(ComponentUtil.findPage(context, this).getActionId())) {
147            UIReload reload = (UIReload) getFacet(FACET_RELOAD);
148            if (!reload.getUpdate()) {
149              if (context.getExternalContext().getResponse() instanceof HttpServletResponse) {
150                 ((HttpServletResponse) context.getExternalContext().getResponse())
151                     .setStatus(HttpServletResponse.SC_NOT_MODIFIED);
152              }
153              context.responseComplete();
154              return;
155            }
156          }
157        }
158        super.processDecodes(context);
159      }
160    
161      public String getShowRowRange() {
162        if (showRowRange != null) {
163          return showRowRange;
164        }
165        ValueBinding vb = getValueBinding(ATTR_SHOW_ROW_RANGE);
166        if (vb != null) {
167          return (String) vb.getValue(getFacesContext());
168        } else {
169          return NONE;
170        }
171      }
172    
173      public void setShowRowRange(String showRowRange) {
174        this.showRowRange = showRowRange;
175      }
176    
177      public String getShowPageRange() {
178        if (showPageRange != null) {
179          return showPageRange;
180        }
181        ValueBinding vb = getValueBinding(ATTR_SHOW_PAGE_RANGE);
182        if (vb != null) {
183          return (String) vb.getValue(getFacesContext());
184        } else {
185          return NONE;
186        }
187      }
188    
189      public void setShowPageRange(String showPageRange) {
190        this.showPageRange = showPageRange;
191      }
192    
193      public String getColumns() {
194        if (columns != null) {
195          return columns;
196        }
197        ValueBinding vb = getValueBinding(ATTR_COLUMNS);
198        if (vb != null) {
199          return (String) vb.getValue(getFacesContext());
200        } else {
201          return null;
202        }
203      }
204    
205      public void setColumns(String columns) {
206        this.columns = columns;
207      }
208    
209      public String getShowDirectLinks() {
210        if (showDirectLinks != null) {
211          return showDirectLinks;
212        }
213        ValueBinding vb = getValueBinding(ATTR_SHOW_DIRECT_LINKS);
214        if (vb != null) {
215          return (String) vb.getValue(getFacesContext());
216        } else {
217          return NONE;
218        }
219      }
220    
221      public void setShowDirectLinks(String showDirectLinks) {
222        this.showDirectLinks = showDirectLinks;
223      }
224    
225      public String getSelectable() {
226        if (selectable != null) {
227          return selectable;
228        }
229        ValueBinding vb = getValueBinding(ATTR_SELECTABLE);
230        if (vb != null) {
231          return (String) vb.getValue(getFacesContext());
232        } else {
233          return DEFAULT_SELECTABLE;
234        }
235      }
236    
237      public void setSelectable(String selectable) {
238        this.selectable = selectable;
239      }
240    
241      public Integer getDirectLinkCount() {
242        if (directLinkCount != null) {
243          return directLinkCount;
244        }
245        ValueBinding vb = getValueBinding(ATTR_DIRECT_LINK_COUNT);
246        if (vb != null) {
247          return (Integer) vb.getValue(getFacesContext());
248        } else {
249          return DEFAULT_DIRECT_LINK_COUNT;
250        }
251      }
252    
253      public void setDirectLinkCount(Integer directLinkCount) {
254        this.directLinkCount = directLinkCount;
255      }
256    
257      private void setupState(FacesContext facesContext) {
258        SheetState state = getSheetState(facesContext);
259        ensureColumnWidthList(facesContext, state);
260      }
261    
262      public void setState(SheetState state) {
263        this.sheetState = state;
264      }
265    
266      public SheetState getSheetState(FacesContext facesContext) {
267        if (sheetState != null) {
268          return sheetState;
269        } else {
270          ValueBinding stateBinding = getValueBinding(ATTR_STATE);
271          if (stateBinding != null) {
272            SheetState state = (SheetState) stateBinding.getValue(facesContext);
273            if (state == null) {
274              state = new SheetState();
275              stateBinding.setValue(facesContext, state);
276            }
277            return state;
278          } else {
279            sheetState = new SheetState();
280            return sheetState;
281          }
282        }
283      }
284    
285      public LayoutTokens getColumnLayout() {
286        if (columnLayout == null) {
287          String columns = getColumns();
288          if (columns != null) {
289            columnLayout = LayoutTokens.parse(columns);
290          }
291        }
292        return columnLayout;
293      }
294    
295      private void ensureColumnWidthList(FacesContext facesContext, SheetState state) {
296        List<Integer> currentWidthList = null;
297        List<UIColumn> rendererdColumns = getRenderedColumns();
298    
299        final Map attributes = getAttributes();
300        String widthListString = null;
301    
302        if (state != null) {
303          widthListString = state.getColumnWidths();
304        }
305        if (widthListString == null) {
306          widthListString = (String) attributes.get(ATTR_WIDTH_LIST_STRING);
307        }
308    
309        if (widthListString != null) {
310          currentWidthList = StringUtils.parseIntegerList(widthListString);
311        }
312        if (currentWidthList != null && currentWidthList.size() != rendererdColumns.size()) {
313          currentWidthList = null;
314        }
315    
316    
317        if (currentWidthList == null) {
318          LayoutTokens tokens = getColumnLayout();
319          List<UIColumn> allColumns = getAllColumns();
320          LayoutTokens newTokens = new LayoutTokens();
321          if (allColumns.size() > 0) {
322            for (int i = 0; i < allColumns.size(); i++) {
323              UIColumn column = allColumns.get(i);
324              if (column.isRendered()) {
325                if (tokens == null) {
326                  if (column instanceof org.apache.myfaces.tobago.component.UIColumn) {
327                    newTokens.addToken(
328                        LayoutTokens.parseToken(((org.apache.myfaces.tobago.component.UIColumn) column).getWidth()));
329                  } else {
330                    newTokens.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
331                  }
332                } else {
333                  if (i < tokens.getSize()) {
334                    newTokens.addToken(tokens.get(i));
335                  } else {
336                    newTokens.addToken(RelativeLayoutToken.DEFAULT_INSTANCE);
337                  }
338                }
339              }
340            }
341          }
342    
343    
344          int space = LayoutUtil.getInnerSpace(facesContext, this, true);
345          SheetRendererWorkaround renderer
346              = (SheetRendererWorkaround) ComponentUtil.getRenderer(facesContext, this);
347          space -= renderer.getContentBorder(facesContext, this);
348          if (renderer.needVerticalScrollbar(facesContext, this)) {
349            space -= renderer.getScrollbarWidth(facesContext, this);
350          }
351          LayoutInfo layoutInfo = new LayoutInfo(newTokens.getSize(), space, newTokens, getClientId(facesContext), false);
352          parseFixedWidth(facesContext, layoutInfo, rendererdColumns);
353          layoutInfo.parseColumnLayout(space);
354          currentWidthList = layoutInfo.getSpaceList();
355        }
356    
357        if (currentWidthList != null) {
358          if (rendererdColumns.size() != currentWidthList.size()) {
359            LOG.warn("widthList.size() = " + currentWidthList.size()
360                + " != columns.size() = " + rendererdColumns.size() + "  widthList : "
361                + LayoutInfo.listToTokenString(currentWidthList));
362          } else {
363            this.widthList = currentWidthList;
364          }
365        }
366      }
367    
368      private void parseFixedWidth(FacesContext facesContext, LayoutInfo layoutInfo, List<UIColumn> rendereredColumns) {
369        LayoutTokens tokens = layoutInfo.getLayoutTokens();
370        for (int i = 0; i < tokens.getSize(); i++) {
371          LayoutToken token = tokens.get(i);
372          if (token instanceof FixedLayoutToken) {
373            int width = 0;
374            if (!rendereredColumns.isEmpty()) {
375              if (i < rendereredColumns.size()) {
376                UIColumn column = rendereredColumns.get(i);
377                if (column instanceof UIColumnSelector) {
378                  LayoutInformationProvider renderer
379                      = ComponentUtil.getRenderer(facesContext, column);
380                  if (renderer == null) {
381                    LOG.warn("can't find renderer for " + column.getClass().getName());
382                    renderer = ComponentUtil.getRenderer(facesContext, UIPanel.COMPONENT_FAMILY, RENDERER_TYPE_OUT);
383                  }
384                  width = renderer.getFixedWidth(facesContext, column);
385    
386                } else {
387                  for (UIComponent component : (List<UIComponent>) column.getChildren()) {
388                    LayoutInformationProvider renderer
389                        = ComponentUtil.getRenderer(facesContext, component);
390                    width += renderer.getFixedWidth(facesContext, component);
391                  }
392                }
393                layoutInfo.update(width, i);
394              } else {
395                layoutInfo.update(0, i);
396                if (LOG.isWarnEnabled()) {
397                  LOG.warn("More LayoutTokens found than rows! skipping!");
398                }
399              }
400            }
401            if (LOG.isDebugEnabled()) {
402              LOG.debug("set column " + i + " from fixed to with " + width);
403            }
404          }
405        }
406      }
407    
408    
409      private void prepareDimensions(FacesContext facesContext) {
410        // prepare width's in column's children components
411    
412        List<Integer> columnWidths = getWidthList();
413        int i = 0;
414        for (UIColumn column : getRenderedColumns()) {
415          if (i < columnWidths.size()) {
416            Integer width = columnWidths.get(i);
417            if (!(column instanceof UIColumnSelector)) {
418              if (column.getChildCount() == 1) {
419                UIComponent child = (UIComponent) column.getChildren().get(0);
420                int cellPaddingWidth = ((LayoutableRendererBase) getRenderer(facesContext))
421                    .getConfiguredValue(facesContext, this, "cellPaddingWidth");
422                child.getAttributes().put(
423                    ATTR_LAYOUT_WIDTH, width - cellPaddingWidth);
424                child.getAttributes().remove(ATTR_INNER_WIDTH);
425              } else {
426                LOG.warn("More or less than 1 child in column! "
427                    + "Can't set width for column " + i + " to " + width);
428              }
429            }
430          } else {
431            LOG.warn("More columns than columnSizes! "
432                + "Can't set width for column " + i);
433          }
434          i++;
435        }
436      }
437    
438      public int getLast() {
439        int last = getFirst() + getRows();
440        return last < getRowCount() ? last : getRowCount();
441      }
442    
443      public int getPage() {
444        int first = getFirst() + 1;
445        int rows = getRows();
446        if (rows == 0) {
447          // avoid division by zero
448          return 0;
449        }
450        if ((first % rows) > 0) {
451          return (first / rows) + 1;
452        } else {
453          return (first / rows);
454        }
455      }
456    
457      public int getPages() {
458        int rows = getRows();
459        if (rows == 0) {
460          return 0;
461        }
462        return getRowCount() / rows + (getRowCount() % rows == 0 ? 0 : 1);
463      }
464    
465      public List<UIComponent> getRenderedChildrenOf(UIColumn column) {
466        List<UIComponent> children = new ArrayList<UIComponent>();
467        for (Object o : column.getChildren()) {
468          UIComponent kid = (UIComponent) o;
469          if (kid.isRendered()) {
470            children.add(kid);
471          }
472        }
473        return children;
474      }
475    
476      public boolean isAtBeginning() {
477        return getFirst() == 0;
478      }
479    
480      public boolean hasRowCount() {
481        return getRowCount() != -1;
482      }
483    
484      public boolean isAtEnd() {
485        if (!hasRowCount()) {
486          setRowIndex(getFirst() + getRows() + 1);
487          return !isRowAvailable();
488        } else {
489          return getFirst() >= getLastPageIndex();
490        }
491      }
492    
493      public int getLastPageIndex() {
494        int rows = getRows();
495        if (rows == 0) {
496          // avoid division by zero
497          return 0;
498        }
499        int rowCount = getRowCount();
500        int tail = rowCount % rows;
501        return rowCount - (tail != 0 ? tail : rows);
502      }
503    
504      public void processUpdates(FacesContext context) {
505        super.processUpdates(context);
506        updateSheetState(context);
507      }
508    
509      private void updateSheetState(FacesContext facesContext) {
510        SheetState state = getSheetState(facesContext);
511        if (state != null) {
512          // ensure sortActionListener
513    //      getSortActionListener();
514    //      state.setSortedColumn(sortActionListener != null ? sortActionListener.getColumn() : -1);
515    //      state.setAscending(sortActionListener != null && sortActionListener.isAscending());
516          Map attributes = getAttributes();
517          //noinspection unchecked
518          final List<Integer> list = (List<Integer>) attributes.get(ATTR_SELECTED_LIST_STRING);
519          state.setSelectedRows(list != null ? list : Collections.<Integer>emptyList());
520          state.setColumnWidths((String) attributes.get(ATTR_WIDTH_LIST_STRING));
521          state.setScrollPosition((Integer[]) attributes.get(ATTR_SCROLL_POSITION));
522          attributes.remove(ATTR_SELECTED_LIST_STRING);
523          attributes.remove(ATTR_SCROLL_POSITION);
524        }
525      }
526    
527    
528      public Object saveState(FacesContext context) {
529        Object[] saveState = new Object[12];
530        saveState[0] = super.saveState(context);
531        saveState[1] = sheetState;
532        saveState[2] = saveAttachedState(context, sortActionListener);
533        saveState[3] = saveAttachedState(context, stateChangeListener);
534        saveState[4] = showHeader;
535        saveState[5] = showRowRange;
536        saveState[6] = showPageRange;
537        saveState[7] = showDirectLinks;
538        saveState[8] = directLinkCount;
539        saveState[9] = selectable;
540        saveState[10] = columns;
541        saveState[11] = rows;
542        return saveState;
543      }
544    
545      public void restoreState(FacesContext context, Object savedState) {
546        Object[] values = (Object[]) savedState;
547        super.restoreState(context, values[0]);
548        sheetState = (SheetState) values[1];
549        sortActionListener = (MethodBinding) restoreAttachedState(context, values[2]);
550        stateChangeListener = (MethodBinding) restoreAttachedState(context, values[3]);
551        showHeader = (Boolean) values[4];
552        showRowRange = (String) values[5];
553        showPageRange = (String) values[6];
554        showDirectLinks = (String) values[7];
555        directLinkCount = (Integer) values[8];
556        selectable = (String) values[9];
557        columns = (String) values[10];
558        rows = (Integer) values[11];
559      }
560    
561    
562      public List<UIColumn> getAllColumns() {
563        List<UIColumn> columns = new ArrayList<UIColumn>();
564        for (UIComponent kid : (List<UIComponent>) getChildren()) {
565          if (kid instanceof UIColumn && !(kid instanceof UIColumnEvent)) {
566            columns.add((UIColumn) kid);
567          }
568        }
569        return columns;
570      }
571    
572      public List<UIColumn> getRenderedColumns() {
573        List<UIColumn> columns = new ArrayList<UIColumn>();
574        for (UIComponent kid : (List<UIComponent>) getChildren()) {
575          if (kid instanceof UIColumn && kid.isRendered() && !(kid instanceof UIColumnEvent)) {
576            columns.add((UIColumn) kid);
577          }
578        }
579        return columns;
580      }
581    
582      public MethodBinding getSortActionListener() {
583        if (sortActionListener != null) {
584          return sortActionListener;
585        } else {
586          return new Sorter();
587        }
588      }
589    
590      public void setSortActionListener(MethodBinding sortActionListener) {
591        this.sortActionListener = sortActionListener;
592      }
593    
594      public void queueEvent(FacesEvent facesEvent) {
595        UIComponent parent = getParent();
596        if (parent == null) {
597          throw new IllegalStateException(
598              "component is not a descendant of a UIViewRoot");
599        }
600    
601        if (facesEvent.getComponent() == this
602            && (facesEvent instanceof SheetStateChangeEvent
603            || facesEvent instanceof PageActionEvent)) {
604          facesEvent.setPhaseId(PhaseId.INVOKE_APPLICATION);
605          if (LOG.isInfoEnabled()) {
606            LOG.info("queueEvent = \"" + facesEvent + "\"");
607          }
608          parent.queueEvent(facesEvent);
609        } else {
610          UIComponent source = facesEvent.getComponent();
611          UIComponent sourceParent = source.getParent();
612          if (sourceParent.getParent() == this
613              && source.getId() != null && source.getId().endsWith(SORTER_ID)) {
614            facesEvent.setPhaseId(PhaseId.INVOKE_APPLICATION);
615            parent.queueEvent(new SortActionEvent(this, (UIColumn) sourceParent));
616          } else {
617            super.queueEvent(facesEvent);
618          }
619        }
620      }
621    
622      public void broadcast(FacesEvent facesEvent) throws AbortProcessingException {
623        super.broadcast(facesEvent);
624        if (facesEvent instanceof SheetStateChangeEvent) {
625          invokeMethodBinding(getStateChangeListener(), facesEvent);
626        } else if (facesEvent instanceof PageActionEvent) {
627          invokeMethodBinding(new Pager(), facesEvent);
628          invokeMethodBinding(getStateChangeListener(), new SheetStateChangeEvent(this));
629        } else if (facesEvent instanceof SortActionEvent) {
630          getSheetState(getFacesContext()).updateSortState((SortActionEvent) facesEvent);
631          invokeMethodBinding(getSortActionListener(), facesEvent);
632        }
633      }
634    
635      private void invokeMethodBinding(MethodBinding methodBinding, FacesEvent event) {
636        if (methodBinding != null && event != null) {
637          try {
638            Object[] objects = new Object[]{event};
639            methodBinding.invoke(getFacesContext(), objects);
640          } catch (EvaluationException e) {
641            Throwable cause = e.getCause();
642            if (cause instanceof AbortProcessingException) {
643              throw (AbortProcessingException) cause;
644            } else {
645              throw e;
646            }
647          }
648        }
649      }
650    
651      public void addStateChangeListener(SheetStateChangeListener listener) {
652        addFacesListener(listener);
653      }
654    
655      public SheetStateChangeListener[] getStateChangeListeners() {
656        return (SheetStateChangeListener[]) getFacesListeners(SheetStateChangeListener.class);
657      }
658    
659      public void removeStateChangeListener(SheetStateChangeListener listener) {
660        removeFacesListener(listener);
661      }
662    
663      public MethodBinding getStateChangeListener() {
664        return stateChangeListener;
665      }
666    
667      public void setStateChangeListener(MethodBinding stateChangeListener) {
668        this.stateChangeListener = stateChangeListener;
669      }
670    
671      public List<Integer> getWidthList() {
672        return widthList;
673      }
674    
675      public int getRows() {
676        if (rows != null) {
677          return rows;
678        }
679        ValueBinding vb = getValueBinding(ATTR_ROWS);
680        if (vb != null) {
681          return (Integer) vb.getValue(getFacesContext());
682        } else {
683          return DEFAULT_ROW_COUNT;
684        }
685      }
686    
687      public void setRows(int rows) {
688        this.rows = rows;
689      }
690    
691      public boolean isShowHeader() {
692        if (showHeader != null) {
693          return showHeader;
694        }
695        ValueBinding vb = getValueBinding(ATTR_SHOW_HEADER);
696        if (vb != null) {
697          return (!Boolean.FALSE.equals(vb.getValue(getFacesContext())));
698        } else {
699          return true;
700        }
701      }
702    
703      public void setShowHeader(boolean showHeader) {
704        this.showHeader = showHeader;
705      }
706    
707      public void encodeAjax(FacesContext facesContext) throws IOException {
708        setupState(facesContext);
709        prepareDimensions(facesContext);
710        // TODO neets more testing!!!
711        //if (!facesContext.getRenderResponse() && !ComponentUtil.hasErrorMessages(facesContext)) {
712        // in encodeBegin of superclass is some logic which clears the DataModel
713        // this must here also done.
714        // in RI and myfaces this could done via setValue(null)
715        ValueBinding binding = getValueBinding("value");
716        if (binding != null) {
717          setValue(null);
718        } else {
719          setValue(getValue());
720        }
721        //}
722        AjaxUtils.encodeAjaxComponent(facesContext, this);
723      }
724    
725      public void processAjax(FacesContext facesContext) throws IOException {
726        final String ajaxId = (String) facesContext.getExternalContext()
727            .getRequestParameterMap().get(AjaxPhaseListener.AJAX_COMPONENT_ID);
728        if (ajaxId.equals(getClientId(facesContext))) {
729          AjaxUtils.processActiveAjaxComponent(facesContext, this);
730        } else {
731          AjaxUtils.processAjaxOnChildren(facesContext, this);
732        }
733      }
734    
735      public Integer[] getScrollPosition() {
736        Integer[] scrollPosition = (Integer[]) getAttributes().get(ATTR_SCROLL_POSITION);
737        if (scrollPosition == null) {
738          scrollPosition = getSheetState(FacesContext.getCurrentInstance()).getScrollPosition();
739        }
740        return scrollPosition;
741      }
742    
743      public UIComponent findComponent(String searchId) {
744        return super.findComponent(stripRowIndex(searchId));
745      }
746    
747      String stripRowIndex(String searchId) {
748        if (searchId.length() > 0 && Character.isDigit(searchId.charAt(0))) {
749          for (int i = 1; i < searchId.length(); ++i) {
750            char c = searchId.charAt(i);
751            if (c == SEPARATOR_CHAR) {
752              searchId = searchId.substring(i + 1);
753              break;
754            }
755            if (!Character.isDigit(c)) {
756              break;
757            }
758          }
759        }
760        return searchId;
761      }
762    }