🚸 Sort results by pack name first, then bot name
[xudocci.git] / src / main / java / net / pterodactylus / xdcc / ui / stdin / Result.java
1 /*
2  * XdccDownloader - Result.java - Copyright Â© 2013 David Roden
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 package net.pterodactylus.xdcc.ui.stdin;
19
20 import static com.google.common.collect.FluentIterable.from;
21 import static java.util.regex.Pattern.compile;
22
23 import java.util.Collection;
24 import java.util.Comparator;
25 import java.util.function.Predicate;
26 import java.util.regex.Pattern;
27
28 import net.pterodactylus.xdcc.core.Core;
29 import net.pterodactylus.xdcc.data.Bot;
30 import net.pterodactylus.xdcc.data.Download;
31 import net.pterodactylus.xdcc.data.Pack;
32
33 import com.google.common.base.Function;
34 import com.google.common.collect.ComparisonChain;
35
36 /**
37  * Container for result information.
38  *
39  * @author <a href="mailto:bombe@pterodactylus.net">David â€˜Bombe’ Roden</a>
40  */
41 public class Result implements Comparable<Result> {
42
43         private static Predicate<String> matches(String regex) {
44                 Pattern pattern = compile(regex);
45                 return new Predicate<String>() {
46                         @Override
47                         public boolean test(String input) {
48                                 return pattern.matcher(input).find();
49                         }
50                 };
51         }
52
53         private static Comparator<Result> preferredComparator(Predicate<String> preferredName, Function<Result, String> stringExtractor) {
54                 return new Comparator<Result>() {
55                         @Override
56                         public int compare(Result leftResult, Result rightResult) {
57                                 boolean leftStringMatches = preferredName.test(stringExtractor.apply(leftResult));
58                                 boolean rightStringMatches = preferredName.test(stringExtractor.apply(rightResult));
59                                 if (leftStringMatches && !rightStringMatches) {
60                                         return -1;
61                                 } else if (rightStringMatches && !leftStringMatches) {
62                                         return 1;
63                                 }
64                                 return 0;
65                         }
66                 };
67         }
68
69         /** {@link Comparator} for {@link Result}s that sorts archives to the back of the list. */
70         private static final Comparator<Result> packArchiveComparator = preferredComparator(matches("(rar|tar|zip|tar\\.(gz|bz2|lzma)|7z)$").negate(), (result) -> result.pack().name());
71
72         /** {@link Comparator} for bot nicknames. */
73         private static final Comparator<Result> botNameComparator =
74                         sortEuropeanBotsToTheFront().thenComparing(sortAmericanBotsToTheBack()).thenComparing(sortPassiveBotsToTheBack());
75
76         private static Comparator<Result> sortEuropeanBotsToTheFront() {
77                 return preferredComparator(matches("(?i)[^\\w]EUR?[^\\w]"), (result) -> result.bot().name());
78         }
79
80         private static Comparator<Result> sortAmericanBotsToTheBack() {
81                 return preferredComparator(matches("(?i)[^\\w]USA?[^\\w]").negate(), (result) -> result.bot().name());
82         }
83
84         private static Comparator<Result> sortPassiveBotsToTheBack() {
85                 return preferredComparator(matches("[-|]P[-|]").negate(), (result) -> result.bot().name());
86         }
87
88         private static final Comparator<Result> packNameComparator = sortRepacksFirst().thenComparing(sortPacksAlphabetically());
89
90         private static Comparator<Result> sortRepacksFirst() {
91                 return preferredComparator(matches("\\.(PROPER|REPACK)\\."), (result) -> result.pack().name());
92         }
93
94         private static Comparator<Result> sortPacksAlphabetically() {
95                 return new Comparator<Result>() {
96                         @Override
97                         public int compare(Result leftResult, Result rightResult) {
98                                 return leftResult.pack().name().compareToIgnoreCase(rightResult.pack().name());
99                         }
100                 };
101         }
102
103         /** Comparator that sorts bots with running downloads to the back of the list. */
104         private final Comparator<Result> botsWithRunningTransfersComparator = new Comparator<Result>() {
105                 @Override
106                 public int compare(Result leftResult, Result rightResult) {
107                         Collection<Bot> botsWithTransfers = from(core.downloads()).transform(new Function<Download, Bot>() {
108                                 @Override
109                                 public Bot apply(Download download) {
110                                         return download.bot();
111                                 }
112                         }).toSet();
113                         boolean leftDownloading = botsWithTransfers.contains(leftResult.bot());
114                         boolean rightDownloading = botsWithTransfers.contains(rightResult.bot());
115                         if (leftDownloading && !rightDownloading) {
116                                 return 1;
117                         }
118                         if (!leftDownloading && rightDownloading) {
119                                 return -1;
120                         }
121                         return 0;
122                 }
123         };
124
125         /** The core. */
126         private final Core core;
127
128         /** The bot carrying the pack. */
129         private final Bot bot;
130
131         /** The pack. */
132         private final Pack pack;
133
134         /**
135          * Creates a new result.
136          *
137          * @param core
138          *              The core
139          * @param bot
140          *              The bot carrying the pack
141          * @param pack
142          *              The pack of the result
143          */
144         Result(Core core, Bot bot, Pack pack) {
145                 this.core = core;
146                 this.bot = bot;
147                 this.pack = pack;
148         }
149
150         //
151         // ACCESSORS
152         //
153
154         /**
155          * Returns the bot carrying the pack.
156          *
157          * @return The bot carrying the pack
158          */
159         public Bot bot() {
160                 return bot;
161         }
162
163         /**
164          * Returns the pack.
165          *
166          * @return The pack
167          */
168         public Pack pack() {
169                 return pack;
170         }
171
172         //
173         // COMPARABLE METHODS
174         //
175
176         @Override
177         public int compareTo(Result result) {
178                 return ComparisonChain.start()
179                                 .compare(this, result, botsWithRunningTransfersComparator)
180                                 .compare(this, result, packArchiveComparator)
181                                 .compare(this, result, packNameComparator)
182                                 .compare(this, result, botNameComparator).result();
183         }
184
185 }