/* * Copyright 2013 GiavaCms.org. * * Licensed under the Eclipse Public License version 1.0, available at * http://www.eclipse.org/legal/epl-v10.html */ package org.giavacms.common.controller; import java.io.Serializable; import java.lang.reflect.Field; import java.lang.reflect.ParameterizedType; import java.util.ArrayList; import javax.annotation.PostConstruct; import javax.faces.event.ActionEvent; import javax.faces.event.AjaxBehaviorEvent; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import org.giavacms.common.annotation.OwnRepository; import org.giavacms.common.model.LocalDataModel; import org.giavacms.common.model.Search; import org.giavacms.common.repository.Repository; import org.giavacms.common.util.JSFUtils; import org.giavacms.common.util.StringUtils; import org.jboss.logging.Logger; /** * * @param <T> */ public abstract class AbstractLookupController<T> implements Serializable { private static final long serialVersionUID = 1L; // ------------------------------------------------ // --- Logger ------------------------------------- // ------------------------------------------------ protected final Logger logger = Logger.getLogger(getClass() .getCanonicalName()); /** * Entity class */ private Class<T> entityClass; /** * Search object */ protected Search<T> search; // ------------------------------------------------ // --- Stato dell'handler ------------------------- // ------------------------------------------------ private static final int PAGE_SIZE = 10; private static final int ROWS_PER_PAGE = 10; private static final int SCROLLER_PAGE = 1; private int rowCount; private int pageSize = PAGE_SIZE; private int rowsPerPage = ROWS_PER_PAGE; private int scrollerPage = SCROLLER_PAGE; protected static final String REDIRECT_PARAM = "?faces-redirect=true"; /** * Risultato della selezione, del caricamento diretto o della crezione di un nuovo entity */ private T element; /** * Risultato della ricerca */ private DataModel<T> model; /** * Repository per fare query su db */ private Repository<T> repository; /** * Questo flag indica se il modal panel è aperto e allora solo in quel caso il modello deve essere fetch-ato dal db. */ private boolean open = false; // ------------------------------------------------ // --- Costruttore interno ------------------------ // ------------------------------------------------ /** * Costruttore con parametri da invocare obbligatoriamente nel costruttore senza argomenti dei sotto-handler: * inizializza i parametri di ricerca e colleziona eventuali vincoli session-wide come quelli che discendono dalla * identita' dell'utente loggato */ /** * */ @SuppressWarnings({ "unchecked", "rawtypes" }) public AbstractLookupController() { this.entityClass = getClassType(); // defaultCriteria(); search = new Search(this.entityClass); } @SuppressWarnings({ "rawtypes", "unchecked" }) private void injectRepositoryAndPages() { // Field[] fields = getClass().getDeclaredFields(); for (Field field : fields) { try { OwnRepository repository_anno = field .getAnnotation(OwnRepository.class); try { if (repository_anno != null) { field.setAccessible(true); Object repositoryObj = field.get(null); this.repository = (Repository) repositoryObj; } } catch (Exception e) { e.printStackTrace(); } } catch (IllegalArgumentException e) { logger.info(e.getMessage()); } } } /** * @return */ @SuppressWarnings({ "rawtypes", "unchecked" }) private Class<T> getClassType() { Class clazz = getClass(); while (!(clazz.getGenericSuperclass() instanceof ParameterizedType)) { clazz = clazz.getSuperclass(); } ParameterizedType parameterizedType = (ParameterizedType) clazz .getGenericSuperclass(); // ParameterizedType parameterizedType = (ParameterizedType) getClass() // .getSuperclass().getGenericSuperclass(); return (Class<T>) parameterizedType.getActualTypeArguments()[0]; } // ------------------------------------------------ // --- getter/setter------------------------------- // ------------------------------------------------ /** * @return */ public Search<T> getSearch() { return this.search; } /** * @return */ public DataModel<T> getModel() { if (model == null) refreshModel(); return model; } /** * @param model */ public void setModel(DataModel<T> model) { this.model = model; } /** * @return */ public T getElement() { return element; } /** * @param element */ public void setElement(T element) { this.element = element; } /** * @return */ public int getRowCount() { return rowCount; } /** * @param rowCount */ public void setRowCount(int rowCount) { this.rowCount = rowCount; } /** * @return */ public int getPageSize() { return pageSize; } /** * @param pageSize */ public void setPageSize(int pageSize) { this.pageSize = pageSize; } /** * @return */ public int getRowsPerPage() { return rowsPerPage; } /** * @param rowsPerPage */ public void setRowsPerPage(int rowsPerPage) { this.rowsPerPage = rowsPerPage; } /** * @return */ public int getScrollerPage() { return scrollerPage; } /** * @param scrollerPage */ public void setScrollerPage(int scrollerPage) { this.scrollerPage = scrollerPage; } /** * Metodo per ottenere l'id di ricerca (il nome del campo non è noto a priori e uguale per tutti gli entity... almeno * finché non introduciamo interfacce a questo scopo... ;) ) * * Serve per poter fornire una implementazione a default del metodo refreshModel(), senza doverlo riscrivere in ogni * sotto-handler, come invece fatto in passato */ public Object getId(T t) { try { return t.getClass().getDeclaredField("id").get(t); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Metodo per ottenere l'ejb generico che effettua la ricerca * * Serve per poter fornire una implementazione a default del metodo refreshModel(), senza doverlo riscrivere in ogni * sotto-handler, come invece fatto in passato * * @return */ public Repository<T> getRepository() { return repository; } public boolean isOpen() { return open; } public void setOpen(boolean open) { this.open = open; } // ============================================================================================ // === LOGICA DI GESTIONE DELLO STATO INTERNO DELL'HANDLER // ============================================================================================ /** * Metodo interno (protected) che carica il risultato della ricerca corrente. Possibile override da parte delle * sottoclassi per aggiungere listener e/o effettuare operazioni aggiuntive specifiche del particolare sotto-handler */ public void refreshModel() { if (!isOpen()) { setModel(new ListDataModel<T>(new ArrayList<T>())); } else { setModel(new LocalDataModel<T>(pageSize, search, getRepository())); } } /** * Metodo interno (protected) da overriddare per assicurare che i criteri di ricerca contengano sempre tutti i * vincoli desiderati (es: identità utente, selezioni esterne, oggetti figli non nulli, ecc...) * * Qui ne viene fornita una implementazione di default che non fa nulla, per evitare di scrivere un metodo vuoto nei * sotto-handler in caso non ce ne sia bisogno */ @PostConstruct public void defaultCriteria() { injectRepositoryAndPages(); } /** * Implements reset specific operations */ protected void preReset() { this.scrollerPage = 1; this.model = null; search = new Search<T>(this.entityClass); defaultCriteria(); } /** * Metodo per forzare dall'esterno la pulizia dell'handler * * Possibili override da parte dei sotto-handler per resettare ulteriori campi non previsti dall'handler generico o * per modificare la vista di destinazione ottenuta come outcome */ public String reset() { preReset(); return ""; } /** * Implements reload specific operations */ protected void preReload() { this.model = null; } /** * Metodo per forzare l'aggiornamento del risultato della ricerca dell'handler, SENZA PERDERE I CRITERI DI RICERCA * CORRENTI!!! * * Possibili override da parte dei sotto-handler per resettare ulteriori campi non previsti dall'handler generico o * per modificare la vista di destinazione ottenuta come outcome */ public String reload() { preReload(); getOrderByParameter(); return ""; } /** * Metodo per forzare dall'esterno la pulizia dell'handler in modo trasparente all'utente (es: value di un * outputText), in modo da ripulire eventuali inconsistenze dovute all'uso del pulsante back del browser in luogo del * pulsante 'indietro' o 'annulla' delle pagine */ public boolean getClear() { reset(); return true; } // ============================================================================================ // === LOGICA DI NAVIGAZIONE // ================================================================== // ============================================================================================ /* * Questi metodi non iniziano per get perché sono da usare nelle action dei componenti ui (method binding * expression), non come valori (value binding expression... get/set.. dot notation... you know...) */ public void getOrderByParameter() { String orderBy = (String) JSFUtils.getParameter("orderBy"); if (!StringUtils.isEmpty(orderBy)) { search.setOrder(orderBy); } } public void closePanel(AjaxBehaviorEvent ajaxBehaviorEvent) { reset(); this.setOpen(false); } public String select(ActionEvent actionEvent) { if (model == null || !model.isRowAvailable()) { return ""; } T rowData = model.getRowData(); if (rowData != null) { setElement(rowData); } return ""; } public void selectAndClose(ActionEvent actionEvent) { if (model == null || !model.isRowAvailable()) { return; } T rowData = model.getRowData(); if (rowData != null) { setElement(rowData); } this.model = null; this.setOpen(false); } public void openPanel(ActionEvent actionEvent) { reset(); setOpen(true); refreshModel(); } }