/* * Copyright 2011-2012 Blazebit * * 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 com.blazebit.blazefaces.component; import com.blazebit.blazefaces.util.ComponentUtils; import java.sql.ResultSet; 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.context.FacesContext; import javax.faces.event.PhaseId; import javax.faces.event.PostValidateEvent; import javax.faces.event.PreValidateEvent; import javax.faces.model.*; import javax.faces.render.Renderer; 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_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; protected enum PropertyKeys { paginator ,paginatorTemplate ,rowsPerPageTemplate ,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 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 calculatePage() { int rows = this.getRowsToRender(); if(rows > 0) { int rowCount = this.getRowCount(); int first = this.getFirst(); int currentPage = (int) (first / rows); int numberOfPages = (int) Math.ceil(rowCount * 1d / rows); if(currentPage > numberOfPages && numberOfPages > 0) { currentPage = numberOfPages; this.setFirst((currentPage-1) * rows); } } } public int getPage() { int rows = this.getRowsToRender(); if(rows > 0) { int first = this.getFirst(); return (int) (first / rows); } 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) { setRowIndex(-1); processFacets(context, phaseId); processChildrenFacets(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 processChildrenFacets(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.getChildren()) { if(child.isRendered()) { process(context, child, phaseId); } } } } protected void process(FacesContext context, UIComponent component, PhaseId phaseId) { if(!shouldProcessChildren(context)) { return; } 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(); String rowIndexVar = this.getRowIndexVar(); if(var != null) { Map<String, Object> requestMap = getFacesContext().getExternalContext().getRequestMap(); if(isRowAvailable()) { requestMap.put(var, getRowData()); if(rowIndexVar != null) requestMap.put(rowIndexVar, rowIndex); } else { requestMap.remove(var); if(rowIndexVar != null) requestMap.remove(rowIndexVar); } } } @Override public int getRowIndex() { return (Integer) getStateHelper().eval(PropertyKeys.rowIndex, -1); } protected void saveDescendantState() { FacesContext context = getFacesContext(); if(getChildCount() > 0) { for(UIComponent kid : getChildren()) { saveDescendantState(kid, 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); } } } 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 { setDataModel(new ScalarDataModel(current)); } return model; } @Override protected void setDataModel(DataModel dataModel) { this.model = dataModel; } protected boolean shouldProcessChildren(FacesContext context) { return true; } }