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

com.holonplatform.vaadin7.internal.data.DefaultItemStore Maven / Gradle / Ivy

/*
 * Copyright 2016-2017 Axioma srl.
 * 
 * Licensed 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 com.holonplatform.vaadin7.internal.data;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Collectors;

import com.holonplatform.core.internal.utils.ObjectUtils;
import com.holonplatform.core.query.QueryConfigurationProvider;
import com.holonplatform.vaadin7.data.ItemDataProvider;
import com.holonplatform.vaadin7.data.ItemIdentifierProvider;
import com.holonplatform.vaadin7.data.ItemDataSource.ItemAction;

/**
 * Default {@link ItemStore} implementation using {@link ItemDataProvider} to load items on demand.
 * 
 * @since 5.0.0
 */
public class DefaultItemStore implements ItemStore {

	private static final long serialVersionUID = 3190254710573117380L;

	/**
	 * Data source configuration
	 */
	private final QueryConfigurationProvider configuration;

	/**
	 * {@link ItemDataProvider} factory
	 */
	private final Supplier> dataProviderFactory;

	/**
	 * Current query
	 */
	private ItemDataProvider query;

	/**
	 * Item identifier provider
	 */
	private final ItemIdentifierProvider itemIdentifierProvider;

	/**
	 * Current query size
	 */
	private int querySize = -1;

	/**
	 * Item ids
	 */
	private List itemIds;

	/**
	 * Items cache
	 */
	private ItemCacheMap itemCache;

	/**
	 * List of added items since last commit or discard
	 */
	private List addedItems;
	/**
	 * List of modified items since last commit or discard
	 */
	private List modifiedItems;
	/**
	 * List of deleted items since last commit or discard
	 */
	private List removedItems;

	/**
	 * Item action listeners
	 */
	private final List> itemActionListeners = new LinkedList<>();

	/**
	 * Freezed state
	 */
	private boolean freezed;

	/**
	 * Batch size
	 */
	private final int batchSize;

	/**
	 * Constructor
	 * @param configuration Data source configuration (not null)
	 * @param dataProvider Items provider (not null)
	 * @param itemIdentifierProvider Item identifier provider
	 * @param maxCacheSize Max cache size
	 */
	public DefaultItemStore(QueryConfigurationProvider configuration, ItemDataProvider dataProvider,
			ItemIdentifierProvider itemIdentifierProvider, int batchSize, int maxCacheSize) {
		super();
		ObjectUtils.argumentNotNull(configuration, "Configuration must be not null");
		ObjectUtils.argumentNotNull(dataProvider, "ItemDataProvider must be not null");
		this.configuration = configuration;
		this.dataProviderFactory = () -> dataProvider;
		this.itemIdentifierProvider = itemIdentifierProvider;
		this.batchSize = batchSize;
		this.itemCache = new ItemCacheMap<>(maxCacheSize);
	}

	/**
	 * Get the data source configuration
	 * @return Data source configuration
	 */
	protected QueryConfigurationProvider getConfiguration() {
		return configuration;
	}

	/**
	 * Get the batch size
	 * @return the batch size
	 */
	public int getBatchSize() {
		return batchSize;
	}

	/**
	 * Get the items cache
	 * @return the items cache
	 */
	protected ItemCacheMap getItemCache() {
		return itemCache;
	}

	/*
	 * (non-Javadoc)
	 * @see
	 * com.holonplatform.vaadin.internal.data.ItemStore#addItemActionListener(com.holonplatform.vaadin.internal.data.
	 * ItemStore.ItemActionListener)
	 */
	@Override
	public void addItemActionListener(ItemActionListener listener) {
		ObjectUtils.argumentNotNull(listener, "ItemActionListener must be not null");
		itemActionListeners.add(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.internal.data.ItemStore#removeItemActionListener(com.holonplatform.vaadin.internal.
	 * data.ItemStore.ItemActionListener)
	 */
	@Override
	public void removeItemActionListener(ItemActionListener listener) {
		ObjectUtils.argumentNotNull(listener, "ItemActionListener must be not null");
		itemActionListeners.remove(listener);
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.internal.container.ItemStore#setFreezed(boolean)
	 */
	@Override
	public void setFreezed(boolean freezed) {
		this.freezed = freezed;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.internal.container.ItemStore#isFreezed()
	 */
	@Override
	public boolean isFreezed() {
		return freezed;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#getMaxCacheSize()
	 */
	@Override
	public int getMaxCacheSize() {
		return itemCache.getMaxSize();
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#setMaxCacheSize(int)
	 */
	@Override
	public void setMaxCacheSize(int maxCacheSize) {
		reset(false, false);
		this.itemCache = new ItemCacheMap<>(maxCacheSize);
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#size()
	 */
	@Override
	public int size() {
		return isFreezed() ? 0 : (getItemQuerySize() + getAddedItemsSize() - getRemovedItemsSize());
	}

	/**
	 * Size of added items
	 * @return Added items count
	 */
	protected int getAddedItemsSize() {
		return (addedItems == null) ? 0 : addedItems.size();
	}

	/**
	 * Size of removed items
	 * @return Removed items count
	 */
	protected int getRemovedItemsSize() {
		return (removedItems == null) ? 0 : removedItems.size();
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#getItemIds()
	 */
	@Override
	public List getItemIds() {
		if (isFreezed()) {
			return Collections.emptyList();
		}
		if (itemIds == null) {
			if (itemIdentifierProvider != null) {
				itemIds = new ItemStoreIds<>(this, itemIdentifierProvider);
			} else {
				itemIds = new NaturalNumberIdsList(size());
			}
		}
		return itemIds;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#indexOfItem(java.lang.Object)
	 */
	@Override
	public int indexOfItem(Object itemId) {
		return isFreezed() ? -1 : getItemIds().indexOf(itemId);
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#containsItem(java.lang.Object)
	 */
	@Override
	public boolean containsItem(Object itemId) {
		return isFreezed() ? false : getItemIds().contains(itemId);
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#getItem(int)
	 */
	@Override
	public ITEM getItem(int index) {
		if (index < 0 || index >= size()) {
			throw new IndexOutOfBoundsException("Container size: " + size() + " and item index requested: " + index);
		}

		if (isFreezed()) {
			return null;
		}

		final int addedItemCount = getAddedItemsSize();
		if (addedItemCount > 0 && index < addedItemCount) {
			// an item from the addedItems was requested
			return addedItems.get(index);
		}
		// get from cache or load using query
		final int itemIndex = index - addedItemCount;
		ITEM item = getItemCache().getItem(itemIndex);
		if (item == null) {
			// item not in cache, query for more items
			return loadItem(itemIndex);
		}
		return item;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.internal.data.ItemStore#refreshItem(java.lang.Object)
	 */
	@Override
	public void refreshItem(ITEM item) {
		ObjectUtils.argumentNotNull(item, "Item to refresh must be not null");
		int index = indexOfItem(getItemId(item));
		if (index > -1) {
			ITEM refreshed = getItemQuery().refresh(item);
			if (refreshed != null) {
				refreshItem(refreshed, index);
			}
		}
	}

	/**
	 * Refresh given Item instance at given item index
	 * @param item Item to refresh
	 * @param index Item index
	 * @return true if found and refreshed
	 */
	protected boolean refreshItem(ITEM item, int index) {
		ITEM previous = null;
		final int addedItemCount = getAddedItemsSize();
		if (addedItemCount > 0 && index < addedItemCount) {
			previous = addedItems.get(index);
			addedItems.remove(index);
			addedItems.add(index, item);
		} else {
			int cacheIndex = index - addedItemCount;
			previous = getItemCache().putItem(cacheIndex, item);
		}

		// fire listeners
		fireItemActionListeners(item, getItemId(item), previous, ItemAction.REFRESHED);

		return previous != null;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#addItem(com.vaadin.data.Item)
	 */
	@Override
	public Object addItem(ITEM item) {
		if (item != null) {

			Object itemId = getNewItemId(item);

			if (addedItems == null) {
				addedItems = new ArrayList<>();
			}
			addedItems.add(0, item);

			// fire listeners
			fireItemActionListeners(item, itemId, null, ItemAction.ADDED);

			return itemId;

		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#removeItem(int)
	 */
	@Override
	public boolean removeItem(int index) {
		final ITEM item = getItem(index);
		if (item != null) {
			if (removedItems == null) {
				removedItems = new ArrayList<>();
			}
			removedItems.add(item);

			// fire listeners
			fireItemActionListeners(item, getItemId(item), null, ItemAction.REMOVED);

			return true;
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.internal.data.ItemStore#removeItem(java.lang.Object)
	 */
	@Override
	public boolean removeItem(ITEM item) {
		if (item != null) {
			if (removedItems == null) {
				removedItems = new ArrayList<>();
			}
			removedItems.add(item);

			// fire listeners
			fireItemActionListeners(item, getItemId(item), null, ItemAction.REMOVED);

			return true;
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#setItemModified(com.vaadin.data.Item)
	 */
	@Override
	public void setItemModified(ITEM item) {
		if (item != null) {
			if (addedItems == null || !addedItems.contains(item)) {
				if (modifiedItems == null) {
					modifiedItems = new ArrayList<>();
				}
				if (!modifiedItems.contains(item)) {
					modifiedItems.add(item);
				}

				// fire listeners
				fireItemActionListeners(item, getItemId(item), null, ItemAction.MODIFIED);
			}
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#isModified()
	 */
	@Override
	public boolean isModified() {
		return getAddedItemsSize() > 0 || getModifiedItems().size() > 0 || getRemovedItems().size() > 0;
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#getAddedItems()
	 */
	@Override
	public List getAddedItems() {
		if (addedItems != null) {
			return Collections.unmodifiableList(addedItems);
		}
		return Collections.emptyList();
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#getModifiedItems()
	 */
	@Override
	public List getModifiedItems() {
		if (modifiedItems != null) {
			return Collections.unmodifiableList(modifiedItems);
		}
		return Collections.emptyList();
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#getRemovedItems()
	 */
	@Override
	public List getRemovedItems() {
		if (removedItems != null) {
			return Collections.unmodifiableList(removedItems);
		}
		return Collections.emptyList();
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#clear()
	 */
	@Override
	public void reset(boolean fireListeners, boolean freeze) {
		freezed = freeze;
		query = null;
		itemIds = null;
		if (getItemCache() != null) {
			getItemCache().clear();
		}
		discard();

		// fire listeners
		if (fireListeners) {
			fireItemActionListeners(null, null, null, ItemAction.SET_CHANGED);
		}
	}

	/*
	 * (non-Javadoc)
	 * @see com.holonplatform.vaadin.data.ItemStore#discard()
	 */
	@Override
	public void discard() {
		addedItems = null;
		modifiedItems = null;
		removedItems = null;
	}

	/**
	 * Get the query size.
	 * @return the query size
	 */
	protected int getItemQuerySize() {
		getItemQuery();
		return querySize;
	}

	/**
	 * Get current {@link ItemDataProvider} or build a new one if not available.
	 * @return Item query
	 */
	protected ItemDataProvider getItemQuery() {
		if (query == null) {
			query = dataProviderFactory.get();
			// size
			querySize = (int) query.size(getConfiguration());
		}
		return query;
	}

	/**
	 * Load item for given index and the surrounding batch of items
	 * @param index The index of requested item
	 * @return Loaded Item, if found
	 */
	protected ITEM loadItem(final int index) {

		ITEM requestedItem = null;

		final int batchSize = getBatchSize();
		final int startIndex = index - index % batchSize;
		final int count = Math.min(batchSize, getItemQuerySize() - startIndex);

		// load more items using query
		List items = getItemQuery().load(getConfiguration(), startIndex, count).collect(Collectors.toList());
		if (items == null) {
			items = Collections.emptyList();
		}

		// put Items in cache and setup value change listeners
		for (int i = 0; i < count; i++) {
			final int itemIndex = startIndex + i;
			if (i < items.size()) {
				ITEM item = items.get(i);
				if (itemIndex == index) {
					// requested item
					requestedItem = item;
				}
				ITEM previous = getItemCache().putItem(itemIndex, item);

				// fire listeners
				fireItemActionListeners(item, null, previous, ItemAction.LOADED);
			}
		}

		return requestedItem;
	}

	/**
	 * Get the id of given item
	 * @param item Item to obtain the id for
	 * @return Item id
	 */
	@Override
	public Object getItemId(ITEM item) {
		if (itemIdentifierProvider != null) {
			return itemIdentifierProvider.getItemId(item);
		} else {
			for (int i = 0; i < size(); i++) {
				ITEM itm = getItem(i);
				if (itm != null && itm.equals(item)) {
					return i;
				}
			}
		}
		return null;
	}

	/**
	 * Get the id of an added item
	 * @param item Added item
	 * @return Item id
	 */
	protected Object getNewItemId(ITEM item) {
		if (itemIdentifierProvider != null) {
			Object itemId = itemIdentifierProvider.getItemId(item);
			if (itemId == null) {
				throw new IllegalStateException("Null item id for added item: " + item);
			}
			return itemId;
		} else {
			itemIds = null;
			return 0;
		}
	}

	/**
	 * Fire registered {@link ItemActionListener}s.
	 * @param item Item subject of the action
	 * @param itemId Item id
	 * @param previous Previous item, if available
	 * @param action Action type
	 */
	protected void fireItemActionListeners(ITEM item, Object itemId, ITEM previous, ItemAction action) {
		for (ItemActionListener listener : itemActionListeners) {
			listener.onItemAction(item, itemId, previous, action);
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy