Revision: 7565
Author: jlaba...@google.com
Date: Fri Feb 12 05:38:22 2010
Log: Abstracting out PlayerStatus to a separate class and adding the concept of cash. Also adds GWT checkstyle to the project and cleans up checkstyle errors.

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

Added:
/trunk/bikeshed/src/com/google/gwt/sample/datawidgets/server/PlayerStatus.java
Modified:
 /trunk/bikeshed/.project
 /trunk/bikeshed/src/com/google/gwt/cells/client/ButtonCell.java
 /trunk/bikeshed/src/com/google/gwt/cells/client/Cell.java
 /trunk/bikeshed/src/com/google/gwt/cells/client/CheckboxCell.java
 /trunk/bikeshed/src/com/google/gwt/cells/client/CurrencyCell.java
 /trunk/bikeshed/src/com/google/gwt/cells/client/Mutator.java
 /trunk/bikeshed/src/com/google/gwt/cells/client/TextCell.java
 /trunk/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java
 /trunk/bikeshed/src/com/google/gwt/list/client/Column.java
 /trunk/bikeshed/src/com/google/gwt/list/client/PagingTableListView.java
 /trunk/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java
 /trunk/bikeshed/src/com/google/gwt/list/shared/AbstractListModel.java
/trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/BuySellPopup.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockQuote.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockRequest.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/Transaction.java
 /trunk/bikeshed/war/WEB-INF/web.xml

=======================================
--- /dev/null
+++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/server/PlayerStatus.java Fri Feb 12 05:38:22 2010
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2010 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.sample.datawidgets.server;
+
+import java.util.HashMap;
+import java.util.TreeSet;
+
+/**
+ * Game state for a single player.
+ */
+public class PlayerStatus {
+
+  /**
+   * The initial amount of cash that the player starts with, in cents.
+   */
+  private static final int INITIAL_CASH = 10000 * 100;
+
+  /**
+   * An impossible stock ticker.
+   */
+  private static final String IMPOSSIBLE_TICKER_SYMBOL = "XXXXXXXXXX";
+
+  /**
+   * The amount of cash that the player has.
+   */
+  private int cash = INITIAL_CASH;
+
+  /**
+   * This players favorite stocks.
+   */
+  private TreeSet<String> favorites = new TreeSet<String>();
+
+  /**
+   * The query used to retrieve favorites.
+   */
+  private String favoritesQuery;
+
+  /**
+   * The number of shares owned for each symbol.
+   */
+ private HashMap<String, Integer> sharesOwnedBySymbol = new HashMap<String, Integer>();
+
+  public PlayerStatus() {
+    generateFavoritesQuery();
+  }
+
+  /**
+   * Add a stock to the favorites list.
+   *
+   * @param ticker the stock ticker
+   */
+  public void addFavorite(String ticker) {
+    favorites.add(ticker);
+    generateFavoritesQuery();
+  }
+
+  /**
+   * Purchase stock.
+   *
+   * @param ticker the stock ticker
+   * @param quantity the number of shares to buy
+   * @param price the price of the stock
+   * @throws IllegalArgumentException if the stock cannot be purchased
+   */
+  public void buy(String ticker, int quantity, int price)
+      throws IllegalArgumentException {
+    // Verify that the player can afford the stock.
+    int totalPrice = price * quantity;
+    if (cash < totalPrice) {
+ throw new IllegalArgumentException("You cannot afford that much stock");
+    }
+
+    // Update the number of shares owned.
+    int current = getSharesOwned(ticker);
+    cash -= totalPrice;
+    current += quantity;
+    sharesOwnedBySymbol.put(ticker, current);
+
+    // Add this stock to the favorites list.
+    addFavorite(ticker);
+  }
+
+  /**
+   * Get the player's current cash amount.
+   *
+   * @return the cash amount
+   */
+  public int getCash() {
+    return cash;
+  }
+
+  /**
+   * Get this players favorite query.
+   *
+   * @return the query
+   */
+  public String getFavoritesQuery() {
+    return favoritesQuery;
+  }
+
+  /**
+   * Get the number of shares owned for a given stock.
+   *
+   * @param ticker the stock ticker
+   * @return the number of shares owned
+   */
+  public int getSharesOwned(String ticker) {
+    Integer current = sharesOwnedBySymbol.get(ticker);
+    return current == null ? 0 : current;
+  }
+
+  /**
+   * Check if the stock ticker is in the favorites list.
+   *
+   * @param ticker the stock sticker
+   * @return true if a favorite, false if not
+   */
+  public boolean isFavorite(String ticker) {
+    return favorites.contains(ticker);
+  }
+
+  /**
+   * Remove a stock from the favorites list.
+   *
+   * @param ticker the stock ticker
+   */
+  public void removeFavorite(String ticker) {
+    favorites.remove(ticker);
+    generateFavoritesQuery();
+  }
+
+  /**
+   * Sell stock.
+   *
+   * @param ticker the stock ticker
+   * @param quantity the number of shares to sell
+   * @param price the price of the stock
+   * @throws IllegalArgumentException if the stock cannot be sold
+   */
+  public void sell(String ticker, int quantity, int price)
+      throws IllegalArgumentException {
+    // Verify that the player has enough stock to sell.
+    int current = sharesOwnedBySymbol.get(ticker);
+    if (quantity > current) {
+      throw new IllegalArgumentException(
+          "You cannot sell more stock than you own");
+    }
+
+    // Perform the transaction.
+    cash += quantity * price;
+    current -= quantity;
+    sharesOwnedBySymbol.put(ticker, current);
+  }
+
+  /**
+   * Regenerate the favorites query.
+   */
+  private void generateFavoritesQuery() {
+    StringBuilder sb = new StringBuilder(IMPOSSIBLE_TICKER_SYMBOL);
+    for (String ticker : favorites) {
+      sb.append('|');
+      sb.append(ticker);
+    }
+    favoritesQuery = sb.toString();
+  }
+}
=======================================
--- /trunk/bikeshed/.project    Tue Feb  9 11:11:52 2010
+++ /trunk/bikeshed/.project    Fri Feb 12 05:38:22 2010
@@ -28,6 +28,11 @@
                <buildCommand>
                        
<name>com.google.gdt.eclipse.core.webAppProjectValidator</name>
                        <arguments>
+                       </arguments>
+               </buildCommand>
+               <buildCommand>
+                       
<name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>
+                       <arguments>
                        </arguments>
                </buildCommand>
        </buildSpec>
@@ -36,5 +41,6 @@
                <nature>com.google.appengine.eclipse.core.gaeNature</nature>
                <nature>com.google.gwt.eclipse.core.gwtNature</nature>
                <nature>com.google.gdt.eclipse.core.webAppNature</nature>
+               
<nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>
        </natures>
 </projectDescription>
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/ButtonCell.java Wed Feb 10 04:54:14 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/ButtonCell.java Fri Feb 12 05:38:22 2010
@@ -18,16 +18,10 @@
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;

+/**
+ * A {...@link Cell} used to render a button.
+ */
 public class ButtonCell extends Cell<String> {
-
-  @Override
-  public void render(String data, StringBuilder sb) {
-    sb.append("<button>");
-    if (data != null) {
-      sb.append(data);
-    }
-    sb.append("</button>");
-  }

   @Override
public void onBrowserEvent(Element parent, String value, NativeEvent event,
@@ -40,4 +34,13 @@
       mutator.mutate(value, null);
     }
   }
-}
+
+  @Override
+  public void render(String data, StringBuilder sb) {
+    sb.append("<button>");
+    if (data != null) {
+      sb.append(data);
+    }
+    sb.append("</button>");
+  }
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/Cell.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/Cell.java Fri Feb 12 05:38:22 2010
@@ -18,6 +18,11 @@
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;

+/**
+ * A light weight representation of a renderable object.
+ *
+ * @param <C> the type that this Cell represents
+ */
 public abstract class Cell<C> {

   /**
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/CheckboxCell.java Wed Feb 10 04:54:14 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/CheckboxCell.java Fri Feb 12 05:38:22 2010
@@ -19,16 +19,10 @@
 import com.google.gwt.dom.client.InputElement;
 import com.google.gwt.dom.client.NativeEvent;

+/**
+ * A {...@link Cell} used to render a checkbox.
+ */
 public class CheckboxCell extends Cell<Boolean> {
-
-  @Override
-  public void render(Boolean data, StringBuilder sb) {
-    sb.append("<input type=\"checkbox\"");
-    if (data == Boolean.TRUE) {
-      sb.append(" checked");
-    }
-    sb.append("/>");
-  }

   @Override
public void onBrowserEvent(Element parent, Boolean value, NativeEvent event,
@@ -42,4 +36,13 @@
       mutator.mutate(value, input.isChecked());
     }
   }
-}
+
+  @Override
+  public void render(Boolean data, StringBuilder sb) {
+    sb.append("<input type=\"checkbox\"");
+    if (data == Boolean.TRUE) {
+      sb.append(" checked");
+    }
+    sb.append("/>");
+  }
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/CurrencyCell.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/CurrencyCell.java Fri Feb 12 05:38:22 2010
@@ -15,13 +15,16 @@
  */
 package com.google.gwt.cells.client;

