/**
  * Use cursors to fetch upto jtwit.maxResults TODO More controlled paging??
  *
  * @param url API method to call
  * @param screenName
  * @param userId
  * @return twitter-id numbers for friends/followers of screenName or userId Is affected by {@link
  *     #maxResults}
  */
 private List<Number> getUserIDs(String url, String screenName, Long userId) {
   Long cursor = -1L;
   List<Number> ids = new ArrayList<Number>();
   if (screenName != null && userId != null)
     throw new IllegalArgumentException(
         "cannot use both screen_name and user_id when fetching user_ids");
   Map<String, String> vars = InternalUtils.asMap("screen_name", screenName, "user_id", userId);
   while (cursor != 0 && !jtwit.enoughResults(ids)) {
     vars.put("cursor", String.valueOf(cursor));
     String json = http.getPage(url, vars, http.canAuthenticate());
     try {
       // it seems Twitter will occasionally return a raw array
       JSONArray jarr;
       if (json.charAt(0) == '[') {
         jarr = new JSONArray(json);
         cursor = 0L;
       } else {
         JSONObject jobj = new JSONObject(json);
         jarr = (JSONArray) jobj.get("ids");
         cursor = new Long(jobj.getString("next_cursor"));
       }
       for (int i = 0; i < jarr.length(); i++) {
         ids.add(jarr.getLong(i));
       }
       if (jarr.length() == 0) {
         // No more
         break;
       }
     } catch (JSONException e) {
       throw new TwitterException.Parsing(json, e);
     }
   }
   return ids;
 }
 /**
  * Returns information of a given user, specified by user-id.
  *
  * @param userId The user-id of a user.
  * @throws exception if the user does not exist - or has been terminated (as happens to spam
  *     bots).
  */
 public User show(Number userId) {
   Map<String, String> vars = InternalUtils.asMap("user_id", userId.toString());
   String json =
       http.getPage(jtwit.TWITTER_URL + "/users/show.json", vars, http.canAuthenticate());
   try {
     User user = new User(new JSONObject(json), null);
     return user;
   } catch (JSONException e) {
     throw new TwitterException.Parsing(json, e);
   }
 }
 /**
  * @return true if followerScreenName <i>is</i> following followedScreenName
  * @throws TwitterException.E403 if one of the users has protected their updates and you don't
  *     have access. This can be counter-intuitive (and annoying) at times! Also throws E403 if one
  *     of the users has been suspended (we use the {@link SuspendedUser} exception sub-class for
  *     this).
  * @throws TwitterException.E404 if one of the users does not exist
  */
 public boolean isFollower(String followerScreenName, String followedScreenName) {
   assert followerScreenName != null && followedScreenName != null;
   try {
     Map vars =
         InternalUtils.asMap(
             "source_screen_name", followerScreenName,
             "target_screen_name", followedScreenName);
     String page =
         http.getPage(jtwit.TWITTER_URL + "/friendships/show.json", vars, http.canAuthenticate());
     JSONObject jo = new JSONObject(page);
     JSONObject trgt = jo.getJSONObject("relationship").getJSONObject("target");
     boolean fby = trgt.getBoolean("followed_by");
     return fby;
   } catch (TwitterException.E403 e) {
     if (e instanceof SuspendedUser) throw e;
     // Should this be a suspended user exception instead?
     // Let's ask Twitter
     // TODO check rate limits - only do if we have spare capacity
     String whoFirst =
         followedScreenName.equals(jtwit.getScreenName())
             ? followerScreenName
             : followedScreenName;
     try {
       // this could throw a SuspendedUser exception
       show(whoFirst);
       String whoSecond =
           whoFirst.equals(followedScreenName) ? followerScreenName : followedScreenName;
       if (whoSecond.equals(jtwit.getScreenName())) throw e;
       show(whoSecond);
     } catch (TwitterException.RateLimit e2) {
       // ignore
     }
     // both shows worked?
     throw e;
   } catch (TwitterException e) {
     // FIXME investigating a weird new bug
     if (e.getMessage() != null
         && e.getMessage().contains("Two user ids or screen_names must be supplied"))
       throw new TwitterException(
           "WTF? inputs: follower="
               + followerScreenName
               + ", followed="
               + followedScreenName
               + ", call-by="
               + jtwit.getScreenName()
               + "; "
               + e.getMessage());
     throw e;
   }
 }
 /**
  * Low-level method for fetching e.g. your friends
  *
  * @param url
  * @param screenName e.g. your screen-name
  * @return
  */
 private List<User> getUsers(String url, String screenName) {
   Map<String, String> vars = InternalUtils.asMap("screen_name", screenName);
   List<User> users = new ArrayList<User>();
   Long cursor = -1L;
   while (cursor != 0 && !jtwit.enoughResults(users)) {
     vars.put("cursor", cursor.toString());
     JSONObject jobj;
     try {
       jobj = new JSONObject(http.getPage(url, vars, http.canAuthenticate()));
       users.addAll(User.getUsers(jobj.getString("users")));
       cursor = new Long(jobj.getString("next_cursor"));
     } catch (JSONException e) {
       throw new TwitterException.Parsing(null, e);
     }
   }
   return users;
 }
 /**
  * Returns information of a given user, specified by screen name.
  *
  * @param screenName The screen name of a user.
  * @throws exception if the user does not exist
  * @throws SuspendedUser if the user has been terminated (as happens to spam bots).
  * @see #show(long)
  */
 public User show(String screenName) throws TwitterException, TwitterException.SuspendedUser {
   Map vars = InternalUtils.asMap("screen_name", screenName);
   // Test Code Debugger at work - expected closures until 2012
   String json = "";
   try {
     json = http.getPage(jtwit.TWITTER_URL + "/users/show.json", vars, http.canAuthenticate());
   } catch (Exception e) {
     // we get here?
     throw new TwitterException.E404(
         "User "
             + screenName
             + " does not seem to exist, their user account may have been removed from the service");
   }
   // Debuggers no longer at work
   if (json.length() == 0) throw new TwitterException.E404(screenName + " does not seem to exist");
   try {
     User user = new User(new JSONObject(json), null);
     return user;
   } catch (JSONException e) {
     throw new TwitterException.Parsing(json, e);
   }
 }