package org.atlasapi.persistence.media.entity;
import java.util.List;
import java.util.Set;
import com.google.common.collect.ComparisonChain;
import com.google.common.collect.Ordering;
import org.atlasapi.media.entity.Certificate;
import org.atlasapi.media.entity.Clip;
import org.atlasapi.media.entity.Content;
import org.atlasapi.media.entity.ContentGroupRef;
import org.atlasapi.media.entity.CrewMember;
import org.atlasapi.media.entity.EventRef;
import org.atlasapi.media.entity.KeyPhrase;
import org.atlasapi.media.entity.TopicRef;
import org.atlasapi.media.entity.Version;
import org.atlasapi.persistence.ModelTranslator;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.metabroadcast.common.ids.NumberToShortStringCodec;
import com.metabroadcast.common.intl.Countries;
import com.metabroadcast.common.persistence.mongo.MongoConstants;
import com.metabroadcast.common.persistence.translator.TranslatorUtils;
import com.mongodb.BasicDBList;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import javax.annotation.Nullable;
import static com.google.common.base.Preconditions.checkNotNull;
public class ContentTranslator implements ModelTranslator<Content> {
public static final String PEOPLE = "people";
public static String CLIPS_KEY = "clips";
public static String TOPICS_KEY = "topics";
private static final String PHRASES_KEY = "phrases";
private static String CONTENT_GROUP_KEY = "contentGroups";
private static final String CERTIFICATES_KEY = "certificates";
private static final String LANGUAGES_KEY = "languages";
private static final String YEAR_KEY = "year";
private static final String GENERIC_DESCRIPTION_KEY = "genericDescription";
private static final String SIMILAR_CONTENT_KEY = "similar";
private static final String EVENTS_KEY = "events";
private static final String EDITORIAL_PRIORITY_KEY = "editorialPriority";
private static final String VERSIONS_KEY = "versions";
private static final String AWARDS = "awards";
private final ClipTranslator clipTranslator;
private final KeyPhraseTranslator keyPhraseTranslator;
private final DescribedTranslator describedTranslator;
private final TopicRefTranslator contentTopicTranslator;
private final ContentGroupRefTranslator contentGroupRefTranslator;
private final CrewMemberTranslator crewMemberTranslator;
private final SimilarContentRefTranslator similarContentRefTranslator;
private final VersionTranslator versionTranslator;
private final EventRefTranslator eventRefTranslator;
private final AwardTranslator awardTranslator;
public ContentTranslator(NumberToShortStringCodec idCodec) {
this(new DescribedTranslator(new IdentifiedTranslator(), new ImageTranslator()), new ClipTranslator(idCodec), new VersionTranslator(idCodec));
}
//TODO: why not use collaborators interface here? ModelTranslator<Described> etc...
public ContentTranslator(DescribedTranslator describedTranslator, ClipTranslator clipTranslator, VersionTranslator versionTranslator) {
this.describedTranslator = checkNotNull(describedTranslator);
this.clipTranslator = checkNotNull(clipTranslator);
this.keyPhraseTranslator = new KeyPhraseTranslator();
this.contentTopicTranslator = new TopicRefTranslator();
this.contentGroupRefTranslator = new ContentGroupRefTranslator();
this.crewMemberTranslator = new CrewMemberTranslator();
this.similarContentRefTranslator = new SimilarContentRefTranslator();
this.versionTranslator = checkNotNull(versionTranslator);
this.eventRefTranslator = new EventRefTranslator();
this.awardTranslator = new AwardTranslator();
}
@Override
public Content fromDBObject(DBObject dbObject, Content entity) {
describedTranslator.fromDBObject(dbObject, entity);
decodeClips(dbObject, entity);
decodeKeyPhrases(dbObject, entity);
decodeTopics(dbObject, entity);
decodeContentGroups(dbObject, entity);
decodeLanguages(dbObject, entity);
decodeEvents(dbObject, entity);
decodeCertificates(dbObject, entity);
entity.setYear(TranslatorUtils.toInteger(dbObject, YEAR_KEY));
entity.setGenericDescription(TranslatorUtils.toBoolean(dbObject, GENERIC_DESCRIPTION_KEY));
entity.setSimilarContent(similarContentRefTranslator.fromDBObjects(TranslatorUtils.toDBObjectList(dbObject, SIMILAR_CONTENT_KEY)));
entity.setEditorialPriority(TranslatorUtils.toInteger(dbObject, EDITORIAL_PRIORITY_KEY));
List<DBObject> list = TranslatorUtils.toDBObjectList(dbObject, PEOPLE);
if (list != null && ! list.isEmpty()) {
for (DBObject dbPerson: list) {
CrewMember crewMember = crewMemberTranslator.fromDBObject(dbPerson, null);
if (crewMember != null) {
entity.addPerson(crewMember);
}
}
}
List<DBObject> versionList = TranslatorUtils.toDBObjectList(dbObject, VERSIONS_KEY);
if (versionList != null && ! versionList.isEmpty()) {
Set<Version> versions = Sets.newHashSet();
for (DBObject versionDbo: versionList) {
if (versionDbo == null) {
throw new IllegalStateException("Cannot read item stored with null version: " + entity.getCanonicalUri());
}
Version version = versionTranslator.fromDBObject(versionDbo, null);
versions.add(version);
}
entity.setVersions(versions);
}
if(dbObject.containsField(AWARDS)) {
entity.setAwards(awardTranslator.fromDBObjects(
TranslatorUtils.toDBObjectList(dbObject, AWARDS)));
}
return entity;
}
protected void decodeLanguages(DBObject dbObject, Content entity) {
if (dbObject.containsField(LANGUAGES_KEY)) {
entity.setLanguages(TranslatorUtils.toSet(dbObject, LANGUAGES_KEY));
}
}
protected void decodeCertificates(DBObject dbObject, Content entity) {
if (dbObject.containsField(CERTIFICATES_KEY)) {
List<DBObject> dbos = TranslatorUtils.toDBObjectList(dbObject, CERTIFICATES_KEY);
entity.setCertificates(Iterables.transform(dbos, certificateFromDbo));
}
}
private final Function<DBObject, Certificate> certificateFromDbo = new Function<DBObject, Certificate>() {
@Override
public Certificate apply(DBObject input) {
return new Certificate(TranslatorUtils.toString(input, "class"),Countries.fromCode(TranslatorUtils.toString(input, "country")));
}
};
@SuppressWarnings("unchecked")
private void decodeContentGroups(DBObject dbObject, Content entity) {
if (dbObject.containsField(CONTENT_GROUP_KEY)) {
entity.setContentGroupRefs(Iterables.transform((Iterable<DBObject>) dbObject.get(CONTENT_GROUP_KEY), new Function<DBObject, ContentGroupRef>() {
@Override
public ContentGroupRef apply(DBObject input) {
return contentGroupRefTranslator.fromDBObject(input);
}
}));
}
}
@SuppressWarnings("unchecked")
private void decodeTopics(DBObject dbObject, Content entity) {
if (dbObject.containsField(TOPICS_KEY)) {
entity.setTopicRefs(Iterables.transform((Iterable<DBObject>) dbObject.get(TOPICS_KEY), new Function<DBObject, TopicRef>() {
@Override
public TopicRef apply(DBObject input) {
return contentTopicTranslator.fromDBObject(input);
}
}));
}
}
private void decodeEvents(DBObject dbObject, Content entity) {
if(dbObject.containsField(EVENTS_KEY)) {
entity.setEventRefs(Iterables.transform((Iterable<DBObject>) dbObject.get(EVENTS_KEY), new Function<DBObject, EventRef>() {
@Override
public EventRef apply(DBObject dbObject) {
return eventRefTranslator.fromDBObject(dbObject);
}
}));
}
}
@SuppressWarnings("unchecked")
private void decodeKeyPhrases(DBObject dbObject, Content entity) {
if (dbObject.containsField(PHRASES_KEY)) {
entity.setKeyPhrases(Iterables.transform((Iterable<DBObject>) dbObject.get(PHRASES_KEY), new Function<DBObject, KeyPhrase>() {
@Override
public KeyPhrase apply(DBObject input) {
return keyPhraseTranslator.fromDBObject(input);
}
}));
}
}
@SuppressWarnings("unchecked")
private void decodeClips(DBObject dbObject, Content entity) {
if (dbObject.containsField(CLIPS_KEY)) {
Iterable<DBObject> clipsDbos = (Iterable<DBObject>) dbObject.get(CLIPS_KEY);
List<Clip> clips = Lists.newArrayList();
for (DBObject dbo : clipsDbos) {
clips.add(clipTranslator.fromDBObject(dbo, null));
}
entity.setClips(clips);
}
}
@Override
public DBObject toDBObject(DBObject dbObject, Content entity) {
dbObject = describedTranslator.toDBObject(dbObject, entity);
encodeClips(dbObject, entity);
encodeKeyPhrases(dbObject, entity);
encodeTopics(dbObject, entity);
encodeContentGroups(dbObject, entity);
encodeLanguages(dbObject, entity);
encodeEvents(dbObject, entity);
encodeCertificates(dbObject, entity.getCertificates());
TranslatorUtils.from(dbObject, YEAR_KEY, entity.getYear());
TranslatorUtils.from(dbObject, GENERIC_DESCRIPTION_KEY, entity.getGenericDescription());
TranslatorUtils.from(dbObject, EDITORIAL_PRIORITY_KEY, entity.getEditorialPriority());
TranslatorUtils.from(dbObject, SIMILAR_CONTENT_KEY, similarContentRefTranslator.toDBList(entity.getSimilarContent()));
if (! entity.people().isEmpty()) {
BasicDBList list = new BasicDBList();
for (CrewMember person: entity.people()) {
list.add(crewMemberTranslator.toDBObject(null, person));
}
dbObject.put(PEOPLE, list);
}
if (!entity.getVersions().isEmpty()) {
BasicDBList list = new BasicDBList();
for (Version version: VERSION_ORDERING.immutableSortedCopy(entity.getVersions())) {
if (version == null) {
throw new IllegalArgumentException("Cannot save item with null version: " + entity.getCanonicalUri());
}
list.add(versionTranslator.toDBObject(null, version));
}
dbObject.put(VERSIONS_KEY, list);
}
dbObject.put(AWARDS, awardTranslator.toDBList(entity.getAwards()));
return dbObject;
}
protected void encodeLanguages(DBObject dbObject, Content entity) {
if (!entity.getLanguages().isEmpty()) {
TranslatorUtils.fromSet(dbObject, entity.getLanguages(), LANGUAGES_KEY);
}
}
private void encodeCertificates(DBObject dbo, Set<Certificate> certificates) {
if(!certificates.isEmpty()) {
BasicDBList values = new BasicDBList();
for(Certificate releaseDate : certificates) {
DBObject certDbo = new BasicDBObject();
TranslatorUtils.from(certDbo, "class", releaseDate.classification());
TranslatorUtils.from(certDbo, "country", releaseDate.country().code());
values.add(certDbo);
}
dbo.put(CERTIFICATES_KEY, values);
}
}
private void encodeContentGroups(DBObject dbObject, Content entity) {
if (!entity.getContentGroupRefs().isEmpty()) {
BasicDBList values = new BasicDBList();
for(ContentGroupRef contentGroupRef : entity.getContentGroupRefs()) {
values.add(contentGroupRefTranslator.toDBObject(contentGroupRef));
}
dbObject.put(CONTENT_GROUP_KEY, values);
}
}
private void encodeTopics(DBObject dbObject, Content entity) {
if (!entity.getTopicRefs().isEmpty()) {
BasicDBList values = new BasicDBList();
for(TopicRef topicRef : entity.getTopicRefs()) {
values.add(contentTopicTranslator.toDBObject(topicRef));
}
dbObject.put(TOPICS_KEY, values);
}
}
private void encodeEvents(DBObject dbObject, Content entity) {
if(!entity.events().isEmpty()) {
BasicDBList values = new BasicDBList();
for(EventRef eventRef : entity.events()){
values.add(eventRefTranslator.toDBObject(eventRef));
}
dbObject.put(EVENTS_KEY, values);
}
}
private void encodeClips(DBObject dbObject, Content entity) {
if (!entity.getClips().isEmpty()) {
BasicDBList clipDbos = new BasicDBList();
for (Clip clip : entity.getClips()) {
clipDbos.add(clipTranslator.toDBObject(new BasicDBObject(), clip));
}
dbObject.put(CLIPS_KEY, clipDbos);
}
}
private void encodeKeyPhrases(DBObject dbObject, Content entity) {
if (!entity.getKeyPhrases().isEmpty()) {
BasicDBList values = new BasicDBList();
for(KeyPhrase phrase : entity.getKeyPhrases()) {
values.add(keyPhraseTranslator.toDBObject(phrase));
}
dbObject.put(PHRASES_KEY, values);
}
}
@SuppressWarnings("unchecked")
public void removeFieldsForHash(DBObject dbObject) {
describedTranslator.removeFieldsForHash(dbObject);
Iterable<DBObject> clips = (Iterable<DBObject>) dbObject.get(ContentTranslator.CLIPS_KEY);
if (clips != null) {
Set<DBObject> unorderedClips = Sets.newHashSet();
for (DBObject clipDbo : clips) {
clipTranslator.removeFieldsForHash(clipDbo);
unorderedClips.add(clipDbo);
}
dbObject.put(ContentTranslator.CLIPS_KEY, unorderedClips);
}
Iterable<DBObject> versions = (Iterable<DBObject>) dbObject.get(VERSIONS_KEY);
if (versions != null) {
dbObject.put(VERSIONS_KEY, removeUpdateTimeFromVersions(versions));
}
}
private static final Ordering<Version> VERSION_ORDERING = new Ordering<Version>() {
@Override
public int compare(Version left, Version right) {
return ComparisonChain.start()
.compare(left.getCanonicalUri(), right.getCanonicalUri(), Ordering.natural().nullsLast())
.compare(left.getCurie(), right.getCurie(), Ordering.natural().nullsLast())
.result();
}
};
@SuppressWarnings("unchecked")
private Set<DBObject> removeUpdateTimeFromVersions(Iterable<DBObject> versions) {
Set<DBObject> unorderedVersions = Sets.newHashSet();
for (DBObject versionDbo : versions) {
versionDbo.removeField(IdentifiedTranslator.LAST_UPDATED);
Iterable<DBObject> broadcasts = (Iterable<DBObject>) versionDbo.get(VersionTranslator.BROADCASTS_KEY);
if (broadcasts != null) {
Set<DBObject> unorderedBroadcasts = Sets.newHashSet();
for (DBObject broadcastDbo : broadcasts) {
broadcastDbo.removeField(IdentifiedTranslator.LAST_UPDATED);
unorderedBroadcasts.add(broadcastDbo);
}
versionDbo.put(VersionTranslator.BROADCASTS_KEY, unorderedBroadcasts);
}
Iterable<DBObject> encodings = (Iterable<DBObject>) versionDbo.get(VersionTranslator.ENCODINGS_KEY);
if (encodings != null) {
versionDbo.put(VersionTranslator.ENCODINGS_KEY, removeUpdateTimesFromEncodings(encodings));
}
unorderedVersions.add(versionDbo);
}
return unorderedVersions;
}
@SuppressWarnings("unchecked")
private Set<DBObject> removeUpdateTimesFromEncodings(Iterable<DBObject> encodings) {
Set<DBObject> unorderedEncodings = Sets.newHashSet();
for (DBObject encodingDbo : encodings) {
encodingDbo.removeField(IdentifiedTranslator.LAST_UPDATED);
Iterable<DBObject> locations = (Iterable<DBObject>) encodingDbo.get(EncodingTranslator.LOCATIONS_KEY);
if (locations != null) {
Set<DBObject> unorderedLocations = Sets.newHashSet();
for (DBObject locationDbo : locations) {
locationDbo.removeField(IdentifiedTranslator.LAST_UPDATED);
DBObject policy = (DBObject) locationDbo.get(LocationTranslator.POLICY);
if(policy != null) {
policy.removeField(IdentifiedTranslator.LAST_UPDATED);
}
unorderedLocations.add(locationDbo);
}
encodingDbo.put(EncodingTranslator.LOCATIONS_KEY, unorderedLocations);
}
unorderedEncodings.add(encodingDbo);
}
return unorderedEncodings;
}
}