+/**
+ * A {...@link Cell} used to render currency.
+ */
 public class CurrencyCell extends Cell<Integer> {

   @Override
   public void render(Integer price, StringBuilder sb) {
     int dollars = price / 100;
     int cents = price % 100;
-
+
     sb.append("$ ");
     sb.append(dollars);
     sb.append('.');
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/Mutator.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/Mutator.java Fri Feb 12 05:38:22 2010
@@ -15,6 +15,12 @@
  */
 package com.google.gwt.cells.client;

+/**
+ * A mutator can be added to a Cell to mutate data.
+ *
+ * @param <T> the data type that will be modified
+ * @param <C> the data type of the cell
+ */
 public interface Mutator<T, C> {
   void mutate(T object, C after);
 }
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/TextCell.java Wed Feb 10 04:54:14 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/TextCell.java Fri Feb 12 05:38:22 2010
@@ -15,6 +15,9 @@
  */
 package com.google.gwt.cells.client;

+/**
+ * A {...@link Cell} used to render text.
+ */
 public class TextCell extends Cell<String> {

   @Override
=======================================
--- /trunk/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/cells/client/TextInputCell.java Fri Feb 12 05:38:22 2010
@@ -19,16 +19,10 @@
 import com.google.gwt.dom.client.InputElement;
 import com.google.gwt.dom.client.NativeEvent;

+/**
+ * A {...@link Cell} used to render a text input.
+ */
 public class TextInputCell extends Cell<String> {
-
-  @Override
-  public void render(String data, StringBuilder sb) {
-    sb.append("<input type='text'");
-    if (data != null) {
-      sb.append(" value='" + data + "'");
-    }
-    sb.append("></input>");
-  }

   @Override
public void onBrowserEvent(Element parent, String value, NativeEvent event,
@@ -42,4 +36,13 @@
       mutator.mutate(value, input.getValue());
     }
   }
-}
+
+  @Override
+  public void render(String data, StringBuilder sb) {
+    sb.append("<input type='text'");
+    if (data != null) {
+      sb.append(" value='" + data + "'");
+    }
+    sb.append("></input>");
+  }
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/list/client/Column.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/list/client/Column.java Fri Feb 12 05:38:22 2010
@@ -20,6 +20,12 @@
 import com.google.gwt.dom.client.Element;
 import com.google.gwt.dom.client.NativeEvent;

+/**
+ * A representation of a column in a table.
+ *
+ * @param <T> the row type
+ * @param <C> the column type
+ */
 public abstract class Column<T, C> {
   private final Cell<C> cell;
   private Mutator<T, C> mutator;
@@ -43,10 +49,11 @@
   public void setMutator(Mutator<T, C> mutator) {
     this.mutator = mutator;
   }
-
-  protected abstract C getValue(T object);

   protected Cell<C> getCell() {
     return cell;
   }
-}
+
+  protected abstract C getValue(T object);
+
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/list/client/PagingTableListView.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/list/client/PagingTableListView.java Fri Feb 12 05:38:22 2010
@@ -38,12 +38,17 @@
 import java.util.ArrayList;
 import java.util.List;

+/**
+ * A list view that supports paging and columns.
+ *
+ * @param <T> the data type of each row.
+ */
 public class PagingTableListView<T> extends Widget {

+  protected int curPage;
   private int pageSize;
   private int numPages;
   private ListRegistration listReg;
-  protected int curPage;
   private int totalSize;
   private List<Column<T, ?>> columns = new ArrayList<Column<T, ?>>();
   private ArrayList<T> data = new ArrayList<T>();
@@ -83,6 +88,19 @@
     createRows();
     setPage(curPage); // TODO: better way to refresh?
   }
+
+  /**
+   * Get the current page.
+   *
+   * @return the current page
+   */
+  public int getPage() {
+    return curPage;
+  }
+
+  public void nextPage() {
+    setPage(curPage + 1);
+  }

   @Override
   public void onBrowserEvent(Event event) {
@@ -106,17 +124,19 @@
             column.onBrowserEvent(elem, value, event);
           } else if (row == pageSize) {
             if (col == 0) {
- prevButton.onBrowserEvent(elem, null, event, new Mutator<String,String>() {
-                public void mutate(String object, String after) {
-                  previousPage();
-                }
-              });
+              prevButton.onBrowserEvent(elem, null, event,
+                  new Mutator<String, String>() {
+                    public void mutate(String object, String after) {
+                      previousPage();
+                    }
+                  });
             } else if (col == 2) {
- nextButton.onBrowserEvent(elem, null, event, new Mutator<String,String>() {
-                public void mutate(String object, String after) {
-                  nextPage();
-                }
-              });
+              nextButton.onBrowserEvent(elem, null, event,
+                  new Mutator<String, String>() {
+                    public void mutate(String object, String after) {
+                      nextPage();
+                    }
+                  });
             }
           }
           break;
@@ -127,45 +147,6 @@
     }
   }

-  protected void render(int start, int length, List<T> values) {
-    TableElement table = getElement().cast();
-    int numCols = columns.size();
-    int pageStart = curPage * pageSize;
-
-    NodeList<TableRowElement> rows = table.getRows();
-    for (int r = start; r < start + length; ++r) {
-      TableRowElement row = rows.getItem(r - pageStart);
-      T q = values.get(r - start);
-
-      data.set(r - pageStart, q);
-      for (int c = 0; c < numCols; ++c) {
-        TableCellElement cell = row.getCells().getItem(c);
-        StringBuilder sb = new StringBuilder();
-        columns.get(c).render(q, sb);
-        cell.setInnerHTML(sb.toString());
-
-        // TODO: really total hack!
-        Element child = cell.getFirstChildElement();
-        if (child != null) {
- Event.sinkEvents(child, Event.ONCHANGE | Event.ONFOCUS | Event.ONBLUR);
-        }
-      }
-    }
-  }
-
-  /**
-   * Get the current page.
-   *
-   * @return the current page
-   */
-  public int getPage() {
-    return curPage;
-  }
-
-  public void nextPage() {
-    setPage(curPage + 1);
-  }
-
   public void previousPage() {
     setPage(curPage - 1);
   }
