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