Source code: irate/common/TrackDatabase.java
1 // Copyright 2003 Anthony Jones, Stephen Blackheath, Taras
2
3 package irate.common;
4
5 import java.io.*;
6 import java.util.*;
7 import nanoxml.XMLElement;
8
9 public class TrackDatabase {
10
11 private int MAX_RATING = 10;
12
13 private final String docElementName = "TrackDatabase";
14 private final String trackElementName = "Track";
15 private final String userElementName = "User";
16 private final String autoDownloadElementName = "AutoDownload";
17 private final String defaultHost = "takahe.blacksapphire.com";
18 private final int defaultPort = 2278;
19 private TreeSet tracks;
20 private Hashtable hash;
21 private File file;
22 private File downloadDir;
23 private XMLElement docElt;
24
25 public TrackDatabase() {
26 create();
27 }
28
29 public TrackDatabase(File file) throws IOException {
30 try {
31 load(file);
32 }
33 catch (Exception e) {
34 e.printStackTrace();
35 if (!(e instanceof IOException))
36 throw new IOException(e.toString());
37 create();
38 }
39 }
40
41 public TrackDatabase(InputStream is) throws IOException {
42 try {
43 load(is);
44 }
45 catch (Exception e) {
46 e.printStackTrace();
47 if (!(e instanceof IOException))
48 throw new IOException(e.toString());
49 create();
50 }
51 }
52
53 private void create() {
54 try {
55 tracks = new TreeSet(new Comparator() {
56 public int compare(Object o0, Object o1) {
57 return ((Track) o0).compareTo((Track) o1);
58 }
59 public boolean equals(Object o0, Object o1) {
60 return compare(o0, o1) == 0;
61 }
62 });
63 hash = new Hashtable();
64 docElt = new XMLElement(new Hashtable(), false, false);
65 docElt.setName(docElementName);
66 }
67 catch (Exception e) {
68 e.printStackTrace();
69 }
70 }
71
72 public Track add(Track track) {
73 synchronized (this) {
74 Track copy;
75 if ((copy = getTrack(track)) == null) {
76 copy = new FastTrack((XMLElement) track.getElement(), downloadDir);
77 docElt.addChild(copy.getElement());
78 tracks.add(copy);
79 hash.put(copy.getKey(), copy);
80 }
81 return copy;
82 }
83 }
84
85 public boolean remove(Track track) {
86 track = getTrack(track);
87 if (track == null)
88 return false;
89 synchronized (this) {
90 docElt.removeChild(track.getElement());
91 tracks.remove(track);
92 hash.remove(track.getKey());
93 }
94 return true;
95 }
96
97 public Track[] getTracks() {
98 synchronized (this) {
99 Track[] tracks = new Track[this.tracks.size()];
100 this.tracks.toArray(tracks);
101 return tracks;
102 }
103 }
104
105 public int getNoOfTracks() {
106 return tracks.size();
107 }
108
109 private XMLElement getElement(String eltName) {
110 Enumeration enum = docElt.enumerateChildren();
111 while(enum.hasMoreElements())
112 {
113 XMLElement elt = (XMLElement) enum.nextElement();
114 if(elt.getName().equals(eltName))
115 return elt;
116 }
117
118 return null;
119 /*NodeList nodeList = docElt.getElementsByTagName(eltName);
120
121 if (nodeList.getLength() != 0) {
122 Node node = nodeList.item(0);
123 if (node instanceof Element)
124 return (Element) node;
125 }
126 return null;*/
127 }
128
129 protected String getAttribute(String name, String attName) {
130 XMLElement elt = getElement(name);
131 if (elt == null)
132 return "";
133
134 String att = elt.getStringAttribute(attName);
135 if (att == null){
136 System.out.println("Can't find attribute "+name+"."+attName+" = "+elt);
137 return "";
138 }
139 return att;
140 }
141
142 protected void setAttribute(String name, String attName, String attValue) {
143 XMLElement elt = getElement(name);
144 if (elt == null) {
145 elt = new XMLElement(new Hashtable(), false, false);
146 elt.setName(name);
147 docElt.addChild(elt);
148 }
149 elt.setAttribute(attName, attValue);
150 // System.out.println("name="+name + " '" +attName+"="+attValue+" "+elt);
151 }
152
153 public String getUserName() {
154 return getAttribute(userElementName, "name").replace('/', '.').replace('\\', '.');
155 }
156
157 public void setUserName(String name) {
158 setAttribute(userElementName, "name", name);
159 }
160
161 public String getPassword() {
162 return getAttribute(userElementName, "password");
163 }
164
165 public void setPassword(String password) {
166 setAttribute(userElementName, "password", password);
167 }
168
169 public String getHost() {
170 String host = getAttribute(userElementName, "host");
171 if (host.length() == 0)
172 return defaultHost;
173 return host;
174 }
175
176 public void setHost(String host) {
177 setAttribute(userElementName, "host", host);
178 }
179
180 public int getPort() {
181 try {
182 return Integer.parseInt(getAttribute(userElementName,"port"));
183 }
184 catch (NumberFormatException e) {
185 }
186 return defaultPort;
187 }
188
189 public void setPort(int port) {
190 setAttribute(userElementName, "port", Integer.toString(port));
191 }
192
193 public int getAutoDownload() {
194 try {
195 return Integer.parseInt(getAttribute(autoDownloadElementName,"setting"));
196 }
197 catch (NumberFormatException e) {
198 }
199 return 0;
200 }
201
202 public void setAutoDownload(int setting) {
203 setAttribute(autoDownloadElementName, "setting", Integer.toString(setting));
204 }
205
206 public int getAutoDownloadCount() {
207 try {
208 return Integer.parseInt(getAttribute(autoDownloadElementName,"count"));
209 }
210 catch (NumberFormatException e) {
211 }
212 return 0;
213 }
214
215 public void setPlayListLength(int length) {
216 setAttribute("PlayList", "length", Integer.toString(length));
217 }
218
219 public int getPlayListLength() {
220 try {
221 return Integer.parseInt(getAttribute("PlayList", "length"));
222 }
223 catch (NumberFormatException e) {
224 }
225 return 5;
226 }
227
228 public void setAutoDownloadCount(int count) {
229 setAttribute(autoDownloadElementName, "count", Integer.toString(count));
230 }
231
232 public void incNoOfPlays() {
233 synchronized (this) {
234 setAutoDownloadCount(getAutoDownloadCount() + 1);
235 }
236 }
237
238 public boolean isRoboJockEnabled() {
239 String s = getAttribute("RoboJock", "enabled").toLowerCase();
240 if (s.equals("true") || s.equals("yes"))
241 return true;
242 return false;
243 }
244
245 public void setRoboJockEnabled(boolean enabled) {
246 setAttribute("RoboJock", "enabled", enabled ? "yes" : "no");
247 }
248
249 public void setFile(File file) {
250 this.file = file;
251 }
252
253 public File getDownloadDirectory() {
254 return downloadDir;
255 }
256
257 public void load(File file) throws IOException {
258 this.file = file;
259 this.downloadDir = new File(file.getParent(), "download");
260 InputStream is = new FileInputStream(file);
261 load(is);
262 is.close();
263 }
264
265 public void load(InputStream is) throws IOException {
266 if(docElt == null)
267 create();
268 docElt.parseFromReader(new InputStreamReader(is));
269
270 if (docElt.getName().equals(docElementName)) {
271 Enumeration enum = docElt.enumerateChildren();
272 while(enum.hasMoreElements()) {
273 XMLElement elt = (XMLElement)enum.nextElement();
274 if(!elt.getName().equals(trackElementName)) continue;
275 //System.out.println(elt.toString());
276 Track track = new FastTrack(elt, downloadDir);
277 tracks.add(track);
278 //System.out.println("key="+track.getKey());
279 hash.put(track.getKey(), track);
280 }
281 }
282 }
283
284 public void save() throws IOException {
285 synchronized (this) {
286 String name = file.getPath()+".backup";
287 File temporaryFile = new File(name);
288 FileWriter fw = null;
289 try {
290 fw = new FileWriter(temporaryFile);
291 fw.write("<?xml version=\"1.0\"?>\n");
292 fw.write(toString());
293 fw.close();
294 fw = null;
295 System.out.println("Successfully Wrote: "+name );
296 // If we wrote the file successfully, then rename the temporary
297 // file to the real name of the configuration file. This makes
298 // the writing of the new file effectively atomic.
299 if (!temporaryFile.renameTo(file)) {
300 System.out.println("failed at renaming " + temporaryFile + " to " + file +"\n attempting to delete"+file);
301 if(!file.delete())
302 throw new IOException("Failed to delete "+ file);
303 if (!temporaryFile.renameTo(file))
304 throw new IOException("Failed to rename "+temporaryFile+" to "+file);
305 }
306 }
307 finally {
308 if (fw != null) try { fw.close(); } catch (IOException e) { e.printStackTrace(); }
309 }
310 }
311 }
312
313 public String toString() {
314 return docElt.toString();
315 }
316
317 public Track getTrack(String key) {
318 return (Track) hash.get(key);
319 }
320
321 public Track getTrack(Track track) {
322 return getTrack(track.getKey());
323 }
324
325 public void update(TrackDatabase trackDatabase) {
326 Track[] tracks = trackDatabase.getTracks();
327 for (int i = 0; i < tracks.length; i++) {
328 Track track = getTrack(tracks[i]);
329 if (track != null) {
330 remove(track);
331 add(tracks[i]);
332 }
333 }
334 }
335
336 public void add(TrackDatabase trackDatabase) {
337 Track[] tracks = trackDatabase.getTracks();
338 for (int i = 0; i < tracks.length; i++)
339 add(tracks[i]);
340 }
341
342 public boolean remove(TrackDatabase trackDatabase) {
343 boolean removed = false;
344 Track[] tracks = trackDatabase.getTracks();
345 for (int i = 0; i < tracks.length; i++)
346 removed |= remove(tracks[i]);
347 return false;
348 }
349
350 public void setPlayer(String path) {
351 setAttribute("Player", "path", path);
352 }
353
354 public String getPlayer() {
355 return getAttribute("Player", "path");
356 }
357
358 public void setError(String code, String url) {
359 setAttribute("Error", "code", code);
360 setAttribute("Error", "url", url);
361 }
362
363 public String getErrorCode() {
364 return getAttribute("Error", "code");
365 }
366
367 public String getErrorURLString() {
368 return getAttribute("Error", "url");
369 }
370
371 public float getProbability(Track track) {
372 if (track.getFile() == null)
373 return 0;
374 return track.getProbability();
375 }
376
377 public Track chooseTrack(Random random)
378 {
379 return chooseTrack(random, null);
380 }
381
382 /**
383 * Choose a track from the track database, excluding tracks in 'toOmit'.
384 * Ignore toOmit if it is null.
385 */
386 public Track chooseTrack(Random random, Hashtable toOmit) {
387 Track[] tracks = getTracks();
388 while (true) {
389 float[] probs = new float[tracks.length];
390
391 // Choose a minimum probability
392 float minRating = (MAX_RATING - 2) * Math.abs(random.nextFloat());
393
394 float totalProb = 0;
395 for (int i = 0; i < tracks.length; i++) {
396 Track track = tracks[i];
397 float rating = track.getRating();
398
399 if (rating >= minRating && (toOmit == null || !toOmit.containsKey(track)) && track.getFile() != null)
400 totalProb += getProbability(track);
401
402 probs[i] = totalProb;
403 }
404
405 if (totalProb == 0) {
406 if (minRating < 2)
407 return null;
408 }
409 else {
410 while (true) {
411 float rand = Math.abs(random.nextFloat()) * totalProb;
412 for (int i = 0; i < tracks.length; i++) {
413 if (toOmit != null)
414 if (toOmit.containsKey(tracks[i]))
415 continue;
416 if (rand <= probs[i])
417 return tracks[i];
418 }
419 }
420 }
421 }
422 }
423
424 /**
425 * Choose an unrated track from the track database, excluding tracks in
426 * 'toOmit'. Ignore toOmit if it is null.
427 */
428 public Track chooseUnratedTrack(Random random, Hashtable toOmit) {
429 Track[] tracks = getTracks();
430 List list = new Vector();
431 for (int i = 0; i < tracks.length; i++) {
432 Track track = tracks[i];
433 if (!track.isRated() && (toOmit == null || !toOmit.containsKey(track)) && track.getFile() != null)
434 list.add(track);
435 }
436
437 // If there are no unrated tracks then return null.
438 if (list.size() == 0)
439 return null;
440
441 int rand = Math.round(Math.abs(random.nextFloat()) * list.size());
442 return (Track) list.get(rand);
443 }
444
445 public int getNoOfUnrated() {
446 Track[] tracks = getTracks();
447 List list = new Vector();
448 int noOfUnrated = 0;
449 for (int i = 0; i < tracks.length; i++)
450 if (!tracks[i].isRated())
451 noOfUnrated++;
452 return noOfUnrated;
453 }
454
455 private int compare(Track track0, Track track1) {
456 return track0.getName().compareToIgnoreCase(track1.getName());
457 }
458
459 public void purge() {
460 for (Iterator itr = tracks.iterator(); itr.hasNext(); ) {
461 Track track = (Track) itr.next();
462 if (track.isHidden())
463 track.erase();
464 }
465 }
466 }