org.directwebremoting.datasync.AbstractStoreProvider Maven / Gradle / Ivy
package org.directwebremoting.datasync;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.SortedSet;
import java.util.regex.Pattern;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.directwebremoting.ServerContextFactory;
import org.directwebremoting.extend.ConverterManager;
import org.directwebremoting.extend.InboundVariable;
import org.directwebremoting.extend.RealRawData;
import org.directwebremoting.io.Item;
import org.directwebremoting.io.MatchedItems;
import org.directwebremoting.io.QueryOptions;
import org.directwebremoting.io.RawData;
import org.directwebremoting.io.StoreChangeListener;
import org.directwebremoting.util.LocalUtil;
import org.directwebremoting.util.Pair;
/**
* Some methods to help implementors create {@link StoreProvider}s. It is
* strongly recommended that all implementors of {@link StoreProvider} inherit
* from this class in case it can provide some form of backwards compatibility.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
public abstract class AbstractStoreProvider implements StoreProvider
{
public AbstractStoreProvider(Class type)
{
this.type = type;
}
/* (non-Javadoc)
* @see org.directwebremoting.datasync.StoreProvider#put(java.lang.String, org.directwebremoting.io.RawData)
*/
public void put(String itemId, RawData rawData)
{
T value = convert(rawData);
put(itemId, value);
}
/* (non-Javadoc)
* @see org.directwebremoting.datasync.StoreProvider#get(java.lang.String)
*/
public Item viewItem(String itemId)
{
T object = getObject(itemId);
return new Item(itemId, object);
}
/**
* Get an object from the store without an Item wrapper.
* @param itemId The ID of the item to fetch
* @return The item, or null if one was not found.
*/
protected abstract T getObject(String itemId);
/**
* Return true if the value
passed in contains a property
* by the name of each and every key in the query
, and where
* the string value (using {@link #toString()}) of the property is equal to
* the value from the filter
map.
* @param pojo The object to be tested to see if it matches
* @param query The set of property/matches to test the value against
* @param options ...
* @return True if the value contains properties that match the filter
*/
protected static boolean passesFilter(Object pojo, Map query, QueryOptions options)
{
if (query == null || query.isEmpty())
{
return true;
}
try
{
for (Map.Entry entry : query.entrySet())
{
String testProperty = entry.getKey();
String testValue = entry.getValue();
String realValue = LocalUtil.getProperty(pojo, testProperty, String.class);
return testPattern(testValue, realValue, options.isIgnoreCase());
}
}
catch (Exception ex)
{
log.warn("Failed to introspect: " + pojo.getClass() + ". Filters will fail.");
return false;
}
return true;
}
protected static boolean testPattern(String pattern, String value, boolean caseInsensitive)
{
boolean result = pattern.equals(value);
if (!result && LocalUtil.hasText(value))
{
String javaPattern = pattern.replace("?", ".").replace("*", ".*");
if (LocalUtil.hasText(pattern))
{
Pattern p = caseInsensitive
? Pattern.compile(javaPattern, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE)
: Pattern.compile(javaPattern);
result = p.matcher(value).matches();
}
else
{
result = true;
}
}
return result;
}
/**
* Chop out the part we are interested in and return the data converted to
* a MatchedItems data set destined for the web.
* @param ...
* @param sortedData The complete set of data we're extracting from
* @param start The initial index to start from.
* @param count The number of data items to return (-1 means, to the end)
* @return Data for the web
*/
protected static MatchedItems selectMatchedItems(SortedSet> sortedData, int start, int count)
{
log.debug("Selecting data from " + sortedData.size() + " items, starting at " + start + " for " + count + " items.");
List- matches = new ArrayList
- ();
int i = 0;
for (Pair
pair : sortedData)
{
if (i >= (start + count) && count != -1)
{
break;
}
if (i >= start)
{
matches.add(new Item(pair.left, pair.right));
}
i++;
}
return new MatchedItems(matches, sortedData.size());
}
/**
* Convert from {@link RawData} to the type that this {@link StoreProvider}
* supports.
* @param rawData The data from the Internet
* @return An object of the type supported by this store
*/
protected T convert(RawData rawData)
{
if (rawData == null)
{
return null;
}
if (converterManager == null)
{
converterManager = ServerContextFactory.get().getContainer().getBean(ConverterManager.class);
}
RealRawData realRawData = (RealRawData) rawData;
InboundVariable inboundVariable = realRawData.getInboundVariable();
T value = converterManager.convertInbound(type, inboundVariable, null);
return value;
}
/**
* Convert from {@link RawData} to the type that this {@link StoreProvider}
* supports.
* @param rawData The data from the Internet
* @param toType ...
* @return An object of the type supported by this store
*/
protected Object convert(RawData rawData, Class> toType)
{
if ((rawData == null) || (toType == null))
{
return null;
}
if (converterManager == null)
{
converterManager = ServerContextFactory.get().getContainer().getBean(ConverterManager.class);
}
RealRawData realRawData = (RealRawData) rawData;
InboundVariable inboundVariable = realRawData.getInboundVariable();
Object value = converterManager.convertInbound(toType, inboundVariable, null);
return value;
}
/**
*
* @param listener ...
* @param itemIds ...
*/
protected synchronized void setWatchedSet(StoreChangeListener listener, Collection itemIds)
{
clearWatchedSet(listener);
addWatchedSet(listener, itemIds);
}
/**
* Starts listening for a new itemId.
*
* @param listener the client store that will be notifed
* @param itemId the new item that is being monitored
*/
protected synchronized void addWatched(StoreChangeListener listener, String itemId)
{
Collection ids = watched.get(listener);
Collection> listeners = watchers.get(itemId);
if (ids == null)
{
ids = new HashSet();
watched.put(listener, ids);
}
ids.add(itemId);
if (listeners == null)
{
listeners = new HashSet>();
watchers.put(itemId, listeners);
}
listeners.add(listener);
}
protected synchronized void addWatchedSet(StoreChangeListener listener, Collection itemIds)
{
if (itemIds == null || itemIds.isEmpty())
{
return;
}
watched.put(listener, itemIds);
for (String itemId : itemIds)
{
Collection> listeners = watchers.get(itemId);
if (listeners == null)
{
listeners = new HashSet>();
watchers.put(itemId, listeners);
}
listeners.add(listener);
}
}
/**
* Unsubscribe from anything the listener was previously subscribed to
* @param listener ...
*/
protected synchronized void clearWatchedSet(StoreChangeListener listener)
{
Collection itemIds = watched.get(listener);
if (itemIds != null)
{
for (String itemId : itemIds)
{
Collection> listeners = watchers.get(itemId);
if (listeners != null)
{
listeners.remove(listener);
if (listeners.isEmpty())
{
watchers.remove(itemId);
}
}
}
watched.remove(listener);
}
}
/**
* If called all listeners (clients) are informed when a new item
* is added to the store.
*
* @param itemId the new item
*/
protected synchronized void updateWatcherSets(String itemId)
{
for (StoreChangeListener listener : watched.keySet())
{
addWatched(listener, itemId);
}
}
/**
* Cause a listener update
* @param item ...
* @param attributes ...
*/
protected synchronized void fireItemChanged(Item item, Collection attributes)
{
Collection> listeners = watchers.get(item.getItemId());
if (listeners != null)
{
log.debug("Firing item changed to " + listeners.size() + " listeners. Changed: " + item);
for (StoreChangeListener listener : listeners)
{
listener.itemChanged(this, item, attributes);
}
}
}
/**
* Cause a listener update
* @param item ...
*/
protected synchronized void fireItemAdded(Item item)
{
updateWatcherSets(item.getItemId());
Collection> listeners = watchers.get(item.getItemId());
if (listeners != null)
{
log.debug("Firing item added to " + listeners.size() + " listeners. Changed: " + item);
for (StoreChangeListener listener : listeners)
{
listener.itemAdded(this, item);
}
}
}
/**
* Cause a listener update
* @param itemId ...
*/
protected synchronized void fireItemRemoved(String itemId)
{
Collection> listeners = watchers.get(itemId);
if (listeners != null)
{
log.debug("Firing item removed to " + listeners.size() + " listeners. Changed: " + itemId);
for (StoreChangeListener listener : listeners)
{
listener.itemRemoved(this, itemId);
}
}
}
/**
* A PairComparator is a way to proxy comparisons to the 'value' of a
* String, Object paring.
* @author Joe Walker [joe at getahead dot ltd dot uk]
*/
protected static class PairComparator implements Comparator>
{
/**
* @param proxy The value object comparator
*/
protected PairComparator(Comparator proxy)
{
this.proxy = proxy;
}
/* (non-Javadoc)
* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
*/
public int compare(Pair p1, Pair p2)
{
return proxy.compare(p1.right, p2.right);
}
/**
* The comparator that we proxy to
*/
private final Comparator proxy;
}
/**
* A map of the itemIds in this store along with who is watching them.
* @protectedBy(this)
*/
private final Map>> watchers = new HashMap>>();
/**
* A map of the itemIds in this store along with who is watching them.
* @protectedBy(this)
*/
private final Map, Collection> watched = new HashMap, Collection>();
/**
* The type that this StoreProvider uses
*/
protected final Class type;
/**
* Cached converterManager so we don't look it up every time
*/
protected ConverterManager converterManager;
/**
* The log stream
*/
private static final Log log = LogFactory.getLog(AbstractStoreProvider.class);
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy