Project: activiti-explorer
/* 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.activiti.explorer.data; 
 
import java.util.AbstractList; 
import java.util.ArrayList; 
import java.util.Collection; 
import java.util.HashMap; 
import java.util.List; 
import java.util.Map; 
 
import org.activiti.engine.ActivitiException; 
 
import com.vaadin.data.Container; 
import com.vaadin.data.Item; 
import com.vaadin.data.Property; 
 
 
 
/**
 * Inspired on the LazyQueryContainer add-on for Vaadin 
 * (https://github.com/tlaukkan/vaadin-lazyquerycontainer)  
 *  
 * @author Joram Barrez 
 */
 
public class LazyLoadingContainer implements Container.Indexed, Container.Sortable{ 
 
  private static final long serialVersionUID = 1L
   
  protected LazyLoadingQuery lazyLoadingQuery; 
  protected int batchSize; 
  protected int size = -1
   
  protected List<Object> containerPropertyIds = new ArrayList<Object>(); 
  protected Map<Object, Class<?>> containerPropertyTypes = new HashMap<Object, Class<?>>(); 
  protected Map<Object, Object> containerPropertyDefaultValues = new HashMap<Object, Object>(); 
   
  protected Map<Integer, Item> itemCache = new HashMap<Integer, Item>(); 
   
  public LazyLoadingContainer(LazyLoadingQuery lazyLoadingQuery, int batchSize) { 
    this.lazyLoadingQuery = lazyLoadingQuery; 
    this.batchSize = batchSize; 
    lazyLoadingQuery.setLazyLoadingContainer(this); 
  } 
   
  public LazyLoadingContainer(LazyLoadingQuery lazyLoadingQuery) { 
    this(lazyLoadingQuery, 15); 
  } 
   
  public boolean addContainerProperty(Object propertyId, Class< ? > type, Object defaultValue) throws UnsupportedOperationException { 
    containerPropertyIds.add(propertyId); 
    containerPropertyTypes.put(propertyId, type); 
    containerPropertyDefaultValues.put(propertyId, defaultValue); 
    return true
  } 
 
  public boolean removeContainerProperty(Object propertyId) throws UnsupportedOperationException { 
    containerPropertyIds.remove(propertyId); 
    containerPropertyTypes.remove(propertyId); 
    containerPropertyDefaultValues.remove(propertyId); 
    return true
  } 
   
  public Collection< ? > getContainerPropertyIds() { 
    return containerPropertyIds; 
  } 
 
  public Class< ? > getType(Object propertyId) { 
    return containerPropertyTypes.get(propertyId); 
  } 
   
  public Object getDefaultValue(Object propertyId) { 
    return 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 >= 0 && index < size(); 
  } 
 
  public Item getItem(Object itemId) { 
    if(itemId != null) { 
      if (!itemCache.containsKey(itemId)) { 
        Integer index = (Integer) itemId; 
        int start = index - (index%batchSize); 
         
        List<Item> batch = lazyLoadingQuery.loadItems(start, batchSize); 
        for (Item batchItem : batch) { 
          itemCache.put(start, batchItem); 
          start++; 
        } 
      } 
      return itemCache.get(itemId); 
    } 
     
    return null
  } 
   
  public int size() { 
    if (size == -1) { 
      size = lazyLoadingQuery.size(); 
    } 
    return size; 
  } 
 
  public Collection< ? > getItemIds() { 
    // We don't need an actual list of elements,  
    // since we map the ids in natural integer order (0,1,2,3, etc) 
    // so we can just override the get() and save some memory 
    return new AbstractList<Integer>() { 
      public int size() { 
        return size(); 
      } 
      public Integer get(int index) { 
        return index; 
      } 
    }; 
  } 
   
  public Object firstItemId() { 
    return 0
  } 
 
  public Object lastItemId() { 
    return size()-1
  } 
 
  public boolean isFirstId(Object itemId) { 
    return ((Integer) itemId).equals(0); 
  } 
 
  public boolean isLastId(Object itemId) { 
    return ((Integer) itemId).equals(size()-1); 
  } 
   
  public Object nextItemId(Object itemId) { 
    Integer index = (Integer) itemId; 
    return index++; 
  } 
 
  public Object prevItemId(Object itemId) { 
   Integer index = (Integer) itemId; 
   return index--; 
  } 
   
  public int indexOfId(Object itemId) { 
    return (Integer) itemId; 
  } 
 
  public Object getIdByIndex(int index) { 
    return new Integer(index); 
  }  
   
  public Collection< ? > getSortableContainerPropertyIds() { 
    return containerPropertyIds; 
  } 
   
  public void sort(Object[] propertyIds, boolean[] ascending) { 
    removeAllItems(); 
    lazyLoadingQuery.setSorting(propertyIds, ascending); 
  } 
   
  public boolean removeAllItems() throws UnsupportedOperationException { 
    itemCache.clear(); 
    size = -1
    return true
  } 
   
  // binary search using the real id of the object, and map it to an index in the container 
   
  // id = real id (eg task id) 
  public int getIndexForObjectId(String id) { 
    Item searched = lazyLoadingQuery.loadSingleResult((String) id);  
    if(searched == null) { 
      return -1
    } 
    return getIndexForObjectId(searched, 0, size()-1); 
  } 
   
  @SuppressWarnings({"unchecked""rawtypes"}) 
  public int getIndexForObjectId(Item searched, int low, int high) { 
    if (high < low) { 
      return -1// not found 
    } 
     
    int middle = low + (high-low)/2
    Item result = null
     
    if (itemCache.containsKey(middle)) { 
      result = itemCache.get(middle); 
    } else { 
      result = lazyLoadingQuery.loadItems(middle, 1).get(0); 
      itemCache.put(middle, result); 
    } 
     
    if (!(searched instanceof Comparable) 
            || !(result instanceof Comparable)) { 
      throw new ActivitiException("Cannot use the getIndexForObjectId method for non-Comparables"); 
    } 
     
    int comparison = ((Comparable) searched).compareTo((Comparable) result);  
    if (comparison < 0) { 
      return getIndexForObjectId(searched, low, middle-1); 
    } else if (comparison > 0) { 
      return getIndexForObjectId(searched, middle+1, high); 
    } else { 
      return middle; 
    } 
  } 
   
  // Unsupported Operations ---------------------------------------------------------------------- 
   
  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(); 
  } 
 
}