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

org.vaadin.viritin.fields.MultiSelectTable Maven / Gradle / Ivy

package org.vaadin.viritin.fields;

import com.vaadin.data.Property;
import com.vaadin.data.util.converter.Converter;
import com.vaadin.server.Resource;
import com.vaadin.shared.ui.MultiSelectMode;
import com.vaadin.ui.Component;
import com.vaadin.ui.CustomField;
import com.vaadin.ui.Table;
import org.vaadin.viritin.ListContainer;
import org.vaadin.viritin.MSize;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * A Table that can be used as a field to modify List>YourDomainObject< or
 * Set>YourDomainObject< typed fields in your domain model.
 * 

* The field supports only non buffered mode. Also, the field tries to keep the * original collection and just modify its content. This helps e.g. some ORM * libraries to create more efficient updates to the database. *

* If the field value is null (and users selects rows) the field tries to add a * value with no-arg constructor or with ArrayList/HashMap if interface is used. * * @author Matti Tahvonen * @param The type in the entity collection */ public class MultiSelectTable extends CustomField { private static final long serialVersionUID = -3245403481676039947L; private String[] visProps; private String[] pendingHeaders; Table table = new Table() { private static final long serialVersionUID = -770469825702542170L; { setSelectable(true); setMultiSelect(true); setSizeFull(); setImmediate(true); } private boolean clientSideChange; @Override public void changeVariables(Object source, Map variables) { clientSideChange = true; super.changeVariables(source, variables); clientSideChange = false; } @Override protected void setValue(Object newValue, boolean repaintIsNotNeeded) throws ReadOnlyException { Set oldvalue = (Set) getValue(); super.setValue(newValue, repaintIsNotNeeded); if (clientSideChange) { // TODO add strategies for maintaining the order in case of List // e.g. same as listing, selection order ... Set newvalue = (Set) getValue(); Set orphaned = new HashSet<>(oldvalue); orphaned.removeAll(newvalue); removeRelation(orphaned); allRemovedRelations.addAll(orphaned); allAddedRelations.removeAll(orphaned); Set newValues = new LinkedHashSet<>(newvalue); newValues.removeAll(oldvalue); addRelation(newValues); allAddedRelations.addAll(newValues); allRemovedRelations.removeAll(newValues); MultiSelectTable.this.fireValueChange(true); } } }; private Class optionType; private Set allAddedRelations = new HashSet<>(); private Set allRemovedRelations = new HashSet<>(); public MultiSelectTable(Class optionType) { this(); table.setContainerDataSource(new ListContainer(optionType)); this.optionType = optionType; } public MultiSelectTable(String caption) { this(); setCaption(caption); } /** * Adds given options to the modified collection. The default implementation * simply uses java.util.Collection APIs to maintain the edited collection. * If the underlying data model needs some some custom maintenance, e.g. a * bi-directional many-to-many relation in JPA, you can add your own logic * by overriding this method. * * @param newValues the new objects to be added to the collection. */ protected void addRelation(Set newValues) { getEditedCollection().addAll(newValues); } /** * Removes given options from the modified collection. The default * implementation simply uses java.util.Collection APIs to maintain the * edited collection. If the underlying data model needs some some custom * maintenance, e.g. a bi-directional many-to-many relation in JPA, you can * add your own logic by overriding this method. * * @param orphaned the new objects to be removed from the collection. */ protected void removeRelation(Set orphaned) { getEditedCollection().removeAll(orphaned); } /** * @return all objects that are added to the edited collection. The added * relations are calculated since last call to setPropertyDataSource method * (when the modified property/collection is set by e.g. FieldGroup) or * explicit call to clearModifiedRelations(). */ public Set getAllAddedRelations() { return allAddedRelations; } /** * @return all objects that are removed from the edited collection. The * deleted relations are calculated since last call to setPropertyDataSource * method (when the modified property/collection is set by e.g. FieldGroup) * or explicit call to clearModifiedRelations(). */ public Set getAllRemovedRelations() { return allRemovedRelations; } /** * @return all objects that are either added or removed to/from the edited * collection. The objects are calculated since last call to * setPropertyDataSource method (when the modified property/collection is * set by e.g. FieldGroup) or explicit call to clearModifiedRelations(). */ public Set getAllModifiedRelations() { HashSet all = new HashSet<>(allAddedRelations); all.addAll(allRemovedRelations); return all; } @Override public void setPropertyDataSource(Property newDataSource) { clearModifiedRelations(); super.setPropertyDataSource(newDataSource); } /** * Clears the fields that track the elements that are added or removed to * the modified collection. This method is automatically called when a new * property is assigned to this field. */ public void clearModifiedRelations() { allAddedRelations.clear(); allRemovedRelations.clear(); } /** * @return the collection being edited by this field. */ protected Collection getEditedCollection() { Collection c = getValue(); if (c == null) { if (getPropertyDataSource() == null) { // this should never happen :-) return new HashSet(); } Class fieldType = getPropertyDataSource().getType(); if (fieldType.isInterface()) { if (fieldType == List.class) { c = new ArrayList(); } else { // Set c = new HashSet(); } } else { try { c = (Collection) fieldType.newInstance(); } catch (IllegalAccessException | InstantiationException ex) { throw new RuntimeException( "Could not instantiate the used colleciton type", ex); } } } return c; } public MultiSelectTable withProperties(String... visibleProperties) { visProps = visibleProperties; if (isContainerInitialized()) { table.setVisibleColumns((Object[]) visibleProperties); } else { for (String string : visibleProperties) { table.addContainerProperty(string, String.class, ""); } } return this; } private boolean isContainerInitialized() { return table.getContainerDataSource() instanceof ListContainer; } public MultiSelectTable withColumnHeaders( String... columnNamesForVisibleProperties) { if (isContainerInitialized()) { table.setColumnHeaders(columnNamesForVisibleProperties); } else { pendingHeaders = columnNamesForVisibleProperties; // Add headers to temporary indexed container, in case table is initially // empty for (String prop : columnNamesForVisibleProperties) { table.addContainerProperty(prop, String.class, ""); } } return this; } @Override protected Component initContent() { return table; } @Override public Class getType() { return Collection.class; } @Override protected void setInternalValue(Collection newValue) { super.setInternalValue(newValue); table.setValue(newValue); } /** * Sets the list of options available. * * @param list the list of available options * @return this for fluent configuration */ public MultiSelectTable setOptions(List list) { if (visProps == null) { table.setContainerDataSource(new ListContainer(optionType, list)); } else { table.setContainerDataSource(new ListContainer(optionType, list), Arrays.asList( visProps)); } if (pendingHeaders != null) { table.setColumnHeaders(pendingHeaders); } return this; } /** * Sets the list of options available. * * @param list the list of available options * @return this for fluent configuration */ public MultiSelectTable setOptions(ET... list) { if (visProps == null) { table.setContainerDataSource(new ListContainer(optionType, Arrays. asList(list))); } else { table.setContainerDataSource(new ListContainer(optionType, Arrays. asList(list)), Arrays.asList( visProps)); } if (pendingHeaders != null) { table.setColumnHeaders(pendingHeaders); } return this; } public MultiSelectTable() { setHeight("230px"); // TODO verify if this is needed in real usage, but at least to pass the test setConverter(new Converter() { private static final long serialVersionUID = -20358585168853508L; @Override public Collection convertToModel(Collection value, Class targetType, Locale locale) throws Converter.ConversionException { return value; } @Override public Collection convertToPresentation(Collection value, Class targetType, Locale locale) throws Converter.ConversionException { return value; } @Override public Class getModelType() { return (Class) getEditedCollection().getClass(); } @Override public Class getPresentationType() { return Collection.class; } }); } public void select(ET objectToSelect) { if (!table.isSelected(objectToSelect)) { table.select(objectToSelect); getEditedCollection().add(objectToSelect); } } public void unSelect(ET objectToDeselect) { if (table.isSelected(objectToDeselect)) { table.unselect(objectToDeselect); getEditedCollection().remove(objectToDeselect); } } /** * @return the underlaying Table * @deprecated use getTable() instead. */ @Deprecated protected Table getUnderlayingTable() { return table; } /** * @return the underlaying table implementation. Note that the component * heavily relies on some features so changing some of the configuration * options in Table is unsafe. */ public Table getTable() { return table; } public MultiSelectTable withColumnHeaderMode(Table.ColumnHeaderMode mode) { getUnderlayingTable().setColumnHeaderMode(mode); return this; } public MultiSelectTable withFullWidth() { setWidth(100, Unit.PERCENTAGE); return this; } public MultiSelectTable withHeight(String height) { setHeight(height); return this; } public MultiSelectTable withFullHeight() { return withHeight("100%"); } public MultiSelectTable withWidth(String width) { setWidth(width); return this; } public MultiSelectTable withSize(MSize mSize) { setWidth(mSize.getWidth(), mSize.getWidthUnit()); setHeight(mSize.getHeight(), mSize.getHeightUnit()); return this; } public MultiSelectTable withCaption(String caption) { setCaption(caption); return this; } public MultiSelectTable withStyleName(String... styleNames) { for (String styleName : styleNames) { addStyleName(styleName); } return this; } public MultiSelectTable withIcon(Resource icon) { setIcon(icon); return this; } public MultiSelectTable withId(String id) { table.setId(id); return this; } public MultiSelectTable expand(String... propertiesToExpand) { for (String property : propertiesToExpand) { table.setColumnExpandRatio(property, 1); } return this; } public void withRowHeaderMode(Table.RowHeaderMode rowHeaderMode) { getUnderlayingTable().setRowHeaderMode(rowHeaderMode); } public MultiSelectTable setMultiSelectMode(MultiSelectMode mode) { getTable().setMultiSelectMode(mode); return this; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy