View Javadoc

1   package org.appfuse.webapp.client.application.base.activity;
2   
3   import java.util.List;
4   import java.util.Set;
5   import java.util.logging.Logger;
6   
7   import javax.validation.ConstraintViolation;
8   
9   import org.appfuse.webapp.client.application.Application;
10  import org.appfuse.webapp.client.application.base.place.EntityProxyPlace;
11  import org.appfuse.webapp.client.application.base.place.EntitySearchPlace;
12  import org.appfuse.webapp.client.application.base.view.ProxySearchView;
13  import org.appfuse.webapp.client.ui.home.HomePlace;
14  
15  import com.google.gwt.activity.shared.Activity;
16  import com.google.gwt.event.shared.EventBus;
17  import com.google.gwt.event.shared.HandlerRegistration;
18  import com.google.gwt.user.cellview.client.ColumnSortList;
19  import com.google.gwt.user.client.History;
20  import com.google.gwt.user.client.Window;
21  import com.google.gwt.user.client.ui.AcceptsOneWidget;
22  import com.google.gwt.view.client.HasData;
23  import com.google.gwt.view.client.Range;
24  import com.google.gwt.view.client.RangeChangeEvent;
25  import com.google.web.bindery.requestfactory.shared.BaseProxy;
26  import com.google.web.bindery.requestfactory.shared.EntityProxy;
27  import com.google.web.bindery.requestfactory.shared.Receiver;
28  import com.google.web.bindery.requestfactory.shared.Request;
29  import com.google.web.bindery.requestfactory.shared.RequestContext;
30  
31  /**
32   * Abstract activity for displaying a list of {@link EntityProxy}. These
33   * activities are not re-usable. Once they are stopped, they cannot be
34   * restarted.
35   * <p/>
36   * Subclasses must provide:
37   * <p/>
38   * <ul>
39   * <li>{@link #createView()}
40   * <li>{@link #createRequestContext()}
41   * <li>{@link #createCountRequest(RequestContext, BaseProxy)}
42   * <li>
43   * {@link #createSearchRequest(RequestContext, BaseProxy, Range, ColumnSortList)}
44   * </ul>
45   * <p/>
46   * Only the properties required by the view will be requested.
47   *
48   * @param <P>
49   *            the type of {@link EntityProxy} listed
50   * @param <S>
51   *            the type of {@link BaseProxy} acting as search criteria
52   */
53  public abstract class AbstractProxySearchActivity<P extends EntityProxy, S> extends AbstractBaseActivity implements Activity, ProxySearchView.Delegate<P> {
54  
55      protected final Logger logger = Logger.getLogger(getClass().getName());
56  
57      private S searchCriteria;
58      protected final Class<S> searchCriteriaType;
59      protected final EntitySearchPlace currentPlace;
60  
61      protected ProxySearchView<P, S> view;
62      private HandlerRegistration rangeChangeHandler;
63  
64      protected abstract RequestContext createRequestContext();
65  
66      protected abstract Request<Long> createCountRequest(RequestContext requestContext, S searchCriteria);
67  
68      protected abstract Request<List<P>> createSearchRequest(RequestContext requestContext, S searchCriteria, Range range, ColumnSortList columnSortList);
69  
70      public AbstractProxySearchActivity(final Application application, final ProxySearchView<P, S> view,
71              final Class<S> searchCriteriaType) {
72          super(application);
73          this.currentPlace = (EntitySearchPlace) application.getPlaceController().getWhere();
74          this.view = view;
75          this.searchCriteriaType = searchCriteriaType;
76      }
77  
78      @Override
79      public void start(final AcceptsOneWidget panel, final EventBus eventBus) {
80          view.setDelegate(this);
81          panel.setWidget(view);
82          setDocumentTitleAndBodyAttributtes();
83  
84          searchCriteria = (S) currentPlace.getSearchCriteria();
85          if (searchCriteria == null) {
86              searchCriteria = createSearchCriteria();
87          }
88          view.setSearchCriteria(searchCriteria);
89  
90          if (currentPlace.getMaxResults() > 0) {
91              view.setPageSize(currentPlace.getMaxResults());
92          }
93  
94          final HasData<P> hasData = view.asHasData();
95          rangeChangeHandler = hasData.addRangeChangeHandler(new RangeChangeEvent.Handler() {
96              @Override
97              public void onRangeChange(final RangeChangeEvent event) {
98                  AbstractProxySearchActivity.this.onRangeChanged(hasData, hasData.getVisibleRange(), view.getColumnSortList());
99              }
100         });
101 
102         // Select the current page range to load (by default or from place
103         // tokens)
104         Range range = hasData.getVisibleRange();
105         if (currentPlace.getFirstResult() > 0 ||
106                 (currentPlace.getMaxResults() != range.getLength() && currentPlace.getMaxResults() > 0))
107         {
108             range = new Range(currentPlace.getFirstResult(), currentPlace.getMaxResults());
109         }
110 
111         loadItems(searchCriteria, range);
112     }
113 
114     protected S createSearchCriteria() {
115         if (!String.class.equals(searchCriteriaType)) {
116             return (S) proxyFactory.create((Class<BaseProxy>) searchCriteriaType);
117         }
118         return null;
119     }
120 
121     protected void loadItems(final S searchCriteria) {
122         // Select the current page size to load
123         final Range currentRange = view.asHasData().getVisibleRange();
124         loadItems(searchCriteria, new Range(0, currentRange.getLength()));
125     }
126 
127     /**
128      * Load items on start.
129      */
130     protected void loadItems(final S searchCriteria, final Range range) {
131         if (searchCriteria instanceof BaseProxy) {
132             proxyFactory.setFrozen((BaseProxy) searchCriteria, true);
133         }
134         final RequestContext requestContext = createRequestContext();
135         createCountRequest(requestContext, searchCriteria).fire(new Receiver<Long>() {
136             @Override
137             public void onSuccess(final Long response) {
138                 if (view == null) {
139                     // This activity is dead
140                     return;
141                 }
142                 view.asHasData().setRowCount(response.intValue(), true);
143                 onRangeChanged(view.asHasData(), range, view.getColumnSortList());
144             }
145         });
146     }
147 
148     /**
149      * Called by the table as it needs data.
150      */
151     protected void onRangeChanged(final HasData<P> hasData, final Range range, final ColumnSortList columnSortList) {
152         final RequestContext requestContext = createRequestContext();
153         createSearchRequest(requestContext, searchCriteria, range, columnSortList)
154                 .with(view.getPaths()).fire(new Receiver<List<P>>() {
155                     @Override
156                     public void onSuccess(final List<P> results) {
157                         if (view == null) {
158                             // This activity is dead
159                             return;
160                         }
161                         hasData.setRowData(range.getStart(), results);
162                         newHistoryToken(searchCriteria, range.getStart(), range.getLength());
163                     }
164                 });
165     }
166 
167     protected void newHistoryToken(final S searchCriteria, final int firstResult, final int maxResults) {
168         final String historyToken = new EntitySearchPlace.Tokenizer(proxyFactory, requests)
169                 .getFullHistoryToken(new EntitySearchPlace(currentPlace.getProxyClass(), firstResult, maxResults, searchCriteria));
170         History.newItem(historyToken, false);
171     }
172 
173     @Override
174     public void addClicked() {
175         placeController.goTo(new EntityProxyPlace(currentPlace.getProxyClass()));
176     }
177 
178     @Override
179     public void showDetails(final Class<? extends EntityProxy> proxyClass, final String entityId) {
180         placeController.goTo(new EntityProxyPlace(proxyClass, entityId, EntityProxyPlace.Operation.EDIT));
181     }
182 
183     @Override
184     public void searchClicked() {
185         if (searchCriteria instanceof BaseProxy) {
186             proxyFactory.setFrozen((BaseProxy) searchCriteria, false);
187         }
188         searchCriteria = view.getSearchCriteria();
189         final Set<ConstraintViolation<S>> violations = validate(searchCriteria);
190         view.setConstraintViolations(violations);
191         if (violations == null || violations.isEmpty()) {
192             loadItems(searchCriteria);
193         }
194     }
195 
196     /**
197      * Validates given searchCriteria.
198      * 
199      * Override if you want to apply validation, example: <code><pre>
200      * protected Set<ConstraintViolation<S>> validate(S searchCriteria){
201      * 	return getValidator().validate(searchCriteria);
202      * }
203      * </pre></code>
204      * 
205      * @param searchCriteria
206      * @return
207      */
208     protected Set<ConstraintViolation<S>> validate(final S searchCriteria) {
209         return null;//
210     }
211 
212     @Override
213     public void deleteClicked(final Class<? extends EntityProxy> proxyClass, final String entityId) {
214         Window.alert("deleteClicked");
215     }
216 
217     @Override
218     public void cancelClicked() {
219         placeController.goTo(new HomePlace());
220     }
221 
222     @Override
223     public void onCancel() {
224         onStop();
225     }
226 
227     @Override
228     public void onStop() {
229         view.setDelegate(null);
230         view = null;
231         rangeChangeHandler.removeHandler();
232         rangeChangeHandler = null;
233     }
234 }