org.jdal.vaadin.data.ContainerDataSource.java Source code

Java tutorial

Introduction

Here is the source code for org.jdal.vaadin.data.ContainerDataSource.java

Source

/*
 * Copyright 2009-2011 the original author or authors.
 *
 * 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 org.jdal.vaadin.data;

import java.beans.PropertyDescriptor;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdal.beans.PropertyUtils;
import org.jdal.dao.Dao;
import org.jdal.dao.Page;
import org.springframework.beans.BeanInstantiationException;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DataAccessException;

import com.vaadin.data.Buffered;
import com.vaadin.data.Container;
import com.vaadin.data.Container.Indexed;
import com.vaadin.data.Container.ItemSetChangeNotifier;
import com.vaadin.data.Container.Sortable;
import com.vaadin.data.Item;
import com.vaadin.data.Item.PropertySetChangeListener;
import com.vaadin.data.Property;
import com.vaadin.data.Validator.InvalidValueException;
import com.vaadin.data.util.BeanItem;

/**
 * <p>
 * An adapter to use PageableDataSources as Vaadin Container.
 * Use a integer as itemId and load data by page from data source on
 * request.
 * </p>
 * 
 * @author Jose Luis Martin - (jlm@joseluismartin.info)
 */
public class ContainerDataSource<T>
        implements Container, Sortable, Indexed, ItemSetChangeNotifier, PropertySetChangeListener, Buffered {

    private static final long serialVersionUID = 1L;
    private static final Log log = LogFactory.getLog(ContainerDataSource.class);

    private Page<T> page = new Page<T>();

    private Dao<T, ? extends Serializable> service;
    private List<String> sortableProperties;
    private List<BeanItem<T>> items = new ArrayList<BeanItem<T>>();
    private Class<T> entityClass;
    private List<ItemSetChangeListener> listeners = new ArrayList<ItemSetChangeListener>();
    private ItemIdStrategy itemIdStrategy;

    private Map<Object, Item> dirtyItems = new HashMap<Object, Item>();
    private Map<Object, Item> newItems = new HashMap<Object, Item>();

    private boolean readThrough = false;
    private boolean writeThrough = false;
    private String sortProperty;

    public ContainerDataSource() {
        this(null, null);
    }

    public ContainerDataSource(Class<T> entityClass) {
        this(entityClass, null);
    }

    public ContainerDataSource(Dao<T, ? extends Serializable> service) {
        this.service = service;
        this.entityClass = service.getEntityClass();
        setItemIdStrategy(new IndexedItemIdStrategy());
    }

    public ContainerDataSource(Class<T> entityClass, Dao<T, ? extends Serializable> service) {
        this.service = service;
        this.entityClass = entityClass;
        setItemIdStrategy(new IndexedItemIdStrategy());
    }

    public void init() {
        page.setSortName(getSortProperty());
        loadPage();
    }

    /**
     * {@inheritDoc}
     */
    public Object nextItemId(Object itemId) {
        return isLastId(itemId) ? null : getIdByIndex(indexOfId(itemId) + 1);
    }

    /**
     * {@inheritDoc}
     */
    public Object prevItemId(Object itemId) {
        return isFirstId(itemId) ? null : getIdByIndex(indexOfId(itemId) - 1);
    }

    /**
     * {@inheritDoc}
     */
    public Object firstItemId() {
        return itemIdStrategy.firstItemId();
    }

    /**
     * {@inheritDoc}
     */
    public Object lastItemId() {
        return itemIdStrategy.lastItemId();
    }

    /**
     * {@inheritDoc}
     */
    public boolean isFirstId(Object itemId) {
        return indexOfId(itemId) == 0;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isLastId(Object itemId) {
        return indexOfId(itemId) == page.getCount() - 1;
    }

    /**
     * {@inheritDoc}
     */
    public Object addItemAfter(Object previousItemId) throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new Items after to container");
    }

    /**
     * {@inheritDoc}
     */
    public Item addItemAfter(Object previousItemId, Object newItemId) throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new records after to container");
    }

    /**
     * {@inheritDoc}
     */
    public int indexOfId(Object itemId) {
        return itemIdStrategy.indexOfId(itemId);
    }

    /**
     * {@inheritDoc}
     */
    public Object getIdByIndex(int index) {
        return itemIdStrategy.getIdByIndex(index);
    }

    /**
     * {@inheritDoc}
     */
    public Object addItemAt(int index) throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new records to container");
    }

    /**
     * {@inheritDoc}
     */
    public Item addItemAt(int index, Object newItemId) throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new records to container");
    }

    /**
     * {@inheritDoc}
     */
    public void sort(Object[] propertyId, boolean[] ascending) {
        // only use the first property :I
        page.setSortName(propertyId[0].toString());
        page.setOrder(ascending[0] ? Page.Order.ASC : Page.Order.DESC);
        loadPage();
        fireItemSetChange();
    }

    /**
     * {@inheritDoc}
     */
    public Collection<?> getSortableContainerPropertyIds() {
        if (sortableProperties != null)
            return sortableProperties;

        if (entityClass != null) {
            List<String> properties = new LinkedList<String>();
            PropertyDescriptor[] pds = BeanUtils.getPropertyDescriptors(entityClass);
            for (PropertyDescriptor pd : pds)
                properties.add(pd.getName());

            return properties;
        }

        // if we have data will try introspection
        if (page.getData().size() > 0) {
            BeanItem<T> item = items.get(0);
            return item.getItemPropertyIds();
        }

        return new LinkedList<Object>();
    }

    /**
     * {@inheritDoc}
     */
    public Item getItem(Object itemId) {
        return itemIdStrategy.getItem(itemId);

    }

    public int getPageContaining(int index) {
        return index / page.getPageSize() + 1;
    }

    /**
     * {@inheritDoc}
     */
    public Collection<?> getContainerPropertyIds() {
        BeanItem<T> item = newItem();
        if (item != null)
            return item.getItemPropertyIds();

        // if we have data will try introspection
        if (page.getData().size() > 0)
            return items.get(0).getItemPropertyIds();

        // this is an error...
        log.error("Can't get property ids from entityClass or data");
        return new LinkedList<Object>();
    }

    /**
     * {@inheritDoc}
     */
    public Collection<?> getItemIds() {
        return itemIdStrategy.getItemIds();
    }

    /**
     * {@inheritDoc}
     */
    public Property getContainerProperty(Object itemId, Object propertyId) {
        Item item = getItem(itemId);
        return item != null ? item.getItemProperty(propertyId) : null;
    }

    /**
     * {@inheritDoc}
     */
    public Class<?> getType(Object propertyId) {
        if (entityClass != null) {
            return PropertyUtils.getPropertyDescriptor(entityClass, (String) propertyId).getPropertyType();
        }

        // if we have data will try introspection
        if (page.getData().size() > 0) {
            BeanItem<T> item = items.get(0);
            return item.getBean().getClass();
        }

        return Object.class;
    }

    /**
     * {@inheritDoc}
     */
    public int size() {
        return page.getCount();
    }

    /**
     * {@inheritDoc}
     */
    public boolean containsId(Object itemId) {
        return itemIdStrategy.containsId(itemId);
    }

    /**
     * {@inheritDoc}
     */
    public Item addItem(Object itemId) throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new records to container");
    }

    /**
     * {@inheritDoc}
     */
    public Object addItem() throws UnsupportedOperationException {
        BeanItem<T> newItem = newItem();

        if (newItem != null) {
            newItem.addListener(this);
            newItems.put(newItem.getBean(), newItem);
        }

        return newItem;
    }

    private BeanItem<T> newItem() {
        T bean = null;
        try {
            bean = BeanUtils.instantiate(entityClass);
        } catch (BeanInstantiationException be) {
            log.error(be);
            return null;
        }

        BeanItem<T> newItem = new BeanItem<T>(bean);
        return newItem;
    }

    /**
     * {@inheritDoc}
     */
    public boolean removeItem(Object itemId) {
        if (!containsId(itemId))
            return false;

        int index = (Integer) itemId;

        if (isInPage(index)) {
            service.delete(page.getData().get(globalToPage(index)));
            loadPage();
        } else {
            Page<T> oneItem = new Page<T>(1, index);
            oneItem.setFilter(page.getFilter());
            service.getPage(oneItem);
            service.delete(oneItem.getData().get(0));
            page.setCount(page.getCount() - 1);
        }

        return true;
    }

    /**
     * {@inheritDoc}
     */
    public boolean addContainerProperty(Object propertyId, Class<?> type, Object defaultValue)
            throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new properties to container");
    }

    /**
     * {@inheritDoc}
     */
    public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException {
        throw new UnsupportedOperationException(
                "ContainerDataSourceAdapter don't support adding new properties to container");
    }

    /**
     * {@inheritDoc}
     */
    public boolean removeAllItems() throws UnsupportedOperationException {
        try {
            Page<T> all = page.clone();
            all.setPageSize(Integer.MAX_VALUE);
            service.delete(all.getData());
        } catch (DataAccessException dae) {
            return false;
        }

        return true;
    }

    private void loadPage() {
        service.getPage(page);
        int index = 0;
        items.clear();
        for (T t : page.getData()) {

            BeanItem<T> item = getDirtyOrCreate(t);
            item.addListener(this);
            items.add(item);
            itemIdStrategy.itemLoaded(pageToGlobal(index++), item);
        }
    }

    /**
     * @param t
     * @return bean item
     */
    @SuppressWarnings("unchecked")
    private BeanItem<T> getDirtyOrCreate(T t) {
        BeanItem<T> item = new BeanItem<T>(t);
        if (readThrough && dirtyItems.containsKey(t))
            item = (BeanItem<T>) dirtyItems.get(t);

        return item;
    }

    /**
     * @param i
     * @return
     */
    private int pageToGlobal(int index) {
        return page.getStartIndex() + index;
    }

    /**
     * Convert global index to page index.
     * @param index global index
     * @return the index in current page
     */
    private int globalToPage(int index) {
        return index - page.getStartIndex();
    }

    private boolean isInPage(int index) {
        return globalToPage(index) >= 0 && globalToPage(index) < page.getPageSize();
    }

    public Dao<T, ? extends Serializable> getService() {
        return service;
    }

    public void setService(Dao<T, Serializable> service) {
        this.service = service;
    }

    public void setPage(Page<T> page) {
        this.page = page;
        loadPage();
    }

    public List<String> getSortableProperties() {
        return sortableProperties;
    }

    public void setSortableProperties(List<String> sortableProperties) {
        this.sortableProperties = sortableProperties;
    }

    public void addListener(ItemSetChangeListener listener) {
        if (!listeners.contains(listener))
            listeners.add(listener);
    }

    public void removeListener(ItemSetChangeListener listener) {
        listeners.remove(listener);
    }

    /**
     * Notity listeners that the item set change
     */
    private void fireItemSetChange() {
        ItemSetChangeEvent isce = new ItemSetChangeEvent() {

            public Container getContainer() {
                return ContainerDataSource.this;
            }
        };
        // must be first 
        itemIdStrategy.containerItemSetChange(isce);

        for (ItemSetChangeListener listener : listeners) {
            listener.containerItemSetChange(isce);
        }
    }

    /**
     * Set the page size
     * @param pageSize the page size to set
     */
    public void setPageSize(int pageSize) {
        page.setPageSize(pageSize);
        loadPage();
    }

    /**
     * Get the page size
     * @return the page size
     */
    public int getPageSize() {
        return page.getPageSize();
    }

    /**
     * @return filter Object
     * @see info.joseluismartin.dao.Page#getFilter()
     */
    public Object getFilter() {
        return page.getFilter();
    }

    /**
     * @param filter
     * @see info.joseluismartin.dao.Page#setFilter(java.lang.Object)
     */
    public void setFilter(Object filter) {
        page.setFilter(filter);
        loadPage();
        fireItemSetChange();
    }

    /**
     * Get all keys from pageable datasource
     */
    public List<Serializable> getKeys() {
        Page<T> p = new Page<T>(Integer.MAX_VALUE);
        p.setFilter(page.getFilter());
        p.setSortName(page.getSortName());
        p.setOrder(page.getOrder());

        return service.getKeys(p);
    }

    /**
     * @return the itemIdStrategy
     */
    public ItemIdStrategy getItemIdStrategy() {
        return itemIdStrategy;
    }

    /**
     * @param itemIdStrategy the itemIdStrategy to set
     */
    public void setItemIdStrategy(ItemIdStrategy itemIdStrategy) {
        this.itemIdStrategy = itemIdStrategy;
        itemIdStrategy.setContainerDataSource(this);
    }

    /**
     * @param index
     */
    public Item getItemByIndex(int index) {

        if (!isInPage(index)) {
            if (log.isDebugEnabled())
                log.debug("Page fault on index: " + index);
            page.setPage(getPageContaining(index));
            loadPage();
        }
        int pageIndex = globalToPage(index);

        return pageIndex < items.size() ? items.get(pageIndex) : null;
    }

    /**
     * {@inheritDoc}
     */
    @SuppressWarnings("unchecked")
    public void itemPropertySetChange(com.vaadin.data.Item.PropertySetChangeEvent event) {
        dirtyItems.put(((BeanItem<T>) event.getItem()).getBean(), event.getItem());
        if (isWriteThrough())
            commit();
    }

    /** 
     * Save changes to Persistent Service
     * @return true if all items was updated.
     */
    @SuppressWarnings("unchecked")
    public boolean save() {

        // insert news 
        for (Item i : newItems.values()) {
            try {
                BeanItem<T> bi = (BeanItem<T>) i;
                service.save(bi.getBean());
                newItems.remove(bi.getBean());
            } catch (DataAccessException dae) {
                log.error(dae);
            }
        }

        // update dirties
        for (Item i : dirtyItems.values()) {
            try {
                BeanItem<T> bi = (BeanItem<T>) i;
                service.save(bi.getBean());
                dirtyItems.remove(bi.getBean());
            } catch (DataAccessException dae) {
                log.error(dae);
            }
        }

        return newItems.isEmpty() && dirtyItems.isEmpty();
    }

    /**
     * {@inheritDoc}
     */
    public void commit() throws SourceException, InvalidValueException {
        if (!save()) {
            // lost changes?
            discard();
            throw new SourceException(this);
        }
    }

    /**
     * {@inheritDoc}
     */
    public void discard() throws SourceException {
        newItems.clear();
        dirtyItems.clear();

    }

    /**
     * {@inheritDoc}
     */
    public boolean isModified() {
        return dirtyItems.size() > 0 || newItems.size() > 0;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isReadThrough() {
        return readThrough;
    }

    /**
     * {@inheritDoc}
     */
    public boolean isWriteThrough() {
        return writeThrough;
    }

    /**
     * {@inheritDoc}
     */
    public void setReadThrough(boolean readThrough) throws SourceException {
        this.readThrough = readThrough;

    }

    /**
     * {@inheritDoc}
     */
    public void setWriteThrough(boolean writeThrough) throws SourceException, InvalidValueException {
        this.writeThrough = writeThrough;

    }

    /**
     * Gets the page
     * @return the page
     */
    public Page<T> getPage() {
        return page;
    }

    /**
     * @return the sortProperty
     */
    public String getSortProperty() {
        return sortProperty;
    }

    /**
     * @param sortProperty the sortBy to set
     */
    public void setSortProperty(String sortProperty) {
        this.sortProperty = sortProperty;
    }

    public void setBuffered(boolean buffered) {
        // TODO Auto-generated method stub

    }

    public boolean isBuffered() {
        // TODO Auto-generated method stub
        return false;
    }

    public void addItemSetChangeListener(ItemSetChangeListener listener) {
        // TODO Auto-generated method stub

    }

    public void removeItemSetChangeListener(ItemSetChangeListener listener) {
        // TODO Auto-generated method stub

    }

    public List<?> getItemIds(int startIndex, int numberOfItems) {
        return this.itemIdStrategy.getItemIds(startIndex, numberOfItems);
    }
}