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 * Contains user information and provides bindings to the methods in the user. namespace.
037 *
038 * @author Janni Kovacs
039 */
040public class User extends ImageHolder {
041
042  static final ItemFactory<User> FACTORY = new UserFactory();
043
044  private String id;
045  private String name;
046  private String url;
047
048  private String realname;
049
050  private String language;
051  private String country;
052  private int age = -1;
053  private String gender;
054  private boolean subscriber;
055  private int numPlaylists;
056  private int playcount;
057  private Date registeredDate;
058
059  private User(String name, String url) {
060    this.name = name;
061    this.url = url;
062  }
063
064  public String getName() {
065    return name;
066  }
067
068  public String getRealname() {
069    return realname;
070  }
071
072  public String getUrl() {
073    return url;
074  }
075
076  public int getAge() {
077    return age;
078  }
079
080  public String getCountry() {
081    return country;
082  }
083
084  public String getGender() {
085    return gender;
086  }
087
088  public String getLanguage() {
089    return language;
090  }
091
092  public int getNumPlaylists() {
093    return numPlaylists;
094  }
095
096  public int getPlaycount() {
097    return playcount;
098  }
099
100  public boolean isSubscriber() {
101    return subscriber;
102  }
103
104  public String getImageURL() {
105    return getImageURL(ImageSize.MEDIUM);
106  }
107
108  public String getId() {
109    return id;
110  }
111
112  public Date getRegisteredDate() {
113    return registeredDate;
114  }
115
116  /**
117   * Get a list of tracks by a given artist scrobbled by this user, including scrobble time. Can be limited to specific timeranges, defaults
118   * to all time.
119   *
120   * @param user The last.fm username to fetch the recent tracks of
121   * @param artist The artist name you are interested in
122   * @param apiKey A Last.fm API key
123   * @return a list of Tracks
124   */
125  public static PaginatedResult<Track> getArtistTracks(String user, String artist, String apiKey) {
126    return getArtistTracks(user, artist, 1, 0, 0, apiKey);
127  }
128
129  /**
130   * Get a list of tracks by a given artist scrobbled by this user, including scrobble time. Can be limited to specific timeranges, defaults
131   * to all time.
132   *
133   * @param user The last.fm username to fetch the recent tracks of
134   * @param artist The artist name you are interested in
135   * @param page An integer used to fetch a specific page of tracks
136   * @param startTimestamp An unix timestamp to start at
137   * @param endTimestamp An unix timestamp to end at
138   * @param apiKey A Last.fm API key
139   * @return a list of Tracks
140   */
141  public static PaginatedResult<Track> getArtistTracks(String user, String artist, int page, long startTimestamp, long endTimestamp, String apiKey) {
142    Map<String, String> params = new HashMap<String, String>();
143    params.put("user", user);
144    params.put("artist", artist);
145    params.put("page", String.valueOf(page));
146    params.put("startTimestamp", String.valueOf(startTimestamp));
147    params.put("endTimestamp", String.valueOf(endTimestamp));
148    Result result = Caller.getInstance().call("user.getArtistTracks", apiKey, params);
149    return ResponseBuilder.buildPaginatedResult(result, Track.class);
150  }
151
152  public static PaginatedResult<User> getFriends(String user, String apiKey) {
153    return getFriends(user, false, 1, 50, apiKey);
154  }
155
156  public static PaginatedResult<User> getFriends(String user, boolean recenttracks, int page, int limit, String apiKey) {
157    Result result = Caller.getInstance().call("user.getFriends", apiKey, "user", user, "recenttracks",
158        String.valueOf(recenttracks ? 1 : 0), "limit", String.valueOf(limit), "page", String.valueOf(page));
159    return ResponseBuilder.buildPaginatedResult(result, User.class);
160  }
161
162  public static Collection<User> getNeighbours(String user, String apiKey) {
163    return getNeighbours(user, 100, apiKey);
164  }
165
166  public static Collection<User> getNeighbours(String user, int limit, String apiKey) {
167    Result result = Caller.getInstance().call("user.getNeighbours", apiKey, "user", user, "limit", String.valueOf(limit));
168    return ResponseBuilder.buildCollection(result, User.class);
169  }
170
171  public static PaginatedResult<Track> getRecentTracks(String user, String apiKey) {
172    return getRecentTracks(user, 1, 10, apiKey);
173  }
174
175  public static PaginatedResult<Track> getRecentTracks(String user, int page, int limit, String apiKey) {
176    Map<String, String> params = new HashMap<String, String>();
177    params.put("user", user);
178    params.put("limit", String.valueOf(limit));
179    params.put("page", String.valueOf(page));
180    Result result = Caller.getInstance().call("user.getRecentTracks", apiKey, params);
181    return ResponseBuilder.buildPaginatedResult(result, Track.class);
182  }
183
184  public static Collection<Album> getTopAlbums(String user, String apiKey) {
185    return getTopAlbums(user, Period.OVERALL, apiKey);
186  }
187
188  public static Collection<Album> getTopAlbums(String user, Period period, String apiKey) {
189    Result result = Caller.getInstance().call("user.getTopAlbums", apiKey, "user", user, "period", period.getString());
190    return ResponseBuilder.buildCollection(result, Album.class);
191  }
192
193  public static Collection<Artist> getTopArtists(String user, String apiKey) {
194    return getTopArtists(user, Period.OVERALL, apiKey);
195  }
196
197  public static Collection<Artist> getTopArtists(String user, Period period, String apiKey) {
198    Result result = Caller.getInstance().call("user.getTopArtists", apiKey, "user", user, "period", period.getString());
199    return ResponseBuilder.buildCollection(result, Artist.class);
200  }
201
202  public static Collection<Track> getTopTracks(String user, String apiKey) {
203    return getTopTracks(user, Period.OVERALL, apiKey);
204  }
205
206  public static Collection<Track> getTopTracks(String user, Period period, String apiKey) {
207    Result result = Caller.getInstance().call("user.getTopTracks", apiKey, "user", user, "period", period.getString());
208    return ResponseBuilder.buildCollection(result, Track.class);
209  }
210
211  public static Collection<Tag> getTopTags(String user, String apiKey) {
212    return getTopTags(user, -1, apiKey);
213  }
214
215  public static Collection<Tag> getTopTags(String user, int limit, String apiKey) {
216    Map<String, String> params = new HashMap<String, String>();
217    params.put("user", user);
218    MapUtilities.nullSafePut(params, "limit", limit);
219    Result result = Caller.getInstance().call("user.getTopTags", apiKey, params);
220    return ResponseBuilder.buildCollection(result, Tag.class);
221  }
222
223  public static Chart<Album> getWeeklyAlbumChart(String user, String apiKey) {
224    return getWeeklyAlbumChart(user, null, null, -1, apiKey);
225  }
226
227  public static Chart<Album> getWeeklyAlbumChart(String user, int limit, String apiKey) {
228    return getWeeklyAlbumChart(user, null, null, limit, apiKey);
229  }
230
231  public static Chart<Album> getWeeklyAlbumChart(String user, String from, String to, int limit, String apiKey) {
232    return Chart.getChart("user.getWeeklyAlbumChart", "user", user, "album", from, to, limit, apiKey);
233  }
234
235  public static Chart<Artist> getWeeklyArtistChart(String user, String apiKey) {
236    return getWeeklyArtistChart(user, null, null, -1, apiKey);
237  }
238
239  public static Chart<Artist> getWeeklyArtistChart(String user, int limit, String apiKey) {
240    return getWeeklyArtistChart(user, null, null, limit, apiKey);
241  }
242
243  public static Chart<Artist> getWeeklyArtistChart(String user, String from, String to, int limit, String apiKey) {
244    return Chart.getChart("user.getWeeklyArtistChart", "user", user, "artist", from, to, limit, apiKey);
245  }
246
247  public static Chart<Track> getWeeklyTrackChart(String user, String apiKey) {
248    return getWeeklyTrackChart(user, null, null, -1, apiKey);
249  }
250
251  public static Chart<Track> getWeeklyTrackChart(String user, int limit, String apiKey) {
252    return getWeeklyTrackChart(user, null, null, limit, apiKey);
253  }
254
255  public static Chart<Track> getWeeklyTrackChart(String user, String from, String to, int limit, String apiKey) {
256    return Chart.getChart("user.getWeeklyTrackChart", "user", user, "track", from, to, limit, apiKey);
257  }
258
259  public static LinkedHashMap<String, String> getWeeklyChartList(String user, String apiKey) {
260    return Chart.getWeeklyChartList("user.getWeeklyChartList", "user", user, apiKey);
261  }
262
263  public static Collection<Chart> getWeeklyChartListAsCharts(String user, String apiKey) {
264    return Chart.getWeeklyChartListAsCharts("user", user, apiKey);
265  }
266
267  /**
268   * GetS a list of upcoming events that this user is attending.
269   *
270   * @param user The user to fetch the events for.
271   * @param apiKey A Last.fm API key.
272   * @return a list of upcoming events
273   */
274  public static PaginatedResult<Event> getEvents(String user, String apiKey) {
275    return getEvents(user, -1, apiKey);
276  }
277
278  /**
279   * GetS a list of upcoming events that this user is attending.
280   *
281   * @param user The user to fetch the events for.
282   * @param page The page number to fetch. Defaults to first page.
283   * @param apiKey A Last.fm API key.
284   * @return a list of upcoming events
285   */
286  public static PaginatedResult<Event> getEvents(String user, int page, String apiKey) {
287    return getEvents(user, false, page, -1, apiKey);
288  }
289
290  /**
291   * GetS a list of upcoming events that this user is attending.
292   *
293   * @param user The user to fetch the events for.
294   * @param page The page number to fetch. Defaults to first page.
295   * @param limit The number of results to fetch per page. Defaults to 50.
296   * @param festivalsOnly Whether only festivals should be returned, or all events.
297   * @param apiKey A Last.fm API key.
298   * @return a list of upcoming events
299   */
300  public static PaginatedResult<Event> getEvents(String user, boolean festivalsOnly, int page, int limit, String apiKey) {
301    Map<String, String> params = new HashMap<String, String>();
302    MapUtilities.nullSafePut(params, "user", user);
303    MapUtilities.nullSafePut(params, "page", page);
304    MapUtilities.nullSafePut(params, "limit", limit);
305    if (festivalsOnly) {
306      params.put("festivalsonly", "1");
307    }
308    Result result = Caller.getInstance().call("user.getEvents", apiKey, params);
309    return ResponseBuilder.buildPaginatedResult(result, Event.class);
310  }
311
312  /**
313   * Get the first page of a paginated result of all events a user has attended in the past.
314   *
315   * @param user The username to fetch the events for.
316   * @param apiKey A Last.fm API key.
317   * @return a list of past {@link Event}s
318   */
319  public static PaginatedResult<Event> getPastEvents(String user, String apiKey) {
320    return getPastEvents(user, -1, apiKey);
321  }
322
323  /**
324   * Gets a paginated list of all events a user has attended in the past.
325   *
326   * @param user The username to fetch the events for.
327   * @param page The page number to scan to.
328   * @param apiKey A Last.fm API key.
329   * @return a list of past {@link Event}s
330   */
331  public static PaginatedResult<Event> getPastEvents(String user, int page, String apiKey) {
332    Map<String, String> params = new HashMap<String, String>();
333    params.put("user", user);
334    MapUtilities.nullSafePut(params, "page", page);
335    Result result = Caller.getInstance().call("user.getPastEvents", apiKey, params);
336    return ResponseBuilder.buildPaginatedResult(result, Event.class);
337  }
338
339  public static PaginatedResult<Event> getRecommendedEvents(Session session) {
340    return getRecommendedEvents(1, session);
341  }
342
343  public static PaginatedResult<Event> getRecommendedEvents(int page, Session session) {
344    Result result = Caller.getInstance().call("user.getRecommendedEvents", session, "page", String.valueOf(page), "user",
345        session.getUsername());
346    return ResponseBuilder.buildPaginatedResult(result, Event.class);
347  }
348
349  /**
350   * Gets a list of a user's playlists on Last.fm. Note that this method only fetches metadata regarding the user's playlists. If you want to
351   * retrieve the list of tracks in a playlist use {@link Playlist#fetch(String, String) Playlist.fetch()}.
352   *
353   * @param user The last.fm username to fetch the playlists of.
354   * @param apiKey A Last.fm API key.
355   * @return a list of Playlists
356   */
357  public static Collection<Playlist> getPlaylists(String user, String apiKey) {
358    Result result = Caller.getInstance().call("user.getPlaylists", apiKey, "user", user);
359    if (!result.isSuccessful())
360      return Collections.emptyList();
361    Collection<Playlist> playlists = new ArrayList<Playlist>();
362    for (DomElement element : result.getContentElement().getChildren("playlist")) {
363      playlists.add(ResponseBuilder.buildItem(element, Playlist.class));
364    }
365    return playlists;
366  }
367
368  /**
369   * Retrieves the loved tracks by a user.
370   *
371   * @param user The user name to fetch the loved tracks for.
372   * @param apiKey A Last.fm API key.
373   * @return the loved tracks
374   */
375  public static PaginatedResult<Track> getLovedTracks(String user, String apiKey) {
376    return getLovedTracks(user, 1, apiKey);
377  }
378
379  /**
380   * Retrieves the loved tracks by a user.
381   *
382   * @param user The user name to fetch the loved tracks for.
383   * @param page The page number to scan to
384   * @param apiKey A Last.fm API key.
385   * @return the loved tracks
386   */
387  public static PaginatedResult<Track> getLovedTracks(String user, int page, String apiKey) {
388    Result result = Caller.getInstance().call("user.getLovedTracks", apiKey, "user", user, "page", String.valueOf(page));
389    return ResponseBuilder.buildPaginatedResult(result, Track.class);
390  }
391
392  /**
393   * Retrieves profile information about the specified user.
394   *
395   * @param user A username
396   * @param apiKey A Last.fm API key.
397   * @return User info
398   */
399  public static User getInfo(String user, String apiKey) {
400    Result result = Caller.getInstance().call("user.getInfo", apiKey, "user", user);
401    return ResponseBuilder.buildItem(result, User.class);
402  }
403
404  /**
405   * Retrieves profile information about the authenticated user.
406   *
407   * @param session A session for the user, for whom to get the profile for
408   * @return User info
409   */
410  public static User getInfo(Session session) {
411    Result result = Caller.getInstance().call("user.getInfo", session);  //, "user", "tgutwin");   //session.getUsername());
412    return ResponseBuilder.buildItem(result, User.class);
413  }
414
415  /**
416   * Get Last.fm artist recommendations for a user.
417   *
418   * @param session A Session instance
419   * @return a list of {@link Artist}s
420   */
421  public static PaginatedResult<Artist> getRecommendedArtists(Session session) {
422    return getRecommendedArtists(1, session);
423  }
424
425  /**
426   * Get Last.fm artist recommendations for a user.
427   *
428   * @param page The page to fetch
429   * @param session A Session instance
430   * @return a list of {@link Artist}s
431   */
432  public static PaginatedResult<Artist> getRecommendedArtists(int page, Session session) {
433    Result result = Caller.getInstance().call("user.getRecommendedArtists", session, "page", String.valueOf(page));
434    return ResponseBuilder.buildPaginatedResult(result, Artist.class);
435  }
436
437  /**
438   * Shout on this user's shoutbox
439   *
440   * @param user The name of the user to shout on
441   * @param message The message to post to the shoutbox
442   * @param session A Session instance
443   * @return the result of the operation
444   */
445  public static Result shout(String user, String message, Session session) {
446    return Caller.getInstance().call("user.shout", session, "user", user, "message", message);
447  }
448
449  /**
450   * Gets a list of forthcoming releases based on a user's musical taste.
451   *
452   * @param user The Last.fm username
453   * @param apiKey A Last.fm API key
454   * @return a Collection of new {@link Album} releases
455   */
456  public static Collection<Album> getNewReleases(String user, String apiKey) {
457    return getNewReleases(user, false, apiKey);
458  }
459
460  /**
461   * Gets a list of forthcoming releases based on a user's musical taste.
462   *
463   * @param user The Last.fm username
464   * @param useRecommendations If <code>true</code>, the feed contains new releases based on Last.fm's artist recommendations for this user.
465   * Otherwise, it is based on their library (the default)
466   * @param apiKey A Last.fm API key
467   * @return a Collection of new {@link Album} releases
468   */
469  public static Collection<Album> getNewReleases(String user, boolean useRecommendations, String apiKey) {
470    Result result = Caller.getInstance().call("user.getNewReleases", apiKey, "user", user, "userecs", useRecommendations ? "1" : "0");
471    return ResponseBuilder.buildCollection(result, Album.class);
472  }
473
474  /**
475   * Returns the tracks banned by the user.
476   *
477   * @param user The user name
478   * @param apiKey A Last.fm API key
479   * @return the banned tracks
480   */
481  public static PaginatedResult<Track> getBannedTracks(String user, String apiKey) {
482    return getBannedTracks(user, 1, apiKey);
483  }
484
485  /**
486   * Returns the tracks banned by the user.
487   *
488   * @param user The user name
489   * @param page The page number to fetch
490   * @param apiKey A Last.fm API key
491   * @return the banned tracks
492   */
493  public static PaginatedResult<Track> getBannedTracks(String user, int page, String apiKey) {
494    Result result = Caller.getInstance().call("user.getBannedTracks", apiKey, "user", user, "page", String.valueOf(page));
495    return ResponseBuilder.buildPaginatedResult(result, Track.class);
496  }
497
498  /**
499   * Get shouts for a user.
500   *
501   * @param user The username to fetch shouts for
502   * @param apiKey A Last.fm API key.
503   * @return a page of <code>Shout</code>s
504   */
505  public static PaginatedResult<Shout> getShouts(String user, String apiKey) {
506    return getShouts(user, -1, -1, apiKey);
507  }
508
509  /**
510   * Get shouts for a user.
511   *
512   * @param user The username to fetch shouts for
513   * @param page The page number to fetch
514   * @param apiKey A Last.fm API key.
515   * @return a page of <code>Shout</code>s
516   */
517  public static PaginatedResult<Shout> getShouts(String user, int page, String apiKey) {
518    return getShouts(user, page, -1, apiKey);
519  }
520
521  /**
522   * Get shouts for a user.
523   *
524   * @param user The username to fetch shouts for
525   * @param page The page number to fetch
526   * @param limit An integer used to limit the number of shouts returned per page or -1 for default
527   * @param apiKey A Last.fm API key.
528   * @return a page of <code>Shout</code>s
529   */
530  public static PaginatedResult<Shout> getShouts(String user, int page, int limit, String apiKey) {
531    Map<String, String> params = new HashMap<String, String>();
532    params.put("user", user);
533    MapUtilities.nullSafePut(params, "limit", limit);
534    MapUtilities.nullSafePut(params, "page", page);
535    Result result = Caller.getInstance().call("user.getShouts", apiKey, params);
536    return ResponseBuilder.buildPaginatedResult(result, Shout.class);
537  }
538
539  /**
540   * Get the user's personal tags.
541   *
542   * @param user The user who performed the taggings
543   * @param tag The tag you're interested in
544   * @param taggingType Either <code>Artist.class</code>, <code>Album.class</code> or <code>Track.class</code>
545   * @param apiKey A Last.fm API key
546   * @return the items the user has tagged with the specified tag
547   * @throws IllegalArgumentException if <code>taggingType</code> is <code>null</code> or not one of the above mentioned classes
548   */
549  public static <T extends MusicEntry> PaginatedResult<T> getPersonalTags(String user, String tag, Class<T> taggingType, String apiKey) {
550    return getPersonalTags(user, tag, taggingType, -1, -1, apiKey);
551  }
552
553  /**
554   * Get the user's personal tags.
555   *
556   * @param user The user who performed the taggings
557   * @param tag The tag you're interested in
558   * @param taggingType Either <code>Artist.class</code>, <code>Album.class</code> or <code>Track.class</code>
559   * @param page The page number to fetch
560   * @param apiKey A Last.fm API key
561   * @return the items the user has tagged with the specified tag
562   * @throws IllegalArgumentException if <code>taggingType</code> is <code>null</code> or not one of the above mentioned classes
563   */
564  public static <T extends MusicEntry> PaginatedResult<T> getPersonalTags(String user, String tag, Class<T> taggingType, int page, String apiKey) {
565    return getPersonalTags(user, tag, taggingType, page, -1, apiKey);
566  }
567
568  /**
569   * Get the user's personal tags.
570   *
571   * @param user The user who performed the taggings
572   * @param tag The tag you're interested in
573   * @param taggingType Either <code>Artist.class</code>, <code>Album.class</code> or <code>Track.class</code>
574   * @param page The page number to fetch
575   * @param limit The number of results to fetch per page. Defaults to 50
576   * @param apiKey A Last.fm API key
577   * @return the items the user has tagged with the specified tag
578   * @throws IllegalArgumentException if <code>taggingType</code> is <code>null</code> or not one of the above mentioned classes
579   */
580  public static <T extends MusicEntry> PaginatedResult<T> getPersonalTags(String user, String tag, Class<T> taggingType, int page, int limit, String apiKey) {
581    Map<String, String> params = StringUtilities.map("user", user, "tag", tag);
582    MapUtilities.nullSafePut(params, "page", page);
583    MapUtilities.nullSafePut(params, "limit", limit);
584
585    String taggingTypeParam = "taggingtype";
586    if (taggingType == Track.class)
587      params.put(taggingTypeParam, "track");
588    else if (taggingType == Artist.class)
589      params.put(taggingTypeParam, "artist");
590    else if (taggingType == Album.class)
591      params.put(taggingTypeParam, "album");
592    else
593      throw new IllegalArgumentException("Parameter taggingType has to be one of Artist.class, Album.class or Track.class.");
594
595    Result result = Caller.getInstance().call("user.getPersonalTags", apiKey, params);
596    if (!result.isSuccessful())
597      return new PaginatedResult<T>(0, 0, Collections.<T>emptyList());
598
599    String childElementName = params.get(taggingTypeParam) + "s";
600    DomElement contentElement = result.getContentElement();
601    DomElement childElement = contentElement.getChild(childElementName);
602    return ResponseBuilder.buildPaginatedResult(contentElement, childElement, taggingType);
603  }
604
605
606  private static class UserFactory implements ItemFactory<User> {
607    public User createItemFromElement(DomElement element) {
608      User user = new User(element.getChildText("name"), element.getChildText("url"));
609      user.id = element.getChildText("id");
610      if (element.hasChild("realname"))
611        user.realname = element.getChildText("realname");
612      ImageHolder.loadImages(user, element);
613      user.language = element.getChildText("lang");
614      user.country = element.getChildText("country");
615      if (element.hasChild("age")) {
616        try {
617          user.age = Integer.parseInt(element.getChildText("age"));
618        } catch (NumberFormatException e) {
619          // no age
620        }
621      }
622      user.gender = element.getChildText("gender");
623      user.subscriber = "1".equals(element.getChildText("subscriber"));
624      if (element.hasChild("playcount")) { // extended user information
625        try {
626          user.playcount = Integer.parseInt(element.getChildText("playcount"));
627        } catch (NumberFormatException e) {
628          // no playcount
629        }
630      }
631      if (element.hasChild("playlists")) { // extended user information
632        try {
633          user.numPlaylists = Integer.parseInt(element.getChildText("playlists"));
634        } catch (NumberFormatException e) {
635          // no playlists
636        }
637      }
638      if (element.hasChild("registered")) {
639        String unixtime = element.getChild("registered").getAttribute("unixtime");
640        user.registeredDate = new Date(Long.parseLong(unixtime) * 1000);
641      }
642      return user;
643    }
644  }
645}