package org.vaadin.viritin.v7.fields; import com.vaadin.server.FontAwesome; import com.vaadin.ui.Button; import com.vaadin.ui.Component; import com.vaadin.ui.Layout; import com.vaadin.v7.ui.Table; import com.vaadin.ui.themes.ValoTheme; import org.vaadin.viritin.v7.MBeanFieldGroup; import org.vaadin.viritin.button.MButton; import org.vaadin.viritin.layouts.MVerticalLayout; import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; /** * A field suitable for editing collection of referenced objects tied to parent * object only. E.g. OneToMany/ElementCollection fields in JPA world. * <p> * Some features/restrictions: * <ul> * <li>The field is valid when all elements are valid. * <li>The field is always non buffered * <li>The element type needs to have an empty paremeter constructor or user * must provide an Instantiator. * </ul> * * Elements in the edited collection are modified with BeanFieldGroup. Fields * should defined in a class. A simple usage example for editing * List>Address< adresses: * <pre><code> * public static class AddressRow { * EnumSelect type = new EnumSelect(); * MTextField street = new MTextField(); * MTextField city = new MTextField(); * MTextField zipCode = new MTextField(); * } * * public static class PersonForm<Person> extends AbstractForm { * private final ElementCollectionTable<Address> addresses * = new ElementCollectionTable<Address>(Address.class, * AddressRow.class).withCaption("Addressess"); * * </code></pre> * * <p> * By default the field contains a button to add new elements. If instances are * added with some other method (or UI shouldn't add them at all), you can * configure this with setAllowNewItems. Deletions can be configured with * setAllowRemovingItems. * <p> * * @param <ET> The type in the entity collection. The type must have empty * parameter constructor or you have to provide Instantiator. */ public class ElementCollectionTable<ET> extends AbstractElementCollection<ET> { private static final long serialVersionUID = 8055987316151594559L; private MTable<ET> table; private MButton addButton = new MButton(FontAwesome.PLUS, new Button.ClickListener() { private static final long serialVersionUID = 6115218255676556647L; @Override public void buttonClick(Button.ClickEvent event) { addElement(createInstance()); } }); private IdentityHashMap<ET, MButton> elementToDelButton = new IdentityHashMap<>(); boolean inited = false; private MVerticalLayout layout = new MVerticalLayout(); private String[] deleteElementStyles; private String disabledDeleteThisElementDescription = "Fill this row to add a new element, currently ignored"; public ElementCollectionTable(Class<ET> elementType, Class<?> formType) { super(elementType, formType); } public ElementCollectionTable(Class<ET> elementType, Instantiator i, Class<?> formType) { super(elementType, i, formType); } @Override public void attach() { super.attach(); ensureInited(); } @Override public void addInternalElement(final ET v) { ensureInited(); table.addBeans(v); } @Override public void removeInternalElement(ET v) { table.removeItem(v); elementToDelButton.remove(v); } @Override public Layout getLayout() { return layout; } public MButton getAddButton() { return addButton; } /** * @return the Table used in the implementation. Configure carefully. */ public MTable<ET> getTable() { return table; } @Override public void setPersisted(ET v, boolean persisted) { // NOP } private void ensureInited() { if (!inited) { layout.setMargin(false); setHeight("300px"); table = new MTable<ET>(getElementType()).withFullWidth(); for (Object propertyId : getVisibleProperties()) { table.addGeneratedColumn(propertyId, new Table.ColumnGenerator() { private static final long serialVersionUID = 3637140096807147630L; @Override public Object generateCell(Table source, Object itemId, Object columnId) { MBeanFieldGroup<ET> fg = getFieldGroupFor( (ET) itemId); if (!isAllowEditItems()) { fg.setReadOnly(true); } Component component = fg.getField(columnId); if(component == null) { getComponentFor((ET) itemId, columnId.toString()); } return component; } }); } ArrayList<Object> cols = new ArrayList<Object>( getVisibleProperties()); if (isAllowRemovingItems()) { table.addGeneratedColumn("__ACTIONS", new Table.ColumnGenerator() { private static final long serialVersionUID = 492486828008202547L; @Override public Object generateCell(Table source, final Object itemId, Object columnId) { MButton b = new MButton(FontAwesome.TRASH_O). withListener( new Button.ClickListener() { private static final long serialVersionUID = -1257102620834362724L; @Override public void buttonClick( Button.ClickEvent event) { removeElement( (ET) itemId); } }).withStyleName( ValoTheme.BUTTON_ICON_ONLY); b.setDescription(getDeleteElementDescription()); if (getDeleteElementStyles() != null) { for (String style : getDeleteElementStyles()) { b.addStyleName(style); } } elementToDelButton.put((ET) itemId, b); return b; } }); table.setColumnHeader("__ACTIONS", ""); cols.add("__ACTIONS"); } table.setVisibleColumns(cols.toArray()); for (Object property : getVisibleProperties()) { table.setColumnHeader(property, getPropertyHeader(property. toString())); } layout.expand(table); if (isAllowNewItems()) { layout.addComponent(addButton); } inited = true; } } @Override public void clear() { if (inited) { table.removeAllItems(); elementToDelButton.clear(); } } public String getDisabledDeleteElementDescription() { return disabledDeleteThisElementDescription; } public void setDisabledDeleteThisElementDescription( String disabledDeleteThisElementDescription) { this.disabledDeleteThisElementDescription = disabledDeleteThisElementDescription; } public String getDeleteElementDescription() { return deleteThisElementDescription; } private String deleteThisElementDescription = "Delete this element"; public void setDeleteThisElementDescription( String deleteThisElementDescription) { this.deleteThisElementDescription = deleteThisElementDescription; } public String[] getDeleteElementStyles() { return deleteElementStyles; } public void addDeleteElementStyles(String... deleteElementStyles) { this.deleteElementStyles = deleteElementStyles; } @Override public void onElementAdded() { // NOP } @Override public ElementCollectionTable<ET> setPropertyHeader(String propertyName, String propertyHeader) { super.setPropertyHeader(propertyName, propertyHeader); return this; } @Override public ElementCollectionTable<ET> setVisibleProperties( List<String> properties, List<String> propertyHeaders) { super.setVisibleProperties(properties, propertyHeaders); return this; } @Override public ElementCollectionTable<ET> setVisibleProperties( List<String> properties) { super.setVisibleProperties(properties); return this; } @Override public ElementCollectionTable<ET> setAllowNewElements( boolean allowNewItems) { super.setAllowNewElements(allowNewItems); return this; } @Override public ElementCollectionTable<ET> setAllowRemovingItems( boolean allowRemovingItems) { super.setAllowRemovingItems(allowRemovingItems); return this; } @Override public ElementCollectionTable<ET> withCaption(String caption) { super.withCaption(caption); return this; } @Override public ElementCollectionTable<ET> removeElementRemovedListener( ElementRemovedListener listener) { super.removeElementRemovedListener(listener); return this; } @Override public ElementCollectionTable<ET> addElementRemovedListener( ElementRemovedListener<ET> listener) { super.addElementRemovedListener(listener); return this; } @Override public ElementCollectionTable<ET> removeElementAddedListener( ElementAddedListener listener) { super.removeElementAddedListener(listener); return this; } @Override public ElementCollectionTable<ET> addElementAddedListener( ElementAddedListener<ET> listener) { super.addElementAddedListener(listener); return this; } public ElementCollectionTable<ET> withEditorInstantiator( Instantiator instantiator) { setEditorInstantiator(instantiator); return this; } }