Rework bot sorting logic.
[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         /**
89          * {@link Comparator} for {@link Result}s that sorts them by the name of the
90          * {@link Pack}.
91          */
92         private static final Comparator<Result> packNameComparator = new Comparator<Result>() {
93                 @Override
94                 public int compare(Result leftResult, Result rightResult) {
95                         return leftResult.pack().name().compareToIgnoreCase(rightResult.pack().name());
96                 }
97         };
98
99         /** Comparator that sorts bots with running downloads to the back of the list. */
100         private final Comparator<Result> botsWithRunningTransfersComparator = new Comparator<Result>() {
101                 @Override
102                 public int compare(Result leftResult, Result rightResult) {
103                         Collection<Bot> botsWithTransfers = from(core.downloads()).transform(new Function<Download, Bot>() {
104                                 @Override
105                                 public Bot apply(Download download) {
106                                         return download.bot();
107                                 }
108                         }).toSet();
109                         boolean leftDownloading = botsWithTransfers.contains(leftResult.bot());
110                         boolean rightDownloading = botsWithTransfers.contains(rightResult.bot());
111                         if (leftDownloading && !rightDownloading) {
112                                 return 1;
113                         }
114                         if (!leftDownloading && rightDownloading) {
115                                 return -1;
116                         }
117                         return 0;
118                 }
119         };
120
121         /** The core. */
122         private final Core core;
123
124         /** The bot carrying the pack. */
125         private final Bot bot;
126
127         /** The pack. */
128         private final Pack pack;
129
130         /**
131          * Creates a new result.
132          *
133          * @param core
134          *              The core
135          * @param bot
136          *              The bot carrying the pack
137          * @param pack
138          *              The pack of the result
139          */
140         Result(Core core, Bot bot, Pack pack) {
141                 this.core = core;
142                 this.bot = bot;
143                 this.pack = pack;
144         }
145
146         //
147         // ACCESSORS
148         //
149
150         /**
151          * Returns the bot carrying the pack.
152          *
153          * @return The bot carrying the pack
154          */
155         public Bot bot() {
156                 return bot;
157         }
158
159         /**
160          * Returns the pack.
161          *
162          * @return The pack
163          */
164         public Pack pack() {
165                 return pack;
166         }
167
168         //
169         // COMPARABLE METHODS
170         //
171
172         @Override
173         public int compareTo(Result result) {
174                 return ComparisonChain.start()
175                                 .compare(this, result, botsWithRunningTransfersComparator)
176                                 .compare(this, result, packArchiveComparator)
177                                 .compare(this, result, botNameComparator)
178                                 .compare(this, result, packNameComparator).result();
179         }
180
181 }