001/** 002 * Copyright (c) 2012, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.twitter; 031 032import java.io.IOException; 033import java.util.ArrayList; 034import java.util.Arrays; 035import java.util.HashMap; 036import java.util.List; 037import java.util.Map; 038import java.util.Scanner; 039 040import org.openimaj.twitter.USMFStatus.Link; 041import org.openimaj.twitter.USMFStatus.User; 042 043/** 044 * GeneralJSONTwitter extends GeneralJSON to provide an object that GSon can 045 * fill from a twitter json string. It can also then be used by USMFFStatus to 046 * fill a USMFSStatus with the relevant twitter fields. 047 * 048 * @author Laurence Willmore (lgw1e10@ecs.soton.ac.uk), Sina Samangooei (ss@ecs.soton.ac.uk) 049 * 050 */ 051public class GeneralJSONTwitter extends GeneralJSON { 052 053 054 055 /* 056 * Twitter has no service field, therefore created. 057 */ 058 /** 059 * 060 */ 061 public String s = "twitter"; 062 063 /** 064 * New style retweets 065 */ 066 public GeneralJSONTwitter retweeted_status; 067 068 /* 069 * Named fields used by Gson to build object from JSON text 070 */ 071 /** 072 * This is a string because sometimes retweet_count can look like: "100+" 073 */ 074 public String retweet_count; 075 /** 076 * 077 */ 078 public String in_reply_to_screen_name; 079 /** 080 * 081 */ 082 public String text; 083 /** 084 * 085 */ 086 public Map<String, List<Map<String, Object>>> entities = null; 087 /** 088 * 089 */ 090 public Map<String, Object> user = null; 091 092 /** 093 * 094 */ 095 public Map<String, Object> place = null; 096 /** 097 * 098 */ 099 public Object geo; 100 /** 101 * 102 */ 103 public Object coordinates = null; 104 /** 105 * 106 */ 107 public boolean retweeted; 108 /** 109 * 110 */ 111 112 public double in_reply_to_status_id; 113 /** 114 * 115 */ 116 public double in_reply_to_user_id; 117 /** 118 * 119 */ 120 public boolean truncated; 121 /** 122 * 123 */ 124 public long id; 125 /** 126 * 127 */ 128 public String created_at; 129 130 /** 131 * 132 */ 133 public String source; 134 135 /** 136 * 137 */ 138 public String id_str; 139 140 @SuppressWarnings("unchecked") 141 @Override 142 public void fillUSMF(USMFStatus status) { 143 // Populate message fields 144 status.application = this.source; 145 status.date = this.created_at; 146 if (this.coordinates != null) { 147 double[] coords = new double[2]; 148 ArrayList<Double> coordList = null; 149 if(coordinates instanceof Map){ 150 coordList = (ArrayList<Double>)((Map<?,?>)coordinates).get("coordinates"); 151 } 152 coords[0] = coordList.get(0); 153 coords[1] = coordList.get(1); 154 status.geo = coords; 155 } 156 if(this.place != null && place.containsKey("name") && place.containsKey("country_code")){ 157 status.location = (String) place.get("name"); 158 status.country_code = (String) place.get("country_code"); 159 } 160 status.id = this.id; 161 status.text = this.text; 162 status.service = "Twitter"; 163 164 // Check if user is null, and make invalid if it is 165 if (this.user != null) 166 { 167 // Populate the User 168 String key = "profile_image_url"; 169 if (this.user.containsKey(key) && this.user.get(key) != null) 170 status.user.avatar = (String) this.user.get(key); 171 key = "description"; 172 if (this.user.containsKey(key) && this.user.get(key) != null) 173 status.user.description = (String) this.user.get(key); 174 key = "id"; 175 if (this.user.containsKey(key) && this.user.get(key) != null) 176 status.user.id = (Double) this.user.get("id"); 177 key = "lang"; 178 if (this.user.containsKey(key) && this.user.get(key) != null) 179 status.user.language = (String) this.user.get("lang"); 180 key = "statuses_count"; 181 if (this.user.containsKey(key) && this.user.get(key) != null) 182 status.user.postings = (Double) this.user.get("statuses_count"); 183 key = "name"; 184 if (this.user.containsKey(key) && this.user.get(key) != null) 185 status.user.real_name = (String) this.user.get("name"); 186 key = "screen_name"; 187 if (this.user.containsKey(key) && this.user.get(key) != null) 188 status.user.name = (String) this.user.get("screen_name"); 189 key = "followers_count"; 190 if (this.user.containsKey(key) && this.user.get(key) != null) 191 status.user.subscribers = (Double) this.user.get("followers_count"); 192 key = "utc_offset"; 193 if (this.user.containsKey(key) && this.user.get(key) != null) 194 status.user.utc = (Double) this.user.get("utc_offset"); 195 key = "url"; 196 if (this.user.containsKey(key) && this.user.get(key) != null) 197 status.user.website = (String) this.user.get("url"); 198 } 199 200 201 202 // Populate the links 203 if (entities != null) { 204 for (Map<String, Object> link : entities.get("urls")) { 205 USMFStatus.Link l = new USMFStatus.Link(); 206 Object url = link.get("expanded_url"); 207 if (url != null) { 208 l.href = (String) url; 209 status.links.add(l); 210 } 211 } 212 213 // Populate the keywords from hashtags 214 for (Map<String, Object> tag : entities.get("hashtags")) { 215 Object st = tag.get("text"); 216 if (st != null) { 217 status.keywords.add((String) st); 218 } 219 } 220 221 // Populate the to users from user mentions 222 for (Map<String, Object> user : entities.get("user_mentions")) { 223 USMFStatus.User u = new USMFStatus.User(); 224 u.name = (String) user.get("screen_name"); 225 u.real_name = (String) user.get("name"); 226 u.id = (Double) user.get("id"); 227 status.to_users.add(u); 228 } 229 if(this.in_reply_to_screen_name != null){ 230 status.reply_to = new User(); 231 status.reply_to.name = this.in_reply_to_screen_name; 232 status.reply_to.id = this.in_reply_to_user_id; 233 234 } 235 } 236 this.fillAnalysis(status); 237 } 238 239 @Override 240 public void fromUSMF(USMFStatus status) { 241 // Populate message fields 242 this.source = status.application; 243 this.created_at = status.date; 244 this.geo = fillCoord(status.geo); 245 246 this.id = status.id; 247 this.text = status.text; 248 if(status.reply_to!=null){ 249 this.in_reply_to_screen_name = status.reply_to.name; 250 this.in_reply_to_user_id = status.reply_to.id; 251 } 252 this.user = fillUserMap(status.user); 253 this.entities = fillEntities(status.links,status.keywords,status.to_users); 254 status.fillAnalysis(this); 255 256 } 257 258 private static Map<String, Object> fillCoord(double[] geo) { 259 Map<String, Object> coord = new HashMap<String,Object>(); 260 coord.put("type", "Point"); 261 coord.put("coordinates", Arrays.asList(geo)); 262 return coord; 263 } 264 265 private static Map<String, List<Map<String, Object>>> fillEntities(ArrayList<Link> links, ArrayList<String> keywords,ArrayList<User> to_users) { 266 Map<String, List<Map<String, Object>>> ents = new HashMap<String, List<Map<String,Object>>>(); 267 ents.put("urls", fillURLsList(links)); 268 ents.put("hashtags", fillHashtagsList(keywords)); 269 ents.put("user_mentions", fillMentionsList(to_users)); 270 return ents ; 271 } 272 273 private static List<Map<String, Object>> fillMentionsList(ArrayList<User> to_users) { 274 List<Map<String, Object>> ret = new ArrayList<Map<String,Object>>(); 275 for (User user : to_users) { 276 ret.add(fillUserMap(user)); 277 } 278 return ret; 279 } 280 281 private static List<Map<String, Object>> fillHashtagsList(ArrayList<String> keywords) { 282 List<Map<String, Object>> ret = new ArrayList<Map<String,Object>>(); 283 for (String string : keywords) { 284 Map<String, Object> item = new HashMap<String, Object>(); 285 item.put("text", string); 286 ret.add(item); 287 } 288 return ret; 289 } 290 291 private static List<Map<String, Object>> fillURLsList(ArrayList<Link> links) { 292 List<Map<String, Object>> urls = new ArrayList<Map<String,Object>>(); 293 for (Link link : links) { 294 urls.add(fillURL(link)); 295 } 296 return urls; 297 } 298 299 private static Map<String, Object> fillURL(Link link) { 300 Map<String, Object> ret = new HashMap<String, Object>(); 301 // Maybe get more for the extras field? 302 ret.put("url", link.href); 303 return ret ; 304 } 305 306 private static Map<String, Object> fillUserMap(User user) { 307 Map<String, Object> map = new HashMap<String,Object>(); 308 // Populate the User 309 String key = "profile_image_url"; 310 fillMapEntry(map,key,user.avatar); 311 key = "description"; 312 fillMapEntry(map,key,user.description); 313 key = "id"; 314 fillMapEntry(map,key,user.id); 315 key = "lang"; 316 fillMapEntry(map,key,user.language); 317 key = "statuses_count"; 318 fillMapEntry(map,key,user.postings); 319 key = "name"; 320 fillMapEntry(map,key,user.real_name); 321 key = "screen_name"; 322 fillMapEntry(map,key,user.name); 323 key = "followers_count"; 324 fillMapEntry(map,key,user.subscribers); 325 key = "utc_offset"; 326 fillMapEntry(map,key,user.utc); 327 key = "url"; 328 fillMapEntry(map,key,user.website); 329 return map; 330 } 331 332 private static void fillMapEntry(Map<String, Object> map, String key, Object value) { 333 if(value!=null) map.put(key, value); 334 } 335 336 @Override 337 public void readASCII(Scanner in) throws IOException { 338 USMFStatus status = new USMFStatus(this.getClass()); 339 status.readASCII(in); 340 this.fromUSMF(status); 341 } 342 343 @Override 344 public GeneralJSON instanceFromString(String line){ 345 GeneralJSONTwitter jsonInstance = null; 346 try { 347 jsonInstance = gson.fromJson(line, GeneralJSONTwitter.class); 348 } catch (Throwable e) { 349 throw new RuntimeException(e); 350 } 351 if (jsonInstance.id == 0) { 352 GeneralJSONTwitterRawText raw = new GeneralJSONTwitterRawText(); 353 raw .text = jsonInstance.text; 354 return raw; 355 } 356 return jsonInstance; 357 } 358 359 360 361}