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 java.util.*;
030
031import de.umass.util.MapUtilities;
032import de.umass.xml.DomElement;
033
034/**
035 * Bean for Chart information. Contains a start date, an end date and a list of entries.
036 *
037 * @author Janni Kovacs
038 */
039public class Chart<T extends MusicEntry> {
040
041        private Date from, to;
042        private Collection<T> entries;
043
044        public Chart(Date from, Date to, Collection<T> entries) {
045                this.from = from;
046                this.to = to;
047                this.entries = entries;
048        }
049
050        public Collection<T> getEntries() {
051                return entries;
052        }
053
054        public Date getFrom() {
055                return from;
056        }
057
058        public Date getTo() {
059                return to;
060        }
061
062        /**
063         * This is an internal method to retrieve Chart data.
064         *
065         * @param method The method to call, must be one of the getWeeklyXXXChart methods
066         * @param sourceType The name of the parameter to get the charts for, either "user", "tag" or "group"
067         * @param source The username, tag or group to get charts from
068         * @param target The expected chart type, either "album", "artist" or "track"
069         * @param from Start date or <code>null</code>
070         * @param to End date or <code>null</code>
071         * @param limit The number of chart items to return or -1
072         * @param apiKey A Last.fm API key.
073         * @return a Chart
074         */
075        static <T extends MusicEntry> Chart<T> getChart(String method, String sourceType, String source,
076                                                                                                        String target, String from, String to, int limit,
077                                                                                                        String apiKey) {
078                Map<String, String> params = new HashMap<String, String>();
079                params.put(sourceType, source);
080                return getChart(method, target, params, from, to, limit, apiKey);
081        }
082
083        /**
084         * This is an internal method to retrieve Chart data.
085         *
086         * @param method The method to call, must be one of the getWeeklyXXXChart methods
087         * @param params Extra parameters that will be passed to the webservice, e.g. containing user or tag name
088         * @param target The expected chart type, either "album", "artist" or "track"
089         * @param from Start date or <code>null</code>
090         * @param to End date or <code>null</code>
091         * @param limit The number of chart items to return or -1
092         * @param apiKey A Last.fm API key.
093         * @return a Chart
094         */
095        @SuppressWarnings("unchecked")
096        static <T extends MusicEntry> Chart<T> getChart(String method, String target, Map<String, String> params, String from, String to,
097                                                                                                        int limit, String apiKey) {
098                if (from != null && to != null) {
099                        params.put("from", from);
100                        params.put("to", to);
101                }
102                MapUtilities.nullSafePut(params, "limit", limit);
103                Result result = Caller.getInstance().call(method, apiKey, params);
104                if (!result.isSuccessful())
105                        return null;
106                DomElement element = result.getContentElement();
107                Collection<DomElement> children = element.getChildren(target);
108                Collection collection = new ArrayList(children.size());
109                boolean targetArtist = "artist".equals(target);
110                boolean targetTrack = "track".equals(target);
111                boolean targetAlbum = "album".equals(target);
112                for (DomElement domElement : children) {
113                        if (targetArtist)
114                                collection.add(ResponseBuilder.buildItem(domElement, Artist.class));
115                        if (targetTrack)
116                                collection.add(ResponseBuilder.buildItem(domElement, Track.class));
117                        if (targetAlbum)
118                                collection.add(ResponseBuilder.buildItem(domElement, Album.class));
119                }
120                long fromTime = 0;
121                long toTime = 0;
122                // workaround for geo.getMetroXXX methods, since they don't have from & to attributes if no dates were given upon calling 
123                if (element.hasAttribute("from")) {
124                        fromTime = 1000 * Long.parseLong(element.getAttribute("from"));
125                        toTime = 1000 * Long.parseLong(element.getAttribute("to"));
126                }
127                return new Chart<T>(new Date(fromTime), new Date(toTime), collection);
128        }
129
130        /**
131         * This is an internal method to get a list of available charts.
132         *
133         * @param methodName The name of the method to be called, e.g. <code>user.getWeeklyChartList</code>
134         * @param paramName The name of the parameter which is passed to the specified method, e.g. <code>user</code>
135         * @param paramValue The value of the parameter which is passed to the specified method, e.g. the user name
136         * @param apiKey A Last.fm API key.
137         * @return a list of available charts as a Map
138         */
139        static LinkedHashMap<String, String> getWeeklyChartList(String methodName, String paramName, String paramValue, String apiKey) {
140                Result result = Caller.getInstance().call(methodName, apiKey, paramName, paramValue);
141                if (!result.isSuccessful())
142                        return new LinkedHashMap<String, String>(0);
143                DomElement element = result.getContentElement();
144                LinkedHashMap<String, String> list = new LinkedHashMap<String, String>();
145                for (DomElement domElement : element.getChildren("chart")) {
146                        list.put(domElement.getAttribute("from"), domElement.getAttribute("to"));
147                }
148                return list;
149        }
150
151        /**
152         * This is an internal method to get a list of available charts.
153         *
154         * @param sourceType The name of the parameter to get the charts for, either "user", "tag" or "group"
155         * @param source The username, tag or group to get charts from
156         * @param apiKey A Last.fm API key.
157         * @return a list of available charts as a Collection of Charts
158         */
159        @SuppressWarnings("unchecked")
160        static Collection<Chart> getWeeklyChartListAsCharts(String sourceType, String source, String apiKey) {
161                Result result = Caller.getInstance().call(sourceType + ".getWeeklyChartList", apiKey, sourceType, source);
162                if (!result.isSuccessful())
163                        return Collections.emptyList();
164                DomElement element = result.getContentElement();
165                List<Chart> list = new ArrayList<Chart>();
166                for (DomElement domElement : element.getChildren("chart")) {
167                        long fromTime = 1000 * Long.parseLong(domElement.getAttribute("from"));
168                        long toTime = 1000 * Long.parseLong(domElement.getAttribute("to"));
169                        list.add(new Chart(new Date(fromTime), new Date(toTime), null));
170                }
171                return list;
172        }
173
174        /**
175         * Get the top artists chart.
176         * 
177         * @param apiKey A Last.fm API key
178         * @return Top artists chart
179         */
180        public static PaginatedResult<Artist> getTopArtists(String apiKey) {
181                return getTopArtists(1, apiKey);
182        }
183        
184        /**
185         * Get the top artists chart.
186         *
187         * @param page The page to fetch
188         * @param apiKey A Last.fm API key
189         * @return Top artists chart
190         */
191        public static PaginatedResult<Artist> getTopArtists(int page, String apiKey) {
192                Result result = Caller.getInstance().call("chart.getTopArtists", apiKey, "page", String.valueOf(page));
193                return ResponseBuilder.buildPaginatedResult(result, Artist.class);
194        }
195
196        /**
197         * Get the top tags chart.
198         *
199         * @param apiKey A Last.fm API key
200         * @return Top tags chart
201         */
202        public static PaginatedResult<Tag> getTopTags(String apiKey) {
203                return getTopTags(1, apiKey);
204        }
205
206        /**
207         * Get the top tags chart.
208         *
209         * @param page The page to fetch
210         * @param apiKey A Last.fm API key
211         * @return Top tags chart
212         */
213        public static PaginatedResult<Tag> getTopTags(int page, String apiKey) {
214                Result result = Caller.getInstance().call("chart.getTopTags", apiKey, "page", String.valueOf(page));
215                return ResponseBuilder.buildPaginatedResult(result, Tag.class);
216        }
217
218        /**
219         * Get the top tracks chart.
220         *
221         * @param apiKey A Last.fm API key
222         * @return Top tracks chart
223         */
224        public static PaginatedResult<Track> getTopTracks(String apiKey) {
225                return getTopTracks(1, apiKey);
226        }
227
228        /**
229         * Get the top tracks chart.
230         *
231         * @param page The page to fetch
232         * @param apiKey A Last.fm API key
233         * @return Top tracks chart
234         */
235        public static PaginatedResult<Track> getTopTracks(int page, String apiKey) {
236                Result result = Caller.getInstance().call("chart.getTopTracks", apiKey, "page", String.valueOf(page));
237                return ResponseBuilder.buildPaginatedResult(result, Track.class);
238        }
239
240        /**
241         * Get the most loved tracks chart.
242         *
243         * @param apiKey A Last.fm API key
244         * @return Most loved tracks chart
245         */
246        public static PaginatedResult<Track> getLovedTracks(String apiKey) {
247                return getLovedTracks(1, apiKey);
248        }
249
250        /**
251         * Get the most loved tracks chart.
252         *
253         * @param page The page to fetch
254         * @param apiKey A Last.fm API key
255         * @return Most loved tracks chart
256         */
257        public static PaginatedResult<Track> getLovedTracks(int page, String apiKey) {
258                Result result = Caller.getInstance().call("chart.getLovedTracks", apiKey, "page", String.valueOf(page));
259                return ResponseBuilder.buildPaginatedResult(result, Track.class);
260        }
261
262        /**
263         * Get the hyped tracks chart.
264         *
265         * @param apiKey A Last.fm API key
266         * @return Hyped tracks chart
267         */
268        public static PaginatedResult<Track> getHypedTracks(String apiKey) {
269                return getHypedTracks(1, apiKey);
270        }
271
272        /**
273         * Get the hyped tracks chart.
274         *
275         * @param page The page to fetch
276         * @param apiKey A Last.fm API key
277         * @return Hyped tracks chart
278         */
279        public static PaginatedResult<Track> getHypedTracks(int page, String apiKey) {
280                Result result = Caller.getInstance().call("chart.getHypedTracks", apiKey, "page", String.valueOf(page));
281                return ResponseBuilder.buildPaginatedResult(result, Track.class);
282        }
283
284        /**
285         * Get the hyped artists chart.
286         *
287         * @param apiKey A Last.fm API key
288         * @return Hyped artists chart
289         */
290        public static PaginatedResult<Artist> getHypedArtists(String apiKey) {
291                return getHypedArtists(1, apiKey);
292        }
293
294        /**
295         * Get the hyped artists chart.
296         *
297         * @param page The page to fetch
298         * @param apiKey A Last.fm API key
299         * @return Hyped artists chart
300         */
301        public static PaginatedResult<Artist> getHypedArtists(int page, String apiKey) {
302                Result result = Caller.getInstance().call("chart.getHypedArtists", apiKey, "page", String.valueOf(page));
303                return ResponseBuilder.buildPaginatedResult(result, Artist.class);
304        }
305}