/* Copyright (c) 2008 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 sample.spreadsheet.cell; import sample.util.SimpleCommandLineParser; import com.google.gdata.client.spreadsheet.CellQuery; import com.google.gdata.client.spreadsheet.FeedURLFactory; import com.google.gdata.client.spreadsheet.SpreadsheetService; import com.google.gdata.data.BaseEntry; import com.google.gdata.data.Link; import com.google.gdata.data.batch.BatchOperationType; import com.google.gdata.data.batch.BatchStatus; import com.google.gdata.data.batch.BatchUtils; import com.google.gdata.data.spreadsheet.CellEntry; import com.google.gdata.data.spreadsheet.CellFeed; import com.google.gdata.data.spreadsheet.SpreadsheetEntry; import com.google.gdata.data.spreadsheet.SpreadsheetFeed; import com.google.gdata.data.spreadsheet.WorksheetEntry; import com.google.gdata.util.AuthenticationException; import com.google.gdata.util.ServiceException; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintStream; import java.net.URL; import java.util.List; /** * Using this demo, you can see how GData can read and write to individual cells * based on their position or send a batch of update commands in one HTTP * request. * * Usage: java CellDemo --username [user] --password [pass] */ public class CellDemo { /** The message for displaying the usage parameters. */ private static final String[] USAGE_MESSAGE = { "Usage: java CellDemo --username [user] --password [pass] ", ""}; /** Welcome message, introducing the program. */ private static final String[] WELCOME_MESSAGE = { "This is a demo of the cells feed!", "", "Using this interface, you can read/write to your spreadsheet's cells.", ""}; /** Help on all available commands. */ private static final String[] COMMAND_HELP_MESSAGE = { "Commands:", " load " + "[[select a spreadsheet and worksheet]]", " list [[shows all cells]]", " range minRow maxRow minCol maxCol [[rectangle]]", " set row# col# formula [[sets a cell]]", " example: set 1 3 =R1C2+1", " search adam [[full text query]]", " batch [[batch request]]", " exit"}; /** Our view of Google Spreadsheets as an authenticated Google user. */ private SpreadsheetService service; /** The URL of the cells feed. */ private URL cellFeedUrl; /** The output stream. */ private PrintStream out; /** A factory that generates the appropriate feed URLs. */ private FeedURLFactory factory; /** * Constructs a cell demo from the specified spreadsheet service, which is * used to authenticate to and access Google Spreadsheets. * * @param service the connection to the Google Spradsheets service. * @param outputStream a handle for stdout. */ public CellDemo(SpreadsheetService service, PrintStream outputStream) { this.service = service; this.out = outputStream; this.factory = FeedURLFactory.getDefault(); } /** * Log in to Google, under the Google Spreadsheets account. * * @param username name of user to authenticate (e.g. yourname@gmail.com) * @param password password to use for authentication * @throws AuthenticationException if the service is unable to validate the * username and password. */ public void login(String username, String password) throws AuthenticationException { // Authenticate service.setUserCredentials(username, password); } /** * Displays the given list of entries and prompts the user to select the index * of one of the entries. NOTE: The displayed index is 1-based and is * converted to 0-based before being returned. * * @param reader to read input from the keyboard * @param entries the list of entries to display * @param type describes the type of things the list contains * @return the 0-based index of the user's selection * @throws IOException if an I/O error occurs while getting input from user */ private int getIndexFromUser(BufferedReader reader, List entries, String type) throws IOException { for (int i = 0; i < entries.size(); i++) { BaseEntry entry = (BaseEntry) entries.get(i); System.out.println("\t(" + (i + 1) + ") " + entry.getTitle().getPlainText()); } int index = -1; while (true) { out.print("Enter the number of the spreadsheet to load: "); String userInput = reader.readLine(); try { index = Integer.parseInt(userInput); if (index < 1 || index > entries.size()) { throw new NumberFormatException(); } break; } catch (NumberFormatException e) { System.out.println("Please enter a valid number for your selection."); } } return index - 1; } /** * Uses the user's credentials to get a list of spreadsheets. Then asks the * user which spreadsheet to load. If the selected spreadsheet has multiple * worksheets then the user will also be prompted to select what sheet to use. * * @param reader to read input from the keyboard * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. * */ public void loadSheet(BufferedReader reader) throws IOException, ServiceException { // Get the spreadsheet to load SpreadsheetFeed feed = service.getFeed(factory.getSpreadsheetsFeedUrl(), SpreadsheetFeed.class); List spreadsheets = feed.getEntries(); int spreadsheetIndex = getIndexFromUser(reader, spreadsheets, "spreadsheet"); SpreadsheetEntry spreadsheet = feed.getEntries().get(spreadsheetIndex); // Get the worksheet to load if (spreadsheet.getWorksheets().size() == 1) { cellFeedUrl = spreadsheet.getWorksheets().get(0).getCellFeedUrl(); } else { List worksheets = spreadsheet.getWorksheets(); int worksheetIndex = getIndexFromUser(reader, worksheets, "worksheet"); WorksheetEntry worksheet = (WorksheetEntry) worksheets .get(worksheetIndex); cellFeedUrl = worksheet.getCellFeedUrl(); } System.out.println("Sheet loaded."); } /** * Sets the particular cell at row, col to the specified formula or value. * * @param row the row number, starting with 1 * @param col the column number, starting with 1 * @param formulaOrValue the value if it doesn't start with an '=' sign; if it * is a formula, be careful that cells are specified in R1C1 format * instead of A1 format. * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. */ public void setCell(int row, int col, String formulaOrValue) throws IOException, ServiceException { CellEntry newEntry = new CellEntry(row, col, formulaOrValue); service.insert(cellFeedUrl, newEntry); out.println("Added!"); } /** * Prints out the specified cell. * * @param cell the cell to print */ public void printCell(CellEntry cell) { String shortId = cell.getId().substring(cell.getId().lastIndexOf('/') + 1); out.println(" -- Cell(" + shortId + "/" + cell.getTitle().getPlainText() + ") formula(" + cell.getCell().getInputValue() + ") numeric(" + cell.getCell().getNumericValue() + ") value(" + cell.getCell().getValue() + ")"); } /** * Shows all cells that are in the spreadsheet. * * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. */ public void showAllCells() throws IOException, ServiceException { CellFeed feed = service.getFeed(cellFeedUrl, CellFeed.class); for (CellEntry entry : feed.getEntries()) { printCell(entry); } } /** * Shows a particular range of cells, limited by minimum/maximum rows and * columns. * * @param minRow the minimum row, inclusive, 1-based * @param maxRow the maximum row, inclusive, 1-based * @param minCol the minimum column, inclusive, 1-based * @param maxCol the maximum column, inclusive, 1-based * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. */ public void showRange(int minRow, int maxRow, int minCol, int maxCol) throws IOException, ServiceException { CellQuery query = new CellQuery(cellFeedUrl); query.setMinimumRow(minRow); query.setMaximumRow(maxRow); query.setMinimumCol(minCol); query.setMaximumCol(maxCol); CellFeed feed = service.query(query, CellFeed.class); for (CellEntry entry : feed.getEntries()) { printCell(entry); } } /** * Performs a full-text search on cells. * * @param fullTextSearchString a full text search string, with space-separated * keywords * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. */ public void search(String fullTextSearchString) throws IOException, ServiceException { CellQuery query = new CellQuery(cellFeedUrl); query.setFullTextQuery(fullTextSearchString); CellFeed feed = service.query(query, CellFeed.class); out.println("Results for [" + fullTextSearchString + "]"); for (CellEntry entry : feed.getEntries()) { printCell(entry); } } /** * Writes (to stdout) a list of the entries in the batch request in a human * readable format. * * @param batchRequest the CellFeed containing entries to display. */ private void printBatchRequest(CellFeed batchRequest) { System.out.println("Current operations in batch"); for (CellEntry entry : batchRequest.getEntries()) { String msg = "\tID: " + BatchUtils.getBatchId(entry) + " - " + BatchUtils.getBatchOperationType(entry) + " row: " + entry.getCell().getRow() + " col: " + entry.getCell().getCol() + " value: " + entry.getCell().getInputValue(); System.out.println(msg); } } /** * Returns a CellEntry with batch id and operation type that will tell the * server to update the specified cell with the given value. The entry is * fetched from the server in order to get the current edit link (for * optimistic concurrency). * * @param row the row number of the cell to operate on * @param col the column number of the cell to operate on * @param value the value to set in case of an update the cell to operate on * * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. */ private CellEntry createUpdateOperation(int row, int col, String value) throws ServiceException, IOException { String batchId = "R" + row + "C" + col; URL entryUrl = new URL(cellFeedUrl.toString() + "/" + batchId); CellEntry entry = service.getEntry(entryUrl, CellEntry.class); entry.changeInputValueLocal(value); BatchUtils.setBatchId(entry, batchId); BatchUtils.setBatchOperationType(entry, BatchOperationType.UPDATE); return entry; } /** * Prompts the user for a set of operations and submits them in a batch * request. * * @param reader to read input from the keyboard. * * @throws ServiceException when the request causes an error in the Google * Spreadsheets service. * @throws IOException when an error occurs in communication with the Google * Spreadsheets service. */ public void processBatchRequest(BufferedReader reader) throws IOException, ServiceException { final String BATCH_PROMPT = "Enter set operations one by one, " + "then enter submit to send the batch request:\n" + " set row# col# value [[add a set operation]]\n" + " submit [[submit the request]]"; CellFeed batchRequest = new CellFeed(); // Prompt user for operation System.out.println(BATCH_PROMPT); String operation = reader.readLine(); while (!operation.startsWith("submit")) { String[] s = operation.split(" ", 4); if (s.length != 4 || !s[0].equals("set")) { System.out.println("Invalid command: " + operation); operation = reader.readLine(); continue; } // Create a new cell entry and add it to the batch request. int row = Integer.parseInt(s[1]); int col = Integer.parseInt(s[2]); String value = s[3]; CellEntry batchOperation = createUpdateOperation(row, col, value); batchRequest.getEntries().add(batchOperation); // Display the current entries in the batch request. printBatchRequest(batchRequest); // Prompt for another operation. System.out.println(BATCH_PROMPT); operation = reader.readLine(); } // Get the batch feed URL and submit the batch request CellFeed feed = service.getFeed(cellFeedUrl, CellFeed.class); Link batchLink = feed.getLink(Link.Rel.FEED_BATCH, Link.Type.ATOM); URL batchUrl = new URL(batchLink.getHref()); CellFeed batchResponse = service.batch(batchUrl, batchRequest); // Print any errors that may have happened. boolean isSuccess = true; for (CellEntry entry : batchResponse.getEntries()) { String batchId = BatchUtils.getBatchId(entry); if (!BatchUtils.isSuccess(entry)) { isSuccess = false; BatchStatus status = BatchUtils.getBatchStatus(entry); System.out.println("\n" + batchId + " failed (" + status.getReason() + ") " + status.getContent()); } } if (isSuccess) { System.out.println("Batch operations successful."); } } /** * Reads and executes one command. * * @param reader to read input from the keyboard * @return false if the user quits, true on exception */ public boolean executeCommand(BufferedReader reader) { for (String s : COMMAND_HELP_MESSAGE) { out.println(s); } System.err.print("Command: "); try { String command = reader.readLine(); String[] parts = command.trim().split(" ", 2); String name = parts[0]; String parameters = parts.length > 1 ? parts[1] : ""; if (name.equals("list")) { showAllCells(); } else if (name.equals("load")) { loadSheet(reader); } else if (name.equals("search")) { search(parameters); } else if (name.equals("range")) { String[] s = parameters.split(" ", 4); showRange(Integer.parseInt(s[0]), Integer.parseInt(s[1]), Integer .parseInt(s[2]), Integer.parseInt(s[3])); } else if (name.equals("set")) { String[] s = parameters.split(" ", 3); setCell(Integer.parseInt(s[0]), Integer.parseInt(s[1]), s[2]); } else if (name.equals("batch")) { processBatchRequest(reader); } else if (name.startsWith("q") || name.startsWith("exit")) { return false; } else { out.println("Unknown command."); } } catch (Exception e) { // Show *exactly* what went wrong. e.printStackTrace(); } return true; } /** * Starts up the demo and prompts for commands. * * @param username name of user to authenticate (e.g. yourname@gmail.com) * @param password password to use for authentication * @throws AuthenticationException if the service is unable to validate the * username and password. */ public void run(String username, String password) throws AuthenticationException { for (String s : WELCOME_MESSAGE) { out.println(s); } BufferedReader reader = new BufferedReader( new InputStreamReader(System.in)); // Login and prompt the user to pick a sheet to use. login(username, password); try { loadSheet(reader); } catch (IOException e) { e.printStackTrace(); } catch (ServiceException e) { e.printStackTrace(); } while (executeCommand(reader)) { } } /** * Runs the demo. * * @param args the command-line arguments * @throws AuthenticationException if the service is unable to validate the * username and password. */ public static void main(String[] args) throws AuthenticationException { SimpleCommandLineParser parser = new SimpleCommandLineParser(args); String username = parser.getValue("username", "user", "u"); String password = parser.getValue("password", "pass", "p"); boolean help = parser.containsKey("help", "h"); if (help || username == null || password == null) { usage(); System.exit(1); } CellDemo demo = new CellDemo(new SpreadsheetService("Cell Demo"), System.out); demo.run(username, password); } /** * Prints out the usage. */ private static void usage() { for (String s : USAGE_MESSAGE) { System.out.println(s); } for (String s : WELCOME_MESSAGE) { System.out.println(s); } } }