package com.nostra13.socialsharing.twitter.extpack.winterwell.jtwitter;
import java.io.Serializable;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import com.nostra13.socialsharing.twitter.extpack.winterwell.json.JSONArray;
import com.nostra13.socialsharing.twitter.extpack.winterwell.json.JSONException;
import com.nostra13.socialsharing.twitter.extpack.winterwell.json.JSONObject;
/**
* A Twitter user. Fields are null if unset.
*
* @author daniel
*/
public final class User implements Serializable {
private static final long serialVersionUID = 1L;
/**
* Convert from a JSON array into a list of users.
*
* @param json
* @throws TwitterException
*/
static List<User> getUsers(String json) throws TwitterException {
if (json.trim().equals(""))
return Collections.emptyList();
try {
JSONArray arr = new JSONArray(json);
return getUsers2(arr);
} catch (JSONException e) {
throw new TwitterException.Parsing(json, e);
}
}
static List<User> getUsers2(JSONArray arr) throws JSONException {
List<User> users = new ArrayList<User>();
for (int i = 0; i < arr.length(); i++) {
JSONObject obj = arr.getJSONObject(i);
User u = new User(obj, null);
users.add(u);
}
return users;
}
public final Date createdAt;
public final String description;
public final int favoritesCount;
private final Boolean followedByYou;
public int followersCount;
private final Boolean followingYou;
/**
* True if the authenticated user has requested to follow this user. This
* will be false unless the friendship request is pending. False if Twitter
* does not say otherwise.
*/
public final boolean followRequestSent;
/**
* The number of people this user is following.
* <p>
* "following count" would be a better name, but historically Twitter calls
* this "friends count".
*/
public final int friendsCount;
public final Long id;
/**
* The number of public lists a user is listed in. -1 if unknown.
*/
public final int listedCount;
/**
* The location, as reported by the user. Can be metaphorical, e.g.
* "close to your heart"), or null; never blank. UberTwitter & similar
* lat/long references will be normalised using
* {@link InternalUtils#latLongLocn}.
*/
public final String location;
/** The display name, e.g. "Daniel Winterstein" */
public final String name;
public final boolean notifications;
private Place place;
public final String profileBackgroundColor;
public final URI profileBackgroundImageUrl;
public final boolean profileBackgroundTile;
/**
* The url for the user's Twitter profile picture.
* <p>
* Note: we allow this to be edited as a convenience for the User objects
* generated by search
*/
public URI profileImageUrl;
public final String profileLinkColor;
public final String profileSidebarBorderColor;
public final String profileSidebarFillColor;
public final String profileTextColor;
/**
* true if this user keeps their updates private
*/
public final boolean protectedUser;
/**
* The login name, e.g. "winterstein" This is the only thing used by
* equals() and hashcode(). This is always lower-case, as Twitter
* screen-names are case insensitive, *unless* you set
* {@link Twitter#CASE_SENSITIVE_SCREENNAMES}
*/
public final String screenName;
/**
* The user's current status - *if* returned by Twitter. Not all calls
* return this, so can be null.
*/
public final Status status;
public final int statusesCount;
public final String timezone;
/**
* Number of seconds between a user's registered time zone and Greenwich
* Mean Time (GMT) - aka Coordinated Universal Time or UTC. Can be positive
* or negative.
*/
public final double timezoneOffSet;
public final boolean verified;
public final URI website;
/**
* Create a User from a json blob
*
* @param obj
* @param status
* can be null
* @throws TwitterException
*/
User(JSONObject obj, Status status) throws TwitterException {
try {
id = obj.getLong("id");
name = InternalUtils.unencode(InternalUtils.jsonGet("name", obj));
String sn = InternalUtils.jsonGet("screen_name", obj);
screenName = Twitter.CASE_SENSITIVE_SCREENNAMES ? sn : sn
.toLowerCase();
// location - normalise a bit
Object _locn = Status.jsonGetLocn(obj);
location = _locn == null ? null : _locn.toString();
if (_locn instanceof Place) {
place = (Place) _locn;
}
description = InternalUtils.unencode(InternalUtils.jsonGet(
"description", obj));
String img = InternalUtils.jsonGet("profile_image_url", obj);
profileImageUrl = img == null ? null : InternalUtils.URI(img);
String url = InternalUtils.jsonGet("url", obj);
website = url == null ? null : InternalUtils.URI(url);
protectedUser = obj.optBoolean("protected");
followersCount = obj.optInt("followers_count");
profileBackgroundColor = InternalUtils.jsonGet(
"profile_background_color", obj);
profileLinkColor = InternalUtils.jsonGet("profile_link_color", obj);
profileTextColor = InternalUtils.jsonGet("profile_text_color", obj);
profileSidebarFillColor = InternalUtils.jsonGet(
"profile_sidebar_fill_color", obj);
profileSidebarBorderColor = InternalUtils.jsonGet(
"profile_sidebar_border_color", obj);
friendsCount = obj.optInt("friends_count");
// date
String c = InternalUtils.jsonGet("created_at", obj);
createdAt = c == null ? null : InternalUtils.parseDate(c); // null
// when
// fetching
// relationship-info
favoritesCount = obj.optInt("favourites_count");
String utcOffSet = InternalUtils.jsonGet("utc_offset", obj);
timezoneOffSet = utcOffSet == null ? 0 : Double
.parseDouble(utcOffSet);
timezone = InternalUtils.jsonGet("time_zone", obj);
img = InternalUtils.jsonGet("profile_background_image_url", obj);
profileBackgroundImageUrl = img == null ? null : InternalUtils
.URI(img);
profileBackgroundTile = obj.optBoolean("profile_background_tile");
statusesCount = obj.optInt("statuses_count");
notifications = obj.optBoolean("notifications");
verified = obj.optBoolean("verified");
// relationship info -- can come in 2 formats...
Object _cons = obj.opt("connections");
if (_cons instanceof JSONArray) { // from a getRelationshipInfo call
JSONArray cons = (JSONArray) _cons;
boolean _following = false, _followedBy = false, _followRequested = false;
for (int i = 0, n = cons.length(); i < n; i++) {
String ci = cons.getString(i);
if ("following".equals(ci)) {
_following = true;
} else if ("followed_by".equals(ci)) {
_followedBy = true;
} else if ("following_requested".equals(ci)) {
_followRequested = true;
}
}
followedByYou = _following;
followingYou = _followedBy;
followRequestSent = _followRequested;
} else { // from a normal User call
followedByYou = InternalUtils.getOptBoolean(obj, "following");
// Warning: Twitter have stopped sending this in many cases.
// Unfortunately, null cannot be interpreted as true/false.
followingYou = InternalUtils.getOptBoolean(obj, "followed_by");
followRequestSent = obj.optBoolean("follow_request_sent");
}
listedCount = obj.optInt("listed_count", -1);
// status
if (status == null) {
JSONObject s = obj.optJSONObject("status");
this.status = s == null ? null : new Status(s, this);
} else {
this.status = status;
}
} catch (JSONException e) {
throw new TwitterException.Parsing(String.valueOf(obj), e);
} catch (NullPointerException e) {
throw new TwitterException(e + " from <" + obj + ">, <" + status
+ ">\n\t" + e.getStackTrace()[0] + "\n\t"
+ e.getStackTrace()[1]);
}
}
/**
* Create a dummy User object. All fields are set to null. This will be
* equals() to an actual User object, so it can be used to query
* collections. E.g. <code><pre>
* // Test whether jtwit is a friend
* twitter.getFriends().contains(new User("jtwit"));
* </pre></code>
*
* @param screenName
* This will be converted to lower-case as Twitter screen-names
* are case insensitive (unless
* {@link Twitter#CASE_SENSITIVE_SCREENNAMES} is set)
*/
public User(String screenName) {
this(screenName, null);
}
private User(String screenName, Long id) {
this.id = id;
name = null;
if (screenName != null && !Twitter.CASE_SENSITIVE_SCREENNAMES) {
screenName = screenName.toLowerCase();
}
this.screenName = screenName;
status = null;
location = null;
description = null;
profileImageUrl = null;
website = null;
protectedUser = false;
followersCount = 0;
profileBackgroundColor = null;
profileLinkColor = null;
profileTextColor = null;
profileSidebarFillColor = null;
profileSidebarBorderColor = null;
friendsCount = 0;
createdAt = null;
favoritesCount = 0;
timezoneOffSet = -1;
timezone = null;
profileBackgroundImageUrl = null;
profileBackgroundTile = false;
statusesCount = 0;
notifications = false;
verified = false;
followedByYou = null;
followingYou = null;
followRequestSent = false;
listedCount = -1;
}
@Override
public boolean equals(Object other) {
if (this == other)
return true;
if (other.getClass() != User.class)
return false;
User ou = (User) other;
// normal case
if (screenName != null && ou.screenName != null)
return screenName.equals(ou.screenName);
// fake user case
if (id != null && ou.id != null)
return id == ou.id;
// can't compare = fail
return false;
}
// /**
// * A 2nd species of fake user. For internal use only.
// * WARNING: these users break {@link #hashCode()}'s behaviour!
// * @param id
// */
// User(Long id) {
// this(null, id);
// }
public Date getCreatedAt() {
return createdAt;
}
public String getDescription() {
return description;
}
/**
* Number of statuses a user has marked as favorite.<br>
* Warning: can be zero if Twitter did not supply the info (e.g. User
* objects from searches or RSS feeds)
* */
public int getFavoritesCount() {
return favoritesCount;
}
/**
* @return Number of followers.<br>
* Warning: can be zero if Twitter did not supply the info (e.g.
* User objects from searches or RSS feeds)
*/
public int getFollowersCount() {
return followersCount;
}
/**
* @return number of people this user is following.<br>
* Warning: can be zero if Twitter did not supply the info (e.g.
* User objects from searches or RSS feeds)
*/
public int getFriendsCount() {
return friendsCount;
}
/**
* @return The Twitter id for this post. This is used by some API methods.
* <p>
* Note: this may switch to BigInteger in the future, if Twitter
* change their id numbering scheme. Use Number (which is a
* super-class for both Long and BigInteger) if you wish to
* future-proof your code.
*/
public Long getId() {
return id;
}
/**
* @see #location
*/
public String getLocation() {
return location;
}
/**
* The display name, e.g. "Daniel Winterstein"
*
* @see #getScreenName()
* */
public String getName() {
return name;
}
public Place getPlace() {
return place;
}
public String getProfileBackgroundColor() {
return profileBackgroundColor;
}
public URI getProfileBackgroundImageUrl() {
return profileBackgroundImageUrl;
}
public URI getProfileImageUrl() {
return profileImageUrl;
}
public String getProfileLinkColor() {
return profileLinkColor;
}
public String getProfileSidebarBorderColor() {
return profileSidebarBorderColor;
}
public String getProfileSidebarFillColor() {
return profileSidebarFillColor;
}
public String getProfileTextColor() {
return profileTextColor;
}
public boolean getProtectedUser() {
return protectedUser;
}
/** The login name, e.g. "winterstein". Never null */
public String getScreenName() {
return screenName;
}
/**
* The user's current status - *if* returned by Twitter. Not all calls
* return this, so can be null.
*/
public Status getStatus() {
return status;
}
/**
* @return number of status updates posted by this User.<br>
* Warning: can be zero if Twitter did not supply the info (e.g.
* User objects from searches or RSS feeds)
*/
public int getStatusesCount() {
return statusesCount;
}
/**
* String version of the timezone
*/
public String getTimezone() {
return timezone;
}
/**
* Number of seconds between a user's registered time zone and Greenwich
* Mean Time (GMT) - aka Coordinated Universal Time or UTC. Can be positive
* or negative.
*/
public double getTimezoneOffSet() {
return timezoneOffSet;
}
public URI getWebsite() {
return website;
}
@Override
public int hashCode() {
// normal case
return screenName.hashCode();
}
/**
* @return true if this is a dummy User object, in which case almost all of
* it's fields will be null - with the exception of screenName and
* possibly {@link #profileImageUrl}. Dummy User objects are
* equals() to full User objects.
*/
public boolean isDummyObject() {
return name == null;
}
/**
* Are you following this person?
*
* @return true if you are following this user.
* null if unset -- though this is quite rare.
*/
public Boolean isFollowedByYou() {
return followedByYou;
}
/**
* Is this person following you?
*
* @return true if this user is following you. null if unset
* -- which is common!
*/
public Boolean isFollowingYou() {
return followingYou;
}
public boolean isNotifications() {
return notifications;
}
public boolean isProfileBackgroundTile() {
return profileBackgroundTile;
}
/**
* true if this user keeps their updates private
*/
public boolean isProtectedUser() {
return protectedUser;
}
/**
* @return true if the account has been verified by Twitter to really be who
* it claims to be.
*/
public boolean isVerified() {
return verified;
}
/**
* Returns the User's screenName (i.e. their Twitter login)
*/
@Override
public String toString() {
return screenName;
}
}