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

org.fryske_akademy.jsf.AbstractEntityController Maven / Gradle / Ivy

The newest version!
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package org.fryske_akademy.jsf;

/*-
 * #%L
 * guiCrudApi
 * %%
 * Copyright (C) 2018 Fryske Akademy
 * %%
 * 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 STATES OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.faces.context.FacesContext;
import javax.faces.model.DataModel;
import org.fryske_akademy.ejb.Auditing;
import org.fryske_akademy.jpa.EntityInterface;
import org.fryske_akademy.jpa.RevInfo;
import org.fryske_akademy.jsf.util.JsfUtil;
import org.hibernate.envers.RevisionType;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.event.RowEditEvent;
import org.primefaces.event.SelectEvent;

/**
 *
 * @author eduard
 */
public abstract class AbstractEntityController extends AbstractController {

    private static final Logger LOGGER = Logger.getLogger(AbstractEntityController.class.getName());

    private E selected;
    private E newEntity;
    protected final Class clazz;

    private boolean rememberTableState = true;

    /**
     * remember sorting, filtering, column order, selection, column widths, page
     * number (@multiViewState). This method returns true when the boolean field
     * is true and when no request parameter "state" with value "filtering" is
     * found.
     *
     * @return
     */
    public boolean isRememberTableState() {
        return rememberTableState && !FILTERING.equals(FacesContext.getCurrentInstance().getExternalContext().getRequestParameterMap().get(STATE));
    }

    public void setRememberTableState(boolean rememberTableState) {
        this.rememberTableState = rememberTableState;
    }

    public AbstractEntityController(Class clazz) {
        this.clazz = clazz;
    }

    public E getSelected() {
        return selected;
    }

    public void setSelected(E selected) {
        this.selected = selected;
    }

    public E getNewEntity() {
        return newEntity;
    }

    public void setNewEntity(E newEntity) {
        this.newEntity = newEntity;
    }

    public void onRowSelect(SelectEvent event) {
        if (event != null) {
            setSelected((E) event.getObject());
        }
    }

    /**
     * creates a new entity and calls {@link #fillNew(org.fryske_akademy.jpa.EntityInterface)
     * }
     *
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public void prepareCreate() throws InstantiationException, IllegalAccessException {
        newEntity = clazz.newInstance();
        fillNew(newEntity);
    }

    /**
     * calls {@link #create(org.fryske_akademy.jpa.EntityInterface) } with {@link #getNewEntity()
     * }.
     *
     * @return
     * @throws java.lang.Exception
     */
    public E create() throws Exception {
        return create(newEntity);
    }

    /**
     * calls {@link #persist(org.fryske_akademy.jpa.EntityInterface, org.fryske_akademy.jsf.util.PersistAction, java.lang.String)
     * }.Uses property "Created" from bundle as message.
     *
     * @param e
     * @return
     * @throws java.lang.Exception
     */
    public E create(E e) throws Exception {
        return persist(e, PersistAction.CREATE, JsfUtil.getFromBundle(JsfUtil.getLocaleBundle(getBundleName()), "Created"));
    }

    /**
     * calls {@link #create(org.fryske_akademy.jpa.EntityInterface) } with {@link RowEditEvent#getObject()
     * } and
     * {@link #refreshRow(org.primefaces.event.RowEditEvent, org.fryske_akademy.jpa.EntityInterface, boolean) }.Uses
     * property "Created" from bundle as message.
     *
     * @param event
     * @return
     * @throws java.lang.Exception
     */
    public E create(RowEditEvent event) throws Exception {
        E e = create((E) event.getObject());
        refreshRow(event, e, true);
        return e;
    }

    /**
     * override if you want to use different bundle name then
     * {@link #DEFAULT_BUNDLE_NAME}
     *
     * @return
     */
    protected String getBundleName() {
        return DEFAULT_BUNDLE_NAME;
    }
    public static final String DEFAULT_BUNDLE_NAME = "/Bundle";

    public abstract Filtering getFiltering();

    /**
     * return the value os a filter, often this will be a String.
     *
     * @param key
     * @return
     */
    public Object getFilterValue(String key) {
        return getFiltering().getFilters().get(key);
    }

    public String getFilterString(String key) {
        Object value = getFiltering().getFilters().get(key);
        return value == null ? null : String.valueOf(value);
    }

