Always lower-case the magnet ID.
[rhynodge.git] / src / main / java / net / pterodactylus / rhynodge / states / TorrentState.java
1 /*
2  * Rhynodge - TorrentState.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.rhynodge.states;
19
20 import java.nio.charset.Charset;
21 import java.util.Iterator;
22 import java.util.List;
23
24 import net.pterodactylus.rhynodge.State;
25 import net.pterodactylus.rhynodge.states.TorrentState.TorrentFile;
26
27 import org.apache.http.NameValuePair;
28 import org.apache.http.client.utils.URLEncodedUtils;
29
30 import com.fasterxml.jackson.annotation.JsonProperty;
31 import com.google.common.collect.Lists;
32
33 /**
34  * {@link State} that contains information about an arbitrary number of torrent
35  * files.
36  *
37  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
38  */
39 public class TorrentState extends AbstractState implements Iterable<TorrentFile> {
40
41         /** The torrent files. */
42         @JsonProperty
43         private List<TorrentFile> files = Lists.newArrayList();
44
45         //
46         // ACCESSORS
47         //
48
49         /**
50          * Adds a torrent file to this state.
51          *
52          * @param torrentFile
53          *            The torrent file to add
54          * @return This state
55          */
56         public TorrentState addTorrentFile(TorrentFile torrentFile) {
57                 files.add(torrentFile);
58                 return this;
59         }
60
61         //
62         // ITERABLE METHODS
63         //
64
65         /**
66          * {@inheritDoc}
67          */
68         @Override
69         public Iterator<TorrentFile> iterator() {
70                 return files.iterator();
71         }
72
73         //
74         // OBJECT METHODS
75         //
76
77         /**
78          * {@inheritDoc}
79          */
80         @Override
81         public String toString() {
82                 return String.format("%s[files=%s]", getClass().getSimpleName(), files);
83         }
84
85         /**
86          * Container for torrent file data.
87          *
88          * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
89          */
90         public static class TorrentFile {
91
92                 /** The name of the file. */
93                 @JsonProperty
94                 private final String name;
95
96                 /** The size of the file. */
97                 @JsonProperty
98                 private final String size;
99
100                 /** The magnet URI of the file. */
101                 @JsonProperty
102                 private final String magnetUri;
103
104                 /** The download URI of the file. */
105                 @JsonProperty
106                 private final String downloadUri;
107
108                 /** The number of files in this torrent. */
109                 @JsonProperty
110                 private final int fileCount;
111
112                 /** The number of seeds connected to this torrent. */
113                 @JsonProperty
114                 private final int seedCount;
115
116                 /** The number of leechers connected to this torrent. */
117                 @JsonProperty
118                 private final int leechCount;
119
120                 /**
121                  * No-arg constructor for deserialization.
122                  */
123                 @SuppressWarnings("unused")
124                 private TorrentFile() {
125                         this(null, null, null, null, 0, 0, 0);
126                 }
127
128                 /**
129                  * Creates a new torrent file.
130                  *
131                  * @param name
132                  *            The name of the file
133                  * @param size
134                  *            The size of the file
135                  * @param magnetUri
136                  *            The magnet URI of the file
137                  * @param downloadUri
138                  *            The download URI of the file
139                  * @param fileCount
140                  *            The number of files
141                  * @param seedCount
142                  *            The number of connected seeds
143                  * @param leechCount
144                  *            The number of connected leechers
145                  */
146                 public TorrentFile(String name, String size, String magnetUri, String downloadUri, int fileCount, int seedCount, int leechCount) {
147                         this.name = name;
148                         this.size = size;
149                         this.magnetUri = magnetUri;
150                         this.downloadUri = downloadUri;
151                         this.fileCount = fileCount;
152                         this.seedCount = seedCount;
153                         this.leechCount = leechCount;
154                 }
155
156                 //
157                 // ACCESSORS
158                 //
159
160                 /**
161                  * Returns the name of the file.
162                  *
163                  * @return The name of the file
164                  */
165                 public String name() {
166                         return name;
167                 }
168
169                 /**
170                  * Returns the size of the file. The returned size may included
171                  * non-numeric information, such as units (e. g. “860.46 MB”).
172                  *
173                  * @return The size of the file
174                  */
175                 public String size() {
176                         return size;
177                 }
178
179                 /**
180                  * Returns the magnet URI of the file.
181                  *
182                  * @return The magnet URI of the file, or {@code null} if there is no
183                  *         magnet URI for this torrent file
184                  */
185                 public String magnetUri() {
186                         return magnetUri;
187                 }
188
189                 /**
190                  * Returns the download URI of the file.
191                  *
192                  * @return The download URI of the file, or {@code null} if there is no
193                  *         download URI for this torrent file
194                  */
195                 public String downloadUri() {
196                         return downloadUri;
197                 }
198
199                 /**
200                  * Returns the number of files in this torrent.
201                  *
202                  * @return The number of files in this torrent
203                  */
204                 public int fileCount() {
205                         return fileCount;
206                 }
207
208                 /**
209                  * Returns the number of seeds connected to this torrent.
210                  *
211                  * @return The number of connected seeds
212                  */
213                 public int seedCount() {
214                         return seedCount;
215                 }
216
217                 /**
218                  * Returns the number of leechers connected to this torrent.
219                  *
220                  * @return The number of connected leechers
221                  */
222                 public int leechCount() {
223                         return leechCount;
224                 }
225
226                 //
227                 // PRIVATE METHODS
228                 //
229
230                 /**
231                  * Generates an ID for this file. If a {@link #magnetUri} is set, an ID
232                  * is {@link #extractId(String) extracted} from it. Otherwise the magnet
233                  * URI is used. If the {@link #magnetUri} is not set, the
234                  * {@link #downloadUri} is used. If that is not set either, the name of
235                  * the file is returned.
236                  *
237                  * @return The generated ID
238                  */
239                 private String generateId() {
240                         if (magnetUri != null) {
241                                 String id = extractId(magnetUri);
242                                 if (id != null) {
243                                         return id;
244                                 }
245                                 return magnetUri;
246                         }
247                         return (downloadUri != null) ? downloadUri : name;
248                 }
249
250                 //
251                 // STATIC METHODS
252                 //
253
254                 /**
255                  * Tries to extract the “exact target” of a magnet URI.
256                  *
257                  * @param magnetUri
258                  *            The magnet URI to extract the “xt” from
259                  * @return The extracted ID, or {@code null} if no ID could be found
260                  */
261                 private static String extractId(String magnetUri) {
262                         List<NameValuePair> parameters = URLEncodedUtils.parse(magnetUri.substring("magnet:?".length()), Charset.forName("UTF-8"));
263                         for (NameValuePair parameter : parameters) {
264                                 if (parameter.getName().equals("xt")) {
265                                         return parameter.getValue().toLowerCase();
266                                 }
267                         }
268                         return null;
269                 }
270
271                 //
272                 // OBJECT METHODS
273                 //
274
275                 /**
276                  * {@inheritDoc}
277                  */
278                 @Override
279                 public int hashCode() {
280                         return (generateId() != null) ? generateId().hashCode() : 0;
281                 }
282
283                 /**
284                  * {@inheritDoc}
285                  */
286                 @Override
287                 public boolean equals(Object object) {
288                         if (!(object instanceof TorrentFile)) {
289                                 return false;
290                         }
291                         if (generateId() != null) {
292                                 return generateId().equals(((TorrentFile) object).generateId());
293                         }
294                         return false;
295                 }
296
297                 /**
298                  * {@inheritDoc}
299                  */
300                 @Override
301                 public String toString() {
302                         return String.format("%s(%s,%s,%s)", name(), size(), magnetUri(), downloadUri());
303                 }
304
305         }
306
307 }