@@ -190,20 +171,6 @@

     updateRowVisibility();
   }
-
-  private void updateRowVisibility() {
-    int visible = Math.min(pageSize, totalSize - curPage * pageSize);
-
-    TableElement table = getElement().cast();
-    for (int r = 0; r < pageSize; ++r) {
-      Style rowStyle = table.getRows().getItem(r).getStyle();
-      if (r < visible) {
-        rowStyle.clearDisplay();
-      } else {
-        rowStyle.setDisplay(Display.NONE);
-      }
-    }
-  }

   /**
    * Set the number of rows per page.
@@ -219,53 +186,68 @@
     setPage(curPage);
   }

-  /**
-   * Update the text that shows the current page.
-   *
-   * @param page the current page
-   */
-  private void updatePageText(int page) {
+  protected void render(int start, int length, List<T> values) {
     TableElement table = getElement().cast();
+    int numCols = columns.size();
+    int pageStart = curPage * pageSize;
+
     NodeList<TableRowElement> rows = table.getRows();
-    String text = "Page " + (page + 1) + " of " + numPages;
- rows.getItem(rows.getLength() - 1).getCells().getItem(1).setInnerText(text);
+    for (int r = start; r < start + length; ++r) {
+      TableRowElement row = rows.getItem(r - pageStart);
+      T q = values.get(r - start);
+
+      data.set(r - pageStart, q);
+      for (int c = 0; c < numCols; ++c) {
+        TableCellElement cell = row.getCells().getItem(c);
+        StringBuilder sb = new StringBuilder();
+        columns.get(c).render(q, sb);
+        cell.setInnerHTML(sb.toString());
+
+        // TODO: really total hack!
+        Element child = cell.getFirstChildElement();
+        if (child != null) {
+ Event.sinkEvents(child, Event.ONCHANGE | Event.ONFOCUS | Event.ONBLUR);
+        }
+      }
+    }
   }

   private void createRows() {
     TableElement table = getElement().cast();
     int numCols = columns.size();
-
+
     // TODO - only delete as needed
     int numRows = table.getRows().getLength();
     while (numRows-- > 0) {
       table.deleteRow(0);
     }
-
+
     for (int r = 0; r < pageSize; ++r) {
       TableRowElement row = table.insertRow(0);
- row.setClassName("pagingTableListView " + ((r & 0x1) == 0 ? "evenRow" : "oddRow"));
+      row.setClassName("pagingTableListView "
+          + ((r & 0x1) == 0 ? "evenRow" : "oddRow"));

       // TODO: use cloneNode() to make this even faster.
       for (int c = 0; c < numCols; ++c) {
         row.insertCell(c);
       }
     }
-
+
     // Add the final row containing paging buttons
     TableRowElement pageRow = table.insertRow(pageSize);
     pageRow.insertCell(0);
     pageRow.insertCell(1);
     pageRow.insertCell(2);
-
+
     StringBuilder sb;
-
+
     sb = new StringBuilder();
     prevButton.render("Previous", sb);
     pageRow.getCells().getItem(0).setInnerHTML(sb.toString());
-
+
pageRow.getCells().getItem(1).setAttribute("colspan", "" + (numCols - 2));
     pageRow.getCells().getItem(1).setAttribute("align", "center");
-
+
     sb = new StringBuilder();
     nextButton.render("Next", sb);
     pageRow.getCells().getItem(2).setInnerHTML(sb.toString());
@@ -277,4 +259,30 @@
       data.add(null);
     }
   }
-}
+
+  /**
+   * Update the text that shows the current page.
+   *
+   * @param page the current page
+   */
+  private void updatePageText(int page) {
+    TableElement table = getElement().cast();
+    NodeList<TableRowElement> rows = table.getRows();
+    String text = "Page " + (page + 1) + " of " + numPages;
+ rows.getItem(rows.getLength() - 1).getCells().getItem(1).setInnerText(text);
+  }
+
+  private void updateRowVisibility() {
+    int visible = Math.min(pageSize, totalSize - curPage * pageSize);
+
+    TableElement table = getElement().cast();
+    for (int r = 0; r < pageSize; ++r) {
+      Style rowStyle = table.getRows().getItem(r).getStyle();
+      if (r < visible) {
+        rowStyle.clearDisplay();
+      } else {
+        rowStyle.setDisplay(Display.NONE);
+      }
+    }
+  }
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/list/client/SimpleCellList.java Fri Feb 12 05:38:22 2010
@@ -30,6 +30,11 @@
 import java.util.ArrayList;
 import java.util.List;

+/**
+ * A single column list of cells.
+ *
+ * @param <T> the data type of list items
+ */
 public class SimpleCellList<T> extends Widget {

   private final Cell<T> cell;
@@ -81,15 +86,20 @@
   @Override
   public void onBrowserEvent(Event event) {
     Element target = event.getEventTarget().cast();
-    String __idx = "";
- while ((target != null) && ((__idx = target.getAttribute("__idx")).length() == 0)) {
+    String idxString = "";
+    while ((target != null)
+        && ((idxString = target.getAttribute("__idx")).length() == 0)) {
       target = target.getParentElement();
     }
-    if (__idx.length() > 0) {
-      int idx = Integer.parseInt(__idx);
+    if (idxString.length() > 0) {
+      int idx = Integer.parseInt(idxString);
       cell.onBrowserEvent(target, data.get(idx), event, mutator);
     }
   }
+
+  public void setMutator(Mutator<T, T> mutator) {
+    this.mutator = mutator;
+  }

   private void gc(int size) {
     // Remove unused children if the size shrinks.
@@ -132,8 +142,4 @@
       }
     }
   }
-
-  public void setMutator(Mutator<T, T> mutator) {
-    this.mutator = mutator;
-  }
-}
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/list/shared/AbstractListModel.java Tue Feb 9 11:11:52 2010 +++ /trunk/bikeshed/src/com/google/gwt/list/shared/AbstractListModel.java Fri Feb 12 05:38:22 2010
@@ -29,7 +29,7 @@
   /**
    * The range of interest for a single handler.
    */
-  static class DefaultRange implements Range, Serializable {
+  public static class DefaultRange implements Range, Serializable {
     private int start;
     private int length;

=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/BuySellPopup.java Thu Feb 11 10:53:53 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/BuySellPopup.java Fri Feb 12 05:38:22 2010
@@ -17,6 +17,9 @@

 import com.google.gwt.event.dom.client.ClickEvent;
 import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.event.dom.client.KeyUpEvent;
+import com.google.gwt.event.dom.client.KeyUpHandler;
+import com.google.gwt.i18n.client.NumberFormat;
 import com.google.gwt.sample.datawidgets.shared.StockQuote;
 import com.google.gwt.sample.datawidgets.shared.Transaction;
 import com.google.gwt.user.client.Command;
@@ -69,6 +72,22 @@
     layout.setHTML(2, 0, "<b>Price:</b>");
     layout.setHTML(3, 0, "<b>Quantity:</b>");
     layout.setWidget(3, 1, quantityBox);
+    layout.setHTML(4, 0, "<b>Total:</b>");
+    layout.setHTML(5, 0, "<b>Available:</b>");
+
+    // Update total price when the quantity changes.
+    quantityBox.addKeyUpHandler(new KeyUpHandler() {
+      public void onKeyUp(KeyUpEvent event) {
+        try {
+          int quantity = Integer.parseInt(quantityBox.getText());
+          double totalPrice = quantity * quote.getPrice() / 100.0;
+ layout.setText(4, 1, NumberFormat.getCurrencyFormat("USD").format(
+              totalPrice));
+        } catch (NumberFormatException e) {
+          layout.setText(4, 1, "Invalid quantity");
+        }
+      }
+    });

     // Buy Button.
     opButton = new Button("", new ClickHandler() {
@@ -82,7 +101,7 @@
         }
       }
     });
-    layout.setWidget(4, 0, opButton);
+    layout.setWidget(6, 0, opButton);

     // Cancel Button.
     Button cancelButton = new Button("Cancel", new ClickHandler() {
@@ -90,7 +109,7 @@
         hide();
       }
     });
-    layout.setWidget(4, 1, cancelButton);
+    layout.setWidget(6, 1, cancelButton);
   }

   /**
@@ -101,6 +120,16 @@
   public Transaction getTransaction() {
     return transaction;
   }
+
+  /**
+   * Set the available cash.
+   *
+   * @param cash the available cash
+   */
+  public void setAvailableCash(double cash) {
+    // TODO: Bind the available cash field.
+ layout.setText(5, 1, NumberFormat.getCurrencyFormat("USD").format(cash));
+  }

   /**
    * Set the current {...@link StockQuote}.
@@ -112,9 +141,10 @@
     this.quote = quote;
     String op = isBuying ? "Buy" : "Sell";
     setText(op + " " + quote.getTicker() + " (" + quote.getName() + ")");
-    layout.setHTML(0, 1, quote.getTicker());
-    layout.setHTML(1, 1, quote.getName());
-    layout.setHTML(2, 1, quote.getDisplayPrice());
+    layout.setText(0, 1, quote.getTicker());
+    layout.setText(1, 1, quote.getName());
+    layout.setText(2, 1, quote.getDisplayPrice());
+ layout.setText(4, 1, NumberFormat.getCurrencyFormat("USD").format(0.0));
     quantityBox.setText("0");
     opButton.setText(op);
     this.isBuying = isBuying;
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java Thu Feb 11 10:53:53 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java Fri Feb 12 05:38:22 2010
@@ -26,6 +26,7 @@
 import com.google.gwt.event.dom.client.KeyUpHandler;
 import com.google.gwt.event.logical.shared.CloseEvent;
 import com.google.gwt.event.logical.shared.CloseHandler;
+import com.google.gwt.i18n.client.NumberFormat;
 import com.google.gwt.list.client.Column;
 import com.google.gwt.list.client.PagingTableListView;
 import com.google.gwt.list.shared.AsyncListModel;
@@ -40,6 +41,7 @@
 import com.google.gwt.user.client.Window;
 import com.google.gwt.user.client.rpc.AsyncCallback;
 import com.google.gwt.user.client.ui.HTML;
+import com.google.gwt.user.client.ui.HorizontalPanel;
 import com.google.gwt.user.client.ui.Label;
 import com.google.gwt.user.client.ui.PopupPanel;
 import com.google.gwt.user.client.ui.RootPanel;
@@ -103,6 +105,7 @@
   };

   private final TextBox queryField = new TextBox();
+  private final Label cashLabel = new Label();

private Column<StockQuote, String> tickerColumn = new Column<StockQuote, String>(
       new TextCell()) {
@@ -225,6 +228,12 @@
       }
     });

+    // Add components to the page.
+    HorizontalPanel hPanel = new HorizontalPanel();
+    hPanel.add(new HTML("<b>Available cash:</b>"));
+    hPanel.add(cashLabel);
+    RootPanel.get().add(hPanel);
+
     RootPanel.get().add(resultsTable);
     RootPanel.get().add(new HTML("<hr>"));
     RootPanel.get().add(favoritesTable);
@@ -240,7 +249,7 @@
   }

   /**
-   * Set or unset a ticker symbol as a 'favorite.'
+   * Set or unset a ticker symbol as a 'favorite'.
    *
    * @param ticker the ticker symbol
    * @param favorite if true, make the stock a favorite
@@ -268,6 +277,33 @@
       });
     }
   }
+
+  /**
+   * Process the {...@link StockResponse} from the server.
+   *
+   * @param response the stock response
+   */
+  private void processStockResponse(StockResponse response) {
+    // Update the search list.
+    StockQuoteList searchResults = response.getSearchResults();
+    searchListModel.updateDataSize(response.getNumSearchResults(), true);
+    searchListModel.updateViewData(searchResults.getStartIndex(),
+        searchResults.size(), searchResults);
+
+    // Update the favorites list.
+    StockQuoteList favorites = response.getFavorites();
+    favoritesListModel.updateDataSize(response.getNumFavorites(), true);
+    favoritesListModel.updateViewData(favorites.getStartIndex(),
+        favorites.size(), favorites);
+
+    // Update available cash.
+    double cash = response.getCash() / 100.0;
+    cashLabel.setText(NumberFormat.getCurrencyFormat("USD").format(cash));
+    buySellPopup.setAvailableCash(cash);
+
+    // Restart the update timer.
+    updateTimer.schedule(UPDATE_DELAY);
+  }

   /**
    * Request data from the server using the last query string.
@@ -288,24 +324,18 @@
         favoritesRanges[0]);
dataService.getStockQuotes(request, new AsyncCallback<StockResponse>() {
       public void onFailure(Throwable caught) {
-        Window.alert("ERROR: " + caught.getMessage());
-        updateTimer.schedule(UPDATE_DELAY);
+        String message = caught.getMessage();
+        if (message.contains("Not logged in")) {
+          // Force the user to login.
+          Window.Location.reload();
+        } else {
+          Window.alert("ERROR: " + caught.getMessage());
+          updateTimer.schedule(UPDATE_DELAY);
+        }
       }

       public void onSuccess(StockResponse result) {
-        StockQuoteList searchResults = result.getSearchResults();
-
-        searchListModel.updateDataSize(result.getNumSearchResults(), true);
-        searchListModel.updateViewData(searchResults.getStartIndex(),
-            searchResults.size(), searchResults);
-
-        StockQuoteList favorites = result.getFavorites();
-
-        favoritesListModel.updateDataSize(result.getNumFavorites(), true);
-        favoritesListModel.updateViewData(favorites.getStartIndex(),
-            favorites.size(), favorites);
-
-        updateTimer.schedule(UPDATE_DELAY);
+        processStockResponse(result);
       }
     });
   }
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java Thu Feb 11 05:08:46 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockQuoteCell.java Fri Feb 12 05:38:22 2010
@@ -18,6 +18,9 @@
 import com.google.gwt.cells.client.Cell;
 import com.google.gwt.sample.datawidgets.shared.StockQuote;

+/**
+ * A cell that represents a {...@link StockQuote}.
+ */
 public class StockQuoteCell extends Cell<StockQuote> {

   @Override
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java Thu Feb 11 10:53:53 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java Fri Feb 12 05:38:22 2010
@@ -15,7 +15,11 @@
  */
 package com.google.gwt.sample.datawidgets.server;

+import com.google.appengine.api.users.User;
+import com.google.appengine.api.users.UserService;
+import com.google.appengine.api.users.UserServiceFactory;
 import com.google.gwt.list.shared.Range;
+import com.google.gwt.list.shared.AbstractListModel.DefaultRange;
 import com.google.gwt.sample.datawidgets.client.StockService;
 import com.google.gwt.sample.datawidgets.shared.StockQuote;
 import com.google.gwt.sample.datawidgets.shared.StockQuoteList;
@@ -47,8 +51,9 @@
 public class StockServiceImpl extends RemoteServiceServlet implements
     StockService {

-  private static final String IMPOSSIBLE_TICKER_SYMBOL = "XXXXXXXXXX";
-
+  /**
+ * The result of a query to the remote service that provides stock quotes.
+   */
   private class Result {
     int numRows;
     StockQuoteList quotes;
@@ -76,15 +81,13 @@
     }
   }

-  private TreeSet<String> favorites = new TreeSet<String>();
-
-  private String favoritesQuery = IMPOSSIBLE_TICKER_SYMBOL;
-
- private HashMap<String, Integer> sharesOwnedBySymbol = new HashMap<String, Integer>();
+  /**
+   * A mapping of usernames to {...@link PlayerStatus}.
+   */
+ private Map<String, PlayerStatus> players = new HashMap<String, PlayerStatus>();

   public void addFavorite(String ticker) {
-    favorites.add(ticker);
-    generateFavoritesQuery();
+    ensurePlayer().addFavorite(ticker);
   }

   public StockResponse getStockQuotes(StockRequest request)
@@ -97,50 +100,59 @@
     Range searchRange = request.getSearchRange();
     Range favoritesRange = request.getFavoritesRange();

+    PlayerStatus player = ensurePlayer();
     Result searchResults = query(query, searchRange);
-    Result favorites = query(favoritesQuery, favoritesRange);
+    Result favorites = query(player.getFavoritesQuery(), favoritesRange);

     return new StockResponse(searchResults.quotes, favorites.quotes,
-        searchResults.numRows, favorites.numRows);
+        searchResults.numRows, favorites.numRows, player.getCash());
   }

   public void removeFavorite(String ticker) {
-    favorites.remove(ticker);
-    generateFavoritesQuery();
+    ensurePlayer().removeFavorite(ticker);
   }

   public Transaction transact(Transaction transaction)
       throws IllegalArgumentException {
-    // TODO: Check that the stock exists.
+    // Get the current stock price.
     String ticker = transaction.getTicker();
-    Integer current = sharesOwnedBySymbol.get(ticker);
-    if (current == null) {
-      current = 0;
-    }
-
+    if (ticker == null || ticker.length() < 0) {
+      throw new IllegalArgumentException("Stock could not be found");
+    }
+    Result result = query(ticker, new DefaultRange(0, 1));
+    if (result.numRows != 1 || result.quotes.size() != 1) {
+      throw new IllegalArgumentException("Could not resolve stock ticker");
+    }
+    StockQuote quote = result.quotes.get(0);
+
+    // Perform the transaction with the user.
     int quantity = transaction.getQuantity();
     if (transaction.isBuy()) {
-      current += quantity;
-      // TODO: Verify player has enough funds.
-      addFavorite(ticker);
+      ensurePlayer().buy(ticker, quantity, quote.getPrice());
     } else {
-      if (quantity > current) {
-        throw new IllegalArgumentException(
-            "You cannot sell more stock than you own");
-      }
-      current -= quantity;
-    }
-    sharesOwnedBySymbol.put(ticker, current);
+      ensurePlayer().sell(ticker, quantity, quote.getPrice());
+    }
+
+    //
     return new Transaction(true, ticker, quantity);
   }

-  private void generateFavoritesQuery() {
-    StringBuilder sb = new StringBuilder(IMPOSSIBLE_TICKER_SYMBOL);
-    for (String ticker : favorites) {
-      sb.append('|');
-      sb.append(ticker);
-    }
-    favoritesQuery = sb.toString();
+  /**
+ * Ensure that a {...@link PlayerStatus} for the current player exists and return
+   * it.
+   *
+   * @return the {...@link PlayerStatus} for the current player
+   */
+  private PlayerStatus ensurePlayer() {
+    UserService userService = UserServiceFactory.getUserService();
+    User user = userService.getCurrentUser();
+    String userId = user.getUserId();
+    PlayerStatus player = players.get(userId);
+    if (player == null) {
+      player = new PlayerStatus();
+      players.put(userId, player);
+    }
+    return player;
   }

   private List<String> getTickers(String query) {
@@ -177,8 +189,16 @@
     return false;
   }

+  /**
+   * Query the remote service to retrieve current stock prices.
+   *
+   * @param query the query string
+   * @param range the range of results requested
+   * @return the stock quotes
+   */
   private Result query(String query, Range range) {
     // Get all symbols for the query.
+    PlayerStatus player = ensurePlayer();
     List<String> symbols = getTickers(query);

     if (symbols.size() == 0) {
@@ -258,8 +278,8 @@
         try {
           iprice = (int) (Double.parseDouble(price) * 100);
           String name = companyNamesBySymbol.get(symbol);
-          Integer sharesOwned = sharesOwnedBySymbol.get(symbol);
-          boolean favorite = favorites.contains(symbol);
+          Integer sharesOwned = player.getSharesOwned(symbol);
+          boolean favorite = player.isFavorite(symbol);
           priceMap.put(symbol, new StockQuote(symbol, name, iprice,
               sharesOwned == null ? 0 : sharesOwned.intValue(), favorite));
         } catch (NumberFormatException e) {
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockQuote.java Thu Feb 11 09:56:07 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockQuote.java Fri Feb 12 05:38:22 2010
@@ -38,17 +38,14 @@
    * @param sharesOwned the number of shares owned by the player
    * @param favorite true if the stock is in the player's favorites
    */
- public StockQuote(String ticker, String name, int price, int sharesOwned, boolean favorite) {
+  public StockQuote(String ticker, String name, int price, int sharesOwned,
+      boolean favorite) {
     this.ticker = ticker;
     this.name = name;
     this.price = price;
     this.sharesOwned = sharesOwned;
     this.favorite = favorite;
   }
-
-  public int getSharesOwned() {
-    return sharesOwned;
-  }

   /**
    * Used for RPC.
@@ -59,7 +56,7 @@
   public String getDisplayPrice() {
     int dollars = getPrice() / 100;
     int cents = getPrice() % 100;
-
+
     StringBuilder sb = new StringBuilder();
     sb.append("$ ");
     sb.append(dollars);
@@ -82,7 +79,11 @@
   public int getPrice() {
     return price;
   }
-
+
+  public int getSharesOwned() {
+    return sharesOwned;
+  }
+
   public String getTicker() {
     return ticker;
   }
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockRequest.java Thu Feb 11 09:56:07 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockRequest.java Fri Feb 12 05:38:22 2010
@@ -19,15 +19,14 @@

 import java.io.Serializable;

+/**
+ * A request for new stock data.
+ */
 public class StockRequest implements Serializable {
-
+
   String searchQuery;
   Range searchRange;
   Range favoritesRange;
-
-  // Used by RPC
-  public StockRequest() {
-  }

   public StockRequest(String searchQuery, Range searchRange,
       Range favoritesRange) {
@@ -35,6 +34,16 @@
     this.searchRange = searchRange;
     this.favoritesRange = favoritesRange;
   }
+
+  /**
+   * Used by RPC.
+   */
+  StockRequest() {
+  }
+
+  public Range getFavoritesRange() {
+    return favoritesRange;
+  }

   public String getSearchQuery() {
     return searchQuery;
@@ -43,8 +52,4 @@
   public Range getSearchRange() {
     return searchRange;
   }
-
-  public Range getFavoritesRange() {
-    return favoritesRange;
-  }
-}
+}
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java Thu Feb 11 09:56:07 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/StockResponse.java Fri Feb 12 05:38:22 2010
@@ -27,6 +27,11 @@
   private int numSearchResults;
   private int numFavorites;

+  /**
+   * The amount of available cash in pennies.
+   */
+  private int cash;
+
   /**
    * Used for RPC.
    */
@@ -34,26 +39,31 @@
   }

public StockResponse(StockQuoteList searchResults, StockQuoteList favorites,
-      int numSearchResults, int numFavorites) {
+      int numSearchResults, int numFavorites, int cash) {
     this.searchResults = searchResults;
     this.favorites = favorites;
     this.numSearchResults = numSearchResults;
     this.numFavorites = numFavorites;
+    this.cash = cash;
   }

-  public StockQuoteList getSearchResults() {
-    return searchResults;
+  public int getCash() {
+    return cash;
   }

   public StockQuoteList getFavorites() {
     return favorites;
   }
+
+  public int getNumFavorites() {
+    return numFavorites;
+  }

   public int getNumSearchResults() {
     return numSearchResults;
   }

-  public int getNumFavorites() {
-    return numFavorites;
+  public StockQuoteList getSearchResults() {
+    return searchResults;
   }
 }
=======================================
--- /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/Transaction.java Thu Feb 11 10:53:53 2010 +++ /trunk/bikeshed/src/com/google/gwt/sample/datawidgets/shared/Transaction.java Fri Feb 12 05:38:22 2010
@@ -40,16 +40,16 @@
   Transaction() {
   }

-  public boolean isBuy() {
-    return isBuy;
+  public int getQuantity() {
+    return quantity;
   }

   public String getTicker() {
     return ticker;
   }

-  public int getQuantity() {
-    return quantity;
+  public boolean isBuy() {
+    return isBuy;
   }

   @Override
=======================================
--- /trunk/bikeshed/war/WEB-INF/web.xml Tue Feb  9 11:11:52 2010
+++ /trunk/bikeshed/war/WEB-INF/web.xml Fri Feb 12 05:38:22 2010
@@ -4,21 +4,31 @@
     "http://java.sun.com/dtd/web-app_2_3.dtd";>

 <web-app>
-
+
   <!-- Servlets -->
   <servlet>
     <servlet-name>stockServlet</servlet-name>
<servlet-class>com.google.gwt.sample.datawidgets.server.StockServiceImpl</servlet-class>
   </servlet>
-
+
   <servlet-mapping>
     <servlet-name>stockServlet</servlet-name>
     <url-pattern>/databackedwidgets/stock</url-pattern>
   </servlet-mapping>
-
+
   <!-- Default page to serve -->
   <welcome-file-list>
     <welcome-file>DataBackedWidgets.html</welcome-file>
   </welcome-file-list>

+  <!-- Require login. -->
+  <security-constraint>
+    <web-resource-collection>
+      <url-pattern>/*</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>*</role-name>
+    </auth-constraint>
+  </security-constraint>
+
 </web-app>

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

Reply via email to