Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright (c) 2012, Codename One and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Codename One designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Codename One through http://www.codenameone.com/ if you
* need additional information or have any questions.
*/
package com.codename1.cloud;
import com.codename1.io.CacheMap;
import com.codename1.io.Log;
import com.codename1.io.Util;
import com.codename1.ui.events.DataChangedListener;
import com.codename1.ui.events.SelectionListener;
import com.codename1.ui.list.ListModel;
import com.codename1.ui.util.EventDispatcher;
import java.util.Hashtable;
import java.util.Vector;
/**
* Seamlessly creates a cloud based searchable list model
*
* @author Shai Almog
* @deprecated the cloud storage API is no longer supported, we recommend switching to a solution such as parse4cn1
*/
public class CloudListModel implements ListModel {
private String type;
private int visibilityScope;
private int batchSize = 20;
private int keyBatchSize = 1000;
private int sortProperty;
private boolean ascending;
private Object[] keys;
private EventDispatcher modelListener = new EventDispatcher();
private EventDispatcher selectionListener = new EventDispatcher();
private int selectedIndex = 0;
private Hashtable loadingPlaceholder;
private CacheMap cache;
private int index;
private String queryValue;
/**
* Refreshes items in the current view every 30 seconds when repainted
*/
private int refreshRateMillis = 30000;
private long[] lastRefresh;
/**
* Creates a list model that shows all the cloud elements that have the given key matching
* the given value at the visibility scope listed below. This model can be further narrowed
* down by using the filter functionality below.
* This is effectively equivalent to issuing a queryEquals method, however it polls in batches
* and caches data as needed.
*
* @param type the type of object shown on the list
* @param visibilityScope the scope of the list (CloudObject.ACCESS_* values)
* @param sortProperty the index by which we sort the entries, 0 for unsorted
* @param ascending whether the sort is ascending or descending
*/
public CloudListModel(String type, int visibilityScope, int sortProperty, boolean ascending) {
this.type = type;
this.visibilityScope = visibilityScope;
this.sortProperty = sortProperty;
this.ascending = ascending;
init();
}
private void init() {
cache = new CacheMap(type + visibilityScope + sortProperty);
cache.setCacheSize(30);
cache.setStorageCacheSize(100);
cache.setAlwaysStore(true);
loadingPlaceholder = new Hashtable();
loadingPlaceholder.put("Line1", "Loading...");
loadingPlaceholder.put("Placeholder", Boolean.TRUE);
// remove loading placeholders that might have gotten stuck in cache
Vector v = cache.getKeysInCache();
int cacheSize = v.size();
for(int iter = 0 ; iter < cacheSize ; iter++) {
Object k = v.elementAt(iter);
Object e = cache.get(k);
if(e instanceof Hashtable) {
Hashtable h = (Hashtable)e;
if(h.containsKey("Placeholder")) {
cache.delete(k);
}
}
}
}
/**
* Creates a list model that shows all the cloud elements that have the given key matching
* the given value at the visibility scope listed below. This model can be further narrowed
* down by using the filter functionality below.
* This is effectively equivalent to issuing a queryEquals method, however it polls in batches
* and caches data as needed.
*
* @param type the type of object shown on the list
* @param visibilityScope the scope of the list (CloudObject.ACCESS_* values)
* @param index the index by which we limit the entries
* @param queryValue the queryValue for the given index
* @param ascending whether the sort is ascending or descending
*/
public CloudListModel(String type, int visibilityScope, int index, String queryValue, boolean ascending) {
this.type = type;
this.visibilityScope = visibilityScope;
this.index = index;
this.queryValue = queryValue;
this.ascending = ascending;
init();
}
/**
* Refreshes the list from the server, this method blocks the EDT until
* completion.
*/
public void refresh() {
Vector vec = cache.getKeysInCache();
int s = vec.size();
Vector cld = new Vector();
for(int iter = 0 ; iter < s ; iter++) {
Object key = vec.elementAt(iter);
Object val = cache.get(key);
if(val != null && val instanceof CloudObject) {
cld.addElement((CloudObject)val);
}
}
if(cld.size() > 0) {
CloudObject[] obj = new CloudObject[cld.size()];
cld.toArray(obj);
int response = CloudStorage.getInstance().refresh(obj);
if(response != CloudStorage.RETURN_CODE_SUCCESS) {
onError(new CloudException(response));
} else {
// persist the object to the cache and fire the list model change
for(int iter = 0 ; iter < obj.length ; iter++) {
cache.put(obj[iter].getCloudId(), obj[iter]);
}
modelListener.fireDataChangeEvent(0, getSize());
}
}
}
/**
* Flushes the cache which might be essential when adding new elements
*/
public void clearCache() {
cache.clearAllCache();
}
/**
* Sets the size of the local cache
* @param elements elements to store in cache
*/
public void setCacheSize(int elements) {
cache.setCacheSize(elements);
}
/**
* Returns the elements cached within the implementation
* @return the cache size
*/
public int getCacheSize() {
return cache.getCacheSize();
}
/**
* Sets the size of the cache in persistent storage
* @param elements elements to store in cache
*/
public void setStorageCacheSize(int elements) {
cache.setStorageCacheSize(elements);
}
/**
* Returns the elements cached within the persistent storage
* @return the cache size
*/
public int getStorageCacheSize() {
return cache.getCacheSize();
}
/**
* When loading the element from the cloud a placehold is shown to indicate to the user that the content isn't fully
* here
* @param loadingPlaceholder shows blank content to the user
*/
public void setLoadingPlaceholder(Hashtable loadingPlaceholder) {
this.loadingPlaceholder = loadingPlaceholder;
this.loadingPlaceholder.put("Placeholder", Boolean.TRUE);
}
/**
* When loading the element from the cloud a placehold is shown to indicate to the user that the content isn't fully
* here
*
* @return the element shown when content isn't available for a specific entry
*/
public Hashtable getLoadingPlaceholder() {
return loadingPlaceholder;
}
/**
* Returns the batch size fetched from the server
* @return the size
*/
public int getBatchSize() {
return batchSize;
}
/**
* Updates the number of elements fetched from the server in a single batch
* @param batchSize the batch size
*/
public void setBatchSize(int batchSize) {
this.batchSize = batchSize;
}
/**
* {@inheritDoc}
*/
public Object getItemAt(int index) {
if(keys != null && index < keys.length && index > -1){
Object value = cache.get(keys[index]);
if(value == null) {
value = getLoadingPlaceholder();
fillUpList(index);
} else {
if(value instanceof CloudObject) {
long time = System.currentTimeMillis();
if(lastRefresh[index] + refreshRateMillis < time) {
CloudObject cld = (CloudObject)value;
if(cld.getLastModified() > lastRefresh[index]) {
lastRefresh[index] = cld.getLastModified();
} else {
if(cld.getStatus() == CloudObject.STATUS_COMMITTED) {
CloudStorage.getInstance().refreshAsync(cld);
}
}
}
}
}
return value;
}
return null;
}
private void fillUpList(final int startIndex) {
final int len = Math.min(batchSize, keys.length - startIndex);
Vector request = new Vector();
for(int iter = startIndex ; iter < startIndex + len ; iter++) {
if(cache.get(keys[iter]) == null) {
cache.put(keys[iter], loadingPlaceholder);
request.addElement((String)keys[iter]);
}
}
CloudResponse resp = new CloudResponse() {
public void onSuccess(CloudObject[] returnValue) {
for(int iter = 0 ; iter < returnValue.length ; iter++) {
cache.put(returnValue[iter].getCloudId(), returnValue[iter]);
}
modelListener.fireDataChangeEvent(startIndex, len);
}
public void onError(CloudException err) {
CloudListModel.this.onError(err);
}
};
String[] arr = new String[request.size()];
request.toArray(arr);
CloudStorage.getInstance().fetch(arr, resp);
}
private void newRefreshRate() {
lastRefresh = new long[keys.length];
long t = System.currentTimeMillis();
for(int iter = 0 ; iter < lastRefresh.length ; iter++) {
lastRefresh[iter] = t;
}
}
/**
* {@inheritDoc}
*/
public int getSize() {
if(keys == null) {
keys = (Object[])cache.get("keyIndex");
if(keys == null) {
keys = new Object[0];
} else {
newRefreshRate();
}
// refresh the key list even if we have them in cache since this might have changed
CloudResponse resp = new CloudResponse() {
private int responseOffset;
public void onSuccess(String[] returnValue) {
if(responseOffset == 0) {
keys = returnValue;
} else {
String[] k = new String[keys.length + returnValue.length];
Util.mergeArrays(keys, returnValue, k);
keys = k;
}
newRefreshRate();
cache.put("keyIndex", keys);
modelListener.fireDataChangeEvent(-1, DataChangedListener.ADDED);
// we might have more data, send another request
if(returnValue.length == keyBatchSize) {
responseOffset = keys.length;
if(index > 0) {
CloudStorage.getInstance().queryEqualsKeys(type, index, queryValue, keys.length, keyBatchSize, visibilityScope, this);
} else {
CloudStorage.getInstance().querySortedKeys(type, sortProperty, ascending, keys.length, keyBatchSize, visibilityScope, this);
}
}
}
public void onError(CloudException err) {
CloudListModel.this.onError(err);
}
};
if(index > 0) {
CloudStorage.getInstance().queryEqualsKeys(type, index, queryValue, 0, keyBatchSize, visibilityScope, resp);
} else {
CloudStorage.getInstance().querySortedKeys(type, sortProperty, ascending, 0, keyBatchSize, visibilityScope, resp);
}
}
return keys.length;
}
/**
* Invoked when a cloud error occurs
* @param err the exception representing the error in cloud communications
*/
protected void onError(CloudException err) {
Log.e(err);
}
/**
* {@inheritDoc}
*/
public int getSelectedIndex() {
return selectedIndex;
}
/**
* {@inheritDoc}
*/
public void setSelectedIndex(int index) {
int oldIndex = selectedIndex;
this.selectedIndex = index;
selectionListener.fireSelectionEvent(oldIndex, selectedIndex);
}
/**
* {@inheritDoc}
*/
public void addDataChangedListener(DataChangedListener l) {
modelListener.addListener(l);
}
/**
* {@inheritDoc}
*/
public void removeDataChangedListener(DataChangedListener l) {
modelListener.removeListener(l);
}
/**
* {@inheritDoc}
*/
public void addSelectionListener(SelectionListener l) {
selectionListener.addListener(l);
}
/**
* {@inheritDoc}
*/
public void removeSelectionListener(SelectionListener l) {
selectionListener.removeListener(l);
}
/**
* Notice this method does NOT commit the data, after committing the data
* the cache MUST be cleared!
* {@inheritDoc}
*/
public void addItem(Object item) {
CloudObject cld = (CloudObject)item;
if(cld.getType() == null) {
cld.setType(type);
}
CloudStorage.getInstance().save(cld);
}
/**
* Notice this method does NOT commit the data, after committing the data
* the cache MUST be cleared!
* {@inheritDoc}
*/
public void removeItem(int index) {
Object o = getItemAt(index);
if(o instanceof CloudObject) {
CloudObject cld = (CloudObject)o;
CloudStorage.getInstance().delete(cld);
}
}
/**
* Indicates the rate in milliseconds in which to poll the server for the current data
* of elements that are visible at the moment.
* @return the refreshRateMillis
*/
public int getRefreshRateMillis() {
return refreshRateMillis;
}
/**
* Indicates the rate in milliseconds in which to poll the server for the current data
* of elements that are visible at the moment.
* @param refreshRateMillis the refreshRateMillis to set
*/
public void setRefreshRateMillis(int refreshRateMillis) {
this.refreshRateMillis = refreshRateMillis;
}
}