package org.swellrt.model.generic;
import org.waveprotocol.wave.model.adt.ObservableBasicValue;
import org.waveprotocol.wave.model.adt.ObservableElementList;
import org.waveprotocol.wave.model.adt.ObservableElementList.Listener;
import org.waveprotocol.wave.model.adt.docbased.DocumentBasedBasicValue;
import org.waveprotocol.wave.model.adt.docbased.DocumentBasedElementList;
import org.waveprotocol.wave.model.adt.docbased.Factory;
import org.waveprotocol.wave.model.adt.docbased.Initializer;
import org.waveprotocol.wave.model.document.Doc;
import org.waveprotocol.wave.model.document.Doc.E;
import org.waveprotocol.wave.model.document.Document;
import org.waveprotocol.wave.model.document.util.DocEventRouter;
import org.waveprotocol.wave.model.document.util.DocHelper;
import org.waveprotocol.wave.model.document.util.DocumentEventRouter;
import org.waveprotocol.wave.model.util.Serializer;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* A observable list view of primitive/simple observable values stored in the
* substrate document of containers types {@MapType} or {@ListType
*
*
*
*
* The container type must manage list's slots properly, for example, reusing
* slots when a key's value change.
*
* In the following {@ListType} substrate document, the
* {@ValuesContainer} manage the 'values' element.
*
* <pre>
* <list>
* <item r="map+RK6dvcSzC2B" t="map" />
* <item r="str+0" t="str" />
* <item r="str+1" t="str" />
* </list>
* <values>
* <i v="Hello 0" />
* <i v="Hello 1" />
* </values>
*
* </pre>
*
*
*
* @author pablojan@gmail.com (Pablo Ojanguren)
*
*/
public class ValuesContainer {
public static interface EventHandler {
void run(String value);
}
public static final String TAG_VALUES = "values";
public static final String TAG_ITEM = "i";
public static final String ATTR_VALUE = "v";
/**
* Factory to add new primitive values to the list of values
*/
private static final Factory<Doc.E, ObservableBasicValue<String>, String> VALUE_FACTORY =
new Factory<Doc.E, ObservableBasicValue<String>, String>() {
@Override
public ObservableBasicValue<String> adapt(DocumentEventRouter<? super E, E, ?> router,
E element) {
return DocumentBasedBasicValue.create(router, element, Serializer.STRING, ATTR_VALUE);
}
@Override
public Initializer createInitializer(final String initialState) {
return new Initializer() {
@Override
public void initialize(Map<String, String> target) {
target.put(ATTR_VALUE, initialState);
}
};
}
};
public static ValuesContainer get(Document document, DocEventRouter router, Type parent) {
Doc.E eltValues = DocHelper.getElementWithTagName(document, TAG_VALUES);
if (eltValues == null) {
eltValues =
document.createChildElement(document.getDocumentElement(), TAG_VALUES,
Collections.<String, String> emptyMap());
}
return new ValuesContainer(DocumentBasedElementList.create(router, eltValues, TAG_ITEM,
VALUE_FACTORY), parent);
}
private final ObservableElementList<ObservableBasicValue<String>, String> values;
private final Type parent;
/** Handlers for events on particular indexes **/
private final Map<Integer, EventHandler> eventHandlers;
protected ValuesContainer(ObservableElementList<ObservableBasicValue<String>, String> values,
Type parent) {
this.values = values;
this.parent = parent;
this.eventHandlers = new HashMap<Integer, EventHandler>();
this.values.addListener(new Listener<ObservableBasicValue<String>>() {
@Override
public void onValueAdded(ObservableBasicValue<String> entry) {
int entryIndex = ValuesContainer.this.values.indexOf(entry);
if (eventHandlers.containsKey(entryIndex)) {
EventHandler handler = eventHandlers.get(entryIndex);
eventHandlers.remove(entryIndex);
handler.run(entry.get());
}
}
@Override
public void onValueRemoved(ObservableBasicValue<String> entry) {
}
});
}
public void registerEventHandler(Integer index, EventHandler handler) {
eventHandlers.put(index, handler);
}
public Type deserialize(String s) {
if (s.startsWith(StringType.PREFIX)) {
String index = s.substring(StringType.PREFIX.length());
StringType t = StringType.deserialize(parent, index);
t.setPath(parent.getPath() + "." + index);
return t;
}
return null;
}
public ObservableBasicValue<String> add(String value) {
return values.add(value);
}
public ObservableBasicValue<String> add(String value, int slotIndex) {
return values.add(slotIndex, value);
}
public int indexOf(ObservableBasicValue<String> value) {
return values.indexOf(value);
}
public ObservableBasicValue<String> get(int slotIndex) {
if (values.size() == 0 || slotIndex >= values.size()) {
// perhaps the values container hasn't got the data yet
// return null to indicate to primitive value a deferred value
return null;
}
return values.get(slotIndex);
}
}