package fr.lteconsulting.hexa.client.tableobserver; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import fr.lteconsulting.hexa.client.interfaces.IAsyncCallback; import fr.lteconsulting.hexa.client.interfaces.IHasIntegerId; public abstract class TableServiceBase<T extends IHasIntegerId> { protected abstract void doDeleteRecord( int recordId, IAsyncCallback<Integer> callback ); protected abstract void doUpdateField( int recordId, String fieldName, String wantedValue, IAsyncCallback<String> callback ); protected abstract void doGetRecords( IAsyncCallback<Iterable<T>> callback ); protected abstract void doGetRecord( int recordId, IAsyncCallback<T> callback ); // to be overriden if needed protected boolean isClientInterestedByRecord( T record, Object clientParam ) { return true; } protected TableServiceBase( String logName ) { this.logName = logName; } String logName; HashMap<Integer, T> records = new HashMap<Integer, T>(); boolean fWholeTableLoaded = false; boolean fFullLoadRequested = false; HashSet<IAsyncCallback<Integer>> waitingLoaded = null; HashSet<ClientInfo> waitingWholeTableLoaded = null; class ClientInfo { Object param; XTableListen<T> callback; ITableCommand<T> command; public ClientInfo( Object param, XTableListen<T> callback ) { this.param = param; this.callback = callback; command = new ITableCommand<T>() { @Override public boolean isLoaded() { return fWholeTableLoaded; } @Override public void waitLoaded( IAsyncCallback<Integer> callback ) { if( fWholeTableLoaded ) { callback.onSuccess( 1 ); return; } if( waitingLoaded == null ) waitingLoaded = new HashSet<IAsyncCallback<Integer>>(); waitingLoaded.add( callback ); } @Override public void quit() { // GWT.log( "TableServiceBase "+logName+" client leaving..." // ); clients.remove( ClientInfo.this ); } @Override public void askRefreshTable() { if( fWholeTableLoaded ) { ClientInfo.this.onWholeTable( records.values() ); } else { if( fFullLoadRequested ) { if( waitingWholeTableLoaded == null ) waitingWholeTableLoaded = new HashSet<ClientInfo>(); waitingWholeTableLoaded.add( ClientInfo.this ); } else { fFullLoadRequested = true; doGetRecords( new IAsyncCallback<Iterable<T>>() { @Override public void onSuccess( Iterable<T> result ) { records.clear(); for( T e : result ) records.put( e.getId(), e ); // GWT.log( // "TableServiceBase "+logName+" WHOLE TABLE RECEIVED / " // + TableServiceBase.this.toString() ); fWholeTableLoaded = true; fFullLoadRequested = false; ClientInfo.this.onWholeTable( records.values() ); if( waitingWholeTableLoaded != null ) { for( ClientInfo waitingClient : waitingWholeTableLoaded ) waitingClient.onWholeTable( records.values() ); waitingWholeTableLoaded.clear(); waitingWholeTableLoaded = null; } if( waitingLoaded != null ) { for( IAsyncCallback<Integer> cb : waitingLoaded ) cb.onSuccess( 1 ); waitingLoaded.clear(); waitingLoaded = null; } } } ); } } } @Override public T getRecord( int recordId ) { assert (fWholeTableLoaded); return records.get( recordId ); } @Override public Iterable<T> getRecords() { assert (fWholeTableLoaded); return records.values(); } @Override public Iterable<T> getRecordsSorted( Comparator<T> comparator ) { assert (fWholeTableLoaded); List<T> sortedList = new ArrayList<T>( records.values() ); Collections.sort( sortedList, comparator ); return sortedList; } @Override public boolean isEmpty() { assert (fWholeTableLoaded); if( ClientInfo.this.param != null ) { Iterable<T> it = new FilteredIterable( records.values() ); return !it.iterator().hasNext(); } return records.isEmpty(); } }; } void onWholeTable( Iterable<T> records ) { if( callback == null ) return; if( param != null ) callback.wholeTable( new FilteredIterable( records ) ); else callback.wholeTable( records ); } void onUpdatedRecord( T record ) { if( callback == null ) return; if( param != null && !isClientInterestedByRecord( record, param ) ) return; callback.updated( record ); } void onUpdatedRecordField( T record, String fieldName ) { if( callback == null ) return; if( param != null && !isClientInterestedByRecord( record, param ) ) return; callback.updatedField( fieldName, record ); } void onDeletedRecord( int recordId, T oldRecord ) { if( callback == null ) return; if( param != null && !isClientInterestedByRecord( oldRecord, param ) ) return; callback.deleted( recordId, oldRecord ); } class FilteredIterable implements Iterable<T> { Iterable<T> raw; public FilteredIterable( Iterable<T> raw ) { this.raw = raw; } @Override public Iterator<T> iterator() { class FilteredIterator implements Iterator<T> { Iterator<T> rawIt = raw.iterator(); T nextElem = null; FilteredIterator() { prepareNextElem(); } @Override public boolean hasNext() { return nextElem != null; } @Override public T next() { T curElem = nextElem; prepareNextElem(); return curElem; } void prepareNextElem() { nextElem = null; while( rawIt.hasNext() ) { T test = rawIt.next(); if( isClientInterestedByRecord( test, param ) ) { nextElem = test; return; } } } @Override public void remove() { assert (false) : "remove is not allowed ..."; } } return new FilteredIterator(); } } } HashSet<ClientInfo> clients = new HashSet<ClientInfo>(); public ITableCommand<T> listen( XTableListen<T> callback ) { return listenInternal( null, callback ); } protected ITableCommand<T> listenInternal( Object clientParam, XTableListen<T> callback ) { ClientInfo client = new ClientInfo( clientParam, callback ); clients.add( client ); return client.command; } protected IAsyncCallback<T> getAddInternalCallback( final IAsyncCallback<T> callback ) { return new IAsyncCallback<T>() { @Override public void onSuccess( T result ) { if( result == null ) { callback.onSuccess( null ); return; } records.put( result.getId(), result ); for( ClientInfo client : clients ) client.onUpdatedRecord( result ); if( callback != null ) callback.onSuccess( result ); } }; } public void delete( final int recordId ) { doDeleteRecord( recordId, new IAsyncCallback<Integer>() { @Override public void onSuccess( Integer result ) { if( result < 0 ) return; T oldRecord = records.remove( recordId ); for( ClientInfo client : clients ) client.onDeletedRecord( recordId, oldRecord ); } } ); } public void updateField( int recordId, final String fieldName, String newValue ) { doUpdateField( recordId, fieldName, newValue, new IAsyncCallback<String>() { @Override public void onSuccess( String result ) { } } ); doGetRecord( recordId, new IAsyncCallback<T>() { @Override public void onSuccess( T result ) { records.put( result.getId(), result ); for( ClientInfo client : clients ) client.onUpdatedRecordField( result, fieldName ); } } ); } public void getRecord( int recordId, IAsyncCallback<T> callback ) { if( fWholeTableLoaded ) callback.onSuccess( records.get( recordId ) ); else doGetRecord( recordId, callback ); } public void getRecords( IAsyncCallback<Iterable<T>> callback ) { if( fWholeTableLoaded ) callback.onSuccess( records.values() ); else doGetRecords( callback ); } }