Checkpoint
git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@7702 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/CurrencyCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/CurrencyCell.java
index 02cbbe6..cbbff4e 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/cells/client/CurrencyCell.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/CurrencyCell.java
@@ -22,10 +22,17 @@
@Override
public void render(Integer price, StringBuilder sb) {
+ boolean negative = price < 0;
+ if (negative) {
+ price = -price;
+ }
int dollars = price / 100;
int cents = price % 100;
- sb.append("$ ");
+ if (negative) {
+ sb.append("-");
+ }
+ sb.append("$");
sb.append(dollars);
sb.append('.');
if (cents < 10) {
diff --git a/bikeshed/src/com/google/gwt/bikeshed/cells/client/ProfitLossCell.java b/bikeshed/src/com/google/gwt/bikeshed/cells/client/ProfitLossCell.java
new file mode 100644
index 0000000..84447d4
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/cells/client/ProfitLossCell.java
@@ -0,0 +1,50 @@
+/*
+ * 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.cells.client;
+
+/**
+ * A {@link Cell} used to render currency. Positive values are shown in green
+ * with a "+" sign and negative values are shown in red with a "-" sign.
+ */
+public class ProfitLossCell extends Cell<Integer> {
+
+ @Override
+ public void render(Integer priceDelta, StringBuilder sb) {
+ boolean negative = priceDelta < 0;
+ if (negative) {
+ priceDelta = -priceDelta;
+ }
+ int dollars = priceDelta / 100;
+ int cents = priceDelta % 100;
+
+ sb.append("<span style=\"color:");
+ if (priceDelta == 0) {
+ sb.append("green\"> ");
+ } else if (negative) {
+ sb.append("red\">-");
+ } else {
+ sb.append("green\">+");
+ }
+ sb.append("$");
+ sb.append(dollars);
+ sb.append('.');
+ if (cents < 10) {
+ sb.append('0');
+ }
+ sb.append(cents);
+ sb.append("</span>");
+ }
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/Header.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/Header.java
index 7a1a923..d2cb09b 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/Header.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/Header.java
@@ -20,25 +20,22 @@
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
+/**
+ * A table column header.
+ *
+ * @param <H> the {#link Cell} type
+ */
public class Header<H> {
private final Cell<H> cell;
private ValueUpdater<H> updater;
private H value;
- public H getValue() {
- return value;
- }
-
- public void setValue(H value) {
- this.value = value;
- }
-
public Header(Cell<H> cell) {
this.cell = cell;
}
- public void setUpdater(ValueUpdater<H> updater) {
- this.updater = updater;
+ public H getValue() {
+ return value;
}
public void onBrowserEvent(Element elem, NativeEvent event) {
@@ -48,4 +45,12 @@
public void render(StringBuilder sb) {
cell.render(value, sb);
}
+
+ public void setUpdater(ValueUpdater<H> updater) {
+ this.updater = updater;
+ }
+
+ public void setValue(H value) {
+ this.value = value;
+ }
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/list/client/TextHeader.java b/bikeshed/src/com/google/gwt/bikeshed/list/client/TextHeader.java
index d442c59..05ab116 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/list/client/TextHeader.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/list/client/TextHeader.java
@@ -17,6 +17,9 @@
import com.google.gwt.bikeshed.cells.client.TextCell;
+/**
+ * A Header containing String data rendered by a TextCell.
+ */
public class TextHeader extends Header<String> {
public TextHeader(String text) {
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/ChangeCell.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/ChangeCell.java
new file mode 100644
index 0000000..8aa7f65
--- /dev/null
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/ChangeCell.java
@@ -0,0 +1,36 @@
+/*
+ * 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;
+
+/**
+ * A cell that represents a {@link StockQuote}.
+ */
+public class ChangeCell extends Cell<String> {
+
+ @Override
+ public void render(String value, StringBuilder sb) {
+ sb.append("<span style=\"color:");
+ if (value.charAt(0) == '-') {
+ sb.append("red\">");
+ } else {
+ sb.append("green\">");
+ }
+ sb.append(value);
+ sb.append("</span>");
+ }
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/Columns.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/Columns.java
index 8f743ba..889fcfe 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/Columns.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/Columns.java
@@ -18,6 +18,7 @@
import com.google.gwt.bikeshed.cells.client.ButtonCell;
import com.google.gwt.bikeshed.cells.client.CheckboxCell;
import com.google.gwt.bikeshed.cells.client.CurrencyCell;
+import com.google.gwt.bikeshed.cells.client.ProfitLossCell;
import com.google.gwt.bikeshed.cells.client.TextCell;
import com.google.gwt.bikeshed.list.client.Column;
import com.google.gwt.bikeshed.sample.stocks.shared.StockQuote;
@@ -36,6 +37,14 @@
}
};
+ static Column<StockQuote, String> changeColumn =
+ new Column<StockQuote, String>(new ChangeCell()) {
+ @Override
+ protected String getValue(StockQuote object) {
+ return object.getChange();
+ }
+ };
+
static Column<StockQuote, Integer> dollarsColumn =
new Column<StockQuote, Integer>(new CurrencyCell()) {
@Override
@@ -68,6 +77,14 @@
}
};
+ static Column<StockQuote, Integer> profitLossColumn =
+ new Column<StockQuote, Integer>(new ProfitLossCell()) {
+ @Override
+ protected Integer getValue(StockQuote object) {
+ return object.getValue() - object.getTotalPaid();
+ }
+ };
+
static Column<StockQuote, String> sellColumn =
new Column<StockQuote, String>(new ButtonCell()) {
@Override
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
index a16284e..ac2ed0f 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/StockSample.java
@@ -29,6 +29,7 @@
import com.google.gwt.bikeshed.sample.stocks.shared.StockResponse;
import com.google.gwt.bikeshed.sample.stocks.shared.Transaction;
import com.google.gwt.bikeshed.tree.client.SideBySideTreeView;
+import com.google.gwt.bikeshed.tree.client.TreeNode;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
@@ -61,7 +62,7 @@
* The delay between updates in milliseconds.
*/
private static final int UPDATE_DELAY = 5000;
-
+
static String getFormattedPrice(int price) {
return NumberFormat.getCurrencyFormat("USD").format(price / 100.0);
}
@@ -79,26 +80,26 @@
private final StockServiceAsync dataService = GWT.create(StockService.class);
private AsyncListModel<StockQuote> favoritesListModel;
-
+
private PagingTableListView<StockQuote> favoritesTable;
private final Label netWorthLabel = new Label();
-
+
private StockQueryWidget queryWidget;
private AsyncListModel<StockQuote> searchListModel;
-
+
private Map<String, ListListModel<Transaction>> transactionListListModelsByTicker =
new HashMap<String, ListListModel<Transaction>>();
-
+
private ListListModel<Transaction> transactionListModel;
-
+
private List<Transaction> transactions;
- private PagingTableListView<Transaction> transactionTable;
-
private SideBySideTreeView transactionTree;
+ private TransactionTreeViewModel treeModel;
+
/**
* The timer used to update the stock quotes.
*/
@@ -109,8 +110,6 @@
}
};
- private TransactionTreeViewModel treeModel;
-
/**
* This is the entry point method.
*/
@@ -130,7 +129,7 @@
update();
}
});
-
+
transactionListModel = new ListListModel<Transaction>();
transactions = transactionListModel.getList();
@@ -138,16 +137,13 @@
favoritesTable = new PagingTableListView<StockQuote>(favoritesListModel, 10);
favoritesTable.addColumn(Columns.tickerColumn, new TextHeader("ticker"));
favoritesTable.addColumn(Columns.priceColumn, new TextHeader("price"));
+ favoritesTable.addColumn(Columns.changeColumn, new TextHeader("change"));
favoritesTable.addColumn(Columns.sharesColumn, new TextHeader("shares"));
favoritesTable.addColumn(Columns.dollarsColumn, new TextHeader("value"));
+ favoritesTable.addColumn(Columns.profitLossColumn, new TextHeader("profit"));
favoritesTable.addColumn(Columns.buyColumn);
favoritesTable.addColumn(Columns.sellColumn);
-
- // Create the transactions table.
- transactionTable = new PagingTableListView<Transaction>(transactionListModel, 10);
- transactionTable.addColumn(Columns.transactionColumn);
- transactionTable.addColumn(Columns.subtotalColumn);
-
+
treeModel = new TransactionTreeViewModel(this,
favoritesListModel, transactionListListModelsByTicker);
transactionTree = new SideBySideTreeView(treeModel, null, 200, 200);
@@ -195,7 +191,7 @@
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 =
@@ -212,9 +208,9 @@
});
// Add components to the page.
-
+
Widget headerWidget = new HTML("<b>Stock Game</b>");
-
+
HorizontalPanel cashPanel = new HorizontalPanel();
cashPanel.add(new HTML("<b>Available cash:</b>"));
cashPanel.add(cashLabel);
@@ -224,7 +220,7 @@
VerticalPanel moneyPanel = new VerticalPanel();
moneyPanel.add(cashPanel);
moneyPanel.add(netWorthPanel);
-
+
DockLayoutPanel westPanel = new DockLayoutPanel(Unit.PCT);
westPanel.addNorth(moneyPanel, 25.0);
westPanel.add(new HTML("<table>" +
@@ -232,16 +228,16 @@
"<tr><td>Joel Webber</td><td>$10000</td></tr>" +
"<tr><td>John Labanca</td><td>$10000</td></tr>" +
"</table>"));
-
+
DockLayoutPanel layoutPanel = new DockLayoutPanel(Unit.EM);
layoutPanel.addNorth(headerWidget, 4.0);
layoutPanel.addWest(westPanel, 15.0);
layoutPanel.addNorth(transactionTree, 18.0);
-
+
DockLayoutPanel innerLayoutPanel = new DockLayoutPanel(Unit.PCT);
this.queryWidget = new StockQueryWidget(searchListModel, this);
innerLayoutPanel.addWest(queryWidget, 60.0);
-
+
DockLayoutPanel favoritesLayoutPanel = new DockLayoutPanel(Unit.EM);
favoritesLayoutPanel.addNorth(new Label("Portfolio / Favorites"), 2.0);
favoritesLayoutPanel.add(new ScrollPanel(favoritesTable));
@@ -292,19 +288,21 @@
if (queryWidget == null) {
return;
}
-
+
updateTimer.cancel();
-
+
Range[] searchRanges = searchListModel.getRanges();
Range[] favoritesRanges = favoritesListModel.getRanges();
- SectorListModel sectorListModel = treeModel.getSectorListModel();
+
+ String sectorName = getSectorName();
+ 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) {
return;
}
-
+
String searchQuery = queryWidget.getSearchQuery();
StockRequest request = new StockRequest(searchQuery,
sectorListModel != null ? sectorListModel.getSector() : null,
@@ -322,13 +320,26 @@
updateTimer.schedule(UPDATE_DELAY);
}
}
-
+
public void onSuccess(StockResponse result) {
processStockResponse(result);
}
});
}
+ // Hack - walk the transaction tree to find the current viewed sector
+ private String getSectorName() {
+ int children = transactionTree.getRootNode().getChildCount();
+ for (int i = 0; i < children; i++) {
+ TreeNode<?> childNode = transactionTree.getRootNode().getChildNode(i);
+ if (childNode.isOpen()) {
+ return (String) childNode.getValue();
+ }
+ }
+
+ return null;
+ }
+
/**
* Process the {@link StockResponse} from the server.
*
@@ -368,7 +379,7 @@
// Update the sector list.
StockQuoteList sectorList = response.getSector();
if (sectorList != null) {
- SectorListModel sectorListModel = treeModel.getSectorListModel();
+ SectorListModel sectorListModel = treeModel.getSectorListModel(getSectorName());
sectorListModel.updateDataSize(response.getNumSector(), true);
sectorListModel.updateViewData(sectorList.getStartIndex(),
sectorList.size(), sectorList);
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/TransactionTreeViewModel.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/TransactionTreeViewModel.java
index f15b3d27..2a57bd0 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/TransactionTreeViewModel.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/client/TransactionTreeViewModel.java
@@ -25,6 +25,7 @@
import com.google.gwt.bikeshed.tree.client.TreeNode;
import com.google.gwt.bikeshed.tree.client.TreeViewModel;
+import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -34,11 +35,11 @@
* level containing Transactions.
*/
class TransactionTreeViewModel implements TreeViewModel {
-
+
class SectorListModel extends AsyncListModel<StockQuote> {
-
+
String sector;
-
+
public SectorListModel(final Updater updater, String sector) {
super(new DataSource<StockQuote>() {
public void requestData(AsyncListModel<StockQuote> listModel) {
@@ -52,28 +53,29 @@
return sector;
}
}
-
+
static class TransactionCell extends Cell<Transaction> {
@Override
public void render(Transaction value, StringBuilder sb) {
sb.append(value.toString());
}
}
-
+
private static final Cell<StockQuote> STOCK_QUOTE_CELL = new Cell<StockQuote>() {
@Override
public void render(StockQuote value, StringBuilder sb) {
sb.append(value.getTicker() + " - " + value.getDisplayPrice());
}
};
-
+
private static final Cell<Transaction> TRANSACTION_CELL =
new TransactionCell();
-
- private SectorListModel sectorListModel;
+
+ private Map<String, SectorListModel> sectorListModels = new HashMap<String, SectorListModel>();
private ListModel<StockQuote> stockQuoteListModel;
private ListListModel<String> topLevelListListModel =
new ListListModel<String>();
+
private Map<String, ListListModel<Transaction>> transactionListListModelsByTicker;
private Updater updater;
@@ -88,7 +90,7 @@
topLevelList.add("S&P 500");
this.transactionListListModelsByTicker = transactionListListModelsByTicker;
}
-
+
@SuppressWarnings("unused")
public <T> NodeInfo<?> getNodeInfo(T value, TreeNode<T> treeNode) {
if (value == null) {
@@ -103,8 +105,9 @@
}
};
} else if (value instanceof String) {
- sectorListModel = new SectorListModel(updater, (String) value);
- return new TreeViewModel.DefaultNodeInfo<StockQuote>(sectorListModel, STOCK_QUOTE_CELL) {
+ SectorListModel listModel = new SectorListModel(updater, (String) value);
+ sectorListModels.put((String) value, listModel);
+ return new TreeViewModel.DefaultNodeInfo<StockQuote>(listModel, STOCK_QUOTE_CELL) {
@Override
public Object getKey(StockQuote value) {
return value.getTicker();
@@ -120,15 +123,15 @@
return new TreeViewModel.DefaultNodeInfo<Transaction>(listModel,
TRANSACTION_CELL);
}
-
+
throw new IllegalArgumentException(value.toString());
}
- public SectorListModel getSectorListModel() {
- return sectorListModel;
+ public SectorListModel getSectorListModel(String value) {
+ return sectorListModels.get(value);
}
public boolean isLeaf(Object value) {
return value instanceof Transaction;
}
-}
\ No newline at end of file
+}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java
index ea6e072..ea6a4e9 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/PlayerStatus.java
@@ -58,6 +58,11 @@
* 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() {
generateFavoritesQuery();
@@ -91,15 +96,38 @@
// Update the number of shares owned.
int current = getSharesOwned(ticker);
- cash -= totalPrice;
+ double averagePrice = getAveragePrice(ticker);
+ double totalPaid = averagePrice * current + totalPrice;
+
+ // Update case and shares owned
current += quantity;
+ 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);
}
/**
+ * 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) {
+ return (int) (Math.round(getAveragePrice(ticker) * getSharesOwned(ticker)));
+ }
+
+ public double getAveragePrice(String ticker) {
+ Double current = averagePriceBySymbol.get(ticker);
+ return current == null ? 0.0 : current;
+ }
+
+ /**
* Get the player's current cash amount.
*
* @return the cash amount
@@ -136,7 +164,7 @@
Integer current = sharesOwnedBySymbol.get(ticker);
return current == null ? 0 : current;
}
-
+
/**
* Check if the stock ticker is in the favorites list.
*
@@ -175,9 +203,16 @@
}
// Perform the transaction.
- cash += quantity * price;
+ int totalPrice = price * quantity;
+ cash += totalPrice;
current -= quantity;
sharesOwnedBySymbol.put(ticker, current);
+
+ if (current == 0) {
+ sharesOwnedBySymbol.remove(ticker);
+ averagePriceBySymbol.remove(ticker);
+ removeFavorite(ticker);
+ }
}
/**
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java
index 126e250..2cac311 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/server/StockServiceImpl.java
@@ -87,7 +87,7 @@
companyNamesBySymbol.put(symbol, companyName);
}
}
-
+
static {
sectorQueries.put("DOW JONES INDUSTRIALS",
"AA|AXP|BA|BAC|CAT|CSCO|CVX|DD|DIS|GE|HD|HPQ|IBM|INTC|JNJ|JPM|KFT|KO|" +
@@ -103,7 +103,7 @@
"CTL|CTSH|CTXS|CVH|CVS|CVX|D|DD|DE|DELL|DF|DFS|DGX|DHI|DHR|DIS|DNB|" +
"DNR|DO|DOV|DOW|DPS|DRI|DTE|DTV|DUK|DV|DVA|DVN|EBAY|ECL|ED|EFX|EIX|" +
"EK|EL|EMC|EMN|EMR|EOG|EP|EQR|EQT|ERTS|ESRX|ETFC|ETN|ETR|EXC|EXPD|" +
- "EXPE|F|FAST|FCX|FDO|FDX|FE|FHN|FII|FIS|FISV|FITB|FLIR|FLR|FLS|FMC|" +
+ "EXPE|F|FAST|FCX|FDO|FDX|FE|FHN|FII|FIS|FISV|FITB|FLIR|FLR|FLS|FMC|" +
"FO|FPL|FRX|FSLR|FTI|FTR|GAS|GCI|GD|GE|GENZ|GILD|GIS|GLW|GME|GNW|" +
"GOOG|GPC|GPS|GR|GS|GT|GWW|HAL|HAR|HAS|HBAN|HCBK|HCN|HCP|HD|HES|HIG|" +
"HNZ|HOG|HON|HOT|HPQ|HRB|HRL|HRS|HSP|HST|HSY|HUM|IBM|ICE|IFF|IGT|" +
@@ -111,7 +111,7 @@
"JNS|JPM|JWN|K|KEY|KFT|KG|KIM|KLAC|KMB|KO|KR|KSS|L|LEG|LEN|LH|LIFE|" +
"LLL|LLTC|LLY|LM|LMT|LNC|LO|LOW|LSI|LTD|LUK|LUV|LXK|M|MA|MAR|MAS|MAT|" +
"MCD|MCHP|MCK|MCO|MDP|MDT|MEE|MET|MFE|MHP|MHS|MI|MIL|MJN|MKC|MMC|MMM|" +
- "MO|MOLX|MON|MOT|MRK|MRO|MS|MSFT|MTB|MU|MUR|MWV|MWW|MXB|MYL|NBL|NBR|" +
+ "MO|MOLX|MON|MOT|MRK|MRO|MS|MSFT|MTB|MU|MUR|MWV|MWW|MXB|MYL|NBL|NBR|" +
"NDAQ|NEM|NI|NKE|NOC|NOV|NOVL|NSC|NSM|NTAP|NTRS|NU|NUE|NVDA|NVLS|NWL|" +
"NWSA|NYT|NYX|ODP|OI|OMC|ORCL|ORLY|OXY|PAYX|PBCT|PBG|PBI|PCAR|PCG|" +
"PCL|PCP|PCS|PDCO|PEG|PEP|PFE|PFG|PG|PGN|PGR|PH|PHM|PKI|PLD|PLL|PM|" +
@@ -124,7 +124,7 @@
"VNO|VRSN|VTR|VZ|WAG|WAT|WDC|WEC|WFC|WFMI|WFR|WHR|WIN|WLP|WM|WMB|WMT|" +
"WPI|WPO|WU|WY|WYN|WYNN|X|XEL|XL|XLNX|XOM|XRAY|XRX|XTO|YHOO|YUM|ZION|" +
"ZMH");
-
+
// Precompile each regex
for (Map.Entry<String,String> entry : sectorQueries.entrySet()) {
sectorPatterns.put(entry.getKey(), compile(entry.getValue()));
@@ -142,24 +142,26 @@
/**
* A mapping of usernames to {@link PlayerStatus}.
*/
- private Map<String, PlayerStatus> players = new HashMap<String, PlayerStatus>();
-
+ private Map<String, PlayerStatus> players =
+ new HashMap<String, PlayerStatus>();
+
public StockResponse addFavorite(String ticker, Range favoritesRange) {
PlayerStatus player = ensurePlayer();
player.addFavorite(ticker);
- Result favorites = query(player.getFavoritesQuery(), player.getFavoritesPattern(), favoritesRange);
- return new StockResponse(null, favorites.quotes, null,
+ Result favorites = query(player.getFavoritesQuery(),
+ player.getFavoritesPattern(), favoritesRange, false);
+ return new StockResponse(null, favorites.quotes, null, null,
0, favorites.numRows, 0, player.getCash());
}
public Result getSectorQuotes(String sector, Range sectorRange) {
sector = sector.toUpperCase();
String sectorQuery = sectorQueries.get(sector);
- Pattern sectorPattern = sectorPatterns.get(sector);
if (sectorQuery == null) {
return null;
}
- return query(sectorQuery, sectorPattern, sectorRange);
+ Pattern sectorPattern = sectorPatterns.get(sector);
+ return query(sectorQuery, sectorPattern, sectorRange, false);
}
public StockResponse getStockQuotes(StockRequest request)
@@ -172,26 +174,30 @@
Range searchRange = request.getSearchRange();
Range favoritesRange = request.getFavoritesRange();
Range sectorRange = request.getSectorRange();
-
+
PlayerStatus player = ensurePlayer();
- Result searchResults = query(query, compile(query), searchRange);
- Result favorites = query(player.getFavoritesQuery(), player.getFavoritesPattern(), favoritesRange);
- Result sector = sectorRange != null ? getSectorQuotes(request.getSector(), sectorRange) : null;
+ Result searchResults = query(query, compile(query), searchRange, true);
+ Result favorites = query(player.getFavoritesQuery(),
+ player.getFavoritesPattern(), favoritesRange, false);
+ String sectorName = request.getSector();
+ 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());
}
-
+
public StockResponse removeFavorite(String ticker, Range favoritesRange) {
PlayerStatus player = ensurePlayer();
player.removeFavorite(ticker);
- Result favorites = query(player.getFavoritesQuery(), player.getFavoritesPattern(), favoritesRange);
- return new StockResponse(null, favorites.quotes, null,
+ Result favorites = query(player.getFavoritesQuery(), player.getFavoritesPattern(), favoritesRange, false);
+ return new StockResponse(null, favorites.quotes, null, null,
0, favorites.numRows, 0, player.getCash());
}
@@ -199,16 +205,24 @@
throws IllegalArgumentException {
// Get the current stock price.
String ticker = transaction.getTicker();
- Pattern tickerPattern = compile(ticker);
if (ticker == null || ticker.length() < 0) {
throw new IllegalArgumentException("Stock could not be found");
}
- Result result = query(ticker, tickerPattern, new DefaultRange(0, 1));
+
+ String tickerRegex = ticker;
+ if (!ticker.startsWith("^")) {
+ tickerRegex = "^" + tickerRegex;
+ }
+ if (!ticker.endsWith("$")) {
+ tickerRegex = tickerRegex + "$";
+ }
+ Pattern tickerPattern = compile(ticker);
+ Result result = query(tickerRegex, tickerPattern, new DefaultRange(0, 1), false);
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();
int price = quote.getPrice();
@@ -217,14 +231,14 @@
} else {
ensurePlayer().sell(ticker, quantity, price);
}
-
- return new Transaction(true, ticker, quantity, price);
+
+ return new Transaction(transaction.isBuy(), ticker, quantity, price);
}
/**
* Ensure that a {@link PlayerStatus} for the current player exists and return
* it.
- *
+ *
* @return the {@link PlayerStatus} for the current player
*/
private PlayerStatus ensurePlayer() {
@@ -237,11 +251,11 @@
return player;
}
- private List<String> getTickers(String query, Pattern pattern) {
+ private List<String> getTickers(String query, Pattern pattern, boolean matchNames) {
Set<String> tickers = new TreeSet<String>();
if (query.length() > 0) {
query = query.toUpperCase();
-
+
int count = 0;
for (String ticker : stockTickers) {
if (ticker.startsWith(query) || (pattern != null && match(ticker, pattern))) {
@@ -252,8 +266,8 @@
}
}
}
-
- if (pattern != null) {
+
+ if (matchNames && pattern != null) {
for (Map.Entry<String,String> entry : companyNamesBySymbol.entrySet()) {
if (match(entry.getValue(), pattern)) {
tickers.add(entry.getKey());
@@ -265,7 +279,7 @@
}
}
}
-
+
return new ArrayList<String>(tickers);
}
@@ -276,15 +290,16 @@
/**
* 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, Pattern queryPattern, Range range) {
+ private Result query(String query, Pattern queryPattern, Range range,
+ boolean matchNames) {
// Get all symbols for the query.
PlayerStatus player = ensurePlayer();
- List<String> symbols = getTickers(query, queryPattern);
+ List<String> symbols = getTickers(query, queryPattern, matchNames);
if (symbols.size() == 0) {
return new Result(new StockQuoteList(0), 0);
@@ -344,6 +359,7 @@
String symbol = null;
String price = null;
+ String change = null;
Matcher dataMatcher = DATA_PATTERN.matcher(group);
while (dataMatcher.find()) {
@@ -353,6 +369,8 @@
symbol = data;
} else if (tag.equals("l_cur")) {
price = data;
+ } else if (tag.equals("c")) {
+ change = data;
}
}
@@ -363,8 +381,10 @@
String name = companyNamesBySymbol.get(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));
+ int totalPaid = player.getAverageCostBasis(symbol);
+ priceMap.put(symbol, new StockQuote(symbol, name, iprice, change,
+ sharesOwned == null ? 0 : sharesOwned.intValue(), favorite,
+ totalPaid));
} catch (NumberFormatException e) {
System.out.println("Bad price " + price + " for symbol " + symbol);
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockQuote.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockQuote.java
index 4e3df91..881ba14 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockQuote.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockQuote.java
@@ -26,8 +26,10 @@
private String name;
private transient String notes;
private int price;
+ private String change;
private String ticker;
private int sharesOwned;
+ private int totalPaid; // Total amount paid for all share, int pennies
/**
* Construct a new {@link StockQuote}.
@@ -38,13 +40,15 @@
* @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, String change, int sharesOwned,
+ boolean favorite, int totalPaid) {
this.ticker = ticker;
this.name = name;
this.price = price;
+ this.change = change;
this.sharesOwned = sharesOwned;
this.favorite = favorite;
+ this.totalPaid = totalPaid;
}
/**
@@ -52,6 +56,10 @@
*/
StockQuote() {
}
+
+ public String getChange() {
+ return change;
+ }
public String getDisplayPrice() {
int dollars = getPrice() / 100;
@@ -87,6 +95,14 @@
public String getTicker() {
return ticker;
}
+
+ public int getTotalPaid() {
+ return totalPaid;
+ }
+
+ public int getValue() {
+ return price * sharesOwned;
+ }
public boolean isFavorite() {
return favorite;
@@ -99,6 +115,7 @@
@Override
public String toString() {
return "StockQuote [ticker=" + ticker + ", name=\"" + name + "\", price="
- + price + ", notes=\"" + notes + "\", favorite=" + favorite + "]";
+ + price + ", notes=\"" + notes + "\", favorite=" + favorite
+ + ", totalPaid=" + totalPaid + "]";
}
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java
index 428a7fc..e8844c3 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/sample/stocks/shared/StockResponse.java
@@ -32,11 +32,13 @@
private int numSector;
private StockQuoteList searchResults;
private StockQuoteList sector;
+ private String sectorName;
public StockResponse(StockQuoteList searchResults, StockQuoteList favorites,
- StockQuoteList sector, int numSearchResults, int numFavorites, int numSector, int cash) {
+ String sectorName, StockQuoteList sector, int numSearchResults, int numFavorites, int numSector, int cash) {
this.searchResults = searchResults;
this.favorites = favorites;
+ this.sectorName = sectorName;
this.sector = sector;
this.numSearchResults = numSearchResults;
this.numFavorites = numFavorites;
@@ -84,4 +86,8 @@
public StockQuoteList getSector() {
return sector;
}
+
+ public String getSectorName() {
+ return sectorName;
+ }
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/SideBySideTreeView.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/SideBySideTreeView.java
index f8cd50d..57f42e8 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/SideBySideTreeView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/SideBySideTreeView.java
@@ -92,7 +92,7 @@
id = id.substring(11);
String[] path = id.split("-");
- TreeNodeView<?> nodeView = getRootNode();
+ TreeNodeView<?> nodeView = getRootTreeNodeView();
for (String s : path) {
nodeView = nodeView.getChildTreeNodeView(Integer.parseInt(s));
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/StandardTreeView.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/StandardTreeView.java
index 2a3665b..e1960dc 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/StandardTreeView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/StandardTreeView.java
@@ -230,7 +230,7 @@
Element currentTarget = event.getCurrentEventTarget().cast();
if (currentTarget == getElement()) {
Element target = event.getEventTarget().cast();
- elementClicked(target, event, getRootNode());
+ elementClicked(target, event, getRootTreeNodeView());
}
break;
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNode.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNode.java
index e9a0694..88f6492 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNode.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNode.java
@@ -29,4 +29,6 @@
TreeNode<?> getParentNode();
T getValue();
+
+ boolean isOpen();
}
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java
index 38c9cbe..98833de 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeNodeView.java
@@ -147,6 +147,13 @@
}
/**
+ * Returns true if the node is open.
+ */
+ public boolean isOpen() {
+ return open;
+ }
+
+ /**
* Check if this is a root node at the top of the tree.
*
* @return true if a root node, false if not
diff --git a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeView.java b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeView.java
index c29fa37..f680956 100644
--- a/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeView.java
+++ b/bikeshed/src/com/google/gwt/bikeshed/tree/client/TreeView.java
@@ -133,6 +133,10 @@
public TreeNodeViewAnimation getAnimation() {
return animation;
}
+
+ public TreeNode<?> getRootNode() {
+ return rootNode;
+ }
public TreeViewModel getTreeViewModel() {
return model;
@@ -210,7 +214,7 @@
return openImageHtml;
}
- protected TreeNodeView<?> getRootNode() {
+ protected TreeNodeView<?> getRootTreeNodeView() {
return rootNode;
}
diff --git a/bikeshed/src/com/google/gwt/sample/expenses/domain/Storage.java b/bikeshed/src/com/google/gwt/sample/expenses/domain/Storage.java
index 260c2a4..47d3c63 100644
--- a/bikeshed/src/com/google/gwt/sample/expenses/domain/Storage.java
+++ b/bikeshed/src/com/google/gwt/sample/expenses/domain/Storage.java
@@ -253,8 +253,9 @@
}
public Void visit(Employee employee) {
- if (null == employee.getUserName())
+ if (null == employee.getUserName()) {
return null;
+ }
if (previous != null) {
Employee prevEmployee = (Employee) previous;
if (!prevEmployee.getUserName().equals(next)) {
diff --git a/bikeshed/test/com/google/gwt/sample/expenses/domain/EntityTester.java b/bikeshed/test/com/google/gwt/sample/expenses/domain/EntityTester.java
index 706bf2d..2a12c15 100644
--- a/bikeshed/test/com/google/gwt/sample/expenses/domain/EntityTester.java
+++ b/bikeshed/test/com/google/gwt/sample/expenses/domain/EntityTester.java
@@ -15,7 +15,6 @@
*/
package com.google.gwt.sample.expenses.domain;
-
class EntityTester {
Currency currency;
Employee employee;