├── .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 --------------------------------------------------------------------------------