Revision: 8174
Author: r...@google.com
Date: Tue May 18 06:37:32 2010
Log: Use cursors for expense report list search queries
Fix checkstyle warnings

http://code.google.com/p/google-web-toolkit/source/detail?r=8174

Added:
/branches/2.1/user/src/com/google/gwt/user/cellview/client/simplePagerFastForward.png /branches/2.1/user/src/com/google/gwt/user/cellview/client/simplePagerFastForwardDisabled.png
Modified:
 /branches/2.1/bikeshed/src/com/google/gwt/mobile/client/Touch.java
 /branches/2.1/bikeshed/src/com/google/gwt/mobile/client/TouchEvent.java
/branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Scaffold.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGenerator.java /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java
 /branches/2.1/user/src/com/google/gwt/cell/client/Cell.java
/branches/2.1/user/src/com/google/gwt/user/cellview/client/AbstractPager.java
 /branches/2.1/user/src/com/google/gwt/user/cellview/client/SimplePager.java

=======================================
--- /dev/null   
+++ /branches/2.1/user/src/com/google/gwt/user/cellview/client/simplePagerFastForward.png Tue May 18 06:37:32 2010
Binary file, no diff available.
=======================================
--- /dev/null
+++ /branches/2.1/user/src/com/google/gwt/user/cellview/client/simplePagerFastForwardDisabled.png Tue May 18 06:37:32 2010
@@ -0,0 +1,14 @@
+‰PNG
+ + +IHDR rP6Ì gAMA ÖØÔOX2 tEXtSoftware Adobe ImageReadyqÉe< IDATxÚ¬TËJÃP ¤ ¤ º Q *¨  A7‚¢HðSô ü ¿Á_p­ +¢â·¢è®"øÀÇ®Ï4M çÜ&iÒ¤ºñ–›ôÎ srfîÌ•\×¥ ½ÈêõúZµZuËå²[*•Ä kØ{ñHâ!Ia’B«Õ*Z–EÍf3 Êd2”Íf)•J år¹§0™Ü¥Fg’b¥R › uOØY!Á þa| D Âa ...@9+£|
/Å”! ¦iŠ0ŠÏo1ðËûW,\ø çÛ 2ä
+1 î®éôü: ~¼¿¥«›ûNHž¯mÛ !«Õj›0¦Óév2ù÷ùñJ{Æ ¹bݶ} ½ÓáÑI †?D   q¼k|‚±ÐÌZ
+vvv©Þ°©å¶ƒh˜UÚ7 jXmEÀ          ³{Hž"‡ OÏ. ¨csèøè0†‘½ø
+Y–     U­ – çQW MQ5Òu=œ;#L¶ ø
+ÇéT4Ï ÁaZÕW •“’
+aë  ¢å¥ aƒ?N x‘C<PÉÜ.HdPc“S3416 ¨Á Ÿœ¦ÂèpðA
+š¦i  i'T3×Í>
+,Û!M  n£i“’IGˆ  ªªëL¶ ëMVvÉ„³8îߺ û
+Ñ +›KìMl°Ã¶¢(ä7º «à 5ìø ü|¢ÄÞ ß
+œÜ"ÊÁ«#რÅä“ Ü =¯ „{­àÿï&H$û¯ñ#À íßSh »ßÖ    IEND®B`‚
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/mobile/client/Touch.java Fri May 14 08:28:05 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/mobile/client/Touch.java Tue May 18 06:37:32 2010
@@ -19,7 +19,7 @@
 import com.google.gwt.dom.client.EventTarget;

 /**
- * TODO: doc
+ * TODO: doc.
  */
 public class Touch extends JavaScriptObject {

=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/mobile/client/TouchEvent.java Fri May 14 08:28:05 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/mobile/client/TouchEvent.java Tue May 18 06:37:32 2010
@@ -19,7 +19,7 @@
 import com.google.gwt.dom.client.NativeEvent;

 /**
- * TODO: doc
+ * TODO: doc.
  */
 public class TouchEvent extends NativeEvent {

=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.java Thu May 13 11:35:58 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/ExpenseList.java Tue May 18 06:37:32 2010
@@ -441,6 +441,9 @@
         }
         searchBox.resetDefaultText();
         searchRegExp = null;
+
+        // Go to the first page of the newly-sorted results
+        pager.firstPage();
         requestReports(false);
       }
     });
@@ -601,6 +604,7 @@
         refreshTimer.schedule(REFRESH_INTERVAL);
       }
     };
+
requestFactory.reportRequest().findReportEntriesBySearch(employeeId, dept, startsWith, orderBy, range.getStart(), range.getLength()).forProperties(
         reportColumns).to(lastDataReceiver).fire();
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Scaffold.java Fri May 14 13:11:53 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/client/Scaffold.java Tue May 18 06:37:32 2010
@@ -40,7 +40,6 @@
 import java.util.Collections;
 import java.util.List;

-
 /**
  * Application for browsing the entities of the Expenses app.
  */
@@ -55,7 +54,6 @@
final PlaceController<ScaffoldPlace> placeController = new PlaceController<ScaffoldPlace>(
         eventBus);

-
     /* Top level UI */

     final ScaffoldShell shell = new ScaffoldShell();
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGenerator.java Fri May 14 13:11:53 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/gwt/server/ReportGenerator.java Tue May 18 06:37:32 2010
@@ -362,7 +362,7 @@
       "Conference", "Sales opportunity", "Sales opprtunity with COMPANY",
       "Maintenance", "Supplies", "Emergency", "Backup", "Research",
       "CITY breakfast", "CITY lunch", "CITY dinner", "CITY conference",
- "Conference in CITY", "Conferend in FULLCITY", "Meals with FIRSTNAME", + "Conference in CITY", "Conference in FULLCITY", "Meals with FIRSTNAME",
       "Meet with FULLNAME", "Meet with FIRSTNAME",
       "Meet with FIRSTNAME in CITY", "Meet with FIRSTNAME in FULLCITY",
       "FULLCITY meetings", "Visit to FULLCITY", "Visit to CITY",
=======================================
--- /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java Mon May 17 05:32:13 2010 +++ /branches/2.1/bikeshed/src/com/google/gwt/sample/expenses/server/domain/Report.java Tue May 18 06:37:32 2010
@@ -15,8 +15,15 @@
  */
 package com.google.gwt.sample.expenses.server.domain;

+import com.google.appengine.api.datastore.Cursor;
+
+import org.datanucleus.store.appengine.query.JPACursorHelper;
+
 import java.util.Date;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
+import java.util.logging.Logger;

 import javax.persistence.Column;
 import javax.persistence.Entity;
@@ -32,12 +39,22 @@
  */
 @Entity
 public class Report {
+
+ private static Map<Integer, String> cursorsByFirstResult = new HashMap<Integer, String>();
+
+ private static final Logger log = Logger.getLogger(Report.class.getName());

   /**
    * The total number of reports in the database.
    */
   private static long REPORT_COUNT = 5080388;
-
+  private static String searchedDepartment;
+  private static Long searchedEmployeeId = -9999L;
+  private static String searchedOrderBy;
+  private static Query searchedQuery;
+
+  private static String searchedStartsWith;
+
   public static long countReports() {
     EntityManager em = entityManager();
     try {
@@ -46,7 +63,7 @@
       em.close();
     }
   }
-
+
public static long countReportsBySearch(Long employeeId, String department,
       String startsWith) {
     EntityManager em = entityManager();
@@ -61,11 +78,9 @@
       em.close();
     }
   }
-
   public static final EntityManager entityManager() {
     return EMF.get().createEntityManager();
   }
-
   @SuppressWarnings("unchecked")
   public static List<Report> findAllReports() {
     EntityManager em = entityManager();
@@ -78,7 +93,7 @@
       em.close();
     }
   }
-
+
   public static Report findReport(Long id) {
     if (id == null) {
       return null;
@@ -104,21 +119,46 @@
       em.close();
     }
   }
-
+
   @SuppressWarnings("unchecked")
   public static List<Report> findReportEntriesBySearch(Long employeeId,
String department, String startsWith, String orderBy, int firstResult,
-      int maxResults) {
+      int maxResults) {
     EntityManager em = entityManager();
     try {
       Query query = queryReportsBySearch(em, employeeId, department,
           startsWith, orderBy, false);
-      query.setFirstResult(firstResult);
-      query.setMaxResults(maxResults);
-      List<Report> reportList = query.getResultList();
+      searchedQuery = query;
+
+      if (firstResult > 0 && equals(employeeId, searchedEmployeeId)
+          && equals(department, searchedDepartment)
+          && equals(startsWith, searchedStartsWith)
+          && equals(orderBy, searchedOrderBy)) {
+ log.info("findReportEntriesBySearch: Existing query with firstResult = " + firstResult);
+        skipTo(firstResult);
+      } else {
+ log.info("findReportEntriesBySearch: New query with firstResult = " + firstResult);
+        searchedEmployeeId = employeeId;
+        searchedDepartment = department;
+        searchedStartsWith = startsWith;
+        searchedOrderBy = orderBy;
+        cursorsByFirstResult.clear();
+
+        if (firstResult + maxResults > 1000) {
+          skipTo(firstResult);
+        } else {
+          query.setFirstResult(firstResult);
+        }
+      }
+
+      searchedQuery.setMaxResults(maxResults);
+      List<Report> reportList = searchedQuery.getResultList();
       // force it to materialize
       reportList.size();
-
+
+      Cursor cursor = JPACursorHelper.getCursor(reportList);
+      String encodedCursor = cursor.toWebSafeString();
+ cursorsByFirstResult.put(firstResult + reportList.size(), encodedCursor);
       return reportList;
     } finally {
       em.close();
@@ -140,6 +180,10 @@
     }
   }

+  private static boolean equals(Object a, Object b) {
+    return a == null ? b == null : a.equals(b);
+  }
+
   /**
    * Query for reports based on the search parameters. If startsWith is
    * specified, the results will not be ordered.
@@ -206,19 +250,47 @@
     return query;
   }

-  @Id
-  @Column(name = "id")
-  @GeneratedValue(strategy = GenerationType.IDENTITY)
-  private Long id;
-
-  @Version
-  @Column(name = "version")
-  private Integer version;
+  @SuppressWarnings("unchecked")
+  private static void skipTo(int firstResult) {
+    String encodedCursor = cursorsByFirstResult.get(firstResult);
+    if (encodedCursor != null) {
+      log.info("skipTo: Using cursor");
+      Cursor cursor = Cursor.fromWebSafeString(encodedCursor);
+      searchedQuery.setHint(JPACursorHelper.CURSOR_HINT, cursor);
+      searchedQuery.setFirstResult(0);
+      return;
+    } else {
+      // TODO - start at closest known position
+      log.info("skipTo: Counting up from 0");
+      searchedQuery.setHint(JPACursorHelper.CURSOR_HINT, null);
+      searchedQuery.setFirstResult(0);
+      while (firstResult > 0) {
+        searchedQuery.setMaxResults(Math.min(firstResult, 1000));
+        List<Report> results = searchedQuery.getResultList();
+        int count = results.size();
+        if (count == 0) {
+          break;
+        }
+
+        Cursor cursor = JPACursorHelper.getCursor(results);
+        searchedQuery.setHint(JPACursorHelper.CURSOR_HINT, cursor);
+        firstResult -= count;
+      }
+    }
+  }
+
+  // @JoinColumn
+  private Long approvedSupervisorKey;

   private Date created;

   private String department;

+  @Id
+  @Column(name = "id")
+  @GeneratedValue(strategy = GenerationType.IDENTITY)
+  private Long id;
+
   private String notes;

   private String purpose;
@@ -237,8 +309,9 @@
   // @JoinColumn
   private Long reporterKey;

-  // @JoinColumn
-  private Long approvedSupervisorKey;
+  @Version
+  @Column(name = "version")
+  private Integer version;

   public Long getApprovedSupervisorKey() {
     return approvedSupervisorKey;
=======================================
--- /branches/2.1/user/src/com/google/gwt/cell/client/Cell.java Wed May 5 06:28:08 2010 +++ /branches/2.1/user/src/com/google/gwt/cell/client/Cell.java Tue May 18 06:37:32 2010
@@ -33,14 +33,14 @@
    * Returns true if the cell is interested in browser events. The default
    * implementation returns false.
    */
-  public abstract boolean consumesEvents();
+  boolean consumesEvents();

   /**
    * Check if this cell depends on the selection state.
    *
    * @return true if dependant on selection, false if not
    */
-  public abstract boolean dependsOnSelection();
+  boolean dependsOnSelection();

   /**
    * Handle a browser event that took place within the cell. The default
@@ -53,7 +53,7 @@
    * @param valueUpdater a {...@link ValueUpdater}, or null
* @return a view data object which may be the one passed in or a new object
    */
-  public abstract Object onBrowserEvent(Element parent, C value,
+  Object onBrowserEvent(Element parent, C value,
       Object viewData, NativeEvent event, ValueUpdater<C> valueUpdater);

   /**
@@ -64,7 +64,7 @@
    * @param viewData view data associated with the cell
    * @param sb the StringBuilder to be written to
    */
-  public abstract void render(C value, Object viewData, StringBuilder sb);
+  void render(C value, Object viewData, StringBuilder sb);

   /**
* This method may be used by cell containers to set the value on a single
@@ -76,5 +76,5 @@
    * @param value the value associated with the cell
    * @param viewData the view data associated with the cell, or null
    */
-  public abstract void setValue(Element parent, C value, Object viewData);
-}
+  void setValue(Element parent, C value, Object viewData);
+}
=======================================
--- /branches/2.1/user/src/com/google/gwt/user/cellview/client/AbstractPager.java Thu May 13 09:13:58 2010 +++ /branches/2.1/user/src/com/google/gwt/user/cellview/client/AbstractPager.java Tue May 18 06:37:32 2010
@@ -80,7 +80,7 @@
   public PagingListView<T> getPagingListView() {
     return view;
   }
-
+
   /**
    * Returns true if there is enough data such that a call to
* {...@link #nextPage()} will succeed in moving the starting point of the table
@@ -89,6 +89,14 @@
   public boolean hasNextPage() {
     return view.getPageStart() + view.getPageSize() < view.getDataSize();
   }
+
+  /**
+   * Returns true if there is enough data to display a given number of
+   * additional pages.
+   */
+  public boolean hasNextPages(int pages) {
+ return view.getPageStart() + pages * view.getPageSize() < view.getDataSize();
+  }

   /**
* Returns true if there is enough data such that the specified page is within
=======================================
--- /branches/2.1/user/src/com/google/gwt/user/cellview/client/SimplePager.java Fri May 14 08:44:01 2010 +++ /branches/2.1/user/src/com/google/gwt/user/cellview/client/SimplePager.java Tue May 18 06:37:32 2010
@@ -36,19 +36,22 @@
  * @param <T> the type of the PagingListView being controlled
  */
 public class SimplePager<T> extends AbstractPager<T> {
-
-  /**
-   * The location of the text relative to the paging buttons.
-   */
-  public static enum TextLocation {
-    LEFT, RIGHT, CENTER;
-  }

   /**
    * A ClientBundle that provides images for this widget.
    */
   public static interface Resources extends ClientBundle {

+    /**
+     * The image used to skip ahead multiple pages.
+     */
+    ImageResource simplePagerFastForward();
+
+    /**
+     * The disabled "fast forward" image.
+     */
+    ImageResource simplePagerFastForwardDisabled();
+
     /**
      * The image used to go to the first page.
      */
@@ -116,6 +119,13 @@
      */
     String pageDetails();
   }
+
+  /**
+   * The location of the text relative to the paging buttons.
+   */
+  public static enum TextLocation {
+    CENTER, LEFT, RIGHT;
+  }

   private static Resources DEFAULT_RESOURCES;

@@ -125,6 +135,10 @@
     }
     return DEFAULT_RESOURCES;
   }
+
+  private final Image fastForward;
+
+  private final int fastForwardPages;

   private final Image firstPage;

@@ -154,6 +168,10 @@
    */
   private final Resources resources;

+  private boolean showLastPageButton;
+
+  private boolean showFastForwardButton;
+
   /**
    * The {...@link Style} used by this widget.
    */
@@ -175,8 +193,10 @@
    * @param location the location of the text relative to the buttons
    */
   @UiConstructor
+  // Hack for Google I/O demo
   public SimplePager(PagingListView<T> view, TextLocation location) {
-    this(view, location, getDefaultResources());
+    this(view, location, getDefaultResources(), true,
+        1000 / view.getPageSize(), false);
   }

   /**
@@ -185,21 +205,36 @@
    * @param view the {...@link PagingListView} to page
    * @param location the location of the text relative to the buttons
    * @param resources the {...@link Resources} to use
+   * @param showFastForwardButton if true, show a fast-forward button that
+   *          advances by a larger increment than a single page
+   * @param fastForwardPages the number of pages to fast forward
+ * @param showLastPageButton if true, show a button to go the the last page
    */
   public SimplePager(PagingListView<T> view, TextLocation location,
-      Resources resources) {
+      Resources resources, boolean showFastForwardButton,
+      final int fastForwardPages,
+      boolean showLastPageButton) {
     super(view);
     this.resources = resources;
+    this.showFastForwardButton = showFastForwardButton;
+    this.fastForwardPages = fastForwardPages;
+    this.showLastPageButton = showLastPageButton;
     this.style = resources.simplePagerStyle();
     this.style.ensureInjected();

     // Create the buttons.
+    fastForward = new Image(resources.simplePagerFastForward());
     firstPage = new Image(resources.simplePagerFirstPage());
     lastPage = new Image(resources.simplePagerLastPage());
     nextPage = new Image(resources.simplePagerNextPage());
     prevPage = new Image(resources.simplePagerPreviousPage());

     // Add handlers.
+    fastForward.addClickHandler(new ClickHandler() {
+      public void onClick(ClickEvent event) {
+        setPage(getPage() + fastForwardPages);
+      }
+    });
     firstPage.addClickHandler(new ClickHandler() {
       public void onClick(ClickEvent event) {
         firstPage();
@@ -234,12 +269,18 @@
       layout.add(label);
     }
     layout.add(nextPage);
-    layout.add(lastPage);
+    if (showFastForwardButton) {
+      layout.add(fastForward);
+    }
+    if (showLastPageButton) {
+      layout.add(lastPage);
+    }
     if (location == TextLocation.LEFT) {
       layout.add(label);
     }

     // Add style names to the cells.
+ fastForward.getElement().getParentElement().addClassName(style.button());
     firstPage.getElement().getParentElement().addClassName(style.button());
     prevPage.getElement().getParentElement().addClassName(style.button());
label.getElement().getParentElement().addClassName(style.pageDetails());
@@ -281,16 +322,32 @@
         lastPage.setResource(resources.simplePagerLastPage());
         nextPage.getElement().getParentElement().removeClassName(
             style.disabledButton());
-        lastPage.getElement().getParentElement().removeClassName(
-            style.disabledButton());
+        if (showLastPageButton) {
+          lastPage.getElement().getParentElement().removeClassName(
+              style.disabledButton());
+        }
       } else if (!hasNext && !nextDisabled) {
         nextDisabled = true;
         nextPage.setResource(resources.simplePagerNextPageDisabled());
         lastPage.setResource(resources.simplePagerLastPageDisabled());
         nextPage.getElement().getParentElement().addClassName(
             style.disabledButton());
-        lastPage.getElement().getParentElement().addClassName(
-            style.disabledButton());
+        if (showLastPageButton) {
+          lastPage.getElement().getParentElement().addClassName(
+              style.disabledButton());
+        }
+      }
+
+      if (showFastForwardButton) {
+        if (hasNextPages(fastForwardPages)) {
+          fastForward.setResource(resources.simplePagerFastForward());
+          fastForward.getElement().getParentElement().removeClassName(
+              style.disabledButton());
+        } else {
+ fastForward.setResource(resources.simplePagerFastForwardDisabled());
+          fastForward.getElement().getParentElement().addClassName(
+              style.disabledButton());
+        }
       }
     }
   }

--
http://groups.google.com/group/Google-Web-Toolkit-Contributors

Reply via email to