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; } }