package gueei.binding;
import java.util.ArrayList;
import java.util.Collection;
import android.content.Context;
public abstract class Attribute<Th, T> extends Observable<T> {
protected Th mHost;
//protected WeakReference<Th> mHostRef;
protected String attributeName;
private boolean readonly = false;
public Attribute(Class<T> type, Th host, String attributeName) {
super(type);
//this.mHostRef = new WeakReference<Th>(host);
mHost = host;
this.attributeName = attributeName;
}
public Th getHost(){
return mHost;
}
/*
@SuppressWarnings("unchecked")
public final <To> void onPropertyChanged(IObservable<To> prop,
Collection<Object> initiators){
set((T)prop.get(), initiators);
}
*/
protected abstract void doSetAttributeValue(Object newValue);
@Override
protected void doSetValue(final T newValue, Collection<Object> initiators) {
if (readonly) return;
doSetAttributeValue(newValue);
/*
uiHandler.post(new Runnable(){
public void run() {
doSetAttributeValue(newValue);
}
});*/
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean readonly) {
this.readonly = readonly;
}
@Override
public void _setObject(final Object newValue, Collection<Object> initiators){
if (readonly) return;
doSetAttributeValue(newValue);
initiators.add(this);
}
@Override
public abstract T get();
// Set to package internal for debug use
Bridge mBridge;
public BindingType BindTo(Context context, IObservable<?> prop) {
if (prop == null) return BindingType.NoBinding;
BindingType binding;
// Dirty fix
// Since for InnerFieldObservable, it may not know what type of it will be
// So, it assumes two way no matter what
if (prop instanceof Undetermined)
binding = BindingType.TwoWay;
else
binding = AcceptThisTypeAs(prop.getType());
if (binding.equals(BindingType.NoBinding)) return binding;
onBind(context, prop, binding);
return binding;
}
/*
* Hook to allow modifying of binding behavior in subclasses
*/
protected void onBind(Context context, IObservable<?> prop, BindingType binding){
mBridge = new Bridge(this, prop);
prop.subscribe(mBridge);
if (binding.equals(BindingType.TwoWay)) this.subscribe(mBridge);
ArrayList<Object> initiators = new ArrayList<Object>();
initiators.add(prop);
this._setObject(prop.get(), initiators);
// Broadcast initial change
notifyChanged(initiators);
}
protected BindingType AcceptThisTypeAs(Class<?> type){
if (this.getType() != type){
if (this.getType().isAssignableFrom(type)){
return BindingType.OneWay;
}
return BindingType.NoBinding;
}
return BindingType.TwoWay;
}
public IObservable<?> getBindedObservable(){
if (mBridge==null) return null;
return mBridge.mBindedObservable;
}
class Bridge implements Observer{
Attribute<Th, T> mAttribute;
IObservable<?> mBindedObservable;
public Bridge(Attribute<Th, T> attribute, IObservable<?> observable){
mAttribute = attribute;
mBindedObservable = observable;
}
public void onPropertyChanged(IObservable<?> prop,
Collection<Object> initiators) {
if (prop==mAttribute){
mBindedObservable._setObject(prop.get(), initiators);
}
else if (prop==mBindedObservable){
if (initiators.contains(Attribute.this)) return;
mAttribute._setObject(prop.get(), initiators);
initiators.add(this);
initiators.add(Attribute.this);
notifyChanged(initiators);
}
}
}
}