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