if (filterSpec.contributorId != null || filterSpec.playerId != null) {
throw new IllegalArgumentException(
"filterSpec.contributorId and filterSpec.playerId should not be set by remote callers."
+ " contributorId = " + filterSpec.contributorId + " playerId = "+ filterSpec.playerId);
}
DisplayContentItemBundle result = Caches.getDisplayContentItemBundle(
livingStoryId, filterSpec, focusedContentItemId, cutoff);
if (result != null) {
return result;
}
FilterSpec localFilterSpec = new FilterSpec(filterSpec);
BaseContentItem focusedContentItem = null;
if (focusedContentItemId != null) {
focusedContentItem = getContentItem(focusedContentItemId, false);
if (focusedContentItem != null) {
if (adjustFilterSpecForContentItem(localFilterSpec, focusedContentItem)) {
// If we had to adjust the filter spec to accommodate the focused content item,
// we'll be switching filter views, so we want to clear the start date
// and reload the list from the beginning.
cutoff = null;
}
}
}
// Some preliminaries. Note that the present implementation just filters all content items for
// a story, which could be a bit expensive if there's a cache miss. By and large, though,
// we'd expect a lot more cache hits than cache misses, unlike the case with, say,
// a twitter "following" feed, which is more likely to be unique to that user.
List<BaseContentItem> allContentItems = getContentItemsForLivingStory(livingStoryId, true);
Map<Long, BaseContentItem> idToContentItemMap = Maps.newHashMap();
List<BaseContentItem> relevantContentItems = Lists.newArrayList();
for (BaseContentItem contentItem : allContentItems) {
idToContentItemMap.put(contentItem.getId(), contentItem);
Date sortKey = contentItem.getDateSortKey();
boolean matchesStartDate = (cutoff == null) ||
(localFilterSpec.oldestFirst ? !sortKey.before(cutoff) : !sortKey.after(cutoff));
if (matchesStartDate && localFilterSpec.doesContentItemMatch(contentItem)) {
relevantContentItems.add(contentItem);
}
}
sortContentItemList(relevantContentItems, localFilterSpec);
// Need to get the focused content item from the map instead of using the object directly.
// This is because we use indexOf() to find the location of the focused content item in the
// list and the original contentItem isn't the same object instance.
List<BaseContentItem> coreContentItems = getSublist(relevantContentItems,
focusedContentItem == null ? null : idToContentItemMap.get(focusedContentItemId), cutoff);
Set<Long> linkedContentItemIds = Sets.newHashSet();
for (BaseContentItem contentItem : coreContentItems) {
if (contentItem.displayTopLevel()) {
// If a content item isn't a top-level display content item, we can get away without
// returning its linked content items.
linkedContentItemIds.addAll(contentItem.getLinkedContentItemIds());
}
}
Set<BaseContentItem> linkedContentItems = Sets.newHashSet();
for (Long id : linkedContentItemIds) {
BaseContentItem linkedContentItem = idToContentItemMap.get(id);
if (linkedContentItem == null) {
System.err.println("Linked content item with id " + id + " is not found.");
} else {
linkedContentItems.add(linkedContentItem);
// For linked narratives, we want to get their own linked content items as well
if (linkedContentItem.getContentItemType() == ContentItemType.NARRATIVE) {
for (Long linkedToLinkedContentItemId : linkedContentItem.getLinkedContentItemIds()) {
BaseContentItem linkedToLinkedContentItem =
idToContentItemMap.get(linkedToLinkedContentItemId);
if (linkedToLinkedContentItem != null) {
linkedContentItems.add(linkedToLinkedContentItem);
}
}
}
}
}
Date nextDateInSequence = getNextDateInSequence(coreContentItems, relevantContentItems);
result = new DisplayContentItemBundle(coreContentItems, linkedContentItems, nextDateInSequence,
localFilterSpec);
Caches.setDisplayContentItemBundle(livingStoryId, filterSpec, focusedContentItemId, cutoff,
result);
return result;
}