    /**
     * active filters on a page, use this as primefaces @filterValue:
     * filterValue="#{ctlr.filters['id']}"
     *
     * @return
     */
    public Map getFilters() {
        return getFiltering().getFilters();
    }

    /**
     * Navigate (i.e. in a datatable) to the page containing the argument
     * entity. Implementors can call Filtering.clear().add(java.lang.String,
     * java.lang.Object) and then for example filter, or redirect.
     *
     * @see #filterAndRedirect(org.fryske_akademy.jsf.AbstractEntityController,
     * java.lang.String, java.lang.String, java.lang.String)
     *
     * @param entity
     * @return the action to perform
     */
    public abstract String gotoPageContaining(E entity);

    /**
     * implementors can fill the new object with values from selected, called
     * from {@link #copy() }.
     *
     * @param newEntity
     * @param selected
     */
    protected abstract void fillCopy(E newEntity, E selected);

    /**
     * empty method, called from {@link #prepareCreate() }
     *
     * @param entity
     */
    protected void fillNew(E entity) {
    }

    /**
     * copy a {@link #getSelected() selected entity}, call {@link #fillCopy(org.fryske_akademy.jpa.EntityInterface, org.fryske_akademy.jpa.EntityInterface)
     * }, set {@link #getNewEntity() } to the copy.
     *
     * @throws InstantiationException
     * @throws IllegalAccessException
     */
    public void copy() throws InstantiationException, IllegalAccessException {
        if (selected != null) {
            E l = clazz.newInstance();
            fillCopy(l, getSelected());
            newEntity = l;
        }
    }

    /**
     * Calls {@link #persist(org.fryske_akademy.jpa.EntityInterface, org.fryske_akademy.jsf.util.PersistAction, java.lang.String)
     * } and {@link #refreshRow(org.primefaces.event.RowEditEvent, org.fryske_akademy.jpa.EntityInterface, boolean)
     * } with the up to date entity and true.Uses property "Updated" from bundle
     * as message.
     *
     * @param editEvent
     * @return the updated entity
     * @throws java.lang.Exception
     */
    public E update(RowEditEvent editEvent) throws Exception {
        E t = persist((E) editEvent.getObject(), PersistAction.UPDATE, JsfUtil.getFromBundle(JsfUtil.getLocaleBundle(getBundleName()), "Updated"));
        refreshRow(editEvent, t, true);
        return t;
    }

    /**
     * {@link #create(org.primefaces.event.RowEditEvent Create} or update an
     * entity based on {@link EntityInterface#isTransient() }.
     *
     * @param event
     * @return the saved entity
     * @throws java.lang.Exception
     */
    public E save(RowEditEvent event) throws Exception {
        E t = (E) event.getObject();
        return t.isTransient() ? create(event) : update(event);
    }

    /**
     * Attempts to show data in sync with the database after an update or cancel
     * edit. This implementation works for DataTables with a List or a DataModel
     * wrapping a List as value.
     *
     * @param editEvent
     * @param e
     * @param update true for updates
     */
    protected void refreshRow(RowEditEvent editEvent, EntityInterface e, boolean update) {
        if (editEvent.getComponent() instanceof DataTable && e != null && e.getId() != null) {
            DataTable dt = (DataTable) editEvent.getComponent();
            Object value = dt.getValue();
            List rows = value instanceof List ? (List) value : null;
            if (value instanceof DataModel && ((DataModel) value).getWrappedData() instanceof List) {
                rows = (List) ((DataModel) value).getWrappedData();
            }
            if (rows != null && dt.getRowIndex() > -1) {
                int rowIndex = value instanceof DataModel ? ((DataModel) value).getRowIndex() : dt.getRowIndex();
                if (update) {
                    rows.set(rowIndex, e);
                } else {
                    rows.set(rowIndex, getCrudReadService().find(e.getId(), clazz));
                }
            } else {
                LOGGER.warning(String.format("Cannot refresh datatable row: %s", dt.getRowIndex()));
            }
        } else {
            LOGGER.warning(String.format("Cannot refresh datatable row for: %s", e));
        }
    }

    /**
     * Calls {@link #refreshRow(org.primefaces.event.RowEditEvent, org.fryske_akademy.jpa.EntityInterface, boolean)
     * } with the canceled entity from the event and false.
     *
     * @param editEvent
     */
    public void cancelRow(RowEditEvent editEvent) {
        refreshRow(editEvent, (E) editEvent.getObject(), false);
    }

    /**
     * Call {@link #destroy(org.fryske_akademy.jpa.EntityInterface) } with
     * selected, clear selected when successful.
     *
     * @throws java.lang.Exception
     */
    public void destroy() throws Exception {
        destroy(selected);
        selected = null; // Remove selection
    }

    /**
     * When the entity argument is transient or equals newEntity call {@link #setNewEntity(org.fryske_akademy.jpa.EntityInterface)
     * } with null otherwise call {@link #persist(org.fryske_akademy.jpa.EntityInterface, org.fryske_akademy.jsf.AbstractController.PersistAction, java.lang.String)
     * }  with property "Deleted" from bundle. Sets selected to null.
     *
     * @param e
     * @throws java.lang.Exception
     */
    public void destroy(E e) throws Exception {
        if (e.isTransient() || e.equals(getNewEntity())) {
            setNewEntity(null);
        } else {
            persist(e, PersistAction.DELETE, JsfUtil.getFromBundle(JsfUtil.getLocaleBundle(getBundleName()), "Deleted"));
        }
        if (e.equals(getSelected())) {
            setSelected(null);
        }
    }

    /**
     * return {@link #getNewEntity() } when it isn't null and {@link #getSelected()
     * } is null, otherwise return {@link #getSelected() }.
     *
     * @return
     */
    public E toDelete() {
        return (getNewEntity() != null && getSelected() == null) ? getNewEntity()
                : getSelected();
    }

    /**
     * delete should be disabled when {@link #toDelete() } is null;
     *
     * @return
     */
    public boolean disableDelete() {
        return toDelete() == null;
    }

    /**
     * show some useful info to a user what is about to be deleted, assumes a
     * converter for the entity class to delete.
     *
     * @return
     */
    public String deleteTitle() {
        return toDelete() == null ? "" : JsfUtil.getFromBundle(JsfUtil.getLocaleBundle(getBundleName()), "Delete") + " "
                + JsfUtil.getConverter(toDelete().getClass()).getAsString(null, null, toDelete()) + "?";
    }

    /**
     *
     * @throws IllegalStateException when {@link #getCrudReadService() } is not
     * an instance of Auditing
     * @return
     */
    protected Auditing getAuditing() {
        if (getCrudReadService() instanceof Auditing) {
            return (Auditing) getCrudReadService();
        } else {
            throw new IllegalStateException("getCrudReadService() does not return an instance of Auditing");
        }
    }

    /**
     * returns username, revision date and {@link RevisionType} for the last
     * revision found
     *
     * @see Auditing#getRevisionInfo(java.io.Serializable, java.lang.Integer,
     * java.lang.Class)
     * @param entity
     * @return
     */
    public String getLastChangedInfo(E entity) {
        List> revisions = getAuditing().getRevisionInfo(entity.getId(), 1, clazz);
        if (revisions.isEmpty()) {
            return "no changes";
        }
        RevInfo rev = revisions.get(0);
        return rev.getRevisionInfo().getUsername() + " at " + rev.getRevisionInfo().getRevisionDate() + " (" + rev.getType() + ")";
    }

    public List> getLastChanged(E entity, int max) {
        return getAuditing().getRevisionInfo(entity, max, clazz);
    }

    /**
     * clears all filters, adds the key/value to it and returns your action with
     * {@link #FACESREDIRECTTRUE} and "state=filtering" appended. To make this
     * work use {@link #isRememberTableState() } in your EL expression for the
     * multiViewState and call filter() on the primefaces datatable object in
     * your javascript.
     *
     * @param controller
     * @param key
     * @param value
     * @param action
     * @return
     */
    protected String filterAndRedirect(AbstractEntityController controller, String key, String value, String action) {
        controller.getFiltering().clear().add(key, value);
        // redirect
        return action + FACESREDIRECTTRUE + "&" + STATE + "=" + FILTERING;
    }
    /**
     * will be appended as param key in {@link #filterAndRedirect(org.fryske_akademy.jsf.AbstractEntityController, java.lang.String, java.lang.String, java.lang.String)
     * }
     */
    protected static final String STATE = "state";

    /**
     * will be appended as param value in {@link #filterAndRedirect(org.fryske_akademy.jsf.AbstractEntityController, java.lang.String, java.lang.String, java.lang.String)
     * }
     */
    protected static final String FILTERING = "filtering";
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy