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