// BlogBridge -- RSS feed reader, manager, and web based service
// Copyright (C) 2002-2006 by R. Pito Salas
//
// This program is free software; you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free Software Foundation;
// either version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
// See the GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along with this program;
// if not, write to the Free Software Foundation, Inc., 59 Temple Place,
// Suite 330, Boston, MA 02111-1307 USA
//
// Contact: R. Pito Salas
// mailto:pitosalas@users.sourceforge.net
// More information: about BlogBridge
// http://www.blogbridge.com
// http://sourceforge.net/projects/blogbridge
//
// $Id: DeliciousService.java,v 1.5 2006/05/19 08:47:01 spyromus Exp $
//
package com.salas.bb.utils.net.delicious;
import com.salas.bb.utils.Assert;
import com.salas.bb.utils.StringUtils;
import com.salas.bb.utils.net.BBHttpClient;
import com.salas.bb.utils.parser.*;
import java.io.IOException;
import java.net.URL;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* <p>Delicious service representative. This class represents a front-end to del.icio.us
* service API.</p>
*
* <p>It follows the API conventions taken from <a href="http://del.icio.us/doc/api">official API
* page</a>.</p>
*/
public final class DeliciousService
{
private static final String SERVICE_URL = "http://del.icio.us/";
private static final String SERVICE_API_URL = "https://api.del.icio.us/";
private static final MessageFormat QUERY_FORMAT =
new MessageFormat("rss/url/?url={0}");
private static final MessageFormat SEND_TAGS_FORMAT =
new MessageFormat("v1/posts/add?url={0}&tags={1}&description={2}&extended={3}");
private static final MessageFormat DELETE_TAGS_FORMAT =
new MessageFormat("v1/posts/delete?url={0}");
/**
* Hidden utility class constructor.
*/
private DeliciousService()
{
}
/**
* Returns the list of tags sets assigned by different users to the given URL.
*
* @param link link to lookup in tags database.
*
* @return list of tags sets.
*
* @throws IOException if communication to service fails.
* @throws NullPointerException if link is not specified.
*/
public static DeliciousTags[] getLinkTags(URL link)
throws IOException
{
Assert.notNull("Link", link);
Channel channel = queryService(link.toString());
return channel != null ? convertChannelToTags(channel) : null;
}
/**
* Puts the link into the bookmarks of some user with given tags and description.
*
* @param link link to place tags for.
* @param user user name registered at the service.
* @param password password for the user.
* @param tags tags to put.
* @param description description to assign (can't be empty or <code>NULL</code>).
* @param extended extended description (can be <code>NULL</code>).
*
* @return <code>TRUE</code> if successfully tagged link.
*
* @throws NullPointerException if link, user, password, tags or description aren't specified.
* @throws IllegalArgumentException if user, password, tags or description are empty.
* @throws IOException if communication to service fails.
*/
public static boolean tagLink(URL link, String user, String password, String[] tags,
String description, String extended) throws IOException
{
Assert.notNull("Link", link);
Assert.notEmpty("User", user);
Assert.notEmpty("Password", password);
Assert.notEmpty("Tags", tags);
Assert.notEmpty("Description", description);
String urlString = SEND_TAGS_FORMAT.format(new String[] {
StringUtils.encodeForURL(link.toString()),
StringUtils.encodeForURL(StringUtils.join(tags, " ")),
StringUtils.encodeForURL(description),
extended == null ? "" : StringUtils.encodeForURL(extended)});
return sendSimpleRequest(urlString, user, password);
}
/**
* Removes the link from bookmarks of user.
*
* @param link link to remove from bookmarks.
* @param user user name registered at the service.
* @param password password for the user.
*
* @return <code>TRUE</code> if link was untagged.
*
* @throws NullPointerException if link, user or password aren't specified.
* @throws IllegalArgumentException if user or password are empty.
* @throws IOException if communication to service fails.
*/
public static boolean untagLink(URL link, String user, String password)
throws IOException
{
Assert.notNull("Link", link);
Assert.notEmpty("User", user);
Assert.notEmpty("Password", password);
String urlString = DELETE_TAGS_FORMAT.format(new String[] {
StringUtils.encodeForURL(link.toString()) });
return sendSimpleRequest(urlString, user, password);
}
/**
* Returns the list of tags used by given user.
*
* @param user user name registered at the service.
* @param password password for the user.
*
* @return list of tags used by this user by the moment.
*
* @throws NullPointerException if user or password aren't specified.
* @throws IllegalArgumentException if user or password are empty.
* @throws IOException if communication to service fails.
*/
public static String[] getUserTags(String user, String password)
throws IOException
{
Assert.notEmpty("User", user);
Assert.notEmpty("Password", password);
return responseToTags(sendApiRequest("v1/tags/get?", user, password));
}
/**
* Converts tags response to tags list.
*
* @param response response.
*
* @return tags.
*/
static String[] responseToTags(String response)
{
List tags = new ArrayList();
if (response != null)
{
Pattern pattern = Pattern.compile("tag=\"([^\"]+)\"");
Matcher matcher = pattern.matcher(response);
while (matcher.find())
{
tags.add(matcher.group(1));
}
}
return (String[])tags.toArray(new String[tags.size()]);
}
/**
* Sends simple request from the name of user. In response server returns simple
* XML which contains either code='done' (success) or code='something went wrong'
* (failure).
*
* @param request request.
* @param user user name.
* @param password user password.
*
* @return <code>TRUE</code> if server returned successful response.
*
* @throws IOException if communication with service fails.
*/
private static boolean sendSimpleRequest(String request, String user, String password)
throws IOException
{
return sendApiRequest(request, user, password).indexOf("code=\"done\"") != -1;
}
/**
* Sends request and returns output of the server. The reuqest is accompanied with
* authentication info.
*
* @param request request.
* @param user user name.
* @param password user password.
*
* @return response.
*
* @throws IOException if communication with service fails.
*/
private static String sendApiRequest(String request, String user, String password)
throws IOException
{
return BBHttpClient.get(SERVICE_API_URL + request, user, password);
}
/**
* Queries service for data for given link. Service returns response in RDF format
* in any case (whether the link is tagged or no). This response is parsed into
* valid {@link com.salas.bb.utils.parser.Channel}.
*
* @param aLink link to query service for.
*
* @return channel or <code>NULL</code> in case of any problems.
*
* @throws IOException if communication fails.
*/
static Channel queryService(String aLink)
throws IOException
{
Channel channel = null;
String query = SERVICE_URL + QUERY_FORMAT.format(new String[] { aLink });
IFeedParser parser = FeedParserConfig.create();
try
{
FeedParserResult result = parser.parse(new URL(query), null, -1);
channel = result.getChannel();
} catch (FeedParserException e)
{
// throw new IOException("Failed to parse delicious response for: " + aLink, e);
}
return channel;
}
/**
* Converts channel (list of user records) into list of tags. The list can contain
* duplicate tags.
*
* @param aChannel channel to convert.
*
* @return list of tags.
*/
static DeliciousTags[] convertChannelToTags(Channel aChannel)
{
int itemsCount = aChannel.getItemsCount();
List tags = new ArrayList(itemsCount);
for (int i = 0; i < itemsCount; i++)
{
Item item = aChannel.getItemAt(i);
DeliciousTags itemTags = convertItemToTags(item);
if (itemTags != null) tags.add(itemTags);
}
return (DeliciousTags[])tags.toArray(new DeliciousTags[tags.size()]);
}
/**
* Converts item (user record) to list of tags.
*
* @param aItem item.
*
* @return tags object or <code>NULL</code> if item has no tags set.
*/
static DeliciousTags convertItemToTags(Item aItem)
{
String user = aItem.getAuthor();
String subject = aItem.getSubject();
String[] tags = (subject != null) ? StringUtils.split(subject, " ") : null;
return tags == null ? null : new DeliciousTags(user, tags);
}
}