package gueei.binding; import gueei.binding.ISyntaxResolver.SyntaxResolveException; import gueei.binding.utility.WeakList; import java.util.ArrayList; import java.util.Collection; /** * This is an observable to help binding to sub-field of a given object * For example: * Suppose binding to "A.B.C" * where A, B is observable and C is plain object * Inner<A> observes B, Inner<B> observes C * When A change, the full path is regenerated, * When B change, subpath from B is regenerated, leaving A unchange, but * will notify A and A will notify its observers. * shit.. what am I writing? * @author andy * * @param <T> */ public class InnerFieldObservable<T> implements IObservable<T>, Undetermined{ private WeakList<Observer> observers = new WeakList<Observer>(); private String mFieldPath; private InnerFieldObservable<T> mChild; private IObservable<T> mObservable; private Observer valueObserver = new Observer(){ @Override public void onPropertyChanged(IObservable<?> prop, Collection<Object> initiators) { if (initiators.contains(InnerFieldObservable.this)) return; // Has child nodes if (mFieldPath.indexOf(".")>0){ createChildNodes(prop.get()); } notifyChanged(initiators); } }; private Observer childObserver = new Observer(){ @Override public void onPropertyChanged(IObservable<?> prop, Collection<Object> initiators) { if (initiators.contains(InnerFieldObservable.this)) return; notifyChanged(initiators); } }; public InnerFieldObservable(String fieldPath) { mFieldPath = fieldPath; } /** * Initialize the IFO with the provided view model * * @param viewModel * @return true if successful, false if cannot associate with the view model */ @SuppressWarnings({ "rawtypes", "unchecked" }) public boolean createNodes(Object viewModel){ if(mObservable!=null) mObservable.unsubscribe(valueObserver); int dot = mFieldPath.indexOf("."); String fieldName = mFieldPath; // Last part of the path if (dot>0){ fieldName = mFieldPath.substring(0, dot).trim(); } Object field; try { field = Binder.getSyntaxResolver().getFieldForModel(fieldName, viewModel); } catch (SyntaxResolveException e) { BindingLog.exception("InnerFieldObservable.createNodes()", e); return false; } if (field instanceof IObservable){ mObservable = (IObservable)field; }else if(field==null){ BindingLog.warning ("InnerFieldObservable.createNodes()", String.format("fieldname '%s' not found", fieldName)); return false; }else{ mObservable = new ConstantObservable(field.getClass(), field); } mObservable.subscribe(valueObserver); if(dot>0) createChildNodes(mObservable.get()); return true; } public void createChildNodes(Object value){ if(mChild!=null) mChild.unsubscribe(childObserver); mChild = null; if (value==null) return; String subPath = mFieldPath.substring(mFieldPath.indexOf(".")+1, mFieldPath.length()); subPath = subPath.trim(); if (subPath.length()==0){ return; }else{ mChild = new InnerFieldObservable<T>(subPath); mChild.createNodes(value); mChild.subscribe(childObserver); } } /** * It has no idea of its type, only the child knows */ @Override public Class<T> getType() { if (mChild!=null) return mChild.getType(); return (Class<T>)mObservable.getType(); } @Override public void subscribe(Observer o) { observers.add(o); } @Override public void unsubscribe(Observer o) { observers.remove(o); } @Override public Observer[] getAllObservers() { return observers.toArray(new Observer[0]); } public final void notifyChanged(Object initiator){ ArrayList<Object> initiators = new ArrayList<Object>(); initiators.add(initiator); this.notifyChanged(initiators); } public final void notifyChanged(Collection<Object> initiators){ initiators.add(this); for(Object o: observers.toArray()){ if (initiators.contains(o)) continue; ((Observer)o).onPropertyChanged(this, initiators); } } public final void notifyChanged(){ ArrayList<Object> initiators = new ArrayList<Object>(); notifyChanged(initiators); } @Override public void set(T newValue, Collection<Object> initiators) { if (mChild!=null){ mChild.set(newValue); notifyChanged(initiators); }else if (mObservable!=null){ mObservable.set(newValue); notifyChanged(initiators); } } @Override public void set(T newValue) { set(newValue, new ArrayList<Object>()); } @Override public void _setObject(Object newValue, Collection<Object> initiators) { if (mChild!=null){ mChild._setObject(newValue, initiators); }else if (mObservable!=null){ mObservable._setObject(newValue, initiators); } } @Override public T get() { if (mChild!=null){ return mChild.get(); } else if( mObservable!= null) { return mObservable.get(); } return null; } @Override public boolean isNull() { return get()==null; } }