Revision: 7718
Author: gwt.mirror...@gmail.com
Date: Fri Mar 12 11:14:43 2010
Log: Adding list of top players to the game.
http://code.google.com/p/google-web-toolkit/source/detail?r=7718
Added:
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/PlayerScoresWidget.java
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/PlayerScoresWidget.ui.xml
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/PlayerInfo.java
Modified:
/trunk/bikeshed/.classpath
/trunk/bikeshed/.project
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.ui.xml
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/common.css
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java
/trunk/bikeshed/war/Stocks.css
/trunk/bikeshed/war/WEB-INF/web.xml
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/PlayerScoresWidget.java
Fri Mar 12 11:14:43 2010
@@ -0,0 +1,70 @@
+/*
+ * 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.bikeshed.sample.stocks.client;
+
+import com.google.gwt.bikeshed.cells.client.Cell;
+import com.google.gwt.bikeshed.list.client.SimpleCellList;
+import com.google.gwt.bikeshed.list.shared.ListModel;
+import com.google.gwt.bikeshed.sample.stocks.shared.PlayerInfo;
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.uibinder.client.UiBinder;
+import com.google.gwt.uibinder.client.UiFactory;
+import com.google.gwt.uibinder.client.UiField;
+import com.google.gwt.user.client.ui.Composite;
+import com.google.gwt.user.client.ui.Widget;
+
+/**
+ * Widget to display player scores.
+ */
+public class PlayerScoresWidget extends Composite {
+
+ interface Binder extends UiBinder<Widget, PlayerScoresWidget> {
+ }
+
+ private static final Binder binder = GWT.create(Binder.class);
+
+ /**
+ * A {...@link Cell} that displays the status of a single player.
+ */
+ private static final class PlayerInfoCell extends Cell<PlayerInfo> {
+ @Override
+ public void render(PlayerInfo value, StringBuilder sb) {
+ sb.append("<div class='playerScoreBox'>");
+ sb.append("<b>Name: </b>");
+ sb.append(value.getDisplayName());
+ sb.append("<br><b>Net Worth: </b>");
+ sb.append(StockSample.getFormattedPrice(value.getNetWorth()));
+ sb.append("<br><b>Cash: </b>");
+ sb.append(StockSample.getFormattedPrice(value.getCash()));
+ sb.append("</div>");
+ }
+ }
+
+ @UiField
+ SimpleCellList<PlayerInfo> listView;
+
+ private final ListModel<PlayerInfo> model;
+
+ public PlayerScoresWidget(ListModel<PlayerInfo> model) {
+ this.model = model;
+ initWidget(binder.createAndBindUi(this));
+ }
+
+ @UiFactory
+ SimpleCellList<PlayerInfo> createListView() {
+ return new SimpleCellList<PlayerInfo>(model, new PlayerInfoCell());
+ }
+}
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/PlayerScoresWidget.ui.xml
Fri Mar 12 11:14:43 2010
@@ -0,0 +1,21 @@
+<ui:UiBinder
+ xmlns:ui='urn:ui:com.google.gwt.uibinder'
+ xmlns:g='urn:import:com.google.gwt.user.client.ui'
+ xmlns:l='urn:import:com.google.gwt.bikeshed.list.client'
+ xmlns:t='urn:import:com.google.gwt.bikeshed.tree.client'
+ xmlns:s='urn:import:com.google.gwt.bikeshed.sample.stocks.client'>
+
+ <ui:style field='common' src='common.css'/>
+
+ <g:DockLayoutPanel unit='EM'>
+ <g:north size='2'>
+ <g:Label styleName='{common.header}'>Everyone Else</g:Label>
+ </g:north>
+
+ <g:center>
+ <g:ScrollPanel styleName="{common.padded}">
+ <l:SimpleCellList ui:field='listView'/>
+ </g:ScrollPanel>
+ </g:center>
+ </g:DockLayoutPanel>
+</ui:UiBinder>
=======================================
--- /dev/null
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/PlayerInfo.java
Fri Mar 12 11:14:43 2010
@@ -0,0 +1,129 @@
+/*
+ * 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.bikeshed.sample.stocks.shared;
+
+import java.io.Serializable;
+
+/**
+ * Information about a single player.
+ */
+public class PlayerInfo implements Serializable {
+
+ /**
+ * The initial amount of cash that the player starts with, in cents.
+ */
+ private static final int INITIAL_CASH = 10000 * 100;
+
+ /**
+ * The net worth of the player in cents.
+ */
+ private int cash = INITIAL_CASH;
+
+ /**
+ * The players name.
+ */
+ private String name;
+
+ /**
+ * The net worth of the player in cents.
+ */
+ private int stockValue;
+
+ public PlayerInfo(String name) {
+ this.name = name;
+ }
+
+ /**
+ * Visible for RPC.
+ */
+ PlayerInfo() {
+ }
+
+ public PlayerInfo copy() {
+ PlayerInfo copy = new PlayerInfo(name);
+ copy.setCash(cash);
+ copy.setStockValue(stockValue);
+ return copy;
+ }
+
+ /**
+ * Get the player's current cash amount.
+ *
+ * @return the cash amount
+ */
+ public int getCash() {
+ return cash;
+ }
+
+ /**
+ * Get the player's display name, which excludes domain information.
+ *
+ * @return the display name of the player.
+ */
+ public String getDisplayName() {
+ String displayName = name;
+ int domain = displayName.indexOf('@');
+ if (domain > 0) {
+ displayName = displayName.substring(0, domain);
+ }
+ return displayName;
+ }
+
+ /**
+ * Get the player's name.
+ *
+ * @return the name of the player.
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Get the players net worth, including cash and stock value.
+ *
+ * @return the net worth in cents
+ */
+ public int getNetWorth() {
+ return cash + stockValue;
+ }
+
+ /**
+ * Get the value of this player's stock.
+ *
+ * @return the value of the stock in cents
+ */
+ public int getStockValue() {
+ return stockValue;
+ }
+
+ /**
+ * Set the amount of cash that the user has.
+ *
+ * @param cash the user's cash in cents
+ */
+ protected void setCash(int cash) {
+ this.cash = cash;
+ }
+
+ /**
+ * Set the value of this player's stock.
+ *
+ * @param value the stock value in cents
+ */
+ protected void setStockValue(int value) {
+ this.stockValue = value;
+ }
+}
=======================================
--- /trunk/bikeshed/.classpath Thu Mar 11 09:45:33 2010
+++ /trunk/bikeshed/.classpath Fri Mar 12 11:14:43 2010
@@ -6,6 +6,7 @@
<classpathentry kind="con"
path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/>
<classpathentry kind="var"
path="GWT_TOOLS/redist/json/r2_20080312/json.jar"
sourcepath="/GWT_TOOLS/redist/json/r2_20080312/json-src.jar"/>
<classpathentry kind="lib" path="war/WEB-INF/lib/gwt-servlet.jar"/>
- <classpathentry kind="con"
path="com.google.gwt.eclipse.core.GWT_CONTAINER/Local"/>
+ <classpathentry kind="con"
path="com.google.gwt.eclipse.core.GWT_CONTAINER/trunk"/>
+ <classpathentry kind="con"
path="com.google.appengine.eclipse.core.GAE_CONTAINER"/>
<classpathentry kind="output" path="war/WEB-INF/classes"/>
</classpath>
=======================================
--- /trunk/bikeshed/.project Thu Mar 4 12:59:01 2010
+++ /trunk/bikeshed/.project Fri Mar 12 11:14:43 2010
@@ -28,6 +28,16 @@
<buildCommand>
<name>com.atlassw.tools.eclipse.checkstyle.CheckstyleBuilder</name>
<arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+
<name>com.google.appengine.eclipse.core.enhancerbuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+
<name>com.google.appengine.eclipse.core.projectValidator</name>
+ <arguments>
</arguments>
</buildCommand>
</buildSpec>
@@ -37,5 +47,6 @@
<nature>com.google.gdt.eclipse.core.webAppNature</nature>
<nature>com.atlassw.tools.eclipse.checkstyle.CheckstyleNature</nature>
<nature>org.eclipse.wst.common.project.facet.core.nature</nature>
+ <nature>com.google.appengine.eclipse.core.gaeNature</nature>
</natures>
</projectDescription>
=======================================
---
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
Thu Mar 11 11:11:10 2010
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
Fri Mar 12 07:16:19 2010
@@ -1,12 +1,12 @@
/*
* 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
@@ -21,6 +21,7 @@
import com.google.gwt.bikeshed.list.shared.Range;
import com.google.gwt.bikeshed.list.shared.AsyncListModel.DataSource;
import
com.google.gwt.bikeshed.sample.stocks.client.TransactionTreeViewModel.SectorListModel;
+import com.google.gwt.bikeshed.sample.stocks.shared.PlayerInfo;
import com.google.gwt.bikeshed.sample.stocks.shared.StockQuote;
import com.google.gwt.bikeshed.sample.stocks.shared.StockQuoteList;
import com.google.gwt.bikeshed.sample.stocks.shared.StockRequest;
@@ -69,6 +70,7 @@
@UiField FavoritesWidget favoritesWidget;
@UiField Label netWorthLabel;
+ @UiField PlayerScoresWidget playerScoresWidget;
@UiField StockQueryWidget queryWidget;
@UiField SideBySideTreeView transactionTree;
@@ -79,6 +81,7 @@
private final StockServiceAsync dataService =
GWT.create(StockService.class);
private AsyncListModel<StockQuote> favoritesListModel;
+ private AsyncListModel<PlayerInfo> playerScoresListModel;
private AsyncListModel<StockQuote> searchListModel;
private Map<String, ListListModel<Transaction>>
transactionListListModelsByTicker =
new HashMap<String, ListListModel<Transaction>>();
@@ -122,8 +125,15 @@
}
});
- treeModel = new TransactionTreeViewModel(this,
- favoritesListModel, transactionListListModelsByTicker);
+ playerScoresListModel = new AsyncListModel<PlayerInfo>(
+ new DataSource<PlayerInfo>() {
+ public void requestData(AsyncListModel<PlayerInfo> listModel) {
+ // Player cannot request data from this view.
+ }
+ });
+
+ treeModel = new TransactionTreeViewModel(this, favoritesListModel,
+ transactionListListModelsByTicker);
transactionListModel = new ListListModel<Transaction>();
transactions = transactionListModel.getList();
@@ -164,7 +174,7 @@
/**
* Process the {...@link StockResponse} from the server.
- *
+ *
* @param response the stock response
*/
public void processStockResponse(StockResponse response) {
@@ -178,6 +188,12 @@
updateFavorites(response);
updateSector(response);
+ // Update the player scores.
+ List<PlayerInfo> playerScores = response.getPlayers();
+ int numPlayers = playerScores.size();
+ playerScoresListModel.updateDataSize(numPlayers, true);
+ playerScoresListModel.updateViewData(0, numPlayers, playerScores);
+
// Update available cash.
int cash = response.getCash();
int netWorth = response.getNetWorth();
@@ -196,7 +212,7 @@
/**
* Set or unset a ticker symbol as a 'favorite'.
- *
+ *
* @param ticker the ticker symbol
* @param favorite if true, make the stock a favorite
*/
@@ -204,28 +220,28 @@
if (favorite) {
dataService.addFavorite(ticker, favoritesListModel.getRanges()[0],
new AsyncCallback<StockResponse>() {
- public void onFailure(Throwable caught) {
- Window.alert("Error adding favorite");
- }
-
- public void onSuccess(StockResponse response) {
- updateFavorites(response);
- }
- });
+ public void onFailure(Throwable caught) {
+ handleRpcError(caught, "Error adding favorite");
+ }
+
+ public void onSuccess(StockResponse response) {
+ updateFavorites(response);
+ }
+ });
} else {
dataService.removeFavorite(ticker, favoritesListModel.getRanges()[0],
new AsyncCallback<StockResponse>() {
- public void onFailure(Throwable caught) {
- Window.alert("Error removing favorite");
- }
-
- public void onSuccess(StockResponse response) {
- updateFavorites(response);
- }
- });
+ public void onFailure(Throwable caught) {
+ handleRpcError(caught, "Error removing favorite");
+ }
+
+ public void onSuccess(StockResponse response) {
+ updateFavorites(response);
+ }
+ });
}
}
-
+
public void transact(Transaction t) {
dataService.transact(t, new AsyncCallback<Transaction>() {
public void onFailure(Throwable caught) {
@@ -238,19 +254,17 @@
}
/**
- * Update transactions (list of all transactions),
- * transactionTickers (set of all tickers involved in
- * transactions), and transactionsByTicker (map from
- * ticker to lists of transactions for that ticker).
+ * Update transactions (list of all transactions),
transactionTickers (set
+ * of all tickers involved in transactions), and
transactionsByTicker (map
+ * from ticker to lists of transactions for that ticker).
*/
private void recordTransaction(Transaction result) {
transactions.add(0, result);
String ticker = result.getTicker();
-
+
// Update the next level of the transaction tree
// for the given ticker
- ListListModel<Transaction> t =
- transactionListListModelsByTicker.get(ticker);
+ ListListModel<Transaction> t =
transactionListListModelsByTicker.get(ticker);
if (t == null) {
t = new ListListModel<Transaction>();
transactionListListModelsByTicker.put(ticker, t);
@@ -272,10 +286,12 @@
Range[] searchRanges = searchListModel.getRanges();
Range[] favoritesRanges = favoritesListModel.getRanges();
-
+
String sectorName = getSectorName();
- SectorListModel sectorListModel = sectorName != null ?
treeModel.getSectorListModel(sectorName) : null;
- Range[] sectorRanges = sectorListModel == null ? null :
sectorListModel.getRanges();
+ SectorListModel sectorListModel = sectorName != null
+ ? treeModel.getSectorListModel(sectorName) : null;
+ Range[] sectorRanges = sectorListModel == null ? null
+ : sectorListModel.getRanges();
if (searchRanges == null || searchRanges.length == 0
|| favoritesRanges == null || favoritesRanges.length == 0) {
@@ -283,20 +299,14 @@
}
String searchQuery = queryWidget.getSearchQuery();
-
+
StockRequest request = new StockRequest(searchQuery,
sectorListModel != null ? sectorListModel.getSector() : null,
- searchRanges[0],
- favoritesRanges[0],
- sectorRanges != null && sectorRanges.length > 0 ?
sectorRanges[0] : null);
+ searchRanges[0], favoritesRanges[0], sectorRanges != null
+ && sectorRanges.length > 0 ? sectorRanges[0] : null);
dataService.getStockQuotes(request, new AsyncCallback<StockResponse>()
{
public void onFailure(Throwable caught) {
- String message = caught.getMessage();
- if (message.contains("Not logged in")) {
- // Force the user to login.
- Window.Location.reload();
- } else {
- Window.alert("ERROR: " + caught.getMessage());
+ if (handleRpcError(caught, null)) {
updateTimer.schedule(UPDATE_DELAY);
}
}
@@ -332,6 +342,11 @@
FavoritesWidget createFavoritesWidget() {
return new FavoritesWidget(favoritesListModel);
}
+
+ @UiFactory
+ PlayerScoresWidget createPlayerScoresWidget() {
+ return new PlayerScoresWidget(playerScoresListModel);
+ }
@UiFactory
StockQueryWidget createQueryWidget() {
@@ -352,7 +367,30 @@
return (String) childNode.getValue();
}
}
-
+
return null;
}
-}
+
+ /**
+ * Display a message to the user when an RPC call fails.
+ *
+ * @param caught the exception
+ * @param displayMessage the message to display to the user, or null to
+ * display a default message
+ * @return true if recoverable, false if not
+ */
+ private boolean handleRpcError(Throwable caught, String displayMessage) {
+ String message = caught.getMessage();
+ if (message.contains("Not logged in")) {
+ // Force the user to login.
+ Window.Location.reload();
+ return false;
+ }
+
+ if (displayMessage == null) {
+ displayMessage = "ERROR: " + caught.getMessage();
+ }
+ Window.alert(displayMessage);
+ return true;
+ }
+}
=======================================
---
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.ui.xml
Thu Mar 11 08:39:56 2010
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.ui.xml
Fri Mar 12 07:16:19 2010
@@ -25,16 +25,7 @@
</g:north>
<g:center>
- <g:ScrollPanel styleName='{common.bg}'>
- <g:HTMLPanel>
- <div class='{common.header}'>Everyone Else</div>
- <table>
- <tr><td>Dan Rice</td><td>$10000</td></tr>
- <tr><td>Joel Webber</td><td>$10000</td></tr>
- <tr><td>John Labanca</td><td>$10000</td></tr>
- </table>
- </g:HTMLPanel>
- </g:ScrollPanel>
+ <s:PlayerScoresWidget styleName='{common.bg}'
ui:field='playerScoresWidget'/>
</g:center>
</g:DockLayoutPanel>
</g:west>
=======================================
---
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/common.css
Thu Mar 11 10:09:28 2010
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/common.css
Fri Mar 12 07:16:19 2010
@@ -45,3 +45,7 @@
.table tfoot th {
border-width: 0px;
}
+
+.padded {
+ padding: 5px;
+}
=======================================
---
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java
Thu Mar 11 03:44:25 2010
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java
Fri Mar 12 07:16:19 2010
@@ -15,6 +15,8 @@
*/
package com.google.gwt.bikeshed.sample.stocks.server;
+import com.google.gwt.bikeshed.sample.stocks.shared.PlayerInfo;
+
import java.util.HashMap;
import java.util.TreeSet;
import java.util.regex.Pattern;
@@ -22,28 +24,18 @@
/**
* 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;
+public class PlayerStatus extends PlayerInfo {
/**
* 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>();
-
+
/**
* A precompiled version of the favorites query.
*/
@@ -58,13 +50,14 @@
* The number of shares owned for each symbol.
*/
private HashMap<String, Integer> sharesOwnedBySymbol = new
HashMap<String, Integer>();
-
+
/**
* The total amount paid for each symbol.
*/
private HashMap<String, Double> averagePriceBySymbol = new
HashMap<String, Double>();
- public PlayerStatus() {
+ public PlayerStatus(String name) {
+ super(name);
generateFavoritesQuery();
}
@@ -89,6 +82,7 @@
public void buy(String ticker, int quantity, int price)
throws IllegalArgumentException {
// Verify that the player can afford the stock.
+ int cash = getCash();
int totalPrice = price * quantity;
if (cash < totalPrice) {
throw new IllegalArgumentException("You cannot afford that much
stock");
@@ -98,16 +92,16 @@
int current = getSharesOwned(ticker);
double averagePrice = getAveragePrice(ticker);
double totalPaid = averagePrice * current + totalPrice;
-
+
// Update case and shares owned
current += quantity;
- cash -= totalPrice;
+ setCash(cash - totalPrice);
sharesOwnedBySymbol.put(ticker, current);
-
+
// Update average price
averagePrice = totalPaid / current;
averagePriceBySymbol.put(ticker, averagePrice);
-
+
// Add this stock to the favorites list.
addFavorite(ticker);
}
@@ -115,7 +109,7 @@
/**
* Returns the total cost of the currently owned shared, using an
average cost
* basis method.
- *
+ *
* @param ticker the stock ticker
*/
public int getAverageCostBasis(String ticker) {
@@ -126,15 +120,6 @@
Double current = averagePriceBySymbol.get(ticker);
return current == null ? 0.0 : current;
}
-
- /**
- * Get the player's current cash amount.
- *
- * @return the cash amount
- */
- public int getCash() {
- return cash;
- }
/**
* Get this players favorite pattern.
@@ -164,7 +149,7 @@
Integer current = sharesOwnedBySymbol.get(ticker);
return current == null ? 0 : current;
}
-
+
/**
* Check if the stock ticker is in the favorites list.
*
@@ -204,16 +189,22 @@
// Perform the transaction.
int totalPrice = price * quantity;
- cash += totalPrice;
+ setCash(getCash() + totalPrice);
+
current -= quantity;
sharesOwnedBySymbol.put(ticker, current);
-
+
if (current == 0) {
sharesOwnedBySymbol.remove(ticker);
averagePriceBySymbol.remove(ticker);
removeFavorite(ticker);
}
}
+
+ @Override
+ public void setStockValue(int value) {
+ super.setStockValue(value);
+ }
/**
* Regenerate the favorites query.
=======================================
---
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java
Fri Mar 12 06:22:00 2010
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java
Fri Mar 12 07:16:19 2010
@@ -15,9 +15,13 @@
*/
package com.google.gwt.bikeshed.sample.stocks.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.bikeshed.list.shared.Range;
import com.google.gwt.bikeshed.list.shared.AbstractListModel.DefaultRange;
import com.google.gwt.bikeshed.sample.stocks.client.StockService;
+import com.google.gwt.bikeshed.sample.stocks.shared.PlayerInfo;
import com.google.gwt.bikeshed.sample.stocks.shared.StockQuote;
import com.google.gwt.bikeshed.sample.stocks.shared.StockQuoteList;
import com.google.gwt.bikeshed.sample.stocks.shared.StockRequest;
@@ -25,7 +29,11 @@
import com.google.gwt.bikeshed.sample.stocks.shared.Transaction;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.HashMap;
+import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -46,7 +54,7 @@
String change;
long createdTime;
int price;
-
+
public Quote(int price, String change) {
this.price = price;
this.change = change;
@@ -65,7 +73,7 @@
return price;
}
}
-
+
/**
* The result of a query to the remote service that provides stock
quotes.
*/
@@ -127,7 +135,7 @@
"ZMH");
// Precompile each regex
- for (Map.Entry<String,String> entry : sectorQueries.entrySet()) {
+ for (Map.Entry<String, String> entry : sectorQueries.entrySet()) {
sectorPatterns.put(entry.getKey(), compile(entry.getValue()));
}
}
@@ -150,8 +158,7 @@
PlayerStatus player = ensurePlayer();
player.addFavorite(ticker);
Result favorites = queryFavorites(favoritesRange);
- return new StockResponse(null, favorites.quotes, null, null,
- 0, favorites.numRows, 0, player.getCash());
+ return createStockResponse(player, null, favorites, null, null);
}
public Result getSectorQuotes(String sector, Range sectorRange) {
@@ -182,22 +189,15 @@
Result sector = sectorRange != null ?
getSectorQuotes(sectorName, sectorRange) : null;
- return new StockResponse(searchResults.quotes,
- favorites.quotes,
- sector != null ? sectorName : null,
- sector != null ? sector.quotes : null,
- searchResults.numRows,
- favorites.numRows,
- sector != null ? sector.numRows : 0,
- player.getCash());
- }
-
+ return createStockResponse(player, searchResults, favorites,
sectorName,
+ sector);
+ }
+
public StockResponse removeFavorite(String ticker, Range favoritesRange)
{
PlayerStatus player = ensurePlayer();
player.removeFavorite(ticker);
Result favorites = queryFavorites(favoritesRange);
- return new StockResponse(null, favorites.quotes, null, null,
- 0, favorites.numRows, 0, player.getCash());
+ return createStockResponse(player, null, favorites, null, null);
}
public Transaction transact(Transaction transaction)
@@ -225,18 +225,65 @@
return new Transaction(transaction.isBuy(), ticker, quantity, price);
}
+
+ /**
+ * Create a stock response, updating the current user's net worth.
+ *
+ * @param player the player info
+ * @param searchResults the search results
+ * @param favorites the users favorites
+ * @param sectorName the name of the sector
+ * @param sectorResults the sector results
+ * @return a {...@link StockResponse}
+ */
+ private StockResponse createStockResponse(PlayerStatus player,
+ Result searchResults, Result favorites, String sectorName,
+ Result sectorResults) {
+ // Default to no search results.
+ if (searchResults == null) {
+ searchResults = new Result(null, 0);
+ }
+
+ // Default to no sector results.
+ if (sectorResults == null) {
+ sectorResults = new Result(null, 0);
+ }
+
+ // Store the new stock value.
+ player.setStockValue(favorites.quotes.getValue());
+
+ // Create a stock response.
+ List<PlayerInfo> playerScores = new ArrayList<PlayerInfo>();
+ for (PlayerStatus curPlayer : players.values()) {
+ playerScores.add(curPlayer.copy());
+ }
+ Collections.sort(playerScores, new Comparator<PlayerInfo>() {
+ public int compare(PlayerInfo o1, PlayerInfo o2) {
+ // Reverse sort so top player is first.
+ return o2.getNetWorth() - o1.getNetWorth();
+ }
+ });
+ StockResponse response = new StockResponse(player.copy(),
+ searchResults.quotes, favorites.quotes, sectorName != null ?
sectorName
+ : null, sectorResults.quotes, searchResults.numRows,
+ favorites.numRows, sectorResults.numRows, playerScores);
+
+ return response;
+ }
/**
* Ensure that a {...@link PlayerStatus} for the current player exists and
return
* it.
- *
+ *
* @return the {...@link PlayerStatus} for the current player
*/
private PlayerStatus ensurePlayer() {
- String userId = "I Am the User";
+ UserService userService = UserServiceFactory.getUserService();
+ User user = userService.getCurrentUser();
+ String userId = user.getUserId();
PlayerStatus player = players.get(userId);
if (player == null) {
- player = new PlayerStatus();
+ player = new PlayerStatus(user.getNickname());
players.put(userId, player);
}
return player;
@@ -245,11 +292,11 @@
private Result getQuotes(SortedSet<String> symbols, Range range) {
int start = range.getStart();
int end = Math.min(start + range.getLength(), symbols.size());
-
+
if (end <= start) {
return new Result(new StockQuoteList(0), 0);
}
-
+
// Get the symbols that are in range.
SortedSet<String> symbolsInRange = new TreeSet<String>();
int idx = 0;
@@ -259,10 +306,10 @@
}
idx++;
}
-
+
// If we already have a price that is less than 5 seconds old,
// don't re-request the data from the server
-
+
SortedSet<String> symbolsToQuery = new TreeSet<String>();
long now = System.currentTimeMillis();
for (String symbol : symbolsInRange) {
@@ -271,19 +318,20 @@
symbolsToQuery.add(symbol);
// System.out.println("retrieving new value of " + symbol);
} else {
- // System.out.println("Using cached value of " + symbol + " (" +
(now - quote.getCreatedTime()) + "ms old)");
+ // System.out.println("Using cached value of " + symbol + " (" +
(now -
+ // quote.getCreatedTime()) + "ms old)");
}
}
-
+
if (symbolsToQuery.size() > 0) {
GoogleFinance.queryServer(symbolsToQuery, QUOTES);
}
-
+
// Create and return a StockQuoteList containing the quotes
StockQuoteList toRet = new StockQuoteList(start);
for (String symbol : symbolsInRange) {
Quote quote = QUOTES.get(symbol);
-
+
if (quote == null) {
System.out.println("Bad symbol " + symbol);
} else {
@@ -292,44 +340,45 @@
Integer sharesOwned = player.getSharesOwned(symbol);
boolean favorite = player.isFavorite(symbol);
int totalPaid = player.getAverageCostBasis(symbol);
-
+
toRet.add(new StockQuote(symbol, name, quote.getPrice(),
- quote.getChange(), sharesOwned == null ? 0 :
sharesOwned.intValue(),
- favorite, totalPaid));
+ quote.getChange(),
+ sharesOwned == null ? 0 : sharesOwned.intValue(), favorite,
+ totalPaid));
}
}
-
+
return new Result(toRet, toRet.size());
}
// If a query is alpha-only ([A-Za-z]+), return stocks for which:
- // 1a) a prefix of the ticker symbol matches the query
- // 2) any substring of the stock name matches the query
+ // 1a) a prefix of the ticker symbol matches the query
+ // 2) any substring of the stock name matches the query
//
// If a query is non-alpha, consider it as a regex and return stocks for
// which:
- // 1b) any portion of the stock symbol matches the regex
- // 2) any portion of the stock name matches the regex
+ // 1b) any portion of the stock symbol matches the regex
+ // 2) any portion of the stock name matches the regex
private Result getSearchQuotes(String query, Range searchRange) {
SortedSet<String> symbols = new TreeSet<String>();
-
+
boolean queryIsAlpha = true;
for (int i = 0; i < query.length(); i++) {
char c = query.charAt(i);
if ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z')) {
queryIsAlpha = false;
break;
- }
+ }
}
// Canonicalize case
query = query.toUpperCase(Locale.US);
-
+
// (1a)
if (queryIsAlpha) {
getTickersByPrefix(query, symbols);
}
-
+
// Use Unicode case-insensitive matching, allow matching of a substring
Pattern pattern = compile("(?iu).*(" + query + ").*");
if (pattern != null) {
@@ -337,7 +386,7 @@
if (!queryIsAlpha) {
getTickersBySymbolRegex(pattern, symbols);
}
-
+
// (2)
if (query.length() > 2) {
getTickersByNameRegex(pattern, symbols);
@@ -352,29 +401,29 @@
if (pattern == null) {
return;
}
-
- for (Map.Entry<String,String> entry :
Stocks.companyNamesBySymbol.entrySet()) {
+
+ for (Map.Entry<String, String> entry :
Stocks.companyNamesBySymbol.entrySet()) {
if (tickers.size() >= MAX_RESULTS_TO_RETURN) {
return;
}
-
+
if (match(entry.getValue(), pattern)) {
tickers.add(entry.getKey());
}
}
}
-
+
// Assume prefix is upper case
private void getTickersByPrefix(String prefix, Set<String> tickers) {
if (prefix == null || prefix.length() == 0) {
return;
}
-
+
for (String ticker : Stocks.stockTickers) {
if (tickers.size() >= MAX_RESULTS_TO_RETURN) {
break;
}
-
+
if (ticker.startsWith(prefix)) {
tickers.add(ticker);
}
@@ -386,7 +435,7 @@
if (pattern == null) {
return;
}
-
+
for (String ticker : Stocks.stockTickers) {
if (tickers.size() >= MAX_RESULTS_TO_RETURN) {
return;
@@ -396,7 +445,7 @@
}
}
}
-
+
private boolean match(String symbol, Pattern pattern) {
Matcher m = pattern.matcher(symbol);
return m.matches();
@@ -416,7 +465,7 @@
if (favoritesPattern != null) {
getTickersBySymbolRegex(favoritesPattern, symbols);
}
-
+
return getQuotes(symbols, favoritesRange);
}
=======================================
---
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java
Thu Mar 11 03:44:25 2010
+++
/trunk/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java
Fri Mar 12 07:16:19 2010
@@ -16,6 +16,7 @@
package com.google.gwt.bikeshed.sample.stocks.shared;
import java.io.Serializable;
+import java.util.List;
/**
* A response to a request for stock data.
@@ -25,7 +26,6 @@
/**
* The amount of available cash in pennies.
*/
- private int cash;
private StockQuoteList favorites;
private int numFavorites;
private int numSearchResults;
@@ -33,9 +33,14 @@
private StockQuoteList searchResults;
private StockQuoteList sector;
private String sectorName;
-
- public StockResponse(StockQuoteList searchResults, StockQuoteList
favorites,
- String sectorName, StockQuoteList sector, int numSearchResults, int
numFavorites, int numSector, int cash) {
+ private PlayerInfo player;
+ private List<PlayerInfo> players;
+
+ public StockResponse(PlayerInfo player, StockQuoteList searchResults,
+ StockQuoteList favorites, String sectorName, StockQuoteList sector,
+ int numSearchResults, int numFavorites, int numSector,
+ List<PlayerInfo> players) {
+ this.player = player;
this.searchResults = searchResults;
this.favorites = favorites;
this.sectorName = sectorName;
@@ -43,7 +48,7 @@
this.numSearchResults = numSearchResults;
this.numFavorites = numFavorites;
this.numSector = numSector;
- this.cash = cash;
+ this.players = players;
}
/**
@@ -53,18 +58,18 @@
}
public int getCash() {
- return cash;
+ return player.getCash();
}
public StockQuoteList getFavorites() {
return favorites;
}
-
+
/**
- * The sum of cash available and portfolio value.
+ * The sum of cash available and portfolio value.
*/
public int getNetWorth() {
- return cash + favorites.getValue();
+ return player.getNetWorth();
}
public int getNumFavorites() {
@@ -78,6 +83,15 @@
public int getNumSector() {
return numSector;
}
+
+ /**
+ * Get the list of all players and their scores.
+ *
+ * @return the ordered list of all players
+ */
+ public List<PlayerInfo> getPlayers() {
+ return players;
+ }
public StockQuoteList getSearchResults() {
return searchResults;
@@ -86,7 +100,7 @@
public StockQuoteList getSector() {
return sector;
}
-
+
public String getSectorName() {
return sectorName;
}
=======================================
--- /trunk/bikeshed/war/Stocks.css Thu Mar 11 10:09:28 2010
+++ /trunk/bikeshed/war/Stocks.css Fri Mar 12 07:16:19 2010
@@ -59,6 +59,13 @@
.serverResponseLabelError {
color: red;
}
+
+.playerScoreBox {
+ border: 1px solid #777;
+ padding:3px;
+ background: #eee;
+ margin-bottom: 5px;
+}
/** Set ids using widget.getElement().setId("idOfElement") */
#closeButton {
=======================================
--- /trunk/bikeshed/war/WEB-INF/web.xml Thu Mar 4 12:59:01 2010
+++ /trunk/bikeshed/war/WEB-INF/web.xml Fri Mar 12 07:16:19 2010
@@ -41,4 +41,14 @@
<welcome-file>Tree.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