001/* 002 * Copyright (c) 2012, the Last.fm Java Project and Committers 003 * All rights reserved. 004 * 005 * Redistribution and use of this software in source and binary forms, with or without modification, are 006 * permitted provided that the following conditions are met: 007 * 008 * - Redistributions of source code must retain the above 009 * copyright notice, this list of conditions and the 010 * following disclaimer. 011 * 012 * - Redistributions in binary form must reproduce the above 013 * copyright notice, this list of conditions and the 014 * following disclaimer in the documentation and/or other 015 * materials provided with the distribution. 016 * 017 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 018 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 019 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 020 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 021 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 022 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR 023 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 024 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 025 */ 026 027package de.umass.lastfm; 028 029import de.umass.xml.DomElement; 030 031import java.text.DateFormat; 032import java.text.ParseException; 033import java.text.SimpleDateFormat; 034import java.util.ArrayList; 035import java.util.Collection; 036import java.util.Date; 037import java.util.Locale; 038 039/** 040 * <code>MusicEntry</code> is the abstract superclass for {@link Track}, {@link Artist} and {@link Album}. It encapsulates data and provides 041 * methods used in all subclasses, for example: name, playcount, images and more. 042 * 043 * @author Janni Kovacs 044 */ 045public abstract class MusicEntry extends ImageHolder { 046 047 private static final DateFormat DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZZ", 048 Locale.ENGLISH); 049 050 protected String name; 051 protected String url; 052 protected String mbid; 053 protected int playcount; 054 protected int userPlaycount; 055 protected int listeners; 056 protected boolean streamable; 057 protected String id; 058 059 /** 060 * This property is only available on hype charts, like {@link Chart#getHypedArtists(String)} or {@link 061 * de.umass.lastfm.Group#getHype(String, String)} 062 */ 063 protected int percentageChange; 064 065 protected Collection<String> tags = new ArrayList<String>(); 066 private Date wikiLastChanged; 067 private String wikiSummary; 068 private String wikiText; 069 070 private float similarityMatch; 071 072 protected MusicEntry(String name, String url) { 073 this(name, url, null, -1, -1, false); 074 } 075 076 protected MusicEntry(String name, String url, String mbid, int playcount, int listeners, boolean streamable) { 077 this.name = name; 078 this.url = url; 079 this.mbid = mbid; 080 this.playcount = playcount; 081 this.listeners = listeners; 082 this.streamable = streamable; 083 } 084 085 public int getListeners() { 086 return listeners; 087 } 088 089 public String getMbid() { 090 return mbid; 091 } 092 093 public String getName() { 094 return name; 095 } 096 097 public String getId() { 098 return id; 099 } 100 101 public int getPlaycount() { 102 return playcount; 103 } 104 105 public int getUserPlaycount() { 106 return userPlaycount; 107 } 108 109 public boolean isStreamable() { 110 return streamable; 111 } 112 113 public String getUrl() { 114 return url; 115 } 116 117 public Collection<String> getTags() { 118 return tags; 119 } 120 121 /** 122 * Returns the value of the "percentage change" fields in weekly hype charts responses, such as in {@link Group#getHype(String, String)} 123 * or {@link Chart#getHypedArtists(String)}. 124 * 125 * @return Weekly percentage change 126 */ 127 public int getPercentageChange() { 128 return percentageChange; 129 } 130 131 public Date getWikiLastChanged() { 132 return wikiLastChanged; 133 } 134 135 public String getWikiSummary() { 136 return wikiSummary; 137 } 138 139 public String getWikiText() { 140 return wikiText; 141 } 142 143 /** 144 * Returns the "similarity" property, which is included in Artist.getSimilar and Track.getSimilar responses 145 * 146 * @return similarity 147 */ 148 public float getSimilarityMatch() { 149 return similarityMatch; 150 } 151 152 @Override 153 public String toString() { 154 return this.getClass().getSimpleName() + "[" + 155 "name='" + name + '\'' + 156 ", id='" + id + '\'' + 157 ", url='" + url + '\'' + 158 ", mbid='" + mbid + '\'' + 159 ", playcount=" + playcount + 160 ", listeners=" + listeners + 161 ", streamable=" + streamable + 162 ']'; 163 } 164 165 static Integer maybeParseInt(String s) { 166 try { 167 return Integer.parseInt(s); 168 } catch (NumberFormatException e) { 169 return null; 170 } 171 } 172 173 /** 174 * Loads all generic information from an XML <code>DomElement</code> into the given <code>MusicEntry</code> instance, i.e. the following 175 * tags:<br/> <ul> <li>playcount/plays</li> <li>listeners</li> <li>streamable</li> <li>name</li> <li>url</li> <li>mbid</li> <li>image</li> 176 * <li>tags</li> </ul> 177 * 178 * @param entry An entry 179 * @param element XML source element 180 */ 181 protected static void loadStandardInfo(MusicEntry entry, DomElement element) { 182 // playcount & listeners 183 DomElement statsChild = element.getChild("stats"); 184 String playcountString; 185 String userPlaycountString; 186 String listenersString; 187 if (statsChild != null) { 188 playcountString = statsChild.getChildText("playcount"); 189 userPlaycountString = statsChild.getChildText("userplaycount"); 190 listenersString = statsChild.getChildText("listeners"); 191 } else { 192 playcountString = element.getChildText("playcount"); 193 userPlaycountString = element.getChildText("userplaycount"); 194 listenersString = element.getChildText("listeners"); 195 } 196 if (element.hasChild("id")) { 197 entry.id = element.getChildText("id"); 198 } 199 // match for similar artists/tracks response 200 if (element.hasChild("match")) { 201 entry.similarityMatch = Float.parseFloat(element.getChildText("match")); 202 } 203 // percentagechange in getHype() responses 204 if (element.hasChild("percentagechange")) { 205 entry.percentageChange = Integer.parseInt(element.getChildText("percentagechange")); 206 } 207 int playcount = playcountString == null || playcountString.length() == 0 ? -1 : Integer 208 .parseInt(playcountString); 209 int userPlaycount = userPlaycountString == null || userPlaycountString.length() == 0 ? -1 : Integer 210 .parseInt(userPlaycountString); 211 int listeners = listenersString == null || listenersString.length() == 0 ? -1 : Integer 212 .parseInt(listenersString); 213 // streamable 214 String s = element.getChildText("streamable"); 215 boolean streamable = s != null && s.length() != 0 && Integer.valueOf(1).equals(maybeParseInt(s)); 216 // copy 217 entry.name = element.getChildText("name"); 218 entry.url = element.getChildText("url"); 219 entry.mbid = element.getChildText("mbid"); 220 entry.playcount = playcount; 221 entry.userPlaycount = userPlaycount; 222 entry.listeners = listeners; 223 entry.streamable = streamable; 224 // tags 225 DomElement tags = element.getChild("tags"); 226 if (tags == null) 227 tags = element.getChild("toptags"); 228 if (tags != null) { 229 for (DomElement tage : tags.getChildren("tag")) { 230 entry.tags.add(tage.getChildText("name")); 231 } 232 } 233 // wiki 234 DomElement wiki = element.getChild("bio"); 235 if (wiki == null) 236 wiki = element.getChild("wiki"); 237 if (wiki != null) { 238 String publishedText = wiki.getChildText("published"); 239 try { 240 entry.wikiLastChanged = DATE_FORMAT.parse(publishedText); 241 } catch (ParseException e) { 242 // try parsing it with current locale 243 try { 244 DateFormat clFormat = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ZZZZ", Locale.getDefault()); 245 entry.wikiLastChanged = clFormat.parse(publishedText); 246 } catch (ParseException e2) { 247 // cannot parse date, wrong locale. wait for last.fm to fix. 248 } 249 } 250 entry.wikiSummary = wiki.getChildText("summary"); 251 entry.wikiText = wiki.getChildText("content"); 252 } 253 // images 254 ImageHolder.loadImages(entry, element); 255 } 256}