View Javadoc

1   package org.varienaja.comments;
2   
3   import java.util.Date;
4   import java.util.HashSet;
5   import java.util.Iterator;
6   import java.util.LinkedList;
7   import java.util.List;
8   import java.util.Set;
9   import java.util.TreeSet;
10  import java.util.regex.Matcher;
11  import java.util.regex.Pattern;
12  import java.util.regex.PatternSyntaxException;
13  
14  import org.apache.log4j.Logger;
15  import org.musicontroller.core.Band;
16  import org.musicontroller.core.Contract_PS;
17  import org.musicontroller.core.Keywordbag;
18  import org.musicontroller.core.Playlist;
19  import org.musicontroller.dao.Dao;
20  import org.musicontroller.model.Linkable;
21  import org.varienaja.util.wikipedia.WikipediaException;
22  import org.varienaja.util.wikipedia.WikipediaSearcher;
23  
24  /**
25   * Service that is used for getting/processing Comments for Bands, 
26   * Playlists, etc..
27   * @author Varienaja
28   * @version $Id: CommentService.java,v 1.1 2010/03/16 18:55:42 varienaja Exp $
29   */
30  public class CommentService {
31  	private static final Logger log = Logger.getLogger(CommentService.class);
32  	private static Dao _dao;
33  	private static final String plPrefix = "review";
34  	private static final String bndPrefix = "band";
35  	
36  	public void setDao(Dao dao) {
37  		_dao = dao;
38  	}
39  	
40  	public Dao getDao() {
41  		return _dao;
42  	}
43  	
44  	/**
45  	 * Gets the Comments for a certain Playlist. If there could be no Comments found,
46  	 * on local storage a try is made to download them from online sources. Hereafter,
47  	 * the new Comments will be stored on local storage for faster future use.
48  	 * @param playlist The Playlist to get Comments for.
49  	 * @return The Comments in an ordered Set (ordered by date of revies, latest first)
50  	 */
51  	public Set<Comment> getComments(Playlist playlist) {
52  		Set<Comment> reviews = CommentDAO.getCommentsFromCache(plPrefix, playlist.getId());
53  		if (reviews==null) {
54  			reviews = downloadComments(playlist);
55  			CommentDAO.saveCommentsToCache(plPrefix, playlist.getId(),reviews);
56  		}
57  		return reviews;
58  	}
59  	
60  	/**
61  	 * Gets the Comments for a certain Band. If there could be no Comments found,
62  	 * on local storage a try is made to download them from online sources. Hereafter,
63  	 * the new Comments will be stored on local storage for faster future use.
64  	 * @param band The Band to get Comments for.
65  	 * @return The info as a Set of one Review.
66  	 */
67  	public Set<Comment> getComments(Band band) {
68  		Set<Comment> reviews = CommentDAO.getCommentsFromCache(bndPrefix, band.getId());
69  		if (reviews==null) {
70  			reviews = downloadComments(band);
71  			CommentDAO.saveCommentsToCache(bndPrefix, band.getId(),reviews);
72  		}
73  		return reviews;
74  	}
75  	
76  	/**
77  	 * Refreshes the Comments for a Playlist. It re-downloads Comments from
78  	 * online sources, and merges them with the existing ones.
79  	 * @param playlist The Playlist to refresh the Comments for.
80  	 * @return Whether the Comments were changed.
81  	 */
82  	public boolean refreshComments(Playlist playlist) {
83  		log.debug("Refreshing comments for playlist: "+playlist.getName());
84  		Set<Comment> comments = CommentDAO.getCommentsFromCache(plPrefix, playlist.getId());
85  		if (comments==null) {
86  			comments = new TreeSet<Comment>();
87  		}
88  		int oldcount = comments.size();
89  		if (comments.addAll(downloadComments(playlist))) { //Only save if the collection changed!
90  			log.debug("Comments refreshed, we had "+oldcount+" reviews, now we have "+comments.size());
91  			for (Comment review : comments) {
92  				enhanceComment(review, playlist);
93  			}
94  			CommentDAO.saveCommentsToCache(plPrefix, playlist.getId(),comments);
95  			return true;
96  		} else {
97  			log.debug("No new comments found online.");
98  			return false;
99  		}
100 	}
101 	
102 	/**
103 	 * Downloads Comments for a Playlist from online sources.
104 	 * @param playlist The Playlist to download the Comments for.
105 	 * @return The Comments in an ordered Set (ordered by date of revies, latest first)
106 	 */
107 	private Set<Comment> downloadComments(Playlist playlist) {
108 		Set<Comment> comments = new TreeSet<Comment>();
109 			
110 		AmazonCommentService amazonService = new AmazonCommentService();
111 		comments.addAll(amazonService.getComments(playlist));
112 		//TODO Retrieve comments from other sources...
113 		return comments;
114 	}
115 
116 	/**
117 	 * Downloads Comments for a Band from Wikipedia.
118 	 * @param band The Band to download Comments for.
119 	 * @return The Comments as a Set, containing one Review object.
120 	 */
121 	private Set<Comment> downloadComments(Band band) {
122 		Set<Comment> reviews = new TreeSet<Comment>();
123 	
124 		String source = WikipediaSearcher.getExternalBandURL(band.getName());
125 		Comment comment = new Comment(source, "", new Date());
126 		String content = "";
127 		try {
128 			content = WikipediaSearcher.getBandInfo(band.getName());
129 		} catch (WikipediaException e) {
130 			log.error("Could not get comment for band: "+band.getName()+" from Wikipedia: "+e);
131 		}
132 		CommentElement elt = new CommentElement();
133 		elt.setText(content);
134 		elt.setType('T');
135 		comment.addElement(elt);
136 		
137 		reviews.add(comment);
138 		return reviews;
139 	}
140 
141 	/**
142 	 * Uses the information of the Playlist to enhance its Reviews. Plain
143 	 * text is hereby transformed into hyperlinked text.
144 	 * @param review A Review
145 	 * @param playlist The playist we're enhancing reviews for.
146 	 */
147     protected void enhanceComment(Comment review, Playlist playlist) {
148     	log.debug("Enhancing comment: "+review.getTitle());
149     	//First, we de-enhance the playlist to get it back to plain text.
150     	StringBuilder sb = new StringBuilder();
151     	for (CommentElement elt : review.getElements()) {
152     		sb.append(elt.getText());
153     		sb.append(" ");
154     	}
155     	
156     	//Now, we iterate over the levels of importance. During this phase,
157     	//more and more plaintext will be converted into hyperlinked text.
158     	//Rule 1: The order of hyperlinking is:
159     	//        a. Band
160     	//        b. Songs in this playlist
161     	//        c. Other playlists of the same band
162     	//        d. Songs of the same band in other playlists
163     	//        e. Artists in this band
164     	//        f. Genres, used in this playlist.
165     	//        g. Other Genres
166     	//        h. Other bands
167     	//Rule 2: once hyperlinked, text can not be enhanced again.
168     	Set<Linkable>[] mappings = new Set[8];
169     	mappings[0] = getBands(playlist);
170     	mappings[1] = getSongs(playlist);
171     	mappings[2] = getPlaylists(playlist);
172     	mappings[3] = getAllSongs(playlist);
173     	mappings[4] = getArtists(playlist);
174     	mappings[5] = getKeywords(playlist);
175     	mappings[6] = getAllKeywords();
176     	mappings[7] = getAllBands();
177     	
178     	for (Set<Linkable> mapping : mappings) {
179     		Iterator<Linkable> it = mapping.iterator();
180     		while (it.hasNext()) {
181     			Linkable link = it.next();
182     			try {
183 		    		Pattern p = Pattern.compile("\\b\\Q" + link.getName() + "\\E\\b"); 
184 	    			
185 	    			boolean somethingHyperlinked = true;
186 	    			while (somethingHyperlinked) {
187 		    			//Put all reviewelements into an array, so we can mess with them
188 		    			//without having the chance of ConcurrentModificationException.
189 		    			CommentElement[] all = new CommentElement[0];
190 		    			all = review.getElements().toArray(all);
191 		    			somethingHyperlinked = false;
192 		    	    	for (CommentElement elt : all) {
193 		    	    		if (elt.isText()) { //Rule 2: only enhance not already enhanced stuff
194 			    	    		String txt = elt.getText();
195 			    	    		//TODO try to program something nifty in
196 			    	    		//order to catch slightly misspelled texts.
197 			    	    		
198 			    	    		Matcher matcher = p.matcher(txt.toLowerCase());
199 			    	    		if (matcher.find()) {
200 			    	    			int index = matcher.start();
201 			    	    			List<CommentElement> newElts = new LinkedList<CommentElement>();
202 			    	    			
203 			    	    			CommentElement before = new CommentElement();
204 			    	    			before.setType('T');
205 			    	    			before.setText(txt.substring(0,index));
206 			    	    			
207 			    	    			CommentElement changed = new CommentElement();
208 			    	    			changed.setType(link.getType().charAt(0));
209 			    	    			changed.setText(link.getName());
210 			    	    			changed.setId(link.getId());
211 			    	    			
212 			    	    			CommentElement after = new CommentElement();
213 			    	    			after.setType('T');
214 			    	    			after.setText(txt.substring(index+link.getName().length()));
215 			    	    			log.debug("Enhanced review with: "+changed.toString());
216 			    	    			
217 			    	    			newElts.add(before);
218 			    	    			newElts.add(changed);
219 			    	    			newElts.add(after);
220 			    	    			
221 			    	    			review.splitElement(elt, newElts);
222 			    	    			somethingHyperlinked = true;
223 			    	    		}
224 		    	    		}
225 		    	    	}
226 	    			}
227     			} catch (PatternSyntaxException e) {
228     				log.error(e.getMessage());
229     			} catch (Exception e) {
230     				log.error(e.getMessage());
231     			} finally {
232     				it.remove();
233     			}
234     		}
235     	}
236     	log.debug("Enhancing comment done.");
237 	}
238 
239 	/**
240 	 * Returns a Set of all Bands known to MusiController.
241 	 * @return A Set of Band objects.
242 	 */
243 	private Set<Linkable> getAllBands() {
244 		return new HashSet<Linkable>(); //TODO Implement this.
245 	}
246 
247 	/**
248 	 * Returns a Set of all Keywords known to MusiController.
249 	 * @return A Set of Keyword objects.
250 	 */
251 	private Set<Linkable> getAllKeywords() {
252 		return new HashSet<Linkable>(); //TODO Implement this.
253 	}
254 
255 	/**
256 	 * Returns a Set of all Keyword which are present in the Playlist.
257 	 * @param playlist The playlist
258 	 * @return A Set of Keyword objects.
259 	 */
260 	private Set<Linkable> getKeywords(Playlist playlist) {
261 		Set<Linkable> result = new HashSet<Linkable>();
262 		for (Contract_PS cps : playlist.getSongs()) {
263 			Keywordbag kwb = cps.getSong().getKeywordbag();
264 			if (kwb!=null) {
265 				result.addAll(kwb.getKeywords());
266 			}
267 		}
268 		return result;
269 	}
270 
271 	/**
272 	 * Returns a Set of all Artists which are present in the Playlist.
273 	 * @param playlist The playlist
274 	 * @return A Set of Artist objects.
275 	 */
276 	private Set<Linkable> getArtists(Playlist playlist) {
277 		return new HashSet<Linkable>(); //TODO Implement this.
278 	}
279 
280 	/**
281 	 * Returns a Set of all Songs of all Bands which are present in the Playlist.
282 	 * @param playlist The Playlist
283 	 * @return A Set of Song objects, but not Songs from the Playlist of the parameter.
284 	 */
285 	private Set<Linkable> getAllSongs(Playlist playlist) {
286 		Set<Linkable> result = new HashSet<Linkable>();
287 		for (Linkable band : getBands(playlist)) {
288 			Playlist allSongs = getDao().songsByBand(band.getId());
289 			for (Contract_PS cps : allSongs.getSongs()) {
290 				result.add(cps.getSong());
291 			}
292 		}
293 		return result;
294 	}
295 
296 	/**
297 	 * Returns a Set of all Playlists of all Bands which are present in the Playlist.
298 	 * @param playlist The Playlist
299 	 * @return A Set of Playlist objects.
300 	 */
301 	private Set<Linkable> getPlaylists(Playlist playlist) {
302 		Set<Linkable> result = new HashSet<Linkable>();
303 		for (Linkable band : getBands(playlist)) {
304 			result.addAll(getDao().listPlaylists((Band) band));
305 		}
306 		return result;
307 	}
308 
309 	/**
310 	 * Returns a Set of all Songs which are present in the Playlist.
311 	 * @param playlist The Playlist
312 	 * @return A Set of Song objects.
313 	 */
314 	private Set<Linkable> getSongs(Playlist playlist) {
315 		Set<Linkable> result = new HashSet<Linkable>();
316 		for (Contract_PS cps : playlist.getSongs()) {
317 			result.add(cps.getSong());
318 		}
319 		return result;
320 	}
321 
322 	/**
323 	 * Returns a Set of all Bands which are present in the Playlist.
324 	 * @param playlist The Playlist
325 	 * @return A Set of Band objects.
326 	 */
327 	private Set<Linkable> getBands(Playlist playlist) {
328 		Set<Linkable> result = new HashSet<Linkable>();
329 		for (Contract_PS cps : playlist.getSongs()) {
330 			result.add(cps.getSong().getBand());
331 		}
332 		return result;
333 	}
334 
335 }