001package net.filebot.web; 002 003import static java.util.Arrays.*; 004import static java.util.Collections.*; 005import static java.util.stream.Collectors.*; 006 007import java.io.Serializable; 008import java.net.URL; 009import java.util.Comparator; 010import java.util.List; 011import java.util.Locale; 012import java.util.Objects; 013import java.util.function.Predicate; 014import java.util.stream.Stream; 015 016public class Artwork implements Serializable { 017 018 protected String[] tags; 019 protected URL url; 020 021 protected String language; 022 protected Double rating; 023 024 public Artwork() { 025 // used by serializer 026 } 027 028 public Artwork(Stream<?> tags, URL url, Locale language, Double rating) { 029 this.tags = tags.filter(Objects::nonNull).map(Object::toString).toArray(String[]::new); 030 this.url = url; 031 this.language = getLanguageCode(language); 032 this.rating = rating; 033 } 034 035 public List<String> getTags() { 036 return unmodifiableList(asList(tags)); 037 } 038 039 public URL getUrl() { 040 return url; 041 } 042 043 public Locale getLanguage() { 044 return language == null ? null : Locale.forLanguageTag(language); 045 } 046 047 public double getRating() { 048 return rating == null ? 0 : rating; 049 } 050 051 public boolean has(Predicate<String> predicate) { 052 return stream(tags).anyMatch(predicate); 053 } 054 055 public boolean matches(Object... tags) { 056 if (tags == null || tags.length == 0) { 057 return true; 058 } 059 060 return stream(tags).filter(Objects::nonNull).map(Object::toString).allMatch(tag -> { 061 return tag.equalsIgnoreCase(language) || has(tag::equalsIgnoreCase); 062 }); 063 } 064 065 @Override 066 public int hashCode() { 067 return url.hashCode(); 068 } 069 070 @Override 071 public boolean equals(Object other) { 072 if (other instanceof Artwork) { 073 Artwork artwork = (Artwork) other; 074 return url.sameFile(artwork.url); 075 } 076 return false; 077 } 078 079 @Override 080 public String toString() { 081 return Stream.of(String.join("/", tags), language, rating, url).filter(Objects::nonNull).map(Object::toString).collect(joining(", ", "[", "]")); 082 } 083 084 public static Comparator<Artwork> ratingOrder() { 085 return Comparator.comparing(Artwork::getRating, reverseOrder()); 086 } 087 088 public static Comparator<Artwork> noLanguageOrder() { 089 return Comparator.comparingInt(a -> a.language == null ? 0 : 1); 090 } 091 092 public static Comparator<Artwork> languageOrder(Locale... locale) { 093 // prefer no language 094 if (locale == null || locale.length == 0) { 095 return noLanguageOrder(); 096 } 097 098 // prefer language in order 099 List<String> code = Stream.of(locale).map(Artwork::getLanguageCode).distinct().collect(toList()); 100 101 Comparator<Artwork> languageOrder = Comparator.comparingInt(a -> { 102 int index = code.indexOf(a.language); 103 return index >= 0 ? index : Integer.MAX_VALUE; 104 }); 105 106 // then prefer no language over other languages 107 return languageOrder.thenComparing(noLanguageOrder()); 108 } 109 110 public static Comparator<Artwork> relevanceOrder(Locale... locale) { 111 // prefer language in order 112 Comparator<Artwork> order = languageOrder(locale); 113 114 // then prefer higher rated ones 115 return order.thenComparing(ratingOrder()); 116 } 117 118 private static String getLanguageCode(Locale locale) { 119 return locale == null || locale.getLanguage().isEmpty() ? null : locale.getLanguage(); 120 } 121 122}