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