├── .classpath
├── .gitignore
├── .project
├── .settings
└── com.google.gwt.eclipse.core.prefs
├── README.md
├── StockWatcherTest-dev.launch
├── StockWatcherTest-prod.launch
├── build.xml
├── src
└── com
│ └── google
│ └── gwt
│ └── sample
│ └── stockwatcher
│ ├── StockWatcher.gwt.xml
│ └── client
│ ├── StockPrice.java
│ └── StockWatcher.java
└── war
├── StockWatcher.css
├── StockWatcher.html
├── WEB-INF
└── web.xml
└── images
└── gwt.png
/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | gwt-unitCache/
2 | war/WEB-INF/classes/
3 | war/WEB-INF/deploy/
4 | war/WEB-INF/lib/
5 | war/stockwatcher/
6 |
--------------------------------------------------------------------------------
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | StockWatcher
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | com.google.gwt.eclipse.core.gwtProjectValidator
15 |
16 |
17 |
18 |
19 | com.google.gdt.eclipse.core.webAppProjectValidator
20 |
21 |
22 |
23 |
24 |
25 | org.eclipse.jdt.core.javanature
26 | com.google.gwt.eclipse.core.gwtNature
27 | com.google.gdt.eclipse.core.webAppNature
28 |
29 |
30 |
--------------------------------------------------------------------------------
/.settings/com.google.gwt.eclipse.core.prefs:
--------------------------------------------------------------------------------
1 | #Thu Dec 03 14:38:21 EST 2009
2 | eclipse.preferences.version=1
3 | filesCopiedToWebInfLib=gwt-servlet.jar
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | StockWatcher is an application to learn GWT following gwtproject tutorials.
2 |
3 | ### Generated by GWT WebAppCreator
4 |
5 | ### Import your project into Eclipse (recommended)
6 |
7 | If you use Eclipse, you can simply import the generated project into Eclipse.
8 |
9 | In Eclipse, go to the File menu and choose:
10 |
11 | File -> Import... -> Existing Projects into Workspace
12 |
13 | Browse to the directory containing this file,
14 | select "StockWatcher".
15 |
16 | Be sure to uncheck "Copy projects into workspace" if it is checked.
17 |
18 | Click Finish.
19 |
20 | You can now browse the project in Eclipse.
21 |
22 | To launch your web app in GWT hosted mode, go to the Run menu and choose:
23 |
24 | Run -> Open Debug Dialog...
25 |
26 | Under Java Application, you should find a launch configuration
27 | named "StockWatcher". Select and click "Debug".
28 |
29 | You can now use the built-in debugger to debug your web app in hosted mode.
30 |
31 | To compile for web mode, just run your app in hosted mode and press the
32 | "Compile/Browse" button.
33 |
34 | ### Build from the command line with Ant
35 |
36 | If you prefer to work from the command line, you can use Ant to build your
37 | project. (http://ant.apache.org/) Ant uses the generated `build.xml` file
38 | which describes exactly how to build your project. This file has been tested
39 | to work against Ant 1.7.1. The following assumes `ant` is on your command
40 | line path.
41 |
42 | To run hosted mode, just type `ant hosted`.
43 |
44 | To compile your project for deployment, just type `ant`.
45 |
46 | To compile and also bundle into a .war file, type `ant war`.
47 |
48 | For a full listing of other targets, type `ant -p`.
49 |
50 | ### Using another IDE
51 |
52 | GWT projects can be run in other IDEs as well, but will require some manual
53 | setup. If you go this route, be sure to:
54 |
55 | * Have your IDE build .class files into `war/WEB-INF/classes`.
56 | * Add gwt-user.jar and gwt-dev-.jar to your project build path.
57 | * When creating a launch configuration, add a classpath entry for your `src`
58 | folder (this is somewhat unusual but GWT needs access to your source files).
59 |
60 | If you get stuck, try to mimic what the Ant `build.xml` would do.
61 |
--------------------------------------------------------------------------------
/StockWatcherTest-dev.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/StockWatcherTest-prod.launch:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/build.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
--------------------------------------------------------------------------------
/src/com/google/gwt/sample/stockwatcher/StockWatcher.gwt.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/com/google/gwt/sample/stockwatcher/client/StockPrice.java:
--------------------------------------------------------------------------------
1 | package com.google.gwt.sample.stockwatcher.client;
2 |
3 | import java.io.Serializable;
4 |
5 | public class StockPrice implements Serializable {
6 |
7 | private static final long serialVersionUID = 1L;
8 | private String symbol;
9 | private double price;
10 | private double change;
11 |
12 | public StockPrice() {
13 | }
14 |
15 | public StockPrice(String symbol, double price, double change) {
16 | this.symbol = symbol;
17 | this.price = price;
18 | this.change = change;
19 | }
20 |
21 | public String getSymbol() {
22 | return this.symbol;
23 | }
24 |
25 | public double getPrice() {
26 | return this.price;
27 | }
28 |
29 | public double getChange() {
30 | return this.change;
31 | }
32 |
33 | public double getChangePercent() {
34 | return 100.0 * this.change / this.price;
35 | }
36 |
37 | public void setSymbol(String symbol) {
38 | this.symbol = symbol;
39 | }
40 |
41 | public void setPrice(double price) {
42 | this.price = price;
43 | }
44 |
45 | public void setChange(double change) {
46 | this.change = change;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/com/google/gwt/sample/stockwatcher/client/StockWatcher.java:
--------------------------------------------------------------------------------
1 | package com.google.gwt.sample.stockwatcher.client;
2 |
3 | import java.util.ArrayList;
4 | import java.util.Date;
5 |
6 | import com.google.gwt.core.client.EntryPoint;
7 | import com.google.gwt.core.client.GWT;
8 | import com.google.gwt.event.dom.client.ClickEvent;
9 | import com.google.gwt.event.dom.client.ClickHandler;
10 | import com.google.gwt.event.dom.client.KeyCodes;
11 | import com.google.gwt.event.dom.client.KeyPressEvent;
12 | import com.google.gwt.event.dom.client.KeyPressHandler;
13 | import com.google.gwt.i18n.client.DateTimeFormat;
14 | import com.google.gwt.i18n.client.NumberFormat;
15 | import com.google.gwt.user.client.Random;
16 | import com.google.gwt.user.client.Timer;
17 | import com.google.gwt.user.client.Window;
18 | import com.google.gwt.user.client.rpc.AsyncCallback;
19 | import com.google.gwt.user.client.ui.Button;
20 | import com.google.gwt.user.client.ui.FlexTable;
21 | import com.google.gwt.user.client.ui.HorizontalPanel;
22 | import com.google.gwt.user.client.ui.Label;
23 | import com.google.gwt.user.client.ui.RootPanel;
24 | import com.google.gwt.user.client.ui.TextBox;
25 | import com.google.gwt.user.client.ui.VerticalPanel;
26 |
27 | public class StockWatcher implements EntryPoint {
28 |
29 | private static final int REFRESH_INTERVAL = 1000; // ms
30 | private VerticalPanel mainPanel = new VerticalPanel();
31 | private FlexTable stocksFlexTable = new FlexTable();
32 | private HorizontalPanel addPanel = new HorizontalPanel();
33 | private TextBox newSymbolTextBox = new TextBox();
34 | private Button addStockButton = new Button("Add");
35 | private Label lastUpdatedLabel = new Label();
36 | private ArrayList stocks = new ArrayList();
37 |
38 | private StockPriceServiceAsync stockPriceSvc = GWT.create(StockPriceService.class);
39 | private Label errorMsgLabel = new Label();
40 |
41 | /**
42 | * Entry point method.
43 | */
44 | public void onModuleLoad() {
45 | // Create table for stock data.
46 | stocksFlexTable.setText(0, 0, "Symbol");
47 | stocksFlexTable.setText(0, 1, "Price");
48 | stocksFlexTable.setText(0, 2, "Change");
49 | stocksFlexTable.setText(0, 3, "Remove");
50 |
51 | // Add styles to elements in the stock list table.
52 | stocksFlexTable.setCellPadding(6);
53 | stocksFlexTable.getRowFormatter().addStyleName(0, "watchListHeader");
54 | stocksFlexTable.addStyleName("watchList");
55 | stocksFlexTable.getCellFormatter().addStyleName(0, 1, "watchListNumericColumn");
56 | stocksFlexTable.getCellFormatter().addStyleName(0, 2, "watchListNumericColumn");
57 | stocksFlexTable.getCellFormatter().addStyleName(0, 3, "watchListRemoveColumn");
58 |
59 | // Assemble Add Stock panel.
60 | addPanel.add(newSymbolTextBox);
61 | addPanel.add(addStockButton);
62 | addPanel.addStyleName("addPanel");
63 |
64 | // Assemble Main panel.
65 | errorMsgLabel.setStyleName("errorMessage");
66 | errorMsgLabel.setVisible(false);
67 |
68 | mainPanel.add(errorMsgLabel); mainPanel.add(stocksFlexTable);
69 | mainPanel.add(addPanel);
70 | mainPanel.add(lastUpdatedLabel);
71 |
72 | // Associate the Main panel with the HTML host page.
73 | RootPanel.get("stockList").add(mainPanel);
74 |
75 | // Move cursor focus to the input box.
76 | newSymbolTextBox.setFocus(true);
77 |
78 | // Setup timer to refresh list automatically.
79 | Timer refreshTimer = new Timer() {
80 | @Override
81 | public void run() {
82 | refreshWatchList();
83 | }
84 | };
85 | refreshTimer.scheduleRepeating(REFRESH_INTERVAL);
86 |
87 | // Listen for mouse events on the Add button.
88 | addStockButton.addClickHandler(new ClickHandler() {
89 | public void onClick(ClickEvent event) {
90 | addStock();
91 | }
92 | });
93 |
94 | // Listen for keyboard events in the input box.
95 | newSymbolTextBox.addKeyPressHandler(new KeyPressHandler() {
96 | public void onKeyPress(KeyPressEvent event) {
97 | if (event.getCharCode() == KeyCodes.KEY_ENTER) {
98 | addStock();
99 | }
100 | }
101 | });
102 |
103 | }
104 |
105 | /**
106 | * Add stock to FlexTable. Executed when the user clicks the addStockButton or
107 | * presses enter in the newSymbolTextBox.
108 | */
109 | private void addStock() {
110 | final String symbol = newSymbolTextBox.getText().toUpperCase().trim();
111 | newSymbolTextBox.setFocus(true);
112 |
113 | // Stock code must be between 1 and 10 chars that are numbers, letters, or dots.
114 | if (!symbol.matches("^[0-9a-zA-Z\\.]{1,10}$")) {
115 | Window.alert("'" + symbol + "' is not a valid symbol.");
116 | newSymbolTextBox.selectAll();
117 | return;
118 | }
119 |
120 | newSymbolTextBox.setText("");
121 |
122 | // Don't add the stock if it's already in the table.
123 | if (stocks.contains(symbol))
124 | return;
125 |
126 | // Add the stock to the table.
127 | int row = stocksFlexTable.getRowCount();
128 | stocks.add(symbol);
129 | stocksFlexTable.setText(row, 0, symbol);
130 | stocksFlexTable.setWidget(row, 2, new Label());
131 | stocksFlexTable.getCellFormatter().addStyleName(row, 1, "watchListNumericColumn");
132 | stocksFlexTable.getCellFormatter().addStyleName(row, 2, "watchListNumericColumn");
133 | stocksFlexTable.getCellFormatter().addStyleName(row, 3, "watchListRemoveColumn");
134 |
135 | // Add a button to remove this stock from the table.
136 | Button removeStockButton = new Button("x");
137 | removeStockButton.addStyleDependentName("remove");
138 | removeStockButton.addClickHandler(new ClickHandler() {
139 | public void onClick(ClickEvent event) {
140 | int removedIndex = stocks.indexOf(symbol);
141 | stocks.remove(removedIndex);
142 | stocksFlexTable.removeRow(removedIndex + 1);
143 | }
144 | });
145 | stocksFlexTable.setWidget(row, 3, removeStockButton);
146 |
147 | // Get the stock price.
148 | refreshWatchList();
149 |
150 | }
151 |
152 |
153 | boolean serverAvailable = true;
154 |
155 | void refreshWatchList() {
156 | if (serverAvailable) {
157 | refreshWatchListServer();
158 | } else {
159 | refreshWatchListClient();
160 | }
161 | }
162 |
163 | /**
164 | * Generate random stock prices.
165 | */
166 | private void refreshWatchListClient() {
167 | final double MAX_PRICE = 100.0; // $100.00
168 | final double MAX_PRICE_CHANGE = 0.02; // +/- 2%
169 |
170 | StockPrice[] prices = new StockPrice[stocks.size()];
171 | for (int i = 0; i < stocks.size(); i++) {
172 | double price = Random.nextDouble() * MAX_PRICE;
173 | double change = price * MAX_PRICE_CHANGE
174 | * (Random.nextDouble() * 2.0 - 1.0);
175 |
176 | prices[i] = new StockPrice(stocks.get(i), price, change);
177 | }
178 |
179 | updateTable(prices);
180 | }
181 |
182 | private void refreshWatchListServer() {
183 | // Initialize the service proxy.
184 | if (stockPriceSvc == null) {
185 | stockPriceSvc = GWT.create(StockPriceService.class);
186 | }
187 |
188 | // Set up the callback object.
189 | AsyncCallback callback = new AsyncCallback() {
190 | public void onFailure(Throwable caught) {
191 | // If the stock code is in the list of delisted codes, display an error message.
192 | String details = caught.getMessage();
193 | if (caught instanceof DelistedException) {
194 | details = "Company '" + ((DelistedException)caught).getSymbol() + "' was delisted";
195 | errorMsgLabel.setText("Error: " + details);
196 | errorMsgLabel.setVisible(true);
197 | } else {
198 | serverAvailable = false;
199 | }
200 | }
201 |
202 | public void onSuccess(StockPrice[] result) {
203 | updateTable(result);
204 | }
205 | };
206 |
207 | // Make the call to the stock price service.
208 | stockPriceSvc.getPrices(stocks.toArray(new String[0]), callback);
209 | }
210 |
211 | /**
212 | * Update the Price and Change fields all the rows in the stock table.
213 | *
214 | * @param prices Stock data for all rows.
215 | */
216 | private void updateTable(StockPrice[] prices) {
217 | for (int i = 0; i < prices.length; i++) {
218 | updateTable(prices[i]);
219 | }
220 |
221 | // Display timestamp showing last refresh.
222 | lastUpdatedLabel.setText("Last update : "
223 | + DateTimeFormat.getMediumDateTimeFormat().format(new Date()));
224 |
225 | // Clear any errors.
226 | errorMsgLabel.setVisible(false);
227 | }
228 |
229 | /**
230 | * Update a single row in the stock table.
231 | *
232 | * @param price Stock data for a single row.
233 | */
234 | private void updateTable(StockPrice price) {
235 | // Make sure the stock is still in the stock table.
236 | if (!stocks.contains(price.getSymbol())) {
237 | return;
238 | }
239 |
240 | int row = stocks.indexOf(price.getSymbol()) + 1;
241 |
242 | // Format the data in the Price and Change fields.
243 | String priceText = NumberFormat.getFormat("#,##0.00").format(
244 | price.getPrice());
245 | NumberFormat changeFormat = NumberFormat.getFormat("+#,##0.00;-#,##0.00");
246 | String changeText = changeFormat.format(price.getChange());
247 | String changePercentText = changeFormat.format(price.getChangePercent());
248 |
249 | // Populate the Price and Change fields with new data.
250 | stocksFlexTable.setText(row, 1, priceText);
251 | Label changeWidget = (Label)stocksFlexTable.getWidget(row, 2);
252 | changeWidget.setText(changeText + " (" + changePercentText + "%)");
253 |
254 | // Change the color of text in the Change field based on its value.
255 | String changeStyleName = "noChange";
256 | if (price.getChangePercent() < -0.1f) {
257 | changeStyleName = "negativeChange";
258 | }
259 | else if (price.getChangePercent() > 0.1f) {
260 | changeStyleName = "positiveChange";
261 | }
262 |
263 | changeWidget.setStyleName(changeStyleName);
264 | }
265 |
266 | }
267 |
--------------------------------------------------------------------------------
/war/StockWatcher.css:
--------------------------------------------------------------------------------
1 | /* Formatting specific to the StockWatcher application */
2 |
3 | body {
4 | padding: 30px !important;
5 | }
6 |
7 | /* stock list header row */
8 | .watchListHeader {
9 | background-color: #2062B8;
10 | color: white;
11 | font-style: italic;
12 | }
13 |
14 | /* stock list flex table */
15 | .watchList {
16 | border: 1px solid silver;
17 | padding: 2px;
18 | margin-bottom:6px;
19 | }
20 |
21 | /* stock list Price and Change fields */
22 | .watchListNumericColumn {
23 | text-align: right;
24 | width:8em;
25 | }
26 |
27 | /* stock list Remove column */
28 | .watchListRemoveColumn {
29 | text-align: center;
30 | }
31 |
32 | /* Add Stock panel */
33 | .addPanel {
34 | margin: 10px 0px 15px 0px;
35 | }
36 |
37 | /* stock list, the Remove button */
38 | .gwt-Button-remove {
39 | width: 50px;
40 | }
41 |
42 | /* Dynamic color changes for the Change field */
43 | .noChange {
44 | color: black;
45 | }
46 |
47 | .positiveChange {
48 | color: green;
49 | }
50 |
51 | .negativeChange {
52 | color: red;
53 | }
54 |
55 | .errorMessage {
56 | color: red;
57 | }
--------------------------------------------------------------------------------
/war/StockWatcher.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | StockWatcher
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
StockWatcher
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/war/WEB-INF/web.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 | StockWatcher.html
11 |
12 |
13 |
14 |
15 | stockPriceServiceImpl
16 | com.google.gwt.sample.stockwatcher.server.StockPriceServiceImpl
17 |
18 |
19 |
20 | stockPriceServiceImpl
21 | /stockwatcher/stockPrices
22 |
23 |
24 |
--------------------------------------------------------------------------------
/war/images/gwt.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/manolo/gwt-stockwatcher/7861b6816197ca330e15895b422b1048a003986a/war/images/gwt.png
--------------------------------------------------------------------------------