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