/* 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 com.google.gdata.client.spreadsheet;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
/**
* Provides feed URLs that can be used with a Spreadsheets server.
*
*
*
*/
public class FeedURLFactory {
/**
* URL of the server to connect to by default.
* Currently {@value #DEFAULT_SPREADSHEETS_URL}.
*/
public static final String DEFAULT_SPREADSHEETS_URL =
"https://spreadsheets.google.com";
private static final String SPREADSHEETS_PATH =
"feeds/spreadsheets/private/full";
private static final String WORKSHEETS_PATH = "feeds/worksheets/";
private static final String LIST_PATH = "feeds/list/";
private static final String CELLS_PATH = "feeds/cells/";
private static final String TABLE_PATH = "/tables/";
private static final String RECORD_PATH = "/records/";
private static final String BASE_PATH = "feeds/";
/** The url used as a base when creating urls. */
private URL baseUrl;
private URL feedSpreadsheets;
private URL feedWorksheets;
private URL feedList;
private URL feedCells;
/**
* The default FeedURLFactory instance targeted
* to the default URL.
*/
private static final FeedURLFactory instance = new FeedURLFactory();
/**
* Gets the default instance of this factory, targeted
* to {@value #DEFAULT_SPREADSHEETS_URL}.
*
* @return the default FeedURLFactory
*/
public static FeedURLFactory getDefault() {
return instance;
}
/**
* Creates an URL factory targeted to {@value #DEFAULT_SPREADSHEETS_URL}.
*
* Access it using {@link #getDefault()}.
*/
private FeedURLFactory() {
try {
init(DEFAULT_SPREADSHEETS_URL);
} catch (MalformedURLException e) {
throw new RuntimeException("Unexpected malformed URL", e);
}
}
/**
* Creates an URL factory targeted to a server.
*
* As long as you don't need to connect to a nonstandard
* URL, different from {@value #DEFAULT_SPREADSHEETS_URL}, you should
* consider calling {@link #getDefault()} instead.
*
* @param url an URL used as a base for the generated URLs
* @throws MalformedURLException
*/
public FeedURLFactory(String url) throws MalformedURLException {
init(url);
}
private void init(String url) throws MalformedURLException {
if (!url.endsWith("/")) {
url += "/";
}
baseUrl = new URL(url);
feedSpreadsheets = new URL(baseUrl, SPREADSHEETS_PATH);
feedWorksheets = new URL(baseUrl, WORKSHEETS_PATH);
feedList = new URL(baseUrl, LIST_PATH);
feedCells = new URL(baseUrl, CELLS_PATH);
}
/** Returns the URL used as a base for the generated URLs. */
public URL getBaseUrl() {
return baseUrl;
}
/**
* Encodes a string using UTF-8.
*
* @param s string to be encoded
* @throws RuntimeException when the JVM does not support UTF-8
*/
private String encode(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("UTF-8 is not supported by the JVM", e);
}
}
/**
* Gets a URL you can use to get a SpreadsheetFeed of all your
* spreadsheets.
*/
public URL getSpreadsheetsFeedUrl() {
return feedSpreadsheets;
}
/**
* Creates a URL you can use to get a WorksheetFeed of all the
* worksheets within a spreadsheet.
*
* This requires the spreadsheet key, which can be obtained from the
* URL of the AJAX page through {@link #getSpreadsheetKeyFromUrl(String)},
* or via using the My Spreadsheets feed.
*
* @param spreadsheetKey a spreadsheet key, like o1123123.12312312
*/
public URL getWorksheetFeedUrl(String spreadsheetKey,
String visibility, String projection) throws MalformedURLException {
if (spreadsheetKey == null) {
throw new NullPointerException("spreadsheetKey is null");
}
return makeUrl(feedWorksheets, encode(spreadsheetKey),
visibility, projection);
}
/**
* Creates a url that you can use to get a Table Feed of all the tables
* within a spreadsheet.
* @param spreadsheetKey key of the workbook to get the table feed from.
* @throws MalformedURLException
*/
public URL getTableFeedUrl(String spreadsheetKey)
throws MalformedURLException {
if (spreadsheetKey == null) {
throw new NullPointerException("spreadsheetKey is null");
}
return new URL(baseUrl, BASE_PATH + encode(spreadsheetKey) + TABLE_PATH);
}
/**
* Creates a url that you can use to get a feed of records from a table.
* @param spreadsheetKey key of the workbook to get the table feed from.
* @param tableId id of the table to get a record feed from.
* @throws MalformedURLException
*/
public URL getRecordFeedUrl(String spreadsheetKey, String tableId)
throws MalformedURLException {
if (spreadsheetKey == null) {
throw new NullPointerException("spreadsheetKey is null");
}
return new URL(baseUrl, BASE_PATH + encode(spreadsheetKey) + RECORD_PATH
+ tableId);
}
/**
* Creates a URL you can use to get a ListFeed, which treats
* the spreadsheet as a list of rows.
*
* Like {@link #getWorksheetFeedUrl(String, String, String)}, this requires
* the spreadsheet key.
*
* This also requires the worksheet identifier. See the
* documentation on how worksheets can be identified.
*
* @param spreadsheetKey a spreadsheet key, like 01123123.12312312
* @param worksheetId a worksheet identifier or a 1-based positional indicator
*/
public URL getListFeedUrl(String spreadsheetKey, String worksheetId,
String visibility, String projection) throws MalformedURLException {
return makeUrl(feedList, spreadsheetKey, worksheetId,
visibility, projection);
}
/**
* Creates a URL you can use to get a CellFeed, which treats
* the spreadsheet like spatially oriented cells.
*
* Like {@link #getWorksheetFeedUrl(String, String, String)}, this requires
* the spreadsheet key.
*
* This also requires the worksheet identifier. See the
* documentation on how worksheets can be identified.
*
* @param spreadsheetKey a spreadsheet key, like 01123123.12312312
* @param worksheetId a worksheet identifier or a 1-based positional indicator
*/
public URL getCellFeedUrl(String spreadsheetKey, String worksheetId,
String visibility, String projection) throws MalformedURLException {
return makeUrl(feedCells, spreadsheetKey, worksheetId,
visibility, projection);
}
/**
* Creates a new URL by urlencoding the parameters and adding
* "spreadsheetKey/parentResourceId/visibility/projection"
* to the provided url.
*/
private URL makeUrl(URL url, String spreadsheetKey, String parentResourceId,
String visibility, String projection) throws MalformedURLException {
if (spreadsheetKey == null) {
throw new NullPointerException("spreadsheetKey is null");
}
if (parentResourceId == null) {
throw new NullPointerException("worksheetId is null");
}
String path = encode(spreadsheetKey) + "/" + encode(parentResourceId);
return makeUrl(url, path, visibility, projection);
}
/**
* Creates a new URL by urlencoding visibility, projection and adding
* "path/visibility/projection" to the provided url.
*/
private URL makeUrl(URL url, String path,
String visibility, String projection) throws MalformedURLException {
if (visibility == null) {
throw new NullPointerException("visibility is null");
}
if (projection == null) {
throw new NullPointerException("projection is null");
}
path = path + "/" + encode(visibility) + "/" + encode(projection);
return new URL(url, path);
}
/**
* Turns a Google Spreadsheets URL directly into the spreadsheet key.
*
* @param url a URL like
* http://abcd.spreadsheets.google.com/ccc?id=o1231.1231.1231.1231;
* if just the ID is specified, this will also work
* @return the spreadsheet key (o1231.1231)
* @throws IllegalArgumentException if the URL is not formatted correctly
*/
public static String getSpreadsheetKeyFromUrl(String url)
throws IllegalArgumentException {
// Make it a URL
URL urlAsUrl;
try {
urlAsUrl = new URL(url);
String query = urlAsUrl.getQuery();
if ( query != null ) {
String[] parts = query.split("&");
int offset = -1;
int numParts = 0;
String keyOrId = "";
for (String part : parts) {
if (part.startsWith("id=")) {
offset = ("id=").length();
keyOrId = part.substring(offset);
numParts = 4;
break;
} else if (part.startsWith("key=")) {
offset = ("key=").length();
keyOrId = part.substring(offset);
if (keyOrId.startsWith("p")) {
return keyOrId;
}
numParts = 2;
break;
}
}
if (offset > -1) {
String[] dottedParts = keyOrId.split("\\.");
if (dottedParts.length == numParts) {
return dottedParts[0] + "." + dottedParts[1];
}
}
}
} catch ( MalformedURLException e ) {
// This is not a URL, maybe it is just an id
String[] dottedParts = url.split("\\.");
if (dottedParts.length == 4 || dottedParts.length == 2) {
return dottedParts[0] + "." + dottedParts[1];
}
}
throw new IllegalArgumentException("Uknown URL format.");
}
}