/* * Copyright 2009-2014 PrimeTek. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.primefaces.component.api; import java.sql.ResultSet; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; import javax.el.ELContext; import javax.el.ValueExpression; import javax.faces.FacesException; import javax.faces.application.Application; import javax.faces.component.*; import javax.faces.component.visit.VisitCallback; import javax.faces.component.visit.VisitContext; import javax.faces.component.visit.VisitHint; import javax.faces.component.visit.VisitResult; import javax.faces.context.FacesContext; import javax.faces.event.PhaseId; import javax.faces.event.PostValidateEvent; import javax.faces.event.PreValidateEvent; import javax.faces.model.ArrayDataModel; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import javax.faces.model.ResultSetDataModel; import javax.faces.model.ScalarDataModel; import javax.faces.render.Renderer; import org.primefaces.component.column.Column; import org.primefaces.component.columngroup.ColumnGroup; import org.primefaces.component.columns.Columns; import org.primefaces.model.CollectionDataModel; import org.primefaces.util.ComponentUtils; public class UIData extends javax.faces.component.UIData { public static final String PAGINATOR_TOP_CONTAINER_CLASS = "ui-paginator ui-paginator-top ui-widget-header"; public static final String PAGINATOR_BOTTOM_CONTAINER_CLASS = "ui-paginator ui-paginator-bottom ui-widget-header"; public static final String PAGINATOR_PAGES_CLASS = "ui-paginator-pages"; public static final String PAGINATOR_PAGE_CLASS = "ui-paginator-page ui-state-default ui-corner-all"; public static final String PAGINATOR_ACTIVE_PAGE_CLASS = "ui-paginator-page ui-state-default ui-state-active ui-corner-all"; public static final String PAGINATOR_CURRENT_CLASS = "ui-paginator-current"; public static final String PAGINATOR_RPP_OPTIONS_CLASS = "ui-paginator-rpp-options ui-widget ui-state-default ui-corner-left"; public static final String PAGINATOR_RPP_LABEL_CLASS = "ui-paginator-rpp-label"; public static final String PAGINATOR_JTP_CLASS = "ui-paginator-jtp-select ui-widget ui-state-default ui-corner-left"; public static final String PAGINATOR_FIRST_PAGE_LINK_CLASS = "ui-paginator-first ui-state-default ui-corner-all"; public static final String PAGINATOR_FIRST_PAGE_ICON_CLASS = "ui-icon ui-icon-seek-first"; public static final String PAGINATOR_PREV_PAGE_LINK_CLASS = "ui-paginator-prev ui-state-default ui-corner-all"; public static final String PAGINATOR_PREV_PAGE_ICON_CLASS = "ui-icon ui-icon-seek-prev"; public static final String PAGINATOR_NEXT_PAGE_LINK_CLASS = "ui-paginator-next ui-state-default ui-corner-all"; public static final String PAGINATOR_NEXT_PAGE_ICON_CLASS = "ui-icon ui-icon-seek-next"; public static final String PAGINATOR_LAST_PAGE_LINK_CLASS = "ui-paginator-last ui-state-default ui-corner-all"; public static final String PAGINATOR_LAST_PAGE_ICON_CLASS = "ui-icon ui-icon-seek-end"; private String clientId = null; private StringBuilder idBuilder = new StringBuilder(); private DataModel model = null; private Object oldVar = null; protected enum PropertyKeys { paginator ,paginatorTemplate ,rowsPerPageTemplate ,rowsPerPageLabel ,currentPageReportTemplate ,pageLinks ,paginatorPosition ,paginatorAlwaysVisible ,rowIndex ,rowIndexVar ,saved ,lazy; String toString; PropertyKeys(String toString) { this.toString = toString; } PropertyKeys() {} public String toString() { return ((this.toString != null) ? this.toString : super.toString()); } } public boolean isPaginator() { return (java.lang.Boolean) getStateHelper().eval(PropertyKeys.paginator, false); } public void setPaginator(boolean _paginator) { getStateHelper().put(PropertyKeys.paginator, _paginator); } public java.lang.String getPaginatorTemplate() { return (java.lang.String) getStateHelper().eval(PropertyKeys.paginatorTemplate, "{FirstPageLink} {PreviousPageLink} {PageLinks} {NextPageLink} {LastPageLink} {RowsPerPageDropdown}"); } public void setPaginatorTemplate(java.lang.String _paginatorTemplate) { getStateHelper().put(PropertyKeys.paginatorTemplate, _paginatorTemplate); } public java.lang.String getRowsPerPageTemplate() { return (java.lang.String) getStateHelper().eval(PropertyKeys.rowsPerPageTemplate, null); } public void setRowsPerPageTemplate(java.lang.String _rowsPerPageTemplate) { getStateHelper().put(PropertyKeys.rowsPerPageTemplate, _rowsPerPageTemplate); } public java.lang.String getRowsPerPageLabel() { return (java.lang.String) getStateHelper().eval(PropertyKeys.rowsPerPageLabel, null); } public void setRowsPerPageLabel(java.lang.String _rowsPerPageLabel) { getStateHelper().put(PropertyKeys.rowsPerPageLabel, _rowsPerPageLabel); } public java.lang.String getCurrentPageReportTemplate() { return (java.lang.String) getStateHelper().eval(PropertyKeys.currentPageReportTemplate, "({currentPage} of {totalPages})"); } public void setCurrentPageReportTemplate(java.lang.String _currentPageReportTemplate) { getStateHelper().put(PropertyKeys.currentPageReportTemplate, _currentPageReportTemplate); } public int getPageLinks() { return (java.lang.Integer) getStateHelper().eval(PropertyKeys.pageLinks, 10); } public void setPageLinks(int _pageLinks) { getStateHelper().put(PropertyKeys.pageLinks, _pageLinks); } public java.lang.String getPaginatorPosition() { return (java.lang.String) getStateHelper().eval(PropertyKeys.paginatorPosition, "both"); } public void setPaginatorPosition(java.lang.String _paginatorPosition) { getStateHelper().put(PropertyKeys.paginatorPosition, _paginatorPosition); } public boolean isPaginatorAlwaysVisible() { return (java.lang.Boolean) getStateHelper().eval(PropertyKeys.paginatorAlwaysVisible, true); } public void setPaginatorAlwaysVisible(boolean _paginatorAlwaysVisible) { getStateHelper().put(PropertyKeys.paginatorAlwaysVisible, _paginatorAlwaysVisible); } public boolean isLazy() { return (java.lang.Boolean) getStateHelper().eval(PropertyKeys.lazy, false); } public void setLazy(boolean _lazy) { getStateHelper().put(PropertyKeys.lazy, _lazy); } public java.lang.String getRowIndexVar() { return (java.lang.String) getStateHelper().eval(PropertyKeys.rowIndexVar, null); } public void setRowIndexVar(java.lang.String _rowIndexVar) { getStateHelper().put(PropertyKeys.rowIndexVar, _rowIndexVar); } public void calculateFirst() { int rows = this.getRows(); if(rows > 0) { int first = this.getFirst(); int rowCount = this.getRowCount(); if(rowCount > 0 && first >= rowCount) { int numberOfPages = (int) Math.ceil(rowCount * 1d / rows); this.setFirst(Math.max((numberOfPages-1) * rows, 0)); } } } public int getPage() { if(this.getRowCount() > 0) { int rows = this.getRowsToRender(); if(rows > 0) { int first = this.getFirst(); return (int) (first / rows); } else { return 0; } } else { return 0; } } public int getPageCount() { return (int) Math.ceil(this.getRowCount() * 1d / this.getRowsToRender()); } public int getRowsToRender() { int rows = this.getRows(); return rows == 0 ? this.getRowCount() : rows; } public boolean isPaginationRequest(FacesContext context) { return context.getExternalContext().getRequestParameterMap().containsKey(getClientId(context) + "_pagination"); } public void updatePaginationData(FacesContext context, UIData data) { data.setRowIndex(-1); String componentClientId = data.getClientId(context); Map<String,String> params = context.getExternalContext().getRequestParameterMap(); ELContext elContext = context.getELContext(); String firstParam = params.get(componentClientId + "_first"); String rowsParam = params.get(componentClientId + "_rows"); data.setFirst(Integer.valueOf(firstParam)); data.setRows(Integer.valueOf(rowsParam)); ValueExpression firstVe = data.getValueExpression("first"); ValueExpression rowsVe = data.getValueExpression("rows"); if(firstVe != null && !firstVe.isReadOnly(elContext)) firstVe.setValue(context.getELContext(), data.getFirst()); if(rowsVe != null && !rowsVe.isReadOnly(elContext)) rowsVe.setValue(context.getELContext(), data.getRows()); } @Override public void processDecodes(FacesContext context) { if(!isRendered()) { return; } pushComponentToEL(context, this); processPhase(context, PhaseId.APPLY_REQUEST_VALUES); decode(context); popComponentFromEL(context); } @Override public void processValidators(FacesContext context) { if(!isRendered()) { return; } pushComponentToEL(context, this); Application app = context.getApplication(); app.publishEvent(context, PreValidateEvent.class, this); processPhase(context, PhaseId.PROCESS_VALIDATIONS); app.publishEvent(context, PostValidateEvent.class, this); popComponentFromEL(context); } @Override public void processUpdates(FacesContext context) { if(!isRendered()) { return; } pushComponentToEL(context, this); processPhase(context, PhaseId.UPDATE_MODEL_VALUES); popComponentFromEL(context); } protected void processPhase(FacesContext context, PhaseId phaseId) { if(shouldSkipChildren(context)) { return; } setRowIndex(-1); processFacets(context, phaseId); if(requiresColumns()) { processColumnFacets(context, phaseId); } processChildren(context, phaseId); setRowIndex(-1); } protected void processFacets(FacesContext context, PhaseId phaseId) { if(this.getFacetCount() > 0) { for(UIComponent facet : getFacets().values()) { process(context, facet, phaseId); } } } protected void processColumnFacets(FacesContext context, PhaseId phaseId) { for(UIComponent child : this.getChildren()) { if(child.isRendered() && (child.getFacetCount() > 0)) { for(UIComponent facet : child.getFacets().values()) { process(context, facet, phaseId); } } } } protected void processChildren(FacesContext context, PhaseId phaseId) { int first = getFirst(); int rows = getRows(); int last = rows == 0 ? getRowCount() : (first + rows); for(int rowIndex = first; rowIndex < last; rowIndex++) { setRowIndex(rowIndex); if(!isRowAvailable()) { break; } for(UIComponent child : this.getIterableChildren()) { if(child.isRendered()) { if(child instanceof Column) { for(UIComponent grandkid : child.getChildren()) { process(context, grandkid, phaseId); } } else { process(context, child, phaseId); } } } } } protected void process(FacesContext context, UIComponent component, PhaseId phaseId) { if(phaseId == PhaseId.APPLY_REQUEST_VALUES) { component.processDecodes(context); } else if(phaseId == PhaseId.PROCESS_VALIDATIONS) { component.processValidators(context); } else if(phaseId == PhaseId.UPDATE_MODEL_VALUES) { component.processUpdates(context); } } @Override public String getClientId(FacesContext context) { if(this.clientId != null) { return this.clientId; } String id = getId(); if(id == null) { UniqueIdVendor parentUniqueIdVendor = ComponentUtils.findParentUniqueIdVendor(this); if(parentUniqueIdVendor == null) { UIViewRoot viewRoot = context.getViewRoot(); if(viewRoot != null) { id = viewRoot.createUniqueId(); } else { throw new FacesException("Cannot create clientId for " + this.getClass().getCanonicalName()); } } else { id = parentUniqueIdVendor.createUniqueId(context, null); } this.setId(id); } UIComponent namingContainer = ComponentUtils.findParentNamingContainer(this); if(namingContainer != null) { String containerClientId = namingContainer.getContainerClientId(context); if(containerClientId != null) { this.clientId = this.idBuilder.append(containerClientId).append(UINamingContainer.getSeparatorChar(context)).append(id).toString(); this.idBuilder.setLength(0); } else { this.clientId = id; } } else { this.clientId = id; } Renderer renderer = getRenderer(context); if(renderer != null) { this.clientId = renderer.convertClientId(context, this.clientId); } return this.clientId; } @Override public String getContainerClientId(FacesContext context) { //clientId is without rowIndex String componentClientId = this.getClientId(context); int rowIndex = getRowIndex(); if(rowIndex == -1) { return componentClientId; } String containerClientId = idBuilder.append(componentClientId).append(UINamingContainer.getSeparatorChar(context)).append(rowIndex).toString(); idBuilder.setLength(0); return containerClientId; } @Override public void setId(String id) { super.setId(id); //clear this.clientId = null; } @Override public void setRowIndex(int rowIndex) { saveDescendantState(); setRowModel(rowIndex); restoreDescendantState(); } public void setRowModel(int rowIndex) { //update rowIndex getStateHelper().put(PropertyKeys.rowIndex, rowIndex); getDataModel().setRowIndex(rowIndex); //clear datamodel if(rowIndex == -1) { setDataModel(null); } //update var String var = (String) this.getVar(); if(var != null) { String rowIndexVar = this.getRowIndexVar(); Map<String, Object> requestMap = getFacesContext().getExternalContext().getRequestMap(); if(rowIndex == -1) { oldVar = requestMap.remove(var); if(rowIndexVar != null) requestMap.remove(rowIndexVar); } else if(isRowAvailable()) { requestMap.put(var, getRowData()); if(rowIndexVar != null) requestMap.put(rowIndexVar, rowIndex); } else { requestMap.remove(var); if(rowIndexVar != null) requestMap.put(rowIndexVar, rowIndex); if(oldVar != null) { requestMap.put(var, oldVar); oldVar = null; } } } } @Override public int getRowIndex() { return (Integer) getStateHelper().eval(PropertyKeys.rowIndex, -1); } protected void saveDescendantState() { FacesContext context = getFacesContext(); if(this.getChildCount() > 0) { for(UIComponent kid : getChildren()) { saveDescendantState(kid, context); } } if(this.getFacetCount() > 0) { for(UIComponent facet : this.getFacets().values()) { saveDescendantState(facet, context); } } } protected void saveDescendantState(UIComponent component, FacesContext context) { Map<String, SavedState> saved = (Map<String, SavedState>) getStateHelper().get(PropertyKeys.saved); if(component instanceof EditableValueHolder) { EditableValueHolder input = (EditableValueHolder) component; SavedState state = null; String componentClientId = component.getClientId(context); if(saved == null) { state = new SavedState(); getStateHelper().put(PropertyKeys.saved, componentClientId, state); } if(state == null) { state = saved.get(componentClientId); if(state == null) { state = new SavedState(); getStateHelper().put(PropertyKeys.saved, componentClientId, state); } } state.setValue(input.getLocalValue()); state.setValid(input.isValid()); state.setSubmittedValue(input.getSubmittedValue()); state.setLocalValueSet(input.isLocalValueSet()); } else if (component instanceof UIForm) { UIForm form = (UIForm) component; String componentClientId = component.getClientId(context); SavedState state = null; if (saved == null) { state = new SavedState(); getStateHelper().put(PropertyKeys.saved, componentClientId, state); } if (state == null) { state = saved.get(componentClientId); if (state == null) { state = new SavedState(); //saved.put(clientId, state); getStateHelper().put(PropertyKeys.saved, componentClientId, state); } } state.setSubmitted(form.isSubmitted()); } //save state for children if(component.getChildCount() > 0) { for(UIComponent kid : component.getChildren()) { saveDescendantState(kid, context); } } //save state for facets if(component.getFacetCount() > 0) { for(UIComponent facet : component.getFacets().values()) { saveDescendantState(facet, context); } } } protected void restoreDescendantState() { FacesContext context = getFacesContext(); if(getChildCount() > 0) { for(UIComponent kid : getChildren()) { restoreDescendantState(kid, context); } } if(this.getFacetCount() > 0) { for(UIComponent facet : this.getFacets().values()) { restoreDescendantState(facet, context); } } } protected void restoreDescendantState(UIComponent component, FacesContext context) { String id = component.getId(); component.setId(id); //reset the client id Map<String, SavedState> saved = (Map<String,SavedState>) getStateHelper().get(PropertyKeys.saved); if(component instanceof EditableValueHolder) { EditableValueHolder input = (EditableValueHolder) component; String componentClientId = component.getClientId(context); SavedState state = saved.get(componentClientId); if(state == null) { state = new SavedState(); } input.setValue(state.getValue()); input.setValid(state.isValid()); input.setSubmittedValue(state.getSubmittedValue()); input.setLocalValueSet(state.isLocalValueSet()); } else if (component instanceof UIForm) { UIForm form = (UIForm) component; String componentClientId = component.getClientId(context); SavedState state = saved.get(componentClientId); if(state == null) { state = new SavedState(); } form.setSubmitted(state.getSubmitted()); state.setSubmitted(form.isSubmitted()); } //restore state of children if(component.getChildCount() > 0) { for(UIComponent kid : component.getChildren()) { restoreDescendantState(kid, context); } } //restore state of facets if(component.getFacetCount() > 0) { for(UIComponent facet : component.getFacets().values()) { restoreDescendantState(facet, context); } } } @Override protected DataModel getDataModel() { if(this.model != null) { return (model); } Object current = getValue(); if(current == null) { setDataModel(new ListDataModel(Collections.EMPTY_LIST)); } else if (current instanceof DataModel) { setDataModel((DataModel) current); } else if (current instanceof List) { setDataModel(new ListDataModel((List) current)); } else if (Object[].class.isAssignableFrom(current.getClass())) { setDataModel(new ArrayDataModel((Object[]) current)); } else if (current instanceof ResultSet) { setDataModel(new ResultSetDataModel((ResultSet) current)); } else if (current instanceof Collection) { setDataModel(new CollectionDataModel((Collection) current)); } else { setDataModel(new ScalarDataModel(current)); } return model; } @Override protected void setDataModel(DataModel dataModel) { this.model = dataModel; } protected boolean shouldSkipChildren(FacesContext context) { return false; } protected boolean shouldVisitChildren(VisitContext context, boolean visitRows) { if(visitRows) { setRowIndex(-1); } Collection<String> idsToVisit = context.getSubtreeIdsToVisit(this); return (!idsToVisit.isEmpty()); } @Override public boolean visitTree(VisitContext context, VisitCallback callback) { if(!isVisitable(context)) { return false; } FacesContext facesContext = context.getFacesContext(); boolean visitRows = shouldVisitRows(facesContext, context); int rowIndex = -1; if(visitRows) { rowIndex = getRowIndex(); setRowIndex(-1); } pushComponentToEL(facesContext, null); try { VisitResult result = context.invokeVisitCallback(this, callback); if(result == VisitResult.COMPLETE) { return true; } if((result == VisitResult.ACCEPT) && shouldVisitChildren(context, visitRows)) { if(visitFacets(context, callback, visitRows)) { return true; } if(requiresColumns() && visitColumnsAndColumnFacets(context, callback, visitRows)) { return true; } if(visitRows(context, callback, visitRows)) { return true; } } } finally { popComponentFromEL(facesContext); if(visitRows) { setRowIndex(rowIndex); } } return false; } protected boolean visitFacets(VisitContext context, VisitCallback callback, boolean visitRows) { if(visitRows) { setRowIndex(-1); } if(getFacetCount() > 0) { for(UIComponent facet : getFacets().values()) { if(facet.visitTree(context, callback)) { return true; } } } return false; } protected boolean visitColumnsAndColumnFacets(VisitContext context, VisitCallback callback, boolean visitRows) { if(visitRows) { setRowIndex(-1); } if(getChildCount() > 0) { for(UIComponent child : getChildren()) { VisitResult result = context.invokeVisitCallback(child, callback); // visit the column directly if (result == VisitResult.COMPLETE) { return true; } if(child instanceof UIColumn) { if(child.getFacetCount() > 0) { if(child instanceof Columns) { Columns columns = (Columns) child; for(int i = 0; i < columns.getRowCount(); i++) { columns.setRowIndex(i); boolean value = visitColumnFacets(context, callback, child); if(value) { return true; } } columns.setRowIndex(-1); } else { boolean value = visitColumnFacets(context, callback, child); if(value) { return true; } } } } else if(child instanceof ColumnGroup) { visitColumnGroup(context, callback, (ColumnGroup) child); } } } return false; } protected boolean visitColumnGroup(VisitContext context, VisitCallback callback, ColumnGroup group) { if(group.getChildCount() > 0) { for(UIComponent row : group.getChildren()) { if(row.getChildCount() > 0) { for(UIComponent col : row.getChildren()) { if(col instanceof Column && col.getFacetCount() > 0) { boolean value = visitColumnFacets(context, callback, (Column) col); if(value) { return true; } } } } } } return false; } protected boolean visitColumnFacets(VisitContext context, VisitCallback callback, UIComponent component) { for(UIComponent columnFacet : component.getFacets().values()) { if(columnFacet.visitTree(context, callback)) { return true; } } return false; } protected boolean visitRows(VisitContext context, VisitCallback callback, boolean visitRows) { boolean requiresColumns = this.requiresColumns(); int processed = 0; int rowIndex = 0; int rows = 0; if (visitRows) { rowIndex = getFirst() - 1; rows = getRows(); } while(true) { if(visitRows) { if((rows > 0) && (++processed > rows)) { break; } setRowIndex(++rowIndex); if(!isRowAvailable()) { break; } } if(getChildCount() > 0) { for(UIComponent kid : getChildren()) { if(requiresColumns) { if(kid instanceof Columns) { Columns uicolumns = (Columns) kid; for(int i = 0; i < uicolumns.getRowCount(); i++) { uicolumns.setRowIndex(i); boolean value = visitColumnContent(context, callback, uicolumns); if(value) { uicolumns.setRowIndex(-1); return true; } } uicolumns.setRowIndex(-1); } else { boolean value = visitColumnContent(context, callback, kid); if(value) { return true; } } } else { if(kid.visitTree(context, callback)) { return true; } } } } if(!visitRows) { break; } } return false; } protected boolean visitColumnContent(VisitContext context, VisitCallback callback, UIComponent component) { if(component.getChildCount() > 0) { for(UIComponent grandkid : component.getChildren()) { if(grandkid.visitTree(context, callback)) { return true; } } } return false; } protected boolean shouldVisitRows(FacesContext context, VisitContext visitContext) { try { //JSF 2.1 VisitHint skipHint = VisitHint.valueOf("SKIP_ITERATION"); return !visitContext.getHints().contains(skipHint); } catch(IllegalArgumentException e) { //JSF 2.0 Object skipHint = context.getAttributes().get("javax.faces.visit.SKIP_ITERATION"); return !Boolean.TRUE.equals(skipHint); } } protected boolean requiresColumns() { return false; } protected List<UIComponent> getIterableChildren() { return this.getChildren(); } }