View Javadoc

1   package org.musicontroller.core;
2   
3   import java.util.Date;
4   import java.util.HashSet;
5   import java.util.Set;
6   import java.util.TreeSet;
7   
8   import org.musicontroller.security.IUser;
9   import org.varienaja.util.DateTools;
10  
11  /**
12   * @author Varienaja
13   */
14  public class Song extends LinkableAbs {
15  	private String _comments;
16  	private int _bpm,_length;
17  	private Song _parent;
18  	private Band _band;
19  	private Link _link;
20  	private Keywordbag _keywordbag;
21  	private AIBag _aibag;
22  	private Set<Contract_PS> _playlists;
23  	private Set<Event> _events;
24  	private EventCountLookup _lookup;
25  
26  	public Song() {
27  		_comments="";
28  		_bpm=-1;
29  		_length=-1;
30  		_parent=null;
31  		_band=null;
32  		_link=null;
33  		_aibag=null;
34  		_keywordbag=null;
35  		_lookup=null;
36  		_playlists=new HashSet<Contract_PS>();
37  		_events=new TreeSet<Event>(); //To preserve ordering (see mapping-file), this is a TreeSet
38  	}
39  	
40  	public Event addEvent(Date moment, IUser user, int eventkind) {
41  		if (user==null) throw new IllegalArgumentException("Cannot add events without a user.");
42  		Event event = new Event();
43  		event.setUser(user);
44  		event.setSong(this);
45  		event.setMoment(moment);
46  		event.setEventkind(eventkind);
47  		getLookup().addEvent(event);
48  		_events.add(event);
49  		return event;
50  	}
51  	
52  	public Event addPlayedEvent(Date moment, IUser user) {
53  		return addEvent(moment,user,Event.played);
54  	}
55  	
56  	public Event addRequestedEvent(Date moment, IUser user) {
57  		return addEvent(moment,user,Event.requested);
58  	}
59  
60  	public Event addSkippedEvent(Date moment, IUser user) {
61  		return addEvent(moment,user,Event.skipped);
62  	}
63  	
64  	public Event addDownloadedEvent(Date moment, IUser user) {
65  		return addEvent(moment,user,Event.downloaded);
66  	}
67  	
68  	public Band getBand() {
69  		return _band;
70  	}
71  	
72  	public void setBand(Band band) {
73  		_band = band;
74  	}
75  	
76  	public int getBpm() {
77  		return _bpm;
78  	}
79  	
80  	public void setBpm(int bpm) {
81  		_bpm = bpm;
82  	}
83  	
84  	public String getComments() {
85  		return _comments;
86  	}
87  	
88  	public void setComments(String comments) {
89  		_comments = comments;
90  	}
91  	
92  	public Link getLink() {
93  		return _link;
94  	}
95  	
96  	public void setLink(Link link) {
97  		_link = link;
98  	}
99  	
100 	/**
101 	 * Returns the song length in milliseconds.
102 	 * @return The songs length in milliseconds.
103 	 */
104 	public int getLength() {
105 		return _length;
106 	}
107 	
108 	/**
109 	 * Sets the song length in milliseconds. Negative lengths are multiplied by -1.
110 	 * @param length The new length in milliseconds.
111 	 */
112 	public void setLength(int length) {
113 		_length = Math.abs(length);
114 	}
115 	
116 	/**
117 	 * Sets the Songs length. The input has to be in the format hh:mm:ss or 
118 	 * mm:ss or ss.
119 	 * @param formattedLength The length in string-format.
120 	 */
121 	public void setLength(String formattedLength) {
122 		if (formattedLength !=null) {
123 			String[] parts = formattedLength.split(":");
124 			try {
125 				if (parts.length==1) {
126 					_length = 1000 * (Integer.parseInt(parts[0]));
127 				} else if (parts.length==2) {
128 					_length = 1000 * (Integer.parseInt(parts[0]) * 60 + 
129 					         	Integer.parseInt(parts[1]));
130 				} else if (parts.length==3) {
131 					_length = 1000 * (Integer.parseInt(parts[0]) * 3600 + 
132 								Integer.parseInt(parts[1]) * 60 +
133 								Integer.parseInt(parts[2]));
134 					
135 				}
136 			} catch (NumberFormatException e) {
137 				//silently ignore.
138 			}
139 		}
140 	}
141 	
142 	public Song getParent() {
143 		return _parent;
144 	}
145 	
146 	public void setParent(Song parent) {
147 		_parent = parent;
148 	}
149 
150 	/**
151 	 * Getter for the bag of keywords associated with the song.
152 	 * @return The bag of keywords of this song.
153 	 */
154 	public Keywordbag getKeywordbag() {
155 		return _keywordbag;
156 	}
157 	
158 	/**
159 	 * Setter for the bag of keywords associated with this song.
160 	 * @param keywordbag The bag of keywords.
161 	 */
162 	public void setKeywordbag(Keywordbag keywordbag) {
163 		_keywordbag = keywordbag;
164 	}
165 
166 	/**
167 	 * Getter for the set of events of the song.
168 	 * @return The set of events.
169 	 */
170 	public Set<Event> getEvents() {
171 		return _events;
172 	}
173 	
174 	/**
175 	 * Setter for the set of events of the song.
176 	 * @param events The set of events (not null).
177 	 */
178 	public void setEvents(Set<Event> events) {
179 		if (events!=null) {
180 			_events = events;
181 		}
182 	}
183 	
184 	/**
185 	 * Get (and create when neccessary) the lookup-object, which contains the
186 	 * counts for our Events.
187 	 * @return The EventCountLookup-object.
188 	 */
189 	private EventCountLookup getLookup() {
190 		if (_lookup==null) {
191 			_lookup = EventCountLookup.create(_events,getId());
192 		}				
193 		return _lookup;
194 	}
195 
196 	public Set<Contract_PS> getPlaylists() {
197 		return _playlists;
198 	}
199 
200 	public void setPlaylists(Set<Contract_PS> playlists) {
201 		if (playlists!=null) _playlists = playlists;
202 	}
203 	
204 	/**
205 	 * Returns the songs' keywords as a comma separated
206 	 * list of keywords. 
207 	 * @return A string with the songs keywords separated by commas.
208 	 */
209 	public String listKeywords() {
210 		String keywords="";
211 		Keywordbag bag = getKeywordbag();
212 		if(bag==null) {
213 			return "";
214 		}
215 		for (Keyword key : bag.getKeywords()) {
216 			keywords+=key.getName()+", ";
217 		}
218 		return (keywords.length()>=2) ? keywords.substring(0,keywords.length()-2) : "";
219 	}
220 	
221 	public String listPlaylists() {
222 		String lists="";
223 		for(Contract_PS c_ps: getPlaylists()) {
224 			Playlist pl = c_ps.getPlaylist();
225 			if(pl!=null && pl.getName()!=null && pl.getName().length()>0) {
226 				lists+=pl.getName()+", ";
227 			}
228 		}
229 		return (lists.length()>=2) ? lists.substring(0,lists.length()-2) : "";
230 	}
231 	
232 	public static String getFormattedLength(int lengthinmillis) {
233 		int L = lengthinmillis / 1000;
234 		int M = L / 60;
235 		int S = L % 60;
236 		
237 		return Integer.toString(M)+":"+ ( (S<10) ? "0"+S : Integer.toString(S));
238 	}
239 	
240 	public String getFormattedLength() {
241 		return getFormattedLength(_length);
242 	}
243 	
244 	public int getPlaycount(IUser user) {
245 		return getPlaycount(user,null,null);
246 	}
247 	
248 	public int getRequestcount(IUser user) {
249 		return getRequestcount(user,null,null);
250 	}
251 	
252 	public int getSkipcount(IUser user) {
253 		return getSkipcount(user,null,null);
254 	}
255 	
256 	public int getPlaycount(IUser user, Date begin, Date end) {
257 		return getEventCount(user,Event.played,begin, end);
258 	}
259 	
260 	public int getRequestcount(IUser user, Date begin, Date end) {
261 		return getEventCount(user,Event.requested, begin, end);
262 	}
263 	
264 	public int getSkipcount(IUser user, Date begin, Date end) {
265 		return getEventCount(user,Event.skipped, begin, end);
266 	}
267 	
268 	public int getDownloadcount(IUser user, Date begin, Date end) {
269 		return getEventCount(user,Event.downloaded, begin, end);
270 	}
271 	
272 	public int getEventcount(IUser user, int kind) {
273 		return getEventCount(user,kind,null,null);
274 	}
275 	
276 	/**
277 	 * Returns an int-representation of the popularity of a song during a
278 	 * certain period. The popularity is defined as follows:
279 	 * <code>popularity = playcount + requestcount + downloadcount - skipcount</code>
280 	 * <p>Only events after the begin-parameter and before the end-parameter are
281 	 * taken into account. These parameters can be left null. When both are null,
282 	 * all events are counted. When begin is null, all events untill end are
283 	 * counted. If end is null, all events after begin are counted.
284 	 * </p>
285 	 * @param user The user to calculate the popularity for.
286 	 * @param begin The begindate, or rather a timestamp.
287 	 * @param end The enddate, also a timestamp
288 	 * @return The popularity of the song.
289 	 */
290 	public int getPopularity(IUser user, Date begin, Date end) {
291 		int score = getPlaycount(user, begin, end) + 
292 		getRequestcount(user, begin, end) +
293 		getDownloadcount(user, begin, end) -
294 		getSkipcount(user, begin, end);
295 		return score;
296 	}
297 	
298 	/**
299 	 * Counts the amount of events of a certain kind by a certain User
300 	 * @param user The User, if null then the events of all user are added up
301 	 * @param kind The eventkind
302 	 * @param begin The Date after which events will be counted
303 	 * @param end The Date before which events will be counted
304 	 * @return The amount of events
305 	 */
306 	public int getEventCount(IUser user, int kind, Date begin, Date end) {
307 		if (user==null) return 0;
308 		
309 		if (begin==null && end==null) { //Total count
310 			return getLookup().getEventCount(user.getId(), kind);
311 		} else {
312 			if (end==null && begin!=null) { //Count for last year?
313 				if (DateTools.sameDay(begin, DateTools.lastYear())) { //Last year!
314 					return getLookup().getEventCountLastYear(user.getId(), kind);
315 				}
316 			}
317 		}
318 		
319 		//Old fashioned and slow way of counting.
320 		int count=0;
321 		for (Event ev: getEvents()) {
322 			if (user==null ? true : user.equals(ev.getUser())) {
323 				if (kind==ev.getEventkind()) {
324 					if (begin==null) {
325 						if (end==null) {
326 							count++;
327 						} else {
328 							if (ev.getMoment().before(end)) count++;
329 						}
330 					} else {
331 						if (end==null) {
332 							if (ev.getMoment().after(begin)) count++;
333 						} else {
334 							if (ev.getMoment().after(begin) && (ev.getMoment().before(end))) count++;
335 						}
336 					}
337 				}
338 			}
339 		}
340 		return count;
341 	}
342 	
343 	/**
344 	 * Returns the Date of the last Played-event of this song
345 	 * @param user The user to return the last Played-event for, if null the last played-event is returned
346 	 * @return The Date of the last Played-event
347 	 */
348 	public Date getLastPlay(IUser user) {
349 		if (user==null) return null;
350 		return getLookup().getLastPlay(user.getId());
351 	}
352 
353 	public AIBag getAibag() {
354 		return _aibag;
355 	}
356 	
357 
358 	public void setAibag(AIBag aibag) {
359 		_aibag = aibag;
360 	}
361 		
362 	public boolean equals(Object o) {
363 		if (o==null) return false;
364 		
365 		if (o instanceof Song) {
366 			Song other = (Song) o;
367 			return getId()==other.getId() && getName().equals(other.getName());
368 		} else {
369 			return false;
370 		}
371 	}
372 	
373 	/*
374 	 * (non-Javadoc)
375 	 * @see java.lang.Object#hashCode()
376 	 */
377 	@Override
378 	public int hashCode() {
379 		return Long.valueOf(getId()).hashCode();
380 	}
381 
382 	/*
383 	 * (non-Javadoc)
384 	 * @see java.lang.Object#toString()
385 	 */
386 	@Override
387 	public String toString() {
388 		StringBuilder sb = new StringBuilder();
389 		if (_band!=null) {
390 			sb.append(_band);
391 			sb.append(" - ");
392 		}
393 		sb.append("[");
394 		sb.append(getId());
395 		sb.append("] ");
396 		sb.append(getName());
397 		sb.append(" (");
398 		sb.append(getFormattedLength());
399 		sb.append(") [");
400 		if (_keywordbag!=null) {
401 			sb.append(_keywordbag);
402 		}
403 		sb.append("] ");
404 		if (_link!=null) {
405 			sb.append(" ");
406 			sb.append(_link);
407 		}
408 		return sb.toString();
409 	}
410 
411 	public String getType() {
412 		return "S";
413 	}
414 
415 }