/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * Copyright (c) 2013, MPL CodeInside http://codeinside.ru */ package ru.codeinside.gses.webui.containers; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.vaadin.addon.jpacontainer.filter.util.AdvancedFilterableSupport; import com.vaadin.data.Container; import com.vaadin.data.Item; import com.vaadin.data.Property; import org.activiti.engine.ActivitiException; import java.util.AbstractList; import java.util.Collection; import java.util.List; import java.util.Map; public class LazyLoadingContainer implements Container.Indexed, Container.Sortable { private static final long serialVersionUID = 1L; protected LazyLoadingQuery lazyLoadingQuery; public AdvancedFilterableSupport sender; protected int batchSize; protected int size = -1; protected List<Object> containerPropertyIds = Lists.newArrayList(); protected List<Object> sortablePropertyIds = Lists.newArrayList(); protected Map<Object, Class<?>> containerPropertyTypes = Maps.newHashMap(); protected Map<Object, Object> containerPropertyDefaultValues = Maps.newHashMap(); protected Map<Integer, Item> itemCache = Maps.newHashMap(); public LazyLoadingContainer(LazyLoadingQuery lazyLoadingQuery, int batchSize, AdvancedFilterableSupport newSender) { this.lazyLoadingQuery = lazyLoadingQuery; this.batchSize = batchSize; this.sender = newSender; lazyLoadingQuery.setLazyLoadingContainer(this); } public LazyLoadingContainer(LazyLoadingQuery lazyLoadingQuery, int batchSize) { this(lazyLoadingQuery, batchSize, null); } public LazyLoadingContainer(LazyLoadingQuery lazyLoadingQuery, AdvancedFilterableSupport sender) { this(lazyLoadingQuery, 15, sender); } public boolean addContainerProperty(Object propertyId, Class<?> type, Object defaultValue) throws UnsupportedOperationException { containerPropertyIds.add(propertyId); sortablePropertyIds.add(propertyId); containerPropertyTypes.put(propertyId, type); containerPropertyDefaultValues.put(propertyId, defaultValue); return true; } public void removeSortarableId(Object propertyId) { sortablePropertyIds.remove(propertyId); } public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException { containerPropertyIds.remove(propertyId); sortablePropertyIds.remove(propertyId); containerPropertyTypes.remove(propertyId); containerPropertyDefaultValues.remove(propertyId); return true; } public Collection<?> getContainerPropertyIds() { return this.containerPropertyIds; } public Class<?> getType(Object propertyId) { return (Class<?>) this.containerPropertyTypes.get(propertyId); } public Object getDefaultValue(Object propertyId) { return this.containerPropertyDefaultValues.get(propertyId); } public Property getContainerProperty(Object itemId, Object propertyId) { return getItem(itemId).getItemProperty(propertyId); } public boolean containsId(Object itemId) { Integer index = (Integer) itemId; return (index.intValue() >= 0) && (index.intValue() < size()); } public Item getItem(Object itemId) { if (itemId != null) { int start; if (!this.itemCache.containsKey(itemId)) { Integer index = (Integer) itemId; start = index.intValue() - index.intValue() % this.batchSize; List<Item> batch = this.lazyLoadingQuery.loadItems(start, this.batchSize); for (Item batchItem : batch) { this.itemCache.put(Integer.valueOf(start), batchItem); start++; } } return (Item) this.itemCache.get(itemId); } return null; } public int size() { if (this.size == -1) { this.size = this.lazyLoadingQuery.size(); } return this.size; } public Collection<?> getItemIds() { return new AbstractList<Integer>() { public int size() { return size(); } public Integer get(int index) { return Integer.valueOf(index); } }; } public Object firstItemId() { return Integer.valueOf(0); } public Object lastItemId() { return Integer.valueOf(size() - 1); } public boolean isFirstId(Object itemId) { return ((Integer) itemId).equals(Integer.valueOf(0)); } public boolean isLastId(Object itemId) { return ((Integer) itemId).equals(Integer.valueOf(size() - 1)); } public Object nextItemId(Object itemId) { Integer index = (Integer) itemId; return Integer.valueOf(index.intValue() + 1); } public Object prevItemId(Object itemId) { Integer index = (Integer) itemId; return Integer.valueOf(index.intValue() - 1); } public int indexOfId(Object itemId) { return ((Integer) itemId).intValue(); } public Object getIdByIndex(int index) { return index; } public Collection<?> getSortableContainerPropertyIds() { return sortablePropertyIds; } public void sort(Object[] propertyIds, boolean[] ascending) { removeAllItems(); this.lazyLoadingQuery.setSorting(propertyIds, ascending); } public boolean removeAllItems() throws UnsupportedOperationException { this.itemCache.clear(); this.size = -1; return true; } public int getIndexForObjectId(String id) { Item searched = this.lazyLoadingQuery.loadSingleResult(id); if (searched == null) { return -1; } return getIndexForObjectId(searched, 0, size() - 1); } public int getIndexForObjectId(Item searched, int low, int high) { if (high < low) { return -1; } int middle = low + (high - low) / 2; Item result = null; if (this.itemCache.containsKey(Integer.valueOf(middle))) { result = (Item) this.itemCache.get(Integer.valueOf(middle)); } else { result = (Item) this.lazyLoadingQuery.loadItems(middle, 1).get(0); this.itemCache.put(Integer.valueOf(middle), result); } int comparison = compareItem(searched, result); if (comparison < 0) return getIndexForObjectId(searched, low, middle - 1); if (comparison > 0) { return getIndexForObjectId(searched, middle + 1, high); } return middle; } @SuppressWarnings("unchecked") private int compareItem(Item searched, Item result) { if ((!(searched instanceof Comparable)) || (!(result instanceof Comparable))) { throw new ActivitiException("Cannot use the getIndexForObjectId method for non-Comparables"); } return ((Comparable<Item>) searched).compareTo(result); } public Object addItem() throws UnsupportedOperationException { throw new UnsupportedOperationException(); } public Object addItemAfter(Object previousItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } public Item addItemAfter(Object previousItemId, Object newItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } public Object addItemAt(int index) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } public Item addItemAt(int index, Object newItemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } public boolean removeItem(Object itemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } public Item addItem(Object itemId) throws UnsupportedOperationException { throw new UnsupportedOperationException(); } }