001/** 002 * Copyright 2014 Tampere University of Technology, Pori Department 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package service.tut.pori.facebookjazz; 017 018import java.util.ArrayList; 019import java.util.EnumSet; 020import java.util.Iterator; 021import java.util.List; 022 023import org.apache.commons.lang3.StringUtils; 024import org.apache.log4j.Logger; 025 026import service.tut.pori.contentanalysis.Definitions; 027import service.tut.pori.contentanalysis.Photo; 028import service.tut.pori.contentanalysis.PhotoDAO; 029import service.tut.pori.contentanalysis.PhotoList; 030import service.tut.pori.contentanalysis.MediaObject; 031import service.tut.pori.contentanalysis.MediaObjectList; 032import service.tut.pori.contentstorage.FacebookDAO; 033import service.tut.pori.contentstorage.FacebookPhotoStorage; 034import service.tut.pori.contentstorage.FacebookPhotoStorage.FacebookEntry; 035import service.tut.pori.facebookjazz.WeightModifier.WeightModifierType; 036import service.tut.pori.users.google.OAuth2Token; 037import service.tut.pori.users.facebook.FacebookUserDAO; 038 039import com.restfb.Connection; 040import com.restfb.DefaultFacebookClient; 041import com.restfb.Facebook; 042import com.restfb.Parameter; 043import com.restfb.Version; 044import com.restfb.types.CategorizedFacebookType; 045import com.restfb.types.Comment; 046import com.restfb.types.Event; 047import com.restfb.types.Group; 048import com.restfb.types.StatusMessage; 049import com.restfb.types.User; 050import com.restfb.types.Video; 051 052import core.tut.pori.context.ServiceInitializer; 053import core.tut.pori.http.parameters.DataGroups; 054import core.tut.pori.users.UserIdentity; 055 056 057/** 058 * A high-level client implementation for retrieving user's Facebook profile contents. 059 */ 060public final class FacebookExtractor { 061 /** default maximum item limit for the extractor */ 062 public static final int DEFAULT_LIMIT = 200; 063 private static final Logger LOGGER = Logger.getLogger(FacebookExtractor.class); 064 065 /* objects */ 066 private static final String OBJECT_USER_DETAIL= "me"; 067 068 /* connections */ 069 private static final String CONNECTION_COMMENTS = "comments"; 070 private static final String CONNECTION_EVENTS = "events"; 071 private static final String CONNECTION_GROUPS = "groups"; 072 private static final String CONNECTION_LIKES = "likes"; 073 private static final String CONNECTION_PHOTOS = "photos"; 074 private static final String CONNECTION_STATUSES = "statuses"; 075 private static final String CONNECTION_USER_EVENTS = OBJECT_USER_DETAIL+"/"+CONNECTION_EVENTS; 076 private static final String CONNECTION_USER_GROUPS = OBJECT_USER_DETAIL+"/"+CONNECTION_GROUPS; 077 private static final String CONNECTION_USER_LIKES = OBJECT_USER_DETAIL+"/"+CONNECTION_LIKES; 078 private static final String CONNECTION_USER_PHOTOS = OBJECT_USER_DETAIL+"/"+CONNECTION_PHOTOS+"/uploaded"; 079 private static final String CONNECTION_USER_STATUSES = OBJECT_USER_DETAIL+"/"+CONNECTION_STATUSES; 080 081 /* parameters */ 082 private static final String PARAMETER_LIMIT = "limit"; 083 084 /* FQL QUERIES */ 085 private static final String FQL_SELECT_USER_DETAILS = "SELECT uid, name FROM user WHERE uid=me()"; 086 private static final String FQL_SELECT_VIDEO_DETAILS = "SELECT owner, title, description, updated_time, created_time, vid FROM video WHERE owner=me()"; 087 088 private DefaultFacebookClient _client = null; 089 private UserIdentity _userId = null; 090 private WeightModifierList _userTagWeights = null; 091 private WeightModifierList _defaultTagWeights = null; 092 093 /** 094 * 095 * Valid content types for a profile 096 * 097 */ 098 public enum ContentType{ 099 /** Facebook events */ 100 EVENTS, 101 /** Include tags generated by other back-ends. Can only be used in combination with PHOTO_DESCRIPTION */ 102 GENERATED_TAGS, 103 /** Facebook groups */ 104 GROUPS, 105 /** Facebook likes */ 106 LIKES, 107 /** Descriptions generated from Facebook photos/status messages */ 108 PHOTO_DESCRIPTIONS, 109 /** Facebook status messages */ 110 STATUS_MESSAGES, 111 /** Descriptions generated from Facebook videos/status messages */ 112 VIDEO_DESCRIPTIONS; 113 114 /** 115 * 116 * @param values 117 * @return values converted to content types 118 * @throws IllegalArgumentException 119 */ 120 public static EnumSet<ContentType> fromString(List<String> values) throws IllegalArgumentException { 121 EnumSet<ContentType> contentTypes = null; 122 if(values != null && !values.isEmpty()){ 123 contentTypes = EnumSet.noneOf(ContentType.class); 124 for(String value : values){ 125 ContentType found = null; 126 for(ContentType t : values()){ 127 if(t.name().equalsIgnoreCase(value)){ 128 found = t; 129 break; 130 } 131 } // for types 132 if(found == null){ 133 throw new IllegalArgumentException("Bad ContentType: "+value); 134 } 135 contentTypes.add(found); 136 } // for values 137 } 138 return contentTypes; 139 } 140 } // enum ContentType 141 142 /** 143 * 144 * @param userId 145 * @return the extractor or null on failure 146 */ 147 public static FacebookExtractor getExtractor(UserIdentity userId){ 148 FacebookExtractor extractor = null; 149 OAuth2Token token = ServiceInitializer.getDAOHandler().getSQLDAO(FacebookUserDAO.class).getToken(userId); 150 if(token == null){ 151 LOGGER.debug("No token."); 152 return null; 153 } 154 extractor = new FacebookExtractor(userId); 155 extractor._client = new DefaultFacebookClient(token.getAccessToken(), Version.UNVERSIONED); 156 return extractor; 157 } 158 159 /** 160 * 161 * @param userId 162 * 163 */ 164 private FacebookExtractor(UserIdentity userId){ 165 _userId = userId; 166 } 167 168 /** 169 * 170 * @return user status messages, if any 171 * 172 */ 173 public List<FacebookStatusMessage> getStatusMessages(){ 174 Connection<StatusMessage> statusConnection = _client.fetchConnection(CONNECTION_USER_STATUSES, StatusMessage.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 175 //also possible to use a timeframe: Parameter.with("since", new Date(1)),Parameter.with("until", new Date()) 176 List<StatusMessage> messages = statusConnection.getData(); 177 if(messages.isEmpty()){ // nothing received 178 return null; 179 } 180 181 List<FacebookStatusMessage> retval = new ArrayList<>();//restfb's lists do not support addAll 182 retval.addAll(FacebookStatusMessage.getFacebookStatusMessages(messages)); 183 184 int received = messages.size(); // compare against the default limit to see if there are more messages 185 while(received == DEFAULT_LIMIT){ // connection.hasNext(), just like the FB's JSON next links cannot be trusted 186 statusConnection = _client.fetchConnectionPage(statusConnection.getNextPageUrl(), StatusMessage.class); 187 messages = statusConnection.getData(); 188 received = messages.size(); 189 if(received > 0){ 190 retval.addAll(FacebookStatusMessage.getFacebookStatusMessages(messages)); 191 } 192 } 193 194 if(retval.isEmpty()){ 195 return null; 196 }else{ 197 Integer messageWeight = getWeight(WeightModifierType.STATUS_MESSAGE__MESSAGE); 198 Integer commentWeight = getWeight(WeightModifierType.STATUS_MESSAGE__COMMENT_MESSAGE); 199 if(messageWeight == null && commentWeight == null){ 200 LOGGER.warn("No "+WeightModifierType.STATUS_MESSAGE__MESSAGE.name()+" or "+WeightModifierType.STATUS_MESSAGE__COMMENT_MESSAGE.name()); 201 return retval; 202 } 203 for(FacebookStatusMessage message : retval){ 204 message.setMessageWeight(messageWeight); 205 setCommentWeights(commentWeight, message.getMessageComments()); 206 } 207 return retval; 208 } 209 } 210 211 /** 212 * helper method for setting the comment weights 213 * @param list 214 * @param commentWeight 215 */ 216 private void setCommentWeights(Integer commentWeight, List<FacebookComment> list){ 217 if(list != null && !list.isEmpty()){ 218 for(FacebookComment c : list){ 219 c.setMessageWeight(commentWeight); 220 } 221 } 222 } 223 224 /** 225 * 226 * @param type 227 * @return weight value for the type 228 */ 229 private Integer getWeight(WeightModifierType type){ 230 if(_userTagWeights == null){ 231 _userTagWeights = ServiceInitializer.getDAOHandler().getSQLDAO(FacebookJazzDAO.class).getWeightModifiers(_userId); 232 if(_userTagWeights == null){ 233 LOGGER.debug("No user tag weights available."); 234 _userTagWeights = new WeightModifierList(); 235 } 236 } 237 238 Integer value = _userTagWeights.getModifier(type); 239 if(value == null){ 240 if(_defaultTagWeights == null){ 241 _defaultTagWeights = ServiceInitializer.getDAOHandler().getSQLDAO(FacebookJazzDAO.class).getWeightModifiers(null); 242 } 243 if(_defaultTagWeights == null){ 244 LOGGER.debug("No default tag weights available."); 245 _defaultTagWeights = new WeightModifierList(); 246 return null; 247 } 248 value = _defaultTagWeights.getModifier(type); 249 } 250 return value; 251 } 252 253 /** 254 * Note: this will only retrieve first {@value service.tut.pori.facebookjazz.FacebookExtractor#DEFAULT_LIMIT} videos 255 * 256 * @return descriptions or null if none found 257 */ 258 public List<FacebookVideoDescription> getVideoDescriptions(){ 259 List<ExtractedVideo> videos = _client.executeFqlQuery(FQL_SELECT_VIDEO_DETAILS, ExtractedVideo.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 260 261 if(videos.isEmpty()){ 262 return null; 263 } 264 265 List<User> users = _client.executeFqlQuery(FQL_SELECT_USER_DETAILS, User.class); 266 String userName = null; 267 if(!users.isEmpty()){ 268 userName = users.get(0).getName(); 269 } 270 271 StringBuilder likeBuilder = new StringBuilder("SELECT object_id FROM like WHERE object_id IN("); 272 List<FacebookVideoDescription> retval = new ArrayList<>(); 273 for(Iterator<ExtractedVideo> iter = videos.iterator();iter.hasNext();){ 274 FacebookVideoDescription desc = new FacebookVideoDescription(iter.next()); 275 likeBuilder.append('\''); 276 likeBuilder.append(desc.getId()); // construct fql for like count retrieval 277 likeBuilder.append("',"); 278 if(desc.isValid()){ 279 desc.setFromName(userName); 280 retval.add(desc); 281 } 282 } 283 284 if(retval.isEmpty()){ 285 return null; 286 }else{ 287 Integer descriptionWeight = getWeight(WeightModifierType.VIDEO_DESCRIPTION__DESCRIPTION); 288 if(descriptionWeight == null){ 289 LOGGER.warn("No "+WeightModifierType.VIDEO_DESCRIPTION__DESCRIPTION.name()); 290 } 291 292 likeBuilder.setCharAt(likeBuilder.length()-1, ')'); 293 List<ObjectId> counts = new ArrayList<>(_client.executeFqlQuery(likeBuilder.toString(), ObjectId.class)); // create new to allow modifications to the list 294 for(Iterator<FacebookVideoDescription> vdIter = retval.iterator();vdIter.hasNext();){ 295 FacebookVideoDescription d = vdIter.next(); 296 d.setDescriptionWeight(descriptionWeight); // set weight 297 retrieveComments(d); // retrieve comments 298 String objectId = d.getId(); 299 long count = 0; 300 // go through the count list, make sure every description gets a like count value 301 for(Iterator<ObjectId> vIter = counts.iterator();vIter.hasNext();){ 302 ObjectId c = vIter.next(); 303 if(c.getObjectId().equals(objectId)){ 304 ++count; 305 vIter.remove(); 306 } 307 } 308 d.setLikeCount(count); 309 } // for descriptions 310 311 return retval; 312 } 313 } 314 315 /** 316 * 317 * @param desc the list of comments will be set to the desc if any are found 318 */ 319 private void retrieveComments(FacebookVideoDescription desc){ 320 String objectId = desc.getId(); 321 if(StringUtils.isBlank(objectId)){ 322 LOGGER.warn("Could not retrieve comments, objectId was missing."); 323 return; 324 } 325 Connection<Comment> commentConnection = _client.fetchConnection(objectId+'/'+CONNECTION_COMMENTS, Comment.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 326 //also possible to use a timeframe: Parameter.with("since", new Date(1)),Parameter.with("until", new Date()) 327 List<Comment> comments = commentConnection.getData(); 328 if(comments.isEmpty()){ // nothing received 329 LOGGER.debug("No comments for video: "+objectId); 330 return; 331 } 332 List<FacebookComment> retval = new ArrayList<>();//restfb's lists do not support addAll 333 retval.addAll(FacebookComment.getCommentList(comments)); 334 335 int received = comments.size(); // compare against the default limit to see if there are more messages 336 while(received == DEFAULT_LIMIT){ // connection.hasNext(), just like the FB's JSON next links cannot be trusted 337 commentConnection = _client.fetchConnectionPage(commentConnection.getNextPageUrl(), Comment.class); 338 comments = commentConnection.getData(); 339 received = comments.size(); 340 if(received > 0){ 341 retval.addAll(FacebookComment.getCommentList(comments)); 342 } 343 } 344 desc.setDescriptionComments(retval); 345 346 Integer commentWeight = getWeight(WeightModifierType.VIDEO_DESCRIPTION__COMMENT_MESSAGE); 347 if(commentWeight == null){ 348 LOGGER.warn("No "+WeightModifierType.VIDEO_DESCRIPTION__COMMENT_MESSAGE.name()); 349 }else{ 350 setCommentWeights(commentWeight, retval); // set comment weights 351 } 352 } 353 354 /** 355 * 356 * @return likes or null if none found 357 */ 358 public List<FacebookLike> getLikes(){ 359 Connection<CategorizedFacebookType> likeConnection = _client.fetchConnection(CONNECTION_USER_LIKES, CategorizedFacebookType.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 360 361 List<CategorizedFacebookType> likes = likeConnection.getData(); 362 if(likes.isEmpty()){ // nothing received 363 return null; 364 } 365 366 List<FacebookLike> retval = new ArrayList<>();//restfb's lists do not support addAll 367 retval.addAll(FacebookLike.getFacebookLikes(likes)); 368 369 int received = likes.size(); // compare against the default limit to see if there are more messages 370 while(received == DEFAULT_LIMIT){ // connection.hasNext(), just like the FB's JSON next links cannot be trusted 371 likeConnection = _client.fetchConnectionPage(likeConnection.getNextPageUrl(), CategorizedFacebookType.class); 372 likes = likeConnection.getData(); 373 received = likes.size(); 374 if(received > 0){ 375 retval.addAll(FacebookLike.getFacebookLikes(likes)); 376 } 377 } 378 379 if(retval.isEmpty()){ 380 return null; 381 }else{ 382 return retval; 383 } 384 } 385 386 /** 387 * 388 * @return groups or null if none found 389 */ 390 public List<FacebookGroup> getGroups(){ 391 Connection<Group> groupConnection = _client.fetchConnection(CONNECTION_USER_GROUPS, Group.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 392 393 List<Group> groups = groupConnection.getData(); 394 if(groups.isEmpty()){ // nothing received 395 return null; 396 } 397 398 List<FacebookGroup> retval = new ArrayList<>();//restfb's lists do not support addAll 399 retval.addAll(FacebookGroup.getFacebookGroups(groups)); 400 401 int received = groups.size(); // compare against the default limit to see if there are more messages 402 while(received == DEFAULT_LIMIT){ // connection.hasNext(), just like the FB's JSON next links cannot be trusted 403 groupConnection = _client.fetchConnectionPage(groupConnection.getNextPageUrl(), Group.class); 404 groups = groupConnection.getData(); 405 received = groups.size(); 406 if(received > 0){ 407 retval.addAll(FacebookGroup.getFacebookGroups(groups)); 408 } 409 } 410 411 if(retval.isEmpty()){ 412 return null; 413 }else{ 414 Integer nameWeight = getWeight(WeightModifierType.GROUP__NAME); 415 Integer descriptionWeight = getWeight(WeightModifierType.GROUP__DESCRIPTION); 416 if(nameWeight == null && descriptionWeight == null){ 417 LOGGER.warn("No "+WeightModifierType.GROUP__NAME.name()+" or "+WeightModifierType.GROUP__DESCRIPTION.name()); 418 return retval; 419 } 420 421 for(FacebookGroup g : retval){ 422 g.setDescriptionWeight(descriptionWeight); 423 g.setNameWeight(nameWeight); 424 } 425 426 return retval; 427 } 428 } 429 430 /** 431 * 432 * @return events or null if none was found 433 */ 434 public List<FacebookEvent> getEvents(){ 435 Connection<Event> eventConnection = _client.fetchConnection(CONNECTION_USER_EVENTS, Event.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 436 437 List<Event> events = eventConnection.getData(); 438 if(events.isEmpty()){ // nothing received 439 return null; 440 } 441 442 List<FacebookEvent> retval = new ArrayList<>();//restfb's lists do not support addAll 443 retval.addAll(FacebookEvent.getFacebookEvents(events)); 444 445 int received = events.size(); // compare against the default limit to see if there are more messages 446 while(received == DEFAULT_LIMIT){ // connection.hasNext(), just like the FB's JSON next links cannot be trusted 447 eventConnection = _client.fetchConnectionPage(eventConnection.getNextPageUrl(), Event.class); 448 events = eventConnection.getData(); 449 received = events.size(); 450 if(received > 0){ 451 retval.addAll(FacebookEvent.getFacebookEvents(events)); 452 } 453 } 454 455 if(retval.isEmpty()){ 456 return null; 457 }else{ 458 Integer descriptionWeight = getWeight(WeightModifierType.EVENT__DESCRIPTION); 459 Integer nameWeight = getWeight(WeightModifierType.EVENT__NAME); 460 if(nameWeight == null && descriptionWeight == null){ 461 LOGGER.warn("No "+WeightModifierType.EVENT__DESCRIPTION.name()+" or "+WeightModifierType.EVENT__NAME.name()); 462 return retval; 463 } 464 465 for(FacebookEvent e : retval){ 466 e.setDescriptionWeight(descriptionWeight); 467 e.setNameWeight(nameWeight); 468 } 469 470 return retval; 471 } 472 } 473 474 /** 475 * This method will not return generated tags 476 * 477 * @return list of photo descriptions, ignoring photos without descriptions or comments 478 */ 479 public List<FacebookPhotoDescription> getPhotoDescriptions(){ 480 return getPhotoDescriptions(false, false); 481 } 482 483 /** 484 * 485 * @param generatedTags if true the previously generated tags will be retrieved from the database and will be included in the results 486 * @param includeEmpty 487 * @return descriptions or null if none was found 488 */ 489 public List<FacebookPhotoDescription> getPhotoDescriptions(boolean generatedTags, boolean includeEmpty){ 490 Connection<com.restfb.types.Photo> photoConnection = _client.fetchConnection(CONNECTION_USER_PHOTOS, com.restfb.types.Photo.class, Parameter.with(PARAMETER_LIMIT, DEFAULT_LIMIT)); 491 List<com.restfb.types.Photo> photos = photoConnection.getData(); 492 if(photos.isEmpty()){ // nothing received 493 return null; 494 } 495 496 List<FacebookPhotoDescription> retval = new ArrayList<>();//for addAll 497 for(Iterator<com.restfb.types.Photo> iter = photos.iterator();iter.hasNext();){ 498 FacebookPhotoDescription photo = new FacebookPhotoDescription(iter.next()); 499 if(includeEmpty || photo.isValid()){ 500 retval.add(photo); 501 } 502 } 503 504 int received = photos.size(); // compare against the default limit to see if there are more messages 505 while(received == DEFAULT_LIMIT){ // connection.hasNext(), just like the FB's JSON next links cannot be trusted 506 photoConnection = _client.fetchConnectionPage(photoConnection.getNextPageUrl(),com.restfb.types.Photo.class); 507 photos = photoConnection.getData(); 508 509 for(Iterator<com.restfb.types.Photo> iter = photos.iterator();iter.hasNext();){ 510 FacebookPhotoDescription photo = new FacebookPhotoDescription(iter.next()); 511 if(includeEmpty || photo.isValid()){ 512 retval.add(photo); 513 } // if 514 } // for 515 } // while 516 517 if(retval.isEmpty()){ 518 return null; 519 }else{ 520 Integer descriptionWeight = getWeight(WeightModifierType.PHOTO_DESCRIPTION__DESCRIPTION); 521 Integer commentWeight = getWeight(WeightModifierType.PHOTO_DESCRIPTION__COMMENT_MESSAGE); 522 if(commentWeight == null && descriptionWeight == null){ 523 LOGGER.warn("No "+WeightModifierType.PHOTO_DESCRIPTION__DESCRIPTION.name()+" or "+WeightModifierType.PHOTO_DESCRIPTION__COMMENT_MESSAGE.name()); 524 return retval; 525 } 526 527 List<String> objectIds = new ArrayList<>(retval.size()); 528 for(FacebookPhotoDescription d : retval){ 529 d.setDescriptionWeight(descriptionWeight); 530 setCommentWeights(commentWeight, d.getDescriptionComments()); 531 objectIds.add(d.getId()); 532 } 533 534 List<FacebookEntry> entries = ServiceInitializer.getDAOHandler().getSQLDAO(FacebookDAO.class).getEntries(objectIds, _userId); 535 if(entries == null){ 536 LOGGER.debug("None of the photos are known by the system."); 537 }else{ 538 PhotoList gTags = null; 539 if(generatedTags){ 540 LOGGER.debug("Retrieving generated tags."); 541 List<String> guids = new ArrayList<>(entries.size()); 542 for(FacebookEntry e : entries){ 543 guids.add(e.getGUID()); 544 } 545 gTags = ServiceInitializer.getDAOHandler().getSolrDAO(PhotoDAO.class).getPhotos(new DataGroups(Definitions.DATA_GROUP_KEYWORDS), guids, null, null, null); 546 } 547 548 LOGGER.debug("Resolving photo GUIDs for descriptions."); 549 for(FacebookPhotoDescription d : retval){ 550 String objectId = d.getId(); 551 for(Iterator<FacebookEntry> eIter = entries.iterator(); eIter.hasNext();){ 552 FacebookEntry e = eIter.next(); 553 if(e.getObjectId().equals(objectId)){ // if the new description was found in the list of already known entries 554 String guid = e.getGUID(); 555 d.setPhotoGUID(guid); 556 d.setServiceType(FacebookPhotoStorage.SERVICE_TYPE); // no need to check from database, all photos from TwitterDAO entries are of the same type 557 if(gTags != null){ // if tags were found 558 Photo p = gTags.getPhoto(guid); // in practice this should always return a photo... 559 if(p != null){ 560 MediaObjectList objects = p.getMediaObjects(); 561 if(!MediaObjectList.isEmpty(objects)){ 562 for(MediaObject vo : objects.getMediaObjects()){ 563 d.addTag(FacebookPhotoTag.getFacebookTag(vo)); 564 } // for 565 } // if photo had media objects 566 }else{ // ..though there is theoretical possibility that the photo has been removed in between retrievals (which are not in a transaction), and were not found anymore 567 LOGGER.warn("No photo found, GUID: "+guid); 568 d.setPhotoGUID(null); // not valid anymore 569 } // else 570 } // if 571 eIter.remove(); 572 break; 573 } // if 574 } // for entries 575 } // for descriptions 576 } 577 578 return retval; 579 } 580 } 581 582 /** 583 * @return the userId 584 */ 585 public UserIdentity getUserId() { 586 return _userId; 587 } 588 589 /** 590 * 591 * @param contentTypes list of content types, by default only the basic user details are returned 592 * @return the extracted profile 593 * @throws IllegalArgumentException on incompatible content types 594 */ 595 public FacebookProfile getProfile(EnumSet<ContentType> contentTypes) throws IllegalArgumentException{ 596 FacebookUserDetails user = new FacebookUserDetails(_client.fetchObject(OBJECT_USER_DETAIL, User.class)); 597 user.setUserId(_userId); 598 FacebookProfile profile = new FacebookProfile(user); 599 600 if(contentTypes != null && !contentTypes.isEmpty()){ 601 boolean generatedTags = contentTypes.contains(ContentType.GENERATED_TAGS); 602 if(generatedTags && contentTypes.size() == 1){ 603 throw new IllegalArgumentException("Only "+ContentType.GENERATED_TAGS.name()+" given."); 604 } 605 606 if(contentTypes.contains(ContentType.STATUS_MESSAGES)){ 607 profile.setStatusMessages(getStatusMessages()); 608 } 609 if(contentTypes.contains(ContentType.LIKES)){ 610 profile.setLikes(getLikes()); 611 } 612 if(contentTypes.contains(ContentType.EVENTS)){ 613 profile.setEvents(getEvents()); 614 } 615 if(contentTypes.contains(ContentType.GROUPS)){ 616 profile.setGroups(getGroups()); 617 } 618 if(contentTypes.contains(ContentType.VIDEO_DESCRIPTIONS)){ 619 profile.setVideoDescriptions(getVideoDescriptions()); 620 } 621 if(contentTypes.contains(ContentType.PHOTO_DESCRIPTIONS)){ 622 profile.setPhotoDescriptions(getPhotoDescriptions(generatedTags, false)); 623 } 624 }else{ 625 LOGGER.debug("No content types requested."); 626 } 627 return profile; 628 } 629 630 /** 631 * 632 * Helper class for extracting a list of object ids 633 * 634 */ 635 private static class ObjectId{ 636 @Facebook(value = "object_id") 637 private String _objectId = null; 638 639 /** 640 * 641 * @return object id value 642 */ 643 public String getObjectId(){ 644 return _objectId; 645 } 646 } 647 648 /** 649 * Extended to contain video id from FQL queries 650 * 651 */ 652 public static class ExtractedVideo extends Video{ 653 /** serial version UID */ 654 private static final long serialVersionUID = 4183565698345218940L; 655 @Facebook(value="vid") 656 private String _objectId = null; 657 658 /** 659 * @return object id (vid) or id if no object id is given 660 */ 661 @Override 662 public String getId() { 663 return (StringUtils.isBlank(_objectId) ? super.getId() : _objectId); 664 } 665 666 } // class ExtractedVideo 667}