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.util.StringUtilities;
033import de.umass.xml.DomElement;
034
035/**
036 * Provides nothing more than a namespace for the API methods starting with geo.
037 *
038 * @author Janni Kovacs
039 */
040public class Geo {
041
042        /**
043         * This inner class represents a Metro, which is composed of its name and the name of its country.
044         *
045         * @see Geo#getMetros(String, String)
046         */
047        public static class Metro {
048                private String name;
049                private String country;
050
051                public Metro(String name, String country) {
052                        this.name = name;
053                        this.country = country;
054                }
055
056                public String getName() {
057                        return name;
058                }
059
060                public String getCountry() {
061                        return country;
062                }
063        }
064
065        private Geo() {
066        }
067
068        /**
069         * Get all events in a specific location by country or city name.<br/> This method returns <em>all</em> events by subsequently calling
070         * {@link #getEvents(String, String, int, String)} and concatenating the single results into one list.<br/> Pay attention if you use this
071         * method as it may produce a lot of network traffic and therefore may consume a long time.
072         *
073         * @param location Specifies a location to retrieve events for
074         * @param distance Find events within a specified radius (in kilometres)
075         * @param apiKey A Last.fm API key.
076         * @return a list containing all events
077         */
078        public static Collection<Event> getAllEvents(String location, String distance, String apiKey) {
079                Collection<Event> events = null;
080                int page = 1, total;
081                do {
082                        PaginatedResult<Event> result = getEvents(location, distance, page, apiKey);
083                        total = result.getTotalPages();
084                        Collection<Event> pageResults = result.getPageResults();
085                        if (events == null) {
086                                // events is initialized here to initialize it with the right size and avoid array copying later on
087                                events = new ArrayList<Event>(total * pageResults.size());
088                        }
089                        for (Event artist : pageResults) {
090                                events.add(artist);
091                        }
092                        page++;
093                } while (page <= total);
094                return events;
095        }
096
097        /**
098         * Get all events in a specific location by country or city name.<br/> This method only returns the first page of a possibly paginated
099         * result. To retrieve all pages get the total number of pages via {@link de.umass.lastfm.PaginatedResult#getTotalPages()} and subsequently
100         * call {@link #getEvents(String, String, int, String)} with the successive page numbers.
101         *
102         * @param location Specifies a location to retrieve events for
103         * @param distance Find events within a specified radius (in kilometres)
104         * @param apiKey A Last.fm API key.
105         * @return a {@link PaginatedResult} containing a list of events
106         */
107        public static PaginatedResult<Event> getEvents(String location, String distance, String apiKey) {
108                return getEvents(location, distance, 1, apiKey);
109        }
110
111        /**
112         * Get all events in a specific location by country or city name.<br/> This method only returns the specified page of a paginated result.
113         *
114         * @param location Specifies a location to retrieve events for
115         * @param distance Find events within a specified radius (in kilometres)
116         * @param page A page number for pagination
117         * @param apiKey A Last.fm API key.
118         * @return a {@link PaginatedResult} containing a list of events
119         */
120        public static PaginatedResult<Event> getEvents(String location, String distance, int page, String apiKey) {
121                return getEvents(location, distance, page, -1, apiKey);
122        }
123
124        public static PaginatedResult<Event> getEvents(String location, String distance, int page, int limit, String apiKey) {
125                Map<String, String> params = new HashMap<String, String>();
126                params.put("page", String.valueOf(page));
127                MapUtilities.nullSafePut(params, "location", location);
128                MapUtilities.nullSafePut(params, "distance", distance);
129                MapUtilities.nullSafePut(params, "limit", limit);
130                Result result = Caller.getInstance().call("geo.getEvents", apiKey, params);
131                return ResponseBuilder.buildPaginatedResult(result, Event.class);
132        }
133
134        /**
135         * Get all events in a specific location by latitude/longitude.<br/> This method only returns the specified page of a paginated result.
136         *
137         * @param latitude Latitude
138         * @param longitude Longitude
139         * @param page A page number for pagination
140         * @param apiKey A Last.fm API key.
141         * @return a {@link PaginatedResult} containing a list of events
142         */
143        public static PaginatedResult<Event> getEvents(double latitude, double longitude, int page, String apiKey) {
144                return getEvents(latitude, longitude, page, -1, apiKey);
145        }
146
147        public static PaginatedResult<Event> getEvents(double latitude, double longitude, int page, int limit, String apiKey) {
148                Map<String, String> params = new HashMap<String, String>();
149                params.put("page", String.valueOf(page));
150                params.put("lat", String.valueOf(latitude));
151                params.put("long", String.valueOf(longitude));
152                MapUtilities.nullSafePut(params, "limit", limit);
153                Result result = Caller.getInstance().call("geo.getEvents", apiKey, params);
154                return ResponseBuilder.buildPaginatedResult(result, Event.class);
155        }
156
157        public static PaginatedResult<Event> getEvents(double latitude, double longitude, String distance, String apiKey) {
158                return getEvents(latitude, longitude, distance, -1, -1, apiKey);
159        }
160
161        /**
162         * Get all events within the specified distance of the location specified by latitude/longitude.<br/>
163         * This method only returns the specified page of a paginated result.
164         *
165         * @param latitude Latitude
166         * @param longitude Longitude
167         * @param distance Find events within a specified radius (in kilometres)
168         * @param page A page number for pagination
169         * @param limit The maximum number of items returned per page
170         * @param apiKey A Last.fm API key.
171         * @return a {@link PaginatedResult} containing a list of events
172         */
173        public static PaginatedResult<Event> getEvents(double latitude, double longitude, String distance, int page, int limit, String apiKey) {
174                Map<String, String> params = new HashMap<String, String>();
175                params.put("lat", String.valueOf(latitude));
176                params.put("long", String.valueOf(longitude));
177                params.put("distance", distance);
178                MapUtilities.nullSafePut(params, "page", page);
179                MapUtilities.nullSafePut(params, "limit", limit);
180                Result result = Caller.getInstance().call("geo.getEvents", apiKey, params);
181                return ResponseBuilder.buildPaginatedResult(result, Event.class);
182        }
183
184        /**
185         * Get the most popular artists on Last.fm by country
186         *
187         * @param country A country name, as defined by the ISO 3166-1 country names standard
188         * @param apiKey A Last.fm API key.
189         * @return list of Artists
190         */
191        public static Collection<Artist> getTopArtists(String country, String apiKey) {
192                Result result = Caller.getInstance().call("geo.getTopArtists", apiKey, "country", country);
193                return ResponseBuilder.buildCollection(result, Artist.class);
194        }
195
196        /**
197         * Get the most popular tracks on Last.fm by country
198         *
199         * @param country A country name, as defined by the ISO 3166-1 country names standard
200         * @param apiKey A Last.fm API key.
201         * @return a list of Tracks
202         */
203        public static Collection<Track> getTopTracks(String country, String apiKey) {
204                Result result = Caller.getInstance().call("geo.getTopTracks", apiKey, "country", country);
205                return ResponseBuilder.buildCollection(result, Track.class);
206        }
207
208        /**
209         * Get a list of valid countries and {@link Metro}s for use in the other webservices.
210         *
211         * @param apiKey A Last.fm API key
212         * @return a List of {@link Metro}s
213         */
214        public static Collection<Metro> getMetros(String apiKey) {
215                return getMetros(null, apiKey);
216        }
217
218        /**
219         * Get a list of valid countries and {@link Metro}s for use in the other webservices.
220         *
221         * @param country Optionally restrict the results to those Metros from a particular country, as defined by the ISO 3166-1 country names
222         * standard
223         * @param apiKey A Last.fm API key
224         * @return a List of {@link Metro}s
225         */
226        public static Collection<Metro> getMetros(String country, String apiKey) {
227                Map<String, String> params = new HashMap<String, String>();
228                MapUtilities.nullSafePut(params, "country", country);
229                Result result = Caller.getInstance().call("geo.getMetros", apiKey, params);
230                if (!result.isSuccessful())
231                        return Collections.emptyList();
232                Collection<DomElement> children = result.getContentElement().getChildren("metro");
233                Collection<Metro> metros = new ArrayList<Metro>(children.size());
234                for (DomElement child : children) {
235                        metros.add(new Metro(child.getChildText("name"), child.getChildText("country")));
236                }
237                return metros;
238        }
239
240        /**
241         * Get a list of available chart periods for this metro, expressed as date ranges which can be sent to the chart services.
242         *
243         * @param metro The name of the metro, or <code>null</code>
244         * @param apiKey A Last.fm API key
245         * @return a list of available charts as a Map
246         */
247        public static LinkedHashMap<String, String> getMetroWeeklyChartList(String metro, String apiKey) {
248                return Chart.getWeeklyChartList("geo.getMetroWeeklyChartList", "metro", metro, apiKey);
249        }
250
251        public static Chart<Artist> getMetroArtistChart(String country, String metro, String apiKey) {
252                return getMetroArtistChart(country, metro, null, null, apiKey);
253        }
254
255        public static Chart<Artist> getMetroArtistChart(Metro metro, String start, String end, String apiKey) {
256                return getMetroArtistChart(metro.getCountry(), metro.getName(), start, end, apiKey);
257        }
258
259        public static Chart<Artist> getMetroArtistChart(String country, String metro, String start, String end, String apiKey) {
260                return Chart.getChart("geo.getMetroArtistChart", "artist", StringUtilities.map("country", country, "metro", metro), start, end, -1,
261                                apiKey);
262        }
263
264        public static Chart<Track> getMetroTrackChart(String country, String metro, String apiKey) {
265                return getMetroTrackChart(country, metro, null, null, apiKey);
266        }
267
268        public static Chart<Track> getMetroTrackChart(Metro metro, String start, String end, String apiKey) {
269                return getMetroTrackChart(metro.getCountry(), metro.getName(), start, end, apiKey);
270        }
271
272        public static Chart<Track> getMetroTrackChart(String country, String metro, String start, String end, String apiKey) {
273                return Chart.getChart("geo.getMetroTrackChart", "track", StringUtilities.map("country", country, "metro", metro), start, end, -1,
274                                apiKey);
275        }
276
277        public static Chart<Artist> getMetroHypeArtistChart(String country, String metro, String apiKey) {
278                return getMetroHypeArtistChart(country, metro, null, null, apiKey);
279        }
280
281        public static Chart<Artist> getMetroHypeArtistChart(Metro metro, String start, String end, String apiKey) {
282                return getMetroHypeArtistChart(metro.getCountry(), metro.getName(), start, end, apiKey);
283        }
284
285        public static Chart<Artist> getMetroHypeArtistChart(String country, String metro, String start, String end, String apiKey) {
286                return Chart.getChart("geo.getMetroHypeArtistChart", "artist", StringUtilities.map("country", country, "metro", metro), start, end,
287                                -1, apiKey);
288        }
289
290        public static Chart<Track> getMetroHypeTrackChart(String country, String metro, String apiKey) {
291                return getMetroHypeTrackChart(country, metro, null, null, apiKey);
292        }
293
294        public static Chart<Track> getMetroHypeTrackChart(Metro metro, String start, String end, String apiKey) {
295                return getMetroHypeTrackChart(metro.getCountry(), metro.getName(), start, end, apiKey);
296        }
297
298        public static Chart<Track> getMetroHypeTrackChart(String country, String metro, String start, String end, String apiKey) {
299                return Chart.getChart("geo.getMetroHypeTrackChart", "track", StringUtilities.map("country", country, "metro", metro), start, end,
300                                -1, apiKey);
301        }
302
303        public static Chart<Artist> getMetroUniqueArtistChart(String country, String metro, String apiKey) {
304                return getMetroUniqueArtistChart(country, metro, null, null, apiKey);
305        }
306
307        public static Chart<Artist> getMetroUniqueArtistChart(Metro metro, String start, String end, String apiKey) {
308                return getMetroUniqueArtistChart(metro.getCountry(), metro.getName(), start, end, apiKey);
309        }
310
311        public static Chart<Artist> getMetroUniqueArtistChart(String country, String metro, String start, String end, String apiKey) {
312                return Chart.getChart("geo.getMetroUniqueArtistChart", "artist", StringUtilities.map("country", country, "metro", metro), start,
313                                end, -1, apiKey);
314        }
315
316        public static Chart<Track> getMetroUniqueTrackChart(String country, String metro, String apiKey) {
317                return getMetroUniqueTrackChart(country, metro, null, null, apiKey);
318        }
319
320        public static Chart<Track> getMetroUniqueTrackChart(Metro metro, String start, String end, String apiKey) {
321                return getMetroUniqueTrackChart(metro.getCountry(), metro.getName(), start, end, apiKey);
322        }
323
324        public static Chart<Track> getMetroUniqueTrackChart(String country, String metro, String start, String end, String apiKey) {
325                return Chart.getChart("geo.getMetroUniqueTrackChart", "track", StringUtilities.map("country", country, "metro", metro), start, end,
326                                -1, apiKey);
327        }
328}