/*
* StatusFeedParser.java
*
* Copyright (C) 2005-2010 Tommi Laukkanen
* http://www.substanceofcode.com
*
* 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.substanceofcode.twitter;
import com.substanceofcode.twitter.model.Status;
import com.substanceofcode.utils.CustomInputStream;
import com.substanceofcode.utils.ResultParser;
import com.substanceofcode.utils.StringUtil;
import com.substanceofcode.utils.XmlParser;
import java.io.IOException;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
import java.util.Vector;
/**
* StatusFeedParser
*
* @author Tommi Laukkanen (tlaukkanen at gmail dot com)
*/
public class StatusFeedParser implements ResultParser {
private Vector statuses;
private boolean isDirect;
private String rawData;
private boolean isReallyEmpty;
/** Creates a new instance of StatusFeedParser */
public StatusFeedParser() {
statuses = new Vector();
isDirect = false;
rawData = "";
isReallyEmpty = false;
}
public String getRawData() {
return rawData;
}
/**
* <status>
<created_at>Wed Aug 29 20:14:27 +0000 2007</created_at>
<id>235455042</id>
<text>
Heheh, I should just go to work for Gawker Media. Now wouldn't THAT be a hoot? (Parent co of Valleywag). One problem: Nick Denton. Boss. ;-)
</text>
−
<source>
<a href="http://iconfactory.com/software/twitterrific">twitterrific</a>
</source>
<favorited>true</favorited>
−
<user>
<id>13348</id>
<name>Scobleizer</name>
<screen_name>Scobleizer</screen_name>
<location>Half Moon Bay, California, USA</location>
<description>Tech geek blogger @ http://scobleizer.com</description>
−
<profile_image_url>
http://assets2.twitter.com/system/user/profile_image/13348/normal/861009_f12647108b.jpg?1182323860
</profile_image_url>
<url>http://scobleshow.com</url>
<protected>false</protected>
<following>false</following>
</user>
</status>
*
*
*/
public Vector getStatuses() {
return statuses;
}
public boolean isReallyEmpty() {
return this.isReallyEmpty;
}
/**
* Parse statuses feed.
* @param is
* @throws IOException
*/
public void parse(CustomInputStream is) throws IOException {
/* If empty then the following XML is returned:
* <nilclasses type="array"></nilclasses>
*/
try {
XmlParser xml = new XmlParser(is);
String text = "";
String screenName = "";
String id = "";
Date date = null;
boolean doSender = false;
boolean doRecipient = false;
boolean isFavorite = false;
boolean isFollowing = false;
String inReplyToId = "";
while (xml.parse() != XmlParser.END_DOCUMENT) {
String elementName = xml.getName();
Thread.yield();
if (elementName.equals("error")) {
// Parse error message
text = "Error from Twitter: " + xml.getText();
screenName = "Twitter";
date = new Date(System.currentTimeMillis());
} else if(elementName.equals("nilclasses")) {
isReallyEmpty = true;
return;
} else if (elementName.equals("status")) {
// Parse normal status
if (text.length() > 0) {
Status status = new Status(screenName, text, date, id);
status.setDirect(isDirect);
status.setInReplyToId(inReplyToId);
status.setFollowing(isFollowing);
status.setFavorite(isFavorite);
statuses.addElement(status);
}
text = "";
screenName = "";
id = "";
date = null;
doSender = false;
doRecipient = false;
isFavorite = false;
isFollowing = false;
inReplyToId = "";
} else if (elementName.equals("direct_message")) {
if (text.length() > 0) {
Status status = new Status(screenName, text, date, id);
status.setDirect(isDirect);
status.setFavorite(isFavorite);
status.setFollowing(isFollowing);
status.setInReplyToId(inReplyToId);
statuses.addElement(status);
}
text = "";
screenName = "";
id = "";
date = null;
doSender = false;
doRecipient = false;
isFavorite = false;
isFollowing = false;
inReplyToId = "";
} else if (elementName.equals("id") && id.equals("")) {
id = xml.getText();
} else if (elementName.equals("text")) {
if(text.length()==0) {
text += xml.getText();
}
} else if (elementName.equals("in_reply_to_status_id")) {
inReplyToId = xml.getText();
} else if (elementName.equals("favorited")) {
String favorited = xml.getText();
if(favorited.startsWith("true")) {
isFavorite = true;
}
} else if (elementName.equals("screen_name")) {
if (doSender || (!doSender && !doRecipient)) {
screenName = xml.getText();
}
} else if (elementName.equals("sender")) {
doSender = true;
doRecipient = false;
} else if (elementName.equals("recipient")) {
doSender = false;
doRecipient = true;
} else if (elementName.equals("created_at")) {
if(date==null) {
String dateString = xml.getText();
date = parseDate(dateString);
}
} else if (elementName.equals("following")) {
String val = xml.getText();
System.out.println("Following " + val);
if(val.startsWith("false")) {
isFollowing = false;
} else {
isFollowing = true;
}
} else if (elementName.equals("html")) {
return;
}
}
if (text.length() > 0) {
Status status = new Status(screenName, text, date, id);
status.setDirect(isDirect);
status.setFavorite(isFavorite);
status.setFollowing(isFollowing);
status.setInReplyToId(inReplyToId);
statuses.addElement(status);
}
rawData = xml.getRawData();
} catch (Exception ex) {
throw new IOException("Error in StatusFeedParser.parse(): " + ex.getMessage());
}
}
/**
* Parse RSS date format to Date object.
* Example of RSS date:
* Sat, 23 Sep 2006 22:25:11 +0000
*/
public static Date parseDate(String dateString) throws Exception {
Date pubDate = null;
try {
// Split date string to values
// 0 = week day
// 1 = day of month
// 2 = month
// 3 = year (could be with either 4 or 2 digits)
// 4 = time
// 5 = GMT
int weekDayIndex = 0;
int dayOfMonthIndex = 2;
int monthIndex = 1;
int yearIndex = 5;
int timeIndex = 3;
int gmtIndex = 4;
String[] values = StringUtil.split(dateString, " ");
int columnCount = values.length;
// Wed Aug 29 20:14:27 +0000 2007
if (columnCount == 5) {
// Expected format:
// 09 Nov 2006 23:18:49 EST
dayOfMonthIndex = 0;
monthIndex = 1;
yearIndex = 2;
timeIndex = 3;
gmtIndex = 4;
} else if (columnCount == 7) {
// Expected format:
// Thu, 19 Jul 2007 00:00:00 N
yearIndex = 4;
timeIndex = 5;
gmtIndex = 6;
} else if (columnCount < 5 || columnCount > 6) {
throw new Exception("Invalid date format: " + dateString);
}
// Day of month
int dayOfMonth = Integer.parseInt(values[dayOfMonthIndex]);
// Month
String[] months = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
String monthString = values[monthIndex];
int month = 0;
for (int monthEnumIndex = 0; monthEnumIndex < 12; monthEnumIndex++) {
if (monthString.equals(months[monthEnumIndex])) {
month = monthEnumIndex;
}
}
// Year
int year = Integer.parseInt(values[yearIndex]);
if (year < 100) {
year += 2000;
}
// Time
String[] timeValues = StringUtil.split(values[timeIndex], ":");
int hours = Integer.parseInt(timeValues[0]);
int minutes = Integer.parseInt(timeValues[1]);
int seconds = Integer.parseInt(timeValues[2]);
pubDate = getCal(dayOfMonth, month, year, hours, minutes, seconds);
//System.out.println("OrigDate: " + dateString);
//System.out.println("ParsDate: " + pubDate.toString());
} catch (Exception ex) {
// TODO: Add exception handling code
throw new Exception("Error in parseDate: " + ex.getMessage());
} catch (Throwable t) {
// TODO: Add exception handling code
throw new Exception("Throwable in parseDate: " + t.getMessage());
}
return pubDate;
}
/** Get calendar date. **/
public static Date getCal(int dayOfMonth, int month, int year, int hours,
int minutes, int seconds) throws Exception {
// Create calendar object from date values
Calendar cal = Calendar.getInstance();
cal.setTimeZone(TimeZone.getTimeZone("GMT+0"));
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
cal.set(Calendar.MONTH, month);
cal.set(Calendar.YEAR, year);
cal.set(Calendar.HOUR_OF_DAY, hours);
cal.set(Calendar.MINUTE, minutes);
cal.set(Calendar.SECOND, seconds);
return cal.getTime();
}
void setDirect(boolean value) {
this.isDirect = value;
}
}