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";
}