All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.primefaces.model.LazyDataModel Maven / Gradle / Ivy

There is a newer version: 14.0.0
Show newest version
/*
 * The MIT License
 *
 * Copyright (c) 2009-2023 PrimeTek Informatics
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.primefaces.model;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.model.ListDataModel;
import java.io.Serializable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.convert.Converter;
import javax.faces.model.DataModelEvent;
import javax.faces.model.DataModelListener;

/**
 * DataModel to deal with huge datasets with by lazy loading, page by page.
 *
 * @param  The model class.
 */
public abstract class LazyDataModel extends ListDataModel implements SelectableDataModel, Serializable {

    private static final long serialVersionUID = 1L;

    private Converter converter;
    private int rowCount;
    private int pageSize;

    // overwrite to restore serialization support; see #7699
    private int rowIndex = -1;
    private List data;

    /**
     * For serialization only
     */
    public LazyDataModel() {
        super();
    }

    /**
     * This constructor allows to skip the implementation of {@link #getRowData(java.lang.String)} and
     * {@link #getRowKey(java.lang.Object)}, when selection is used.
     *
     * @param converter The converter used to convert rowKey to rowData and vice versa.
     */
    public LazyDataModel(Converter converter) {
        super();
        this.converter = converter;
    }

    /**
     * Counts the all available data for the given filters.
     *
     * In case of SQL, this would execute a "SELECT COUNT ... WHERE ...".
     *
     * In case you dont use SQL and receive both rowCount
     * and data within a single call, this method should just return 0.
     * You must call {@link #recalculateFirst(int, int, int)} and {@link #setRowCount(int)}
     * in your {@link #load(int, int, java.util.Map, java.util.Map)} method.
     *
     * @param filterBy a map with all filter information (only relevant for DataTable, not for eg DataView)
     * @return the data count
     */
    public abstract int count(Map filterBy);

    /**
     * Loads the data for the given parameters.
     *
     * @param first the first entry
     * @param pageSize the page size
     * @param sortBy a map with all sort information (only relevant for DataTable, not for eg DataView)
     * @param filterBy a map with all filter information (only relevant for DataTable, not for eg DataView)
     * @return the data
     */
    public abstract List load(int first, int pageSize, Map sortBy, Map filterBy);

    @Override
    public T getRowData(String rowKey) {
        if (converter != null) {
            FacesContext context = FacesContext.getCurrentInstance();
            return (T) converter.getAsObject(context, UIComponent.getCurrentComponent(context), rowKey);
        }

        throw new UnsupportedOperationException(
                getMessage("Provide a Converter via constructor or implement getRowData(String rowKey) in %s"
                        + ", when basic rowKey algorithm is not used [component=%s,view=%s]."));
    }

    /**
     * Loads a single row for the rowIndex provided.
     *
     * @param rowIndex the row index to load
     * @param sortBy a map with all sort information (only relevant for DataTable, not for eg DataView)
     * @param filterBy a map with all filter information (only relevant for DataTable, not for eg DataView)
     * @return the data
     */
    public T getRowData(int rowIndex, Map sortBy, Map filterBy) {
        List loaded = load(rowIndex, 1, sortBy, filterBy);
        if (loaded == null || loaded.isEmpty()) {
            return null;
        }
        return loaded.get(0);
    }

    /**
     * Recalculates first, see #1921.
     * Also see: {@link org.primefaces.component.api.UIPageableData#calculateFirst()}
     *
     * @param first the first param from the {@link #load(int, int, java.util.Map, java.util.Map)} method.
     * @param pageSize the pageSize param from the {@link #load(int, int, java.util.Map, java.util.Map)} method.
     * @param rowCount the new rowCount.
     * @return the recalculated first.
     */
    protected int recalculateFirst(int first, int pageSize, int rowCount) {
        if (rowCount > 0 && first >= rowCount) {
            int numberOfPages = (int) Math.ceil(rowCount * 1d / pageSize);
            first = Math.max((numberOfPages - 1) * pageSize, 0);
        }
        return first;
    }

    @Override
    public String getRowKey(T object) {
        if (converter != null) {
            FacesContext context = FacesContext.getCurrentInstance();
            return converter.getAsString(context, UIComponent.getCurrentComponent(context), object);
        }

        throw new UnsupportedOperationException(
                getMessage("Provide a Converter via constructor or implement getRowKey(T object) in %s"
                        + ", when basic rowKey algorithm is not used [component=%s,view=%s]."));
    }

    protected String getMessage(String msg) {
        FacesContext facesContext = FacesContext.getCurrentInstance();
        String viewId = facesContext.getViewRoot().getViewId();
        UIComponent component = UIComponent.getCurrentComponent(facesContext);
        String clientId = component == null ? "" : component.getClientId(facesContext);
        return String.format(msg, getClass().getName(), clientId, viewId);
    }


    @Override
    public boolean isRowAvailable() {
        if (data == null) {
            return false;
        }

        return rowIndex >= 0 && rowIndex < data.size();
    }

    @Override
    public T getRowData() {
        return data.get(rowIndex);
    }

    @Override
    public List getWrappedData() {
        return data;
    }

    @Override
    public void setWrappedData(Object list) {
        this.data = (List) list;
    }

    @Override
    public int getRowIndex() {
        return this.rowIndex;
    }

    @Override
    public void setRowIndex(int rowIndex) {
        int oldIndex = this.rowIndex;

        if (rowIndex == -1) {
            this.rowIndex = -1;
        }
        else if (pageSize > 0) {
            this.rowIndex = (rowIndex % pageSize);
        }
        else {
            this.rowIndex = rowIndex;
        }

        if (data == null) {
            return;
        }

        DataModelListener[] listeners = getDataModelListeners();
        if (listeners != null && oldIndex != this.rowIndex) {
            Object rowData = null;
            if (isRowAvailable()) {
                rowData = getRowData();
            }

            DataModelEvent dataModelEvent = new DataModelEvent(this, rowIndex, rowData);
            for (int i = 0; i < listeners.length; i++) {
                listeners[i].rowSelected(dataModelEvent);
            }
        }
    }

    @Override
    public Iterator iterator() {
        return new LazyDataModelIterator<>(this);
    }

    public Iterator iterator(Map sortBy, Map filterBy) {
        return new LazyDataModelIterator<>(this, sortBy, filterBy);
    }

    @Override
    public int getRowCount() {
        return rowCount;
    }

    public int getPageSize() {
        return pageSize;
    }

    public void setPageSize(int pageSize) {
        this.pageSize = pageSize;
    }

    public void setRowCount(int rowCount) {
        this.rowCount = rowCount;
    }

    public Converter getConverter() {
        return converter;
    }

    public void setConverter(Converter converter) {
        this.converter = converter;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy