Adding an option to buy or sell stock the datawidgets sample.
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7563 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/BuySellPopup.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/BuySellPopup.java
new file mode 100644
index 0000000..98cc9ed
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/BuySellPopup.java
@@ -0,0 +1,134 @@
+/*
+ * 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.client;
+
+import com.google.gwt.event.dom.client.ClickEvent;
+import com.google.gwt.event.dom.client.ClickHandler;
+import com.google.gwt.sample.datawidgets.shared.StockQuote;
+import com.google.gwt.sample.datawidgets.shared.Transaction;
+import com.google.gwt.user.client.Command;
+import com.google.gwt.user.client.DeferredCommand;
+import com.google.gwt.user.client.Window;
+import com.google.gwt.user.client.ui.Button;
+import com.google.gwt.user.client.ui.DialogBox;
+import com.google.gwt.user.client.ui.FlexTable;
+import com.google.gwt.user.client.ui.TextBox;
+
+/**
+ * A popup used for purchasing stock.
+ */
+public class BuySellPopup extends DialogBox {
+
+ private StockQuote quote;
+
+ /**
+ * The table used for layout.
+ */
+ private FlexTable layout = new FlexTable();
+
+ /**
+ * The box used to change the quantity.
+ */
+ private TextBox quantityBox = new TextBox();
+
+ /**
+ * The button used to buy or sell.
+ */
+ private Button opButton;
+
+ /**
+ * True if we are buying, false if hiding.
+ */
+ private boolean isBuying;
+
+ /**
+ * The last transaction.
+ */
+ private Transaction transaction;
+
+ public BuySellPopup() {
+ super(false, true);
+ setGlassEnabled(true);
+ setWidget(layout);
+
+ layout.setHTML(0, 0, "<b>Ticker:</b>");
+ layout.setHTML(1, 0, "<b>Name:</b>");
+ layout.setHTML(2, 0, "<b>Price:</b>");
+ layout.setHTML(3, 0, "<b>Quantity:</b>");
+ layout.setWidget(3, 1, quantityBox);
+
+ // Buy Button.
+ opButton = new Button("", new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ try {
+ int quantity = Integer.parseInt(quantityBox.getText());
+ transaction = new Transaction(isBuying, quote.getTicker(), quantity);
+ hide();
+ } catch (NumberFormatException e) {
+ Window.alert("You must enter a valid quantity");
+ }
+ }
+ });
+ layout.setWidget(4, 0, opButton);
+
+ // Cancel Button.
+ Button cancelButton = new Button("Cancel", new ClickHandler() {
+ public void onClick(ClickEvent event) {
+ hide();
+ }
+ });
+ layout.setWidget(4, 1, cancelButton);
+ }
+
+ /**
+ * Get the last transaction.
+ *
+ * @return the last transaction, or null if cancelled
+ */
+ public Transaction getTransaction() {
+ return transaction;
+ }
+
+ /**
+ * Set the current {@link StockQuote}.
+ *
+ * @param quote the stock quote to buy
+ * @param isBuying true if buying the stock
+ */
+ public void setStockQuote(StockQuote quote, boolean isBuying) {
+ 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());
+ quantityBox.setText("0");
+ opButton.setText(op);
+ this.isBuying = isBuying;
+ transaction = null;
+ }
+
+ @Override
+ protected void onLoad() {
+ super.onLoad();
+ DeferredCommand.addCommand(new Command() {
+ public void execute() {
+ quantityBox.selectAll();
+ quantityBox.setFocus(true);
+ }
+ });
+ }
+}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java
index d98c001..4ceb9dd 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/DataBackedWidgets.java
@@ -15,15 +15,17 @@
*/
package com.google.gwt.sample.datawidgets.client;
+import com.google.gwt.cells.client.ButtonCell;
import com.google.gwt.cells.client.CheckboxCell;
import com.google.gwt.cells.client.CurrencyCell;
import com.google.gwt.cells.client.Mutator;
import com.google.gwt.cells.client.TextCell;
-import com.google.gwt.cells.client.TextInputCell;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.KeyUpEvent;
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.list.client.Column;
import com.google.gwt.list.client.PagingTableListView;
import com.google.gwt.list.shared.AsyncListModel;
@@ -33,33 +35,33 @@
import com.google.gwt.sample.datawidgets.shared.StockQuoteList;
import com.google.gwt.sample.datawidgets.shared.StockRequest;
import com.google.gwt.sample.datawidgets.shared.StockResponse;
+import com.google.gwt.sample.datawidgets.shared.Transaction;
import com.google.gwt.user.client.Timer;
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.Label;
+import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TextBox;
-import java.util.HashMap;
-
/**
* Entry point classes define <code>onModuleLoad()</code>.
*/
public class DataBackedWidgets implements EntryPoint {
-
+
/**
* The delay between updates in milliseconds.
*/
private static final int UPDATE_DELAY = 5000;
-
+
/**
* The {@link StockService} used to retrieve data.
*/
private final StockServiceAsync dataService = GWT.create(StockService.class);
private final Label errorLabel = new Label();
-
+
private Column<StockQuote, Boolean> favoriteColumn = new Column<StockQuote, Boolean>(
new CheckboxCell()) {
@Override
@@ -67,7 +69,7 @@
return object.isFavorite();
}
};
-
+
private Column<StockQuote, String> nameColumn = new Column<StockQuote, String>(
new TextCell()) {
@Override
@@ -84,6 +86,22 @@
}
};
+ private Column<StockQuote, String> buyColumn = new Column<StockQuote, String>(
+ new ButtonCell()) {
+ @Override
+ protected String getValue(StockQuote object) {
+ return "Buy";
+ }
+ };
+
+ private Column<StockQuote, String> sellColumn = new Column<StockQuote, String>(
+ new ButtonCell()) {
+ @Override
+ protected String getValue(StockQuote object) {
+ return "Sell";
+ }
+ };
+
private final TextBox queryField = new TextBox();
private Column<StockQuote, String> tickerColumn = new Column<StockQuote, String>(
@@ -104,11 +122,6 @@
private AsyncListModel<StockQuote> favoritesListModel;
- /**
- * User supplied notes, indexed by ticker symbol.
- */
- private HashMap<String,String> notesByTicker = new HashMap<String,String>();
-
private PagingTableListView<StockQuote> resultsTable;
private AsyncListModel<StockQuote> searchListModel;
@@ -126,6 +139,11 @@
private PagingTableListView<StockQuote> favoritesTable;
/**
+ * The popup used to purchase stock.
+ */
+ private BuySellPopup buySellPopup = new BuySellPopup();
+
+ /**
* This is the entry point method.
*/
public void onModuleLoad() {
@@ -139,19 +157,21 @@
// Focus the cursor on the name field when the app loads
queryField.setFocus(true);
queryField.selectAll();
-
+
// Create the list models
- searchListModel = new AsyncListModel<StockQuote>(new DataSource<StockQuote>() {
- public void requestData(AsyncListModel<StockQuote> listModel) {
- update();
- }
- });
-
- favoritesListModel = new AsyncListModel<StockQuote>(new DataSource<StockQuote>() {
- public void requestData(AsyncListModel<StockQuote> listModel) {
- update();
- }
- });
+ searchListModel = new AsyncListModel<StockQuote>(
+ new DataSource<StockQuote>() {
+ public void requestData(AsyncListModel<StockQuote> listModel) {
+ update();
+ }
+ });
+
+ favoritesListModel = new AsyncListModel<StockQuote>(
+ new DataSource<StockQuote>() {
+ public void requestData(AsyncListModel<StockQuote> listModel) {
+ update();
+ }
+ });
// Create the results table.
resultsTable = new PagingTableListView<StockQuote>(searchListModel, 10);
@@ -159,18 +179,52 @@
resultsTable.addColumn(tickerColumn);
resultsTable.addColumn(nameColumn);
resultsTable.addColumn(priceColumn);
-
+ resultsTable.addColumn(buyColumn);
+
favoritesTable = new PagingTableListView<StockQuote>(favoritesListModel, 10);
favoritesTable.addColumn(tickerColumn);
favoritesTable.addColumn(priceColumn);
favoritesTable.addColumn(sharesColumn);
-
+ favoritesTable.addColumn(buyColumn);
+ favoritesTable.addColumn(sellColumn);
+
favoriteColumn.setMutator(new Mutator<StockQuote, Boolean>() {
public void mutate(StockQuote object, Boolean after) {
setFavorite(object.getTicker(), after);
}
});
+ buyColumn.setMutator(new Mutator<StockQuote, String>() {
+ public void mutate(StockQuote object, String after) {
+ buySellPopup.setStockQuote(object, true);
+ buySellPopup.center();
+ }
+ });
+
+ sellColumn.setMutator(new Mutator<StockQuote, String>() {
+ public void mutate(StockQuote object, String after) {
+ buySellPopup.setStockQuote(object, false);
+ buySellPopup.center();
+ }
+ });
+
+ buySellPopup.addCloseHandler(new CloseHandler<PopupPanel>() {
+ public void onClose(CloseEvent<PopupPanel> event) {
+ Transaction t = buySellPopup.getTransaction();
+ if (t != null) {
+ dataService.transact(t, new AsyncCallback<Transaction>() {
+ public void onFailure(Throwable caught) {
+ Window.alert("Error: " + caught.getMessage());
+ }
+
+ public void onSuccess(Transaction result) {
+ update();
+ }
+ });
+ }
+ }
+ });
+
RootPanel.get().add(resultsTable);
RootPanel.get().add(new HTML("<hr>"));
RootPanel.get().add(favoritesTable);
@@ -184,10 +238,10 @@
update();
}
-
+
/**
* Set or unset a ticker symbol as a 'favorite.'
- *
+ *
* @param ticker the ticker symbol
* @param favorite if true, make the stock a favorite
*/
@@ -214,22 +268,24 @@
});
}
}
-
+
/**
* Request data from the server using the last query string.
*/
private void update() {
updateTimer.cancel();
-
+
Range[] searchRanges = searchListModel.getRanges();
Range[] favoritesRanges = favoritesListModel.getRanges();
-
- if (searchRanges == null || searchRanges.length == 0 || favoritesRanges == null || favoritesRanges.length == 0) {
+
+ if (searchRanges == null || searchRanges.length == 0
+ || favoritesRanges == null || favoritesRanges.length == 0) {
return;
}
-
+
String searchQuery = queryField.getText();
- StockRequest request = new StockRequest(searchQuery, searchRanges[0], favoritesRanges[0]);
+ StockRequest request = new StockRequest(searchQuery, searchRanges[0],
+ favoritesRanges[0]);
dataService.getStockQuotes(request, new AsyncCallback<StockResponse>() {
public void onFailure(Throwable caught) {
Window.alert("ERROR: " + caught.getMessage());
@@ -238,15 +294,17 @@
public void onSuccess(StockResponse result) {
StockQuoteList searchResults = result.getSearchResults();
-
+
searchListModel.updateDataSize(result.getNumSearchResults(), true);
- searchListModel.updateViewData(searchResults.getStartIndex(), searchResults.size(), searchResults);
+ searchListModel.updateViewData(searchResults.getStartIndex(),
+ searchResults.size(), searchResults);
StockQuoteList favorites = result.getFavorites();
-
+
favoritesListModel.updateDataSize(result.getNumFavorites(), true);
- favoritesListModel.updateViewData(favorites.getStartIndex(), favorites.size(), favorites);
-
+ favoritesListModel.updateViewData(favorites.getStartIndex(),
+ favorites.size(), favorites);
+
updateTimer.schedule(UPDATE_DELAY);
}
});
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java
index 78d03c8..46d3a3d 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockService.java
@@ -17,6 +17,7 @@
import com.google.gwt.sample.datawidgets.shared.StockRequest;
import com.google.gwt.sample.datawidgets.shared.StockResponse;
+import com.google.gwt.sample.datawidgets.shared.Transaction;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.client.rpc.RemoteServiceRelativePath;
@@ -25,10 +26,13 @@
*/
@RemoteServiceRelativePath("stock")
public interface StockService extends RemoteService {
-
+
StockResponse getStockQuotes(StockRequest request)
throws IllegalArgumentException;
void addFavorite(String ticker);
+
void removeFavorite(String ticker);
+
+ Transaction transact(Transaction transaction) throws IllegalArgumentException;
}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java
index bd56425..fca11c4 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/client/StockServiceAsync.java
@@ -17,15 +17,19 @@
import com.google.gwt.sample.datawidgets.shared.StockRequest;
import com.google.gwt.sample.datawidgets.shared.StockResponse;
+import com.google.gwt.sample.datawidgets.shared.Transaction;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* The async counterpart of <code>DataService</code>.
*/
public interface StockServiceAsync {
- void getStockQuotes(StockRequest request, AsyncCallback<StockResponse> callback);
+ void getStockQuotes(StockRequest request,
+ AsyncCallback<StockResponse> callback);
void addFavorite(String ticker, AsyncCallback<Void> callback);
void removeFavorite(String ticker, AsyncCallback<Void> callback);
+
+ void transact(Transaction transaction, AsyncCallback<Transaction> callback);
}
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java b/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java
index b7225d6..bf64eec 100644
--- a/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/server/StockServiceImpl.java
@@ -21,6 +21,7 @@
import com.google.gwt.sample.datawidgets.shared.StockQuoteList;
import com.google.gwt.sample.datawidgets.shared.StockRequest;
import com.google.gwt.sample.datawidgets.shared.StockResponse;
+import com.google.gwt.sample.datawidgets.shared.Transaction;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import java.io.IOException;
@@ -51,7 +52,7 @@
private class Result {
int numRows;
StockQuoteList quotes;
-
+
public Result(StockQuoteList quotes, int numRows) {
this.quotes = quotes;
this.numRows = numRows;
@@ -79,34 +80,60 @@
private String favoritesQuery = IMPOSSIBLE_TICKER_SYMBOL;
- private HashMap<String,Integer> sharesOwnedBySymbol = new HashMap<String,Integer>();
+ private HashMap<String, Integer> sharesOwnedBySymbol = new HashMap<String, Integer>();
public void addFavorite(String ticker) {
favorites.add(ticker);
generateFavoritesQuery();
}
-
+
public StockResponse getStockQuotes(StockRequest request)
throws IllegalArgumentException {
-
+
String query = request.getSearchQuery();
if (query == null | query.length() == 0) {
query = ".*";
}
Range searchRange = request.getSearchRange();
Range favoritesRange = request.getFavoritesRange();
-
+
Result searchResults = query(query, searchRange);
Result favorites = query(favoritesQuery, favoritesRange);
-
- return new StockResponse(searchResults.quotes, favorites.quotes, searchResults.numRows, favorites.numRows);
+
+ return new StockResponse(searchResults.quotes, favorites.quotes,
+ searchResults.numRows, favorites.numRows);
}
-
+
public void removeFavorite(String ticker) {
favorites.remove(ticker);
generateFavoritesQuery();
}
+ public Transaction transact(Transaction transaction)
+ throws IllegalArgumentException {
+ // TODO: Check that the stock exists.
+ String ticker = transaction.getTicker();
+ Integer current = sharesOwnedBySymbol.get(ticker);
+ if (current == null) {
+ current = 0;
+ }
+
+ int quantity = transaction.getQuantity();
+ if (transaction.isBuy()) {
+ current += quantity;
+ // TODO: Verify player has enough funds.
+ addFavorite(ticker);
+ } else {
+ if (quantity > current) {
+ throw new IllegalArgumentException(
+ "You cannot sell more stock than you own");
+ }
+ current -= quantity;
+ }
+ sharesOwnedBySymbol.put(ticker, current);
+ return new Transaction(true, ticker, quantity);
+ }
+
private void generateFavoritesQuery() {
StringBuilder sb = new StringBuilder(IMPOSSIBLE_TICKER_SYMBOL);
for (String ticker : favorites) {
@@ -153,14 +180,14 @@
private Result query(String query, Range range) {
// Get all symbols for the query.
List<String> symbols = getTickers(query);
-
+
if (symbols.size() == 0) {
return new Result(new StockQuoteList(0), 0);
}
int start = range.getStart();
int end = Math.min(start + range.getLength(), symbols.size());
-
+
// Get the symbols that are in range.
Set<String> symbolsInRange = new HashSet<String>();
if (end > start) {
@@ -256,4 +283,3 @@
return new Result(toRet, symbols.size());
}
}
-
diff --git a/bikeshed/src/com/google/gwt/sample/datawidgets/shared/Transaction.java b/bikeshed/src/com/google/gwt/sample/datawidgets/shared/Transaction.java
new file mode 100644
index 0000000..94b9cd8
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/sample/datawidgets/shared/Transaction.java
@@ -0,0 +1,60 @@
+/*
+ * 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.shared;
+
+import java.io.Serializable;
+
+/**
+ * The buy or sell transaction.
+ */
+public class Transaction implements Serializable {
+
+ /**
+ * True if a buy transaction, false if a sell transaction.
+ */
+ private boolean isBuy;
+
+ private String ticker;
+ private int quantity;
+
+ public Transaction(boolean isBuy, String ticker, int quantity) {
+ super();
+ this.isBuy = isBuy;
+ this.ticker = ticker;
+ this.quantity = quantity;
+ }
+
+ Transaction() {
+ }
+
+ public boolean isBuy() {
+ return isBuy;
+ }
+
+ public String getTicker() {
+ return ticker;
+ }
+
+ public int getQuantity() {
+ return quantity;
+ }
+
+ @Override
+ public String toString() {
+ String op = isBuy ? "Bought" : "Sold";
+ return op + " " + quantity + " shares of " + ticker;
+ }
+}