/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package de.unioninvestment.eai.portal.support.vaadin.container; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.Property; import com.vaadin.data.util.AbstractInMemoryContainer; import com.vaadin.data.util.filter.UnsupportedFilterException; /** * Generische Implementierung des {@link Container}, erweitert um * Transaktionalität, Event-Handling usw. * * @author carsten.mjartan */ public class GenericVaadinContainer extends AbstractInMemoryContainer<GenericItemId, String, GenericItem> implements Container, Container.Filterable, Container.Sortable, Container.ItemSetChangeNotifier { private static final long serialVersionUID = 1L; private final GenericDelegate delegate; private final MetaData metaData; private Map<GenericItemId, GenericItem> items = new HashMap<GenericItemId, GenericItem>(); private Map<GenericItemId, GenericItem> modifiedItems = new HashMap<GenericItemId, GenericItem>(); private Map<GenericItemId, GenericItem> addedItems = new HashMap<GenericItemId, GenericItem>(); private Map<GenericItemId, GenericItem> deletedItems = new HashMap<GenericItemId, GenericItem>(); private boolean filteringByDelegate; /** * @param delegate * die Backend-Klasse für CRUD-Operationen */ public GenericVaadinContainer(GenericDelegate delegate) { this.delegate = delegate; this.metaData = delegate.getMetaData(); filteringByDelegate = false; refresh(); } @Override public Collection<?> getContainerPropertyIds() { return metaData.getColumnNames(); } @Override public Class<?> getType(Object propertyId) { return metaData.getColumnType(propertyId.toString()); } /** * Verwendung der Default-Implementierung der Superklasse (Spalten mit * primitiven Datentypen oder Comparable Interface) * * {@inheritDoc} */ @Override public Collection<?> getSortableContainerPropertyIds() { return super.getSortablePropertyIds(); } /** * Verwendung des Standardsortieralgorithmus der Superklasse * * {@inheritDoc} */ @Override public void sort(Object[] propertyId, boolean[] ascending) { super.sortContainer(propertyId, ascending); } @Override public void addContainerFilter(Filter filter) throws UnsupportedFilterException { super.addFilter(filter); } @Override public void removeContainerFilter(Filter filter) { super.removeFilter(filter); } @Override public void removeAllContainerFilters() { super.removeAllFilters(); } @Override public Collection<Filter> getContainerFilters() { return super.getContainerFilters(); } /** * @param filter * die Liste der Filter als Ersatz */ public void replaceContainerFilter(Filter filter) { super.removeAllFilters(); super.addFilter(filter); } @SuppressWarnings("rawtypes") @Override public Property getContainerProperty(Object itemId, Object propertyId) { return getItem(itemId).getItemProperty(propertyId); } @Override protected void registerNewItem(int position, GenericItemId itemId, GenericItem item) { items.put(itemId, item); } @Override protected GenericItem getUnfilteredItem(Object itemId) { return items.get(itemId); } /** * @return <code>true</code>, falls Inhalte seit dem letzten commit/rollback * geändert wurden */ public boolean isModified() { return numberOfModifiedItems() > 0; } /** * Lädt die Container-Inhalte neu aus dem Backend. */ public void refresh() { internalRemoveAllItems(); items.clear(); for (Object[] cells : delegate.getRows()) { GenericItemId newItemId = createItemId(cells); if (modifiedItems.containsKey(newItemId)) { GenericItem item = modifiedItems.get(newItemId); internalAddItemAtEnd(newItemId, item, false); } else { GenericItem item = createItem(newItemId, cells); internalAddItemAtEnd(newItemId, item, false); } } if (!filteringByDelegate) { removeDroppedModifiedItems(); } filterAll(); fireContentsChange(); } private void removeDroppedModifiedItems() { for (Iterator<Entry<GenericItemId, GenericItem>> it = modifiedItems .entrySet().iterator(); it.hasNext();) { Entry<GenericItemId, GenericItem> entry = it.next(); if (!items.containsKey(entry.getKey())) { it.remove(); } } } @Override public boolean removeItem(Object itemId) throws UnsupportedOperationException { GenericItem item = items.remove(itemId); if (item == null) { return false; } internalRemoveItem(itemId); if (!item.isNewItem() || addedItems.remove(itemId) == null) { deletedItems.put((GenericItemId) itemId, item); } return true; }; /** * @param cells * Liste der Spaltenwerte des {@link Item} in Reihenfolge der * MetaDaten * @return die ID */ GenericItemId createItemId(Object[] cells) { Object[] id = new Object[metaData.getPrimaryKeys().size()]; int idx = 0; for (String pk : metaData.getPrimaryKeys()) { id[idx++] = cells[metaData.getIndex(pk)]; } return new GenericItemId(id); } /** * @param id * die ID der Zeile * @param cells * die Spaltenwerte * @return die generierte Zeile */ public GenericItem createItem(GenericItemId id, Object[] cells) { @SuppressWarnings("rawtypes") Collection<GenericProperty> properties = new ArrayList<GenericProperty>( cells.length); int idx = 0; for (Column column : metaData.getColumns()) { properties.add(new GenericProperty<Object>(column, cells[idx++])); } return new GenericItem(this, id, properties); } @Override public Object addItem() throws UnsupportedOperationException { GenericItemId newItemId = new TemporaryItemId(new Object[metaData .getPrimaryKeys().size()]); GenericItem item = createItem(newItemId, new Object[metaData .getColumns().size()]); created(item); internalAddItemAtEnd(newItemId, item, true); addedItems.put(newItemId, item); return newItemId; } /** * Methode, die bei der Erstellung einer neuen Zeile per addItem() * aufgerufen wird. * * @param item * eine neue Zeile */ protected void created(GenericItem item) { // to be overwritten } /** * Erlaubt das Anstoßen des Rerenderings bei Table ohne dass die Datenbank * gepollt wird. */ public void fireContentsChange() { super.fireItemSetChange(); } /** * Rollback der seit dem letzten commit() gemachten Änderungen. */ public void rollback() { addedItems.clear(); modifiedItems.clear(); deletedItems.clear(); refresh(); } /** * This is called every time an item property has changed. * * @param item */ void itemChangeNotification(GenericItem item) { if (!item.isNewItem()) { modifiedItems.put(item.getId(), item); } } /** * Committed gemachte Änderungen in das Backend. */ public void commit() { int size = numberOfModifiedItems(); if (size > 0) { ArrayList<GenericItem> items = new ArrayList<GenericItem>(size); items.addAll(addedItems.values()); items.addAll(modifiedItems.values()); items.addAll(deletedItems.values()); UpdateContext context = new UpdateContext(); delegate.update(items, context); committed(items); addedItems.clear(); modifiedItems.clear(); deletedItems.clear(); if (context.isRefreshRequired()) { refresh(); } else { fireContentsChange(); } } else { // der Content hat sich hier beim generischen Container nicht // geändert, aber Table benötigt es für das Re-Rendering der // Anzeige fireContentsChange(); } } /** * Methode die nach eineme erfolgreichem Commit aufgerufen wird * * @param items * die neuen/geänderten/gelöschten Zeilen */ protected void committed(ArrayList<GenericItem> items) { // to be overridden } private int numberOfModifiedItems() { return addedItems.size() + modifiedItems.size() + deletedItems.size(); } boolean isDeleted(GenericItemId itemId) { return deletedItems.containsKey(itemId); } @Override protected boolean passesFilters(Object itemId) { if (itemId instanceof TemporaryItemId) { return true; } return super.passesFilters(itemId); } }