package io.hummer.prefetch.context; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import io.hummer.prefetch.context.Path.PathPoint; import io.hummer.util.coll.CollectionsUtil.MapBuilder; /** * Simple implementation of a user context, * defined by a set of context attributes. * @author Waldemar Hummer (hummer@dsg.tuwien.ac.at) */ public class Context<T> { /* some attributes */ public static final String ATTR_LOCATION = "__loc__"; public static final String ATTR_LOCATION_LAT = "__loc_lat__"; public static final String ATTR_LOCATION_LON = "__loc_lon__"; public static final String ATTR_NETWORK_AVAILABLE = "__net_available__"; public static final String ATTR_TIME = "__time__"; public static final String ATTR_PATH = "__path__"; public static final String ATTR_FUTURE_PATH = "__future_path__"; /** * The entries which represent the state of this context. */ private final Map<String, ContextEntry<T>> entries = new HashMap<>(); /** * List of context change listeners. */ private final List<ContextChangeListener<T>> listeners = new LinkedList<>(); public static interface ContextChangeListener<T> { void onContextChanged(Map<String,T> newValue, Map<String,T> oldValue); } private static class ContextEntry<T> { T value; long expiryDate; public String toString() { return "E[" + value + "]"; } } public boolean containsAttribute(String key) { return containsAttribute(key, null); } public boolean containsAttribute(String key, T value) { T a = getAttribute(key); if(a == null) return false; if(value == null) return true; return value.equals(a); } public T getAttribute(String key) { /* */ cleanAttributes(); ContextEntry<T> e = entries.get(key); if(e == null) return null; return e.value; } public void setContextAttribute(String key, T value) { setContextAttribute(key, value, -1); } public void setContextAttribute(String key, T value, long expiryPeriodMS) { Map<String,T> values = new MapBuilder<String,T>(key, value); setContextAttributes(values); } public void setContextAttributes(Map<String,T> values) { setContextAttributes(values, -1); } public void setContextAttributes(Map<String,T> values, long expiryPeriodMS) { Map<String,T> oldValues = new HashMap<>(); for(String key : values.keySet()) { oldValues.put(key, null); if(entries.containsKey(key)) { oldValues.put(key, entries.get(key).value); } ContextEntry<T> e = new ContextEntry<>(); T value = values.get(key); e.value = value; if(expiryPeriodMS > 0) { e.expiryDate = System.currentTimeMillis() + expiryPeriodMS; } entries.put(key, e); } /* notify listeners */ for(ContextChangeListener<T> l : listeners) { l.onContextChanged(values, oldValues); } } private synchronized void cleanAttributes() { for(String s : new LinkedList<String>(entries.keySet())) { ContextEntry<T> e = entries.get(s); if(e.expiryDate > 0 && e.expiryDate < System.currentTimeMillis()) { entries.remove(s); } } } public Set<String> keySet() { return entries.keySet(); } public void addChangeListener(ContextChangeListener<T> listener) { listeners.add(listener); } public boolean isNetworkUnavailable() { Boolean avail = (Boolean)getAttribute(ATTR_NETWORK_AVAILABLE); return avail != null && !avail; } public Time getTime() { return (Time)getAttribute(Context.ATTR_TIME); } public Context<T> copy() { return copy(false); } public Context<T> copy(boolean includeListeners) { Context<T> copy = new Context<>(); copy.entries.putAll(entries); if(includeListeners) { copy.listeners.addAll(listeners); } return copy; } public static Map<String, Object> generateUpdates(double time, Path path) { return generateUpdates(time, path, path.getLocationAtTime(time)); } public static Map<String, Object> generateUpdates(double time, Path path, PathPoint location) { Map<String,Object> ctxUpdates = new HashMap<>(); /* set current time */ ctxUpdates.put(Context.ATTR_TIME, new Time(time)); /* set predicted path */ ctxUpdates.put(Context.ATTR_PATH, path); ctxUpdates.put(Context.ATTR_FUTURE_PATH, path.getFuturePathAt(time)); /* set current location */ ctxUpdates.put(Context.ATTR_LOCATION, location); ctxUpdates.put(Context.ATTR_LOCATION_LAT, location.coordinates.lat); ctxUpdates.put(Context.ATTR_LOCATION_LON, location.coordinates.lon); /* set network availability */ boolean netAvail = location.cellNetworkCoverage.hasSufficientCoverage(); ctxUpdates.put(Context.ATTR_NETWORK_AVAILABLE, netAvail); return ctxUpdates; } @Override public String toString() { return "Context" + entries; } }