package api.http.google.picasa;
import api.KeyValuePair;
import api.http.HttpRequest;
import api.http.google.GoogleAPI;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class PicasaAlbum{
/**
* the access right of public Picasa Album object for all.
*/
public static final String ACCESS_PUBLIC = "public";
/**
* the access right of protected Picasa Album object who has the auth token
* can access.
*/
public static final String ACCESS_PROTECTED = "private";
/**
* the access right of public Picasa Album object who is the owner.
*/
public static final String ACCESS_PRIVATE = "protected";
/**
* creates the metadata XML of an Picasa Album.
*
* @param title the title of Picasa Album.
* @param description the description of Picasa Album.
* @param access the access level of Picasa Album, can be "public",
* "private" or "protected".
* @param lastModified the last modified of Picasa Album.
* @param canComment can Picasa Album post the comment.
* @param position the position of Picasa Album.
* @param location the location of Picasa Album.
* @param cover the cover of Picasa Album.
* @param albumId the album id of Picasa Album. this field uses to update
* the album.
* @return the metadata XML of Picasa Album.
*/
public static String createAlbumXML(String title, String description, String access, Date lastModified, Boolean canComment, String position, String location, URL cover, String albumId){
StringBuilder sbr = new StringBuilder();
sbr.append("<entry xmlns=\"http://www.w3.org/2005/Atom\" xmlns:media=\"http://search.yahoo.com/mrss/\" xmlns:gphoto=\"http://schemas.google.com/photos/2007\" xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\">");
sbr.append(String.format("<title type=\"text\">%s</title>", title));
sbr.append(String.format("<summary type=\"text\">%s</summary>", description));
if (cover != null){
sbr.append(String.format("<icon>%s</icon>", cover.toString()));
}
if (albumId != null){
sbr.append(String.format("<gphoto:id>%s</gphoto:id>", albumId));
}
sbr.append(String.format("<gphoto:timestamp>%s</gphoto:timestamp>", ((lastModified != null) ? lastModified.getTime() : System.currentTimeMillis())));
if (position != null){
sbr.append(String.format("<georss:where><gml:Point><gml:pos>%s</gml:pos></gml:Point></georss:where>", position));
}
if (location != null){
sbr.append(String.format("<gphoto:location>%s</gphoto:location>", location));
}
if (access != null){
access = access.toLowerCase();
List<String> accesses = new ArrayList<String>();
accesses.add(PicasaAlbum.ACCESS_PUBLIC);
accesses.add(PicasaAlbum.ACCESS_PROTECTED);
if (!accesses.contains(access)){
access = PicasaAlbum.ACCESS_PRIVATE;
}
sbr.append(String.format("<gphoto:access>%s</gphoto:access>", access));
}
if (canComment != null){
sbr.append(String.format("<gphoto:commentingEnabled>%s</gphoto:commentingEnabled>", canComment));
}
sbr.append("<category scheme=\"http://schemas.google.com/g/2005#kind\" term=\"http://schemas.google.com/photos/2007#album\"></category>");
sbr.append("</entry>");
return sbr.toString();
}
private PicasaWeb picasaWeb;
private String title;
private String albumId;
private String description;
private String access;
private Date lastModified;
private boolean canComment;
private String position;
private String location;
private URL cover;
private URL webUrl;
private URL feedUrl;
private URL entryUrl;
private URL editUrl;
/**
* returns the Picasa Web object which cantains this object.
*
* @return the Picasa Web object which cantains this object.
*/
public PicasaWeb getPicasaWeb(){
return this.picasaWeb;
}
/**
* returns the title.
*
* @return the title.
*/
public String getTitle(){
return this.title;
}
/**
* returns the album id.
*
* @return the album id.
*/
public String getAlbumId(){
return this.albumId;
}
/**
* returns the description.
*
* @return the description.
*/
public String getDescription(){
return this.description;
}
/**
* returns the access level.
*
* @return the access level.
*/
public String getAccess(){
return this.access;
}
/**
* returns the last modified.
*
* @return the last modified.
*/
public Date getLastModified(){
return this.lastModified;
}
/**
* returns can be comment or not.
*
* @return can be comment or not.
*/
public boolean getCanComment(){
return this.canComment;
}
/**
* returns the position.
*
* @return the position.
*/
public String getPosition(){
return this.position;
}
/**
* returns the location.
*
* @return the location.
*/
public String getLocation(){
return this.location;
}
/**
* returns the cover.
*
* @return the cover.
*/
public URL getCover(){
return this.cover;
}
/**
* returns the web url.
*
* @return the web url.
*/
public URL getWebUrl(){
return this.webUrl;
}
/**
* returns the feed url.
*
* @return the feed url.
*/
public URL getFeedUrl(){
return this.feedUrl;
}
/**
* returns the entry url.
*
* @return the entry url.
*/
public URL getEntryUrl(){
return this.entryUrl;
}
/**
* returns the edit url.
*
* @return the edit url.
*/
public URL getEditUrl(){
return this.editUrl;
}
PicasaAlbum(PicasaWeb picasaWeb, Element documentRoot){
this.picasaWeb = picasaWeb;
this.initial(documentRoot);
}
/**
* constructs this object with given user and album id.
*
* @param user Google user.
* @param albumId Picasa Album id.
* @param googleapi the Google API with exist auth token.
* @throws IOException the url is not exist or something wrong of the server
* of target url.
* @throws ParserConfigurationException the response is not a parsable XML.
* @throws SAXException the response is not a parsable XML.
*/
public PicasaAlbum(String user, String albumId, GoogleAPI googleapi) throws IOException, ParserConfigurationException, SAXException{
PicasaWeb $picasaWeb = new PicasaWeb(user, googleapi);
PicasaAlbum $picasaAlbum = $picasaWeb.getAlbum(albumId);
this.picasaWeb = $picasaAlbum.picasaWeb;
this.title = $picasaAlbum.title;
this.albumId = $picasaAlbum.albumId;
this.description = $picasaAlbum.description;
this.access = $picasaAlbum.access;
this.lastModified = $picasaAlbum.lastModified;
this.canComment = $picasaAlbum.canComment;
this.position = $picasaAlbum.position;
this.location = $picasaAlbum.location;
this.cover = $picasaAlbum.cover;
this.webUrl = $picasaAlbum.webUrl;
this.feedUrl = $picasaAlbum.feedUrl;
this.entryUrl = $picasaAlbum.entryUrl;
this.editUrl = $picasaAlbum.editUrl;
}
/**
* constructs this object with given user and album id.
*
* @param user Google user.
* @param albumId Picasa Album id.
* @throws IOException the url is not exist or something wrong of the server
* of target url.
* @throws ParserConfigurationException the response is not a parsable XML.
* @throws SAXException the response is not a parsable XML.
*/
public PicasaAlbum(String user, String albumId) throws IOException, ParserConfigurationException, SAXException{
this(user, albumId, null);
}
private void initial(Element documentRoot){
try{
String id = documentRoot.getElementsByTagName("id").item(0).getTextContent();
int index = id.lastIndexOf('/');
this.albumId = id.substring(index + 1);
} catch (Exception ex){
}
try{
this.title = documentRoot.getElementsByTagName("title").item(0).getTextContent();
} catch (Exception ex){
}
try{
this.description = documentRoot.getElementsByTagName("summary").item(0).getTextContent();
} catch (Exception ex){
}
try{
this.access = documentRoot.getElementsByTagName("rights").item(0).getTextContent();
} catch (Exception ex){
}
try{
this.lastModified = new Date(Long.parseLong(documentRoot.getElementsByTagName("gphoto:timestamp").item(0).getTextContent()));
} catch (Exception ex){
}
try{
this.canComment = Boolean.parseBoolean(documentRoot.getElementsByTagName("gphoto:commentingEnabled").item(0).getTextContent());
} catch (Exception ex){
}
try{
this.position = ((Element)((Element)documentRoot.getElementsByTagName("georss:where").item(0)).getElementsByTagName("gml:Point").item(0)).getElementsByTagName("gml:pos").item(0).getTextContent();
} catch (Exception ex){
}
try{
this.location = documentRoot.getElementsByTagName("gphoto:location").item(0).getTextContent();
} catch (Exception ex){
}
try{
this.cover = new URL(documentRoot.getElementsByTagName("icon").item(0).getTextContent());
} catch (Exception ex){
}
try{
NodeList links = documentRoot.getElementsByTagName("link");
for (int i = 0; i < links.getLength(); i++){
Node link = links.item(i);
if (link.getParentNode() == documentRoot){
try{
NamedNodeMap attributes = link.getAttributes();
String rel = attributes.getNamedItem("rel").getTextContent();
URL href = new URL(attributes.getNamedItem("href").getTextContent());
if (rel.equals("http://schemas.google.com/g/2005#feed")){
this.feedUrl = href;
} else if (rel.equals("self")){
this.entryUrl = href;
} else if (rel.equals("alternate")){
this.webUrl = href;
} else if (rel.equals("edit")){
this.editUrl = href;
}
} catch (Exception ex){
}
}
}
} catch (Exception ex){
}
}
/**
* updates the Picasa Album.
*
* @param title the title of Picasa Album.
* @param description the description of Picasa Album.
* @param access the access level of Picasa Album, can be "public",
* "private" or "protected".
* @param lastModified the last modified of Picasa Album.
* @param canComment can Picasa Album post the comment.
* @param position the position of Picasa Album.
* @param location the location of Picasa Album.
* @param cover the cover of Picasa Album.
* @throws IOException the url is not exist or something wrong of the server
* of target url.
*/
public void update(String title, String description, String access, Date lastModified, Boolean canComment, String position, String location, URL cover) throws IOException{
HttpRequest httpRequest = new HttpRequest(this.editUrl);
if (this.getPicasaWeb().getGoogleapi() != null){
KeyValuePair pair = this.getPicasaWeb().getGoogleapi().toAuthHeader();
httpRequest.addHeader(pair.key, pair.value);
}
if (title != null){
this.title = title;
}
if (description != null){
this.description = description;
}
if (access != null){
this.access = access;
}
if (lastModified != null){
this.lastModified = lastModified;
}
if (canComment != null){
this.canComment = canComment;
}
if (position != null){
this.position = position;
}
if (location != null){
this.location = location;
}
if (cover != null){
this.cover = cover;
}
String data = PicasaAlbum.createAlbumXML(this.title, this.description, this.access, this.lastModified, this.canComment, this.position, this.location, this.cover, this.albumId);
httpRequest.setData(data);
httpRequest.setMethod(HttpRequest.METHOD_PUT);
httpRequest.setContentType("application/atom+xml");
String response = httpRequest.send();
}
/**
* deleted the Picasa Album.
*
* @throws IOException the url is not exist or something wrong of the server
* of target url.
*/
public void delete() throws IOException{
HttpRequest httpRequest = new HttpRequest(this.editUrl);
if (this.getPicasaWeb().getGoogleapi() != null){
KeyValuePair pair = this.getPicasaWeb().getGoogleapi().toAuthHeader();
httpRequest.addHeader(pair.key, pair.value);
}
httpRequest.setMethod(HttpRequest.METHOD_DELETE);
httpRequest.setContentType("application/atom+xml");
String response = httpRequest.send();
this.albumId = null;
this.title = null;
this.description = null;
this.access = null;
this.lastModified = null;
this.canComment = false;
this.position = null;
this.location = null;
this.cover = null;
}
/**
* get a Picasa Photo object with given photo id.
*
* @param id photo id.
* @return a Picasa Photo object.
* @throws IOException the url is not exist or something wrong of the server
* of target url.
* @throws ParserConfigurationException the response is not a parsable XML.
* @throws SAXException the response is not a parsable XML.
*/
public PicasaPhoto getPhoto(String id) throws IOException, ParserConfigurationException, SAXException{
URL url = new URL(this.entryUrl.toString() + "/photoid/" + id);
return this.getPhoto(url);
}
private PicasaPhoto getPhoto(URL url) throws IOException, ParserConfigurationException, SAXException{
HttpRequest httpRequest = new HttpRequest(url);
if (this.picasaWeb.getGoogleapi() != null){
KeyValuePair pair = this.picasaWeb.getGoogleapi().toAuthHeader();
httpRequest.addHeader(pair.key, pair.value);
}
String response = httpRequest.send();
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(response)));
return new PicasaPhoto(this, document.getDocumentElement());
}
/**
* get an array of Picasa Photo objects.
*
* @return an array Picasa Photo objects.
* @throws IOException the url is not exist or something wrong of the server
* of target url.
* @throws ParserConfigurationException the response is not a parsable XML.
* @throws SAXException the response is not a parsable XML.
*/
public PicasaPhoto[] listPhotos() throws IOException, ParserConfigurationException, SAXException{
HttpRequest httpRequest = new HttpRequest(this.feedUrl);
if (this.picasaWeb.getGoogleapi() != null){
KeyValuePair pair = this.picasaWeb.getGoogleapi().toAuthHeader();
httpRequest.addHeader(pair.key, pair.value);
}
String response = httpRequest.send();
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(response)));
List<PicasaPhoto> picasaPhotos = new ArrayList<PicasaPhoto>();
NodeList entries = document.getElementsByTagName("entry");
for (int i = 0; i < entries.getLength(); i++){
Element entry = (Element)entries.item(i);
picasaPhotos.add(new PicasaPhoto(this, entry));
}
return picasaPhotos.toArray(new PicasaPhoto[picasaPhotos.size()]);
}
/**
* upload a photo to this Picasa Album.
*
* @param file the file will upload.
* @param title the title of Picasa Photo.
* @param description the description of Picasa Photo.
* @param lastModified the last modified of Picasa Photo.
* @param canComment can Picasa Album post the comment.
* @param position the position of Picasa Album.
* @return the uploaded photo of Picasa Photo.
* @throws IOException the url is not exist or something wrong of the server
* of target url.
* @throws ParserConfigurationException the response is not a parsable XML.
* @throws SAXException the response is not a parsable XML.
*/
public PicasaPhoto uploadPhoto(File file, String title, String description, Date lastModified, String keywords, Boolean canComment, String position) throws IOException, ParserConfigurationException, SAXException{
String response = this.uploadPhotoMetaData(file, title, description, lastModified, keywords, canComment, position);
Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(new InputSource(new StringReader(response)));
return new PicasaPhoto(this, document.getDocumentElement());
}
private String uploadPhotoMetaData(File file, String title, String description, Date lastModified, String keywords, Boolean canComment, String position) throws IOException{
HttpURLConnection connection = (HttpURLConnection)this.feedUrl.openConnection();
connection.setDoInput(true);
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.addRequestProperty("Content-Type", "multipart/related; boundary=\"END_OF_PART\"");
connection.addRequestProperty("Authorization", String.format("GoogleLogin auth=\"%s\"", this.picasaWeb.getGoogleapi().getAuthToken()));
connection.addRequestProperty("MIME-Version", "1.0");
OutputStream os = connection.getOutputStream();
os.write("Media multipart posting".getBytes());
os.write("\n--END_OF_PART".getBytes());
os.write("\nContent-Type: application/atom+xml".getBytes());
os.write("\n".getBytes());
os.write("\n".getBytes());
os.write(PicasaPhoto.createPhotoXML(title, description, lastModified, keywords, canComment, position).getBytes());
os.write("\n--END_OF_PART".getBytes());
os.write("\nContent-Type: ".getBytes());
os.write(URLConnection.guessContentTypeFromName(file.getAbsolutePath()).getBytes());
os.write("\n".getBytes());
os.write("\n".getBytes());
{
InputStream is = new FileInputStream(file);
byte buffer[] = new byte[1024];
for (int length; (length = is.read(buffer, 0, buffer.length)) > 0;){
os.write(buffer, 0, length);
}
is.close();
}
os.write("\n--END_OF_PART--".getBytes());
os.flush();
os.close();
ByteArrayOutputStream baso = new ByteArrayOutputStream();
{
InputStream is = connection.getInputStream();
byte buffer[] = new byte[1024];
for (int length; (length = is.read(buffer, 0, buffer.length)) > 0;){
baso.write(buffer, 0, length);
}
is.close();
}
baso.flush();
String response = baso.toString();
baso.close();
return response;
}
/**
* returns the string represent this object.
*
* @return the string represent this object.
*/
@Override
public String toString(){
StringBuilder sbr = new StringBuilder();
sbr.append(String.format("title = \"%s\"\n", this.title));
sbr.append(String.format("album id = \"%s\"\n", this.albumId));
sbr.append(String.format("description = \"%s\"\n", this.description));
sbr.append(String.format("last modified = \"%s\"\n", ((this.lastModified == null) ? System.currentTimeMillis() : this.lastModified.getTime())));
sbr.append(String.format("access = \"%s\"\n", this.access));
sbr.append(String.format("can comment = \"%s\"\n", this.canComment));
sbr.append(String.format("position = \"%s\"\n", this.position));
sbr.append(String.format("location = \"%s\"\n", this.location));
sbr.append(String.format("cover = \"%s\"\n", ((this.cover == null) ? "" : this.cover.toString())));
sbr.append(String.format("feed url = \"%s\"\n", ((this.feedUrl == null) ? "" : this.feedUrl.toString())));
sbr.append(String.format("web url = \"%s\"\n", ((this.webUrl == null) ? "" : this.webUrl.toString())));
sbr.append(String.format("entry url = \"%s\"\n", ((this.entryUrl == null) ? "" : this.entryUrl.toString())));
sbr.append(String.format("edit url = \"%s\"\n", ((this.editUrl == null) ? "" : this.editUrl.toString())));
return sbr.toString();
}
}