2 * Copyright (C) 2010 Prasanta Paul, http://prasanta-paul.blogspot.com
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
19 import java.util.ArrayList;
\r
20 import java.util.HashMap;
\r
22 import com.pras.conn.HttpConHandler;
\r
23 import com.pras.conn.Response;
\r
24 import com.pras.sp.Entry;
\r
25 import com.pras.sp.Feed;
\r
26 import com.pras.sp.ParseFeed;
\r
27 import com.pras.table.Table;
\r
30 * This class represents a given Spreadsheet. You can add multiple WorkSheets into a Spreadsheet.
\r
31 * It provides methods to Add/Retrieve/Delete WorkSheet and Share SpreadSheet.
\r
33 * <br/>API Ref:<br/>
\r
34 * <a href="http://code.google.com/apis/spreadsheets/data/3.0/developers_guide.html">http://code.google.com/apis/spreadsheets/data/3.0/developers_guide.html</a>
\r
38 * <a href="http://code.google.com/apis/documents/docs/3.0/reference.html">http://code.google.com/apis/documents/docs/3.0/reference.html</a>
\r
42 * <i>Resource ID is required to delete a SpreadSheet</i>
\r
44 * @author Prasanta Paul
\r
46 public class SpreadSheet {
\r
49 * How to get Resource_ID ?
\r
50 * From SpreadSheet Feed (SpreadSheet List) read the "key" of the following field
\r
51 * <link rel='alternate' type='text/html' href='https://spreadsheets.google.com/ccc?key=0Asn_4k-vXoTXdHhVcUIyeDZwV3VWREdMZll5RTJTMmc'/>
\r
53 private String TAG = "SpreadSheet";
\r
55 * This will contain info specific to SpreadSheet and not about its
\r
59 ArrayList<WorkSheet> wks = new ArrayList<WorkSheet>();
\r
61 * List of People with whom this SpreadSheet is shared and their
\r
62 * access rights (ACL)
\r
64 ArrayList<Collaborator> collaborators = new ArrayList<Collaborator>();
\r
68 public boolean equals(Object obj) {
\r
71 if(obj instanceof SpreadSheet){
\r
72 SpreadSheet s = (SpreadSheet) obj;
\r
73 if(this.hashCode() == s.hashCode())
\r
77 //return super.equals(obj);
\r
81 public int hashCode() {
\r
83 * ResID will be unique for each SpreadSheet
\r
85 * spreadsheet:0Asn_4k-vXoTXdHhVcUIyeDZwV3VWREdMZll5RTJTMmc
\r
88 return entry.getResID().hashCode();
\r
90 return super.hashCode();
\r
93 public SpreadSheet(Entry entry){
\r
98 * Get Entry Object of SpreadSheet Feed. Entry holds all SpreadSheet Feed details
\r
102 public Entry getEntry() {
\r
107 * Set Entry Object of SpreadSheet Feed.
\r
110 public void setEntry(Entry entry) {
\r
111 this.entry = entry;
\r
115 * Get SpreadSheet Title
\r
118 public String getTitle(){
\r
120 return entry.getTitle();
\r
125 * Get SpreadSheet Resource ID. Each SpreadSheet has an unique Resource ID.
\r
126 * This ID is required to Delete this SpreadSheet.
\r
130 public String getResourceID(){
\r
132 return entry.getResID();
\r
137 * Get SpreadSheet Feed Key
\r
140 public String getKey(){
\r
142 return entry.getKey();
\r
146 * Get the list of all Collaborator details (users/groups and their access rights) for a given SpreadSheet
\r
150 public ArrayList<Collaborator> getCollaborators() {
\r
151 return collaborators;
\r
155 * Add a Collaborator
\r
158 public void addCollaborator(Collaborator c){
\r
159 collaborators.add(c);
\r
163 * Clear existing list of Collaborators
\r
165 public void clearCollaboratorList(){
\r
166 collaborators.clear();
\r
170 * Set a list of Collaborators
\r
171 * @param collaborators
\r
173 public void setCollaborators(ArrayList<Collaborator> collaborators) {
\r
174 this.collaborators = collaborators;
\r
178 * This will create an WorkSheet without Table. It will create WorkSheet with 1 Column and 1 Row. It can
\r
179 * be accessed/modified by Web Interface.
\r
181 * <b>IMPORTANT:</b>
\r
182 * This Library supports Record Handling only through Table. If you use this method, it will not
\r
183 * create Table internally and thus unable to add/edit/delete record from this WorkSheet. Instead,
\r
184 * use addWorkSheet(String name, String[] cols)
\r
186 * @param name WorkSheet name
\r
188 public void addWorkSheet(String name){
\r
191 throw new IllegalAccessError("Please provide WorkSheet Name");
\r
193 addWorkSheet(name, 1, 1);
\r
197 * Create WorkSheet.
\r
199 * @param name name of WorkSheet
\r
200 * @param col number of columns
\r
201 * @param row number of rows
\r
203 private WorkSheet addWorkSheet(String name, int col, int row){
\r
204 // Sample URL: https://spreadsheets.google.com/feeds/worksheets/key/private/full
\r
205 String workSheetURL = "https://spreadsheets.google.com/feeds/worksheets/"+ entry.getKey() +"/private/full";
\r
208 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
209 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
210 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
211 httpHeaders.put(HttpConHandler.CONTENT_TYPE_HTTP_HEADER, "application/atom+xml");
\r
213 String postData = "<entry xmlns=\"http://www.w3.org/2005/Atom\""+
\r
214 " xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\">"+
\r
215 "<title>"+ name +"</title>"+
\r
216 "<gs:rowCount>"+ row +"</gs:rowCount>"+
\r
217 "<gs:colCount>"+ col +"</gs:colCount>"+
\r
219 Log.p(TAG, "POST Data- "+ postData);
\r
220 HttpConHandler http = new HttpConHandler();
\r
221 Response resp = http.doConnect(workSheetURL, HttpConHandler.HTTP_POST, httpHeaders, postData);
\r
225 // Create WorkSheet instance from the response
\r
226 return parseWorkSheet(resp.getOutput());
\r
229 private WorkSheet parseWorkSheet(String xmlFeed)
\r
231 if(xmlFeed == null)
\r
235 ParseFeed pf = new ParseFeed();
\r
236 Feed f = pf.parse(xmlFeed.getBytes());
\r
237 ArrayList<Entry> entries = f.getEntries();
\r
239 if(entries == null || entries.size() == 0){
\r
243 WorkSheet ws = new WorkSheet();
\r
245 for(int i=0; i<entries.size(); i++){
\r
246 Entry e = entries.get(i);
\r
248 ws.setWorkSheetID(e.getKey());
\r
249 ws.setWorkSheetURL(e.getWorkSheetURL());
\r
250 ws.setTitle(e.getTitle());
\r
251 ws.setColCount(e.getColCount());
\r
252 ws.setRowCount(e.getRowCount());
\r
259 * This will create an Internal Table and manage WorkSheet data in that Table.
\r
260 * This Lib supports only Table based record Add/Edit/Delete
\r
263 * @param name name of WorkSheet
\r
264 * @param cols name of Columns
\r
266 public void addWorkSheet(String name, String[] cols){
\r
268 if(cols == null || cols.length == 0){
\r
269 throw new IllegalAccessError("Please provide column name");
\r
276 * If I use row count as 1, it gives HTTP 403 error while adding Record
\r
277 * Error Msg: It looks like someone else already deleted this cell.
\r
279 addWorkSheet(name, name, 2, cols);
\r
283 * Add WorkSheet <br/>
\r
284 * It will create a Table for this WorkSheet. WorkSheet and Table Name will be the same
\r
285 * The Table will be used for Add/Edit/Delete Records
\r
288 * @param name Name of the WorkSheet
\r
289 * @param description Description of the WorkSheet
\r
290 * @param row Number of Rows
\r
291 * @param cols Name of Columns
\r
293 public void addWorkSheet(String name, String description, int row, String[] cols){
\r
295 if(name == null || description == null || cols == null){
\r
296 throw new IllegalAccessError("Please provide correct input parameters");
\r
299 int col = cols.length;
\r
301 addWorkSheet(name, col, row);
\r
303 // Create a Table for this WorkSheet
\r
304 String tableCreateXML = "<entry xmlns='http://www.w3.org/2005/Atom' xmlns:gs='http://schemas.google.com/spreadsheets/2006'>"+
\r
305 "<title type='text'>"+ name +"</title>"+ // Table name will be same as WorkSheet Name
\r
306 "<summary type='text'>"+ description +"</summary>"+
\r
307 "<gs:worksheet name='"+ name +"'/>"+
\r
308 "<gs:header row='1' />"+
\r
309 "<gs:data numRows='0' startRow='2'>";
\r
310 for(int i=0; i<cols.length; i++){
\r
311 // "index" attributes are mandatory
\r
312 tableCreateXML = tableCreateXML.concat("<gs:column index='"+ (i+1) +"' name='"+ cols[i] +"' />");
\r
314 tableCreateXML = tableCreateXML.concat("</gs:data></entry>");
\r
317 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
318 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
319 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
320 httpHeaders.put(HttpConHandler.CONTENT_TYPE_HTTP_HEADER, "application/atom+xml");
\r
323 String tableURL = "https://spreadsheets.google.com/feeds/"+ entry.getKey() +"/tables";
\r
324 HttpConHandler http = new HttpConHandler();
\r
325 http.doConnect(tableURL, HttpConHandler.HTTP_POST, httpHeaders, tableCreateXML);
\r
330 * Create List feed based WorkSheet
\r
332 * @param name Name of the WorkSheet
\r
333 * @param rowCount Number of row. This doesn't limit future row addition.
\r
334 * @param cols Array of column name. First row of the WorkSheet is header row.
\r
338 public WorkSheet addListWorkSheet(String name, int rowCount, String[] cols){
\r
339 if(name == null || cols == null){
\r
340 throw new IllegalAccessError("Please provide correct input parameters");
\r
345 * 1. Create an empty Worksheet with specified number of Rows and Columns
\r
346 * 2. Create the Header Row
\r
349 Log.p(TAG, "## Create WorkSheet...");
\r
350 WorkSheet workSheet = addWorkSheet(name, cols.length, rowCount);
\r
352 if(workSheet == null)
\r
355 workSheet.setColumns(cols);
\r
357 // 2. Create Header Row
\r
359 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
360 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
361 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
362 httpHeaders.put(HttpConHandler.CONTENT_TYPE_HTTP_HEADER, "application/atom+xml");
\r
363 // Ignore updates done by other on the Header Row
\r
364 httpHeaders.put("If-Match", "*");
\r
366 // XML to Add HEADER ROW
\r
367 StringBuffer cellUpdateXML = new StringBuffer();
\r
368 // Batch Cell Update
\r
369 // XML- Batch query for multiple Cell Update
\r
370 cellUpdateXML.append("<feed xmlns=\"http://www.w3.org/2005/Atom\" xmlns:batch=\"http://schemas.google.com/gdata/batch\" xmlns:gs=\"http://schemas.google.com/spreadsheets/2006\">");
\r
371 cellUpdateXML.append("<id>https://spreadsheets.google.com/feeds/cells/"+ entry.getKey() +"/"+ workSheet.getWorkSheetID() +"/private/full</id>");
\r
373 for(int i=0; i<cols.length; i++)
\r
375 // Column Name should be lower_case and without space and special chars- Google SP API has limitations
\r
376 // One entry per column
\r
377 cellUpdateXML.append("<entry>");
\r
378 cellUpdateXML.append("<batch:id>B"+ i +"</batch:id>");
\r
379 cellUpdateXML.append("<batch:operation type=\"update\"/>");
\r
380 cellUpdateXML.append("<id>https://spreadsheets.google.com/feeds/cells/"+ entry.getKey() + "/"+ workSheet.getWorkSheetID() +"/private/full/R1C"+ (i+1) +"</id>");
\r
381 cellUpdateXML.append("<link rel=\"edit\" type=\"application/atom+xml\"");
\r
382 cellUpdateXML.append(" href=\"https://spreadsheets.google.com/feeds/cells/"+ entry.getKey() + "/"+ workSheet.getWorkSheetID() +"/private/full/R1C"+ (i+1) +"\"/>");
\r
383 cellUpdateXML.append("<gs:cell row=\"1\" col=\""+ (i+1) +"\" inputValue=\""+ cols[i].toLowerCase() +"\"/>");
\r
384 cellUpdateXML.append("</entry>");
\r
386 cellUpdateXML.append("</feed>");
\r
388 // Do Server transaction
\r
389 String headerRowUpdateUrl = "https://spreadsheets.google.com/feeds/cells/"+ entry.getKey() + "/"+ workSheet.getWorkSheetID() +"/private/full/batch";
\r
390 new HttpConHandler().doConnect(headerRowUpdateUrl, HttpConHandler.HTTP_POST, httpHeaders, cellUpdateXML.toString());
\r
396 * It will retrieve all WorkSheets of this SpreadSheet from Server
\r
400 public ArrayList<WorkSheet> getAllWorkSheets(){
\r
401 return getAllWorkSheets(true);
\r
405 * It will retrieve all WorkSheets of this SpreadSheet either from Server or from Local Cache
\r
407 * @param doRefresh Do you want to Synch with Server ?
\r
408 * @return list of available WorkSheets
\r
410 public ArrayList<WorkSheet> getAllWorkSheets(boolean doRefresh){
\r
411 return getAllWorkSheets(doRefresh, null, false);
\r
415 * It will retrieve WorkSheets with matching title from Server
\r
417 * @param title SpreadSheet title. <b>null</b> means all SpreadSheets. No need to do URL encode.
\r
418 * @param isTitleExact whether title string should be an exact match
\r
421 public ArrayList<WorkSheet> getWorkSheet(String title, boolean isTitleExact){
\r
422 return getAllWorkSheets(true, title, isTitleExact);
\r
426 * It will retrieve WorkSheets with matching title either from Server or from Local Cache
\r
428 * @param doRefresh Do you want to Synch with Server ?
\r
429 * @param title SpreadSheet title. <b>null</b> means all SpreadSheets. No need to do URL encode.
\r
430 * @param isTitleExact Whether title string should be an exact match
\r
431 * @return list of available WorkSheets
\r
433 public ArrayList<WorkSheet> getAllWorkSheets(boolean doRefresh, String title, boolean isTitleExact){
\r
434 // Sample URL: https://spreadsheets.google.com/feeds/worksheets/key/private/full
\r
437 * Check with other projection values instead of "private" and "full"
\r
438 * private - unpublished work sheets
\r
439 * public- published work sheets
\r
443 // Don't synch with Server
\r
447 String workSheetListURL = "https://spreadsheets.google.com/feeds/worksheets/"+ entry.getKey() +"/private/full";
\r
450 // WorkSheet Query Parameters
\r
451 workSheetListURL = workSheetListURL.concat("?title="+ HttpConHandler.encode(title));
\r
452 workSheetListURL = workSheetListURL.concat("&title-exact="+ isTitleExact);
\r
456 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
457 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
458 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
460 HttpConHandler http = new HttpConHandler();
\r
461 Response res = http.doConnect(workSheetListURL, HttpConHandler.HTTP_GET, httpHeaders, null);
\r
467 String xmlOut = res.getOutput();
\r
470 ParseFeed pf = new ParseFeed();
\r
471 Feed f = pf.parse(xmlOut.getBytes());
\r
472 ArrayList<Entry> entries = f.getEntries();
\r
474 HashMap<String, Table> tables = null;
\r
476 if(entries != null && entries.size() > 0){
\r
477 // Fetch Table details of each Work Sheet
\r
478 Log.p(TAG, "Get Table Feed");
\r
479 tables = getTables(title, isTitleExact);
\r
481 //No WorkSheet exists
\r
485 // clear existing entries
\r
488 for(int i=0; i<entries.size(); i++){
\r
489 Entry e = entries.get(i);
\r
491 WorkSheet ws = new WorkSheet();
\r
493 ws.setWorkSheetID(e.getKey());
\r
494 ws.setWorkSheetURL(e.getWorkSheetURL());
\r
495 ws.setTitle(e.getTitle());
\r
496 ws.setColCount(e.getColCount());
\r
497 ws.setRowCount(e.getRowCount());
\r
500 // Table name and WorkSheet name will be same
\r
501 if(tables != null){
\r
502 Table table = tables.get(ws.getTitle());
\r
504 // This WorkSheet is Table Feed based
\r
505 ws.setTable(tables.get(ws.getTitle()));
\r
508 // This WorkSheet is List Feedback
\r
510 ws.setColumns(readHeaderRow(ws.getWorkSheetID()));
\r
520 * Read clumns of List Feed based WorkSheet. Columns are placed in Header Row.
\r
522 * @param workSheetID
\r
525 private String[] readHeaderRow(String workSheetID){
\r
526 // Read the first Row
\r
527 String headerRowURL = "https://spreadsheets.google.com/feeds/cells/"+ entry.getKey() +"/"+ workSheetID +"/private/full?min-row=1&max-row=1";
\r
529 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
530 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
531 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
533 HttpConHandler http = new HttpConHandler();
\r
534 Response res = http.doConnect(headerRowURL, HttpConHandler.HTTP_GET, httpHeaders, null);
\r
539 String xmlOut = res.getOutput();
\r
542 ParseFeed pf = new ParseFeed();
\r
543 Feed f = pf.parse(xmlOut.getBytes());
\r
544 ArrayList<Entry> entries = f.getEntries();
\r
546 if(entries == null)
\r
549 String[] cols = new String[entries.size()];
\r
551 for(int i=0; i<entries.size(); i++)
\r
553 Entry e = entries.get(i);
\r
554 WorkSheetCell cellInfo = e.getCellInfo();
\r
555 if(cellInfo != null)
\r
556 cols[i] = cellInfo.getName();
\r
562 * Get details of associated table. WorkSheet and Table Name will be the same
\r
564 * @param title SpreadSheet title. <b>null</b> means all SpreadSheets. No need to do URL encode.
\r
565 * @param isTitleExact whether title string should be an exact match
\r
569 private HashMap<String, Table> getTables(String title, boolean isTitleExact){
\r
571 //Get list of all Tables- one per WorkSheet
\r
572 HashMap<String, Table> tables = new HashMap<String, Table>();
\r
574 // Table URL- Get list of all Tables- one per WorkSheet
\r
575 String tableURL = "https://spreadsheets.google.com/feeds/"+ entry.getKey() +"/tables";
\r
578 tableURL = tableURL.concat("?title="+ HttpConHandler.encode(title));
\r
579 tableURL = tableURL.concat("&title-exact="+ isTitleExact);
\r
583 HashMap<String, String> httpHeaders = new HashMap<String, String>();
\r
584 httpHeaders.put(HttpConHandler.AUTHORIZATION_HTTP_HEADER, "GoogleLogin auth="+ SpreadSheetFactory.authToken);
\r
585 httpHeaders.put(HttpConHandler.GDATA_VERSION_HTTP_HEADER, "3.0");
\r
587 HttpConHandler http = new HttpConHandler();
\r
588 Response res = http.doConnect(tableURL, HttpConHandler.HTTP_GET, httpHeaders, null);
\r
594 String xmlOut = res.getOutput();
\r
597 ParseFeed pf = new ParseFeed();
\r
598 Feed f = pf.parse(xmlOut.getBytes());
\r
600 ArrayList<Entry> entries = f.getEntries();
\r
602 if(entries == null) // No Table exists for this WorkSheet/SpreadSheet
\r
605 for(int i=0; i<entries.size(); i++){
\r
606 Entry e = entries.get(i);
\r
607 Table t = new Table();
\r
608 t.setId(e.getKey());
\r
609 t.setName(e.getTitle());
\r
610 t.setDescription(e.getSummary());
\r
611 t.setCols(e.getCols());
\r
612 // TODO: how to identify table of a WorkSheet
\r
613 t.setUrl("https://spreadsheets.google.com/feeds/"+ entry.getKey() +"/tables/"+ t.getId());
\r
615 Log.p(TAG, "Table Name: "+ t.getName());
\r
616 Log.p(TAG, "Table ID: "+ t.getId());
\r
617 Log.p(TAG, "Table URL: "+ t.getUrl());
\r
618 Log.p(TAG, "Number of Cols: "+ t.getCols().size());
\r
620 tables.put(e.getTitle(), t);
\r
627 * Delete this WorkSheet
\r
628 * @param wk WorkSheet to be deleted
\r
630 public void deleteWorkSheet(WorkSheet wk){
\r