Java tutorial
/* * Copyright 2013-2018 Emmanuel BRUN (contact@amapj.fr) * * This file is part of AmapJ. * * AmapJ is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * AmapJ is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with AmapJ. If not, see <http://www.gnu.org/licenses/>. * * */ package fr.amapj.view.engine.collectioneditor; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.BeanItem; import com.vaadin.event.Action; import com.vaadin.event.FieldEvents.FocusNotifier; import com.vaadin.ui.AbstractField; import com.vaadin.ui.Button; import com.vaadin.ui.CheckBox; import com.vaadin.ui.ComboBox; import com.vaadin.ui.Component; import com.vaadin.ui.CustomField; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.PopupDateField; import com.vaadin.ui.Table; import com.vaadin.ui.TextField; import com.vaadin.ui.VerticalLayout; import fr.amapj.common.AmapjRuntimeException; import fr.amapj.view.engine.collectioneditor.columns.ColumnInfo; import fr.amapj.view.engine.collectioneditor.columns.SearcherColumn; import fr.amapj.view.engine.enumselector.EnumSearcher; import fr.amapj.view.engine.searcher.Searcher; import fr.amapj.view.engine.searcher.SearcherDefinition; import fr.amapj.view.engine.tools.BaseUiTools; /** * Permet la saisie de multi valeur dans un tableau * en lien avec un BeanItem * * A noter : la collection donne en sortie est totalement independante de la collection * donne en entre * Tous les objets sont re crs , et les champs affichs sont copis * */ @SuppressWarnings({ "unchecked", "rawtypes" }) public class CollectionEditor<BEANTYPE> extends CustomField implements Action.Handler { private final static Logger logger = LogManager.getLogger(); final private Action add = new Action(getMasterDetailAddItemCaption()); final private Action remove = new Action(getMasterDetailRemoveItemCaption()); final private Action up = new Action(getMasterDetailUpItemCaption()); final private Action down = new Action(getMasterDetailDownItemCaption()); private Table table; private BeanItem item; private Object propertyId; private Class<BEANTYPE> beanType; // La liste des objects graphiques permettant l'dition private RowList rows; // La liste des colonnes afficher private List<ColumnInfo> columns; // Permet de desactiver les boutons monter, descendre,ajouter, supprimer private boolean btnAjouter = true; private boolean btnSupprimer = true; private boolean btnMonter = true; private boolean btnDescendre = true; /** * * */ public CollectionEditor(String caption, BeanItem item, Object propertyId, Class<BEANTYPE> beanType) { this.item = item; this.beanType = beanType; this.propertyId = propertyId; rows = new RowList(); columns = new ArrayList<ColumnInfo>(); setCaption(caption); } public void addColumn(String propertyId, String title, FieldType fieldType, Object defaultValue) { columns.add(new ColumnInfo(propertyId, title, fieldType, true, defaultValue)); } public void addColumn(String propertyId, String title, FieldType fieldType, boolean editable, Object defaultValue) { columns.add(new ColumnInfo(propertyId, title, fieldType, editable, defaultValue)); } public void addSearcherColumn(String propertyId, String title, FieldType fieldType, Object defaultValue, SearcherDefinition searcher, Searcher linkedSearcher) { columns.add(new SearcherColumn(propertyId, title, fieldType, true, defaultValue, searcher, linkedSearcher)); } public void addSearcherColumn(String propertyId, String title, FieldType fieldType, boolean editable, Object defaultValue, SearcherDefinition searcher, Searcher linkedSearcher) { columns.add( new SearcherColumn(propertyId, title, fieldType, editable, defaultValue, searcher, linkedSearcher)); } private void buildTable() { table = new Table(); table.addStyleName("no-stripes"); table.addStyleName("no-vertical-lines"); table.addStyleName("no-horizontal-lines"); for (ColumnInfo col : columns) { Class clazz = getFieldAsClass(col.fieldType); table.addContainerProperty(col.title, clazz, null); } // Ajout des lignes List<BEANTYPE> beans = (List<BEANTYPE>) item.getItemProperty(propertyId).getValue(); for (BEANTYPE bean : beans) { addRow(bean); } table.setPageLength(10); table.addActionHandler(this); table.setEditable(true); table.setSelectable(true); table.setImmediate(true); table.setSortEnabled(false); } private Class getFieldAsClass(FieldType fieldType) { switch (fieldType) { case STRING: return TextField.class; case SEARCHER: return ComboBox.class; case CURRENCY: return TextField.class; case QTE: return TextField.class; case INTEGER: return TextField.class; case DATE: return PopupDateField.class; case CHECK_BOX: return CheckBox.class; case COMBO: return ComboBox.class; } throw new AmapjRuntimeException(); } private AbstractField getField(FieldType fieldType, ColumnInfo col) { switch (fieldType) { case STRING: TextField str = new TextField(); str.addStyleName("text"); return str; case SEARCHER: SearcherColumn s = (SearcherColumn) col; Searcher box = new Searcher(s.searcher, null); if (s.params != null) { box.setParams(s.params); } if (s.linkedSearcher != null) { box.setLinkedSearcher(s.linkedSearcher); } box.addStyleName("searcher"); return box; case CURRENCY: TextField currency = BaseUiTools.createCurrencyField("", false); currency.addStyleName("currency"); return currency; case QTE: TextField qte = BaseUiTools.createQteField(""); qte.addStyleName("qte"); return qte; case INTEGER: TextField integer = BaseUiTools.createIntegerField(""); integer.addStyleName("integer"); return integer; case DATE: PopupDateField dateField = BaseUiTools.createDateField(""); dateField.addStyleName("date"); return dateField; case CHECK_BOX: CheckBox checkBox = BaseUiTools.createCheckBoxField(""); checkBox.addStyleName("checkbox"); return checkBox; case COMBO: ComboBox combo = EnumSearcher.createEnumSearcher("", (Enum) col.defaultValue); combo.addStyleName("combo"); return combo; } throw new AmapjRuntimeException(); } /** * Permet l'ajout d'une ligne dans le tableau * * Si bean est null, alors la ligne est charg avec les valeurs par dfaut * * @param bean */ private void addRow(BEANTYPE bean) { // Create the table row. final Row row = new Row(); BeanItem beanItem = null; if (bean != null) { beanItem = new BeanItem(bean); } // Ajout de toutes les colonnes for (ColumnInfo col : columns) { Object val1 = col.defaultValue; // Rcupration des donnes if (beanItem != null) { val1 = beanItem.getItemProperty(col.propertyId).getValue(); } AbstractField f = getField(col.fieldType, col); f.setConvertedValue(val1); f.setReadOnly(!col.editable); // GESTION DU FOCUS : on slectionne la ligne dont un lement a le focus if (shouldHaveFocusNotifier(f, col.editable)) { FocusNotifier tf = (FocusNotifier) f; tf.addFocusListener(e -> table.select(row.getItemId())); } row.addField(f); } // Ajout de la ligne et calcul de l'item id et memorisation de l'id eventuel Object idInfo = getIdBeanInfo(beanItem); rows.add(row, idInfo); // table.addItem(row.getColumnTable(), row.getItemId()); } /** * Point delicat et etrange * * Pour les textfield avec setreadonly = true, il ne faut pas mettre le listener sur le focus * Par contre, il faut bien le mettre sur les autres, comme par exemple les combo * */ private boolean shouldHaveFocusNotifier(AbstractField f, boolean editable) { // if ((f instanceof FocusNotifier) == false) { return false; } // Si c'est editable : on ajoute toujours le listener if (editable) { return true; } // Si text field non editable : pas de listener if (f instanceof TextField) { return false; } else { return true; } } private void remove(Object itemId) { if (itemId == null) { return; } // Object selectedRow = rows.remove(itemId); table.removeItem(itemId); if (selectedRow != null) { table.select(selectedRow); } } protected Table getTable() { return table; } protected String getMasterDetailRemoveItemCaption() { return "Supprimer"; } protected String getMasterDetailAddItemCaption() { return "Ajouter"; } protected String getMasterDetailUpItemCaption() { return "Monter"; } protected String getMasterDetailDownItemCaption() { return "Descendre"; } public void handleAction(Action action, Object sender, Object target) { if (action == add) { addRow(null); } else if (action == remove) { remove(target); } else if (action == up) { up(target); } else if (action == down) { down(target); } } private void down(Object target) { if (target == null) { return; } int index = rows.getIndex(target); if (rows.canDown(index) == false) { return; } Object itemId = rows.downRow(index); table.select(itemId); } private void up(Object target) { if (target == null) { return; } int index = rows.getIndex(target); if (rows.canUp(index) == false) { return; } Object itemId = rows.upRow(index); table.select(itemId); } @Override public Action[] getActions(Object target, Object sender) { List<Action> acts = new ArrayList<Action>(); if (btnAjouter) { acts.add(add); } if (btnSupprimer) { acts.add(remove); } if (btnMonter) { acts.add(up); } if (btnDescendre) { acts.add(down); } return acts.toArray(new Action[acts.size()]); } @Override public Object getValue() { return computeValue(); } @Override public void commit() throws SourceException, InvalidValueException { List<BEANTYPE> ls = computeValue(); item.getItemProperty(propertyId).setValue(ls); } private List<BEANTYPE> computeValue() { try { List<BEANTYPE> ls = new ArrayList<BEANTYPE>(); for (Row row : rows.getRows()) { BEANTYPE elt = beanType.newInstance(); BeanItem beanItem = new BeanItem(elt); int i = 0; for (ColumnInfo col : columns) { Object val = row.getFieldValue(i); beanItem.getItemProperty(col.propertyId).setValue(val); i++; } setIdBeanInfo(beanItem, row.getIdBeanInfo()); ls.add(elt); } return ls; } catch (InstantiationException | IllegalAccessException | ReadOnlyException e) { logger.warn("Commit failed", e); throw new RuntimeException("Erreur inattendue", e); } } @Override public Class<?> getType() { return List.class; } public Collection getElements() { return (Collection) getPropertyDataSource().getValue(); } @Override protected Component initContent() { VerticalLayout vl = new VerticalLayout(); vl.addStyleName("collection-editor"); buildTable(); vl.addComponent(getTable()); if (hasOneButtonOrMore()) { addButtons(vl); } return vl; } private void addButtons(VerticalLayout vl) { HorizontalLayout buttons = new HorizontalLayout(); if (btnAjouter) { buttons.addComponent(new Button(getMasterDetailAddItemCaption(), e -> addRow(null))); } if (btnSupprimer) { buttons.addComponent( new Button(getMasterDetailRemoveItemCaption(), e -> remove(getTable().getValue()))); } if (btnMonter) { buttons.addComponent(new Button(getMasterDetailUpItemCaption(), e -> up(getTable().getValue()))); } if (btnDescendre) { buttons.addComponent(new Button(getMasterDetailDownItemCaption(), e -> down(getTable().getValue()))); } vl.addComponent(buttons); } private boolean hasOneButtonOrMore() { return btnAjouter || btnSupprimer || btnMonter || btnDescendre; } /** * Permet de desactiver/activer les boutons monter, descendre,ajouter, supprimer */ public void activeButton(boolean btnAjouter, boolean btnSupprimer, boolean btnMonter, boolean btnDescendre) { this.btnAjouter = btnAjouter; this.btnSupprimer = btnSupprimer; this.btnMonter = btnMonter; this.btnDescendre = btnDescendre; } public void disableAllButtons() { activeButton(false, false, false, false); } private String propertyIdBeanToPreserve; /** * Permet de gerer un identifiant qui sera prserv lors du dplacement des lignes par monter / descendre * * Dans le cas des lignes ajouts, cet identifiant reste null */ public void addBeanIdToPreserve(String propertyIdBeanToPreserve) { this.propertyIdBeanToPreserve = propertyIdBeanToPreserve; } private Object getIdBeanInfo(BeanItem beanItem) { if (propertyIdBeanToPreserve == null) { return null; } if (beanItem == null) { return null; } return beanItem.getItemProperty(propertyIdBeanToPreserve).getValue(); } private void setIdBeanInfo(BeanItem beanItem, Object idBeanInfo) { if (propertyIdBeanToPreserve == null) { return; } beanItem.getItemProperty(propertyIdBeanToPreserve).setValue(idBeanInfo); } }