org.runbuddy.libtomahawk.infosystem.hatchet.Store.java Source code

Java tutorial

Introduction

Here is the source code for org.runbuddy.libtomahawk.infosystem.hatchet.Store.java

Source

/* == This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
*   Copyright 2015, Enno Gottschalk <mrmaffen@googlemail.com>
*
*   Tomahawk is free software: you can redistribute it and/or modify
*   it under the terms of the GNU General Public License as published by
*   the Free Software Foundation, either version 3 of the License, or
*   (at your option) any later version.
*
*   Tomahawk is distributed in the hope that it will be useful,
*   but WITHOUT ANY WARRANTY; without even the implied warranty of
*   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
*   GNU General Public License for more details.
*
*   You should have received a copy of the GNU General Public License
*   along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
package org.runbuddy.libtomahawk.infosystem.hatchet;

import android.util.Log;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonIOException;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.Response;

import org.runbuddy.libtomahawk.collection.Album;
import org.runbuddy.libtomahawk.collection.AlphaComparator;
import org.runbuddy.libtomahawk.collection.Artist;
import org.runbuddy.libtomahawk.collection.Image;
import org.runbuddy.libtomahawk.collection.ListItemString;
import org.runbuddy.libtomahawk.collection.Playlist;
import org.runbuddy.libtomahawk.collection.PlaylistComparator;
import org.runbuddy.libtomahawk.collection.PlaylistEntry;
import org.runbuddy.libtomahawk.infosystem.InfoRequestData;
import org.runbuddy.libtomahawk.infosystem.QueryParams;
import org.runbuddy.libtomahawk.infosystem.Relationship;
import org.runbuddy.libtomahawk.infosystem.SocialAction;
import org.runbuddy.libtomahawk.infosystem.User;
import org.runbuddy.libtomahawk.resolver.Query;
import org.runbuddy.libtomahawk.utils.GsonHelper;
import org.runbuddy.libtomahawk.utils.ISO8601Utils;
import org.runbuddy.libtomahawk.utils.NetworkUtils;
import org.runbuddy.tomahawk.app.TomahawkApp;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;

import retrofit.RequestInterceptor;
import retrofit.RestAdapter;
import retrofit.android.MainThreadExecutor;
import retrofit.client.OkClient;
import retrofit.converter.GsonConverter;

import static android.os.Process.THREAD_PRIORITY_LOWEST;

public class Store {

    private final static String TAG = Store.class.getSimpleName();

    public static final String HATCHET_BASE_URL = "https://api.hatchet.is";

    public static final String HATCHET_API_VERSION = "/v2";

    private final Cache mCache = new Cache();

    private static class Cache {

        private Map<Class, Map> mCaches = new HashMap<>();

        public Cache() {
        }

        public <T> void addCache(Class<T> clss) {
            mCaches.put(clss, new HashMap<String, T>());
        }

        public <T> void put(Class<T> clss, String id, T object) {
            mCaches.get(clss).put(id, object);
        }

        public <T> T get(Class<T> clss, String id) {
            return (T) mCaches.get(clss).get(id);
        }

    }

    private final OkHttpClient mOkHttpClient;

    private final Hatchet mHatchet;

    private final Hatchet mHatchetBackground;

    public Store() {
        RequestInterceptor requestInterceptor = new RequestInterceptor() {
            @Override
            public void intercept(RequestFacade request) {
                if (!NetworkUtils.isNetworkAvailable()) {
                    int maxStale = 60 * 60 * 24 * 7; // tolerate 1-week stale
                    request.addHeader("Cache-Control", "public, max-stale=" + maxStale);
                }
                request.addHeader("Content-type", "application/json; charset=utf-8");
            }
        };
        mOkHttpClient = new OkHttpClient();
        File cacheDir = new File(TomahawkApp.getContext().getCacheDir(), "responseCache");
        com.squareup.okhttp.Cache cache = new com.squareup.okhttp.Cache(cacheDir, 1024 * 1024 * 20);
        mOkHttpClient.setCache(cache);
        RestAdapter restAdapter = new RestAdapter.Builder().setLogLevel(RestAdapter.LogLevel.BASIC)
                .setEndpoint(HATCHET_BASE_URL + HATCHET_API_VERSION)
                .setConverter(new GsonConverter(GsonHelper.get())).setRequestInterceptor(requestInterceptor)
                .setClient(new OkClient(mOkHttpClient)).build();
        mHatchet = restAdapter.create(Hatchet.class);

        Executor httpExecutor = Executors.newCachedThreadPool(new ThreadFactory() {
            @Override
            public Thread newThread(final Runnable r) {
                return new Thread(new Runnable() {
                    @Override
                    public void run() {
                        android.os.Process.setThreadPriority(THREAD_PRIORITY_LOWEST);
                        r.run();
                    }
                }, "Retrofit-Idle-Background");
            }
        });
        restAdapter = new RestAdapter.Builder().setLogLevel(RestAdapter.LogLevel.BASIC)
                .setEndpoint(HATCHET_BASE_URL + HATCHET_API_VERSION)
                .setConverter(new GsonConverter(GsonHelper.get())).setRequestInterceptor(requestInterceptor)
                .setClient(new OkClient(mOkHttpClient)).setExecutors(httpExecutor, new MainThreadExecutor())
                .build();
        mHatchetBackground = restAdapter.create(Hatchet.class);

        mCache.addCache(Image.class);
        mCache.addCache(Artist.class);
        mCache.addCache(Album.class);
        mCache.addCache(Query.class);
        mCache.addCache(ChartItem.class);
        mCache.addCache(Chart.class);
        mCache.addCache(PlaybackLogEntry.class);
        mCache.addCache(PlaylistEntry.class);
        mCache.addCache(User.class);
        mCache.addCache(Playlist.class);
        mCache.addCache(SocialAction.class);
        mCache.addCache(Search.class);
        mCache.addCache(SearchResult.class);
        mCache.addCache(Relationship.class);
    }

    public Hatchet getImplementation(boolean isBackgroundRequest) {
        return isBackgroundRequest ? mHatchetBackground : mHatchet;
    }

    public <T> T findRecord(String id, Class<T> resultType, boolean isBackgroundRequest) throws IOException {
        T record = mCache.get(resultType, id);
        if (record == null) {
            Hatchet hatchet = getImplementation(isBackgroundRequest);
            List<String> ids = new ArrayList<>();
            ids.add(String.valueOf(id));
            if (resultType == Image.class) {
                storeRecords(hatchet.getImages(ids), resultType, isBackgroundRequest);
            } else if (resultType == Artist.class) {
                storeRecords(hatchet.getArtists(ids, null), resultType, isBackgroundRequest);
            } else if (resultType == Album.class) {
                storeRecords(hatchet.getAlbums(ids, null, null), resultType, isBackgroundRequest);
            } else if (resultType == PlaylistEntry.class) {
                storeRecords(hatchet.getTracks(ids, null, null), resultType, isBackgroundRequest);
            } else if (resultType == User.class) {
                storeRecords(hatchet.getUsers(ids, null, null, null), resultType, isBackgroundRequest);
            } else if (resultType == Playlist.class) {
                storeRecords(hatchet.getPlaylists(ids), resultType, isBackgroundRequest);
            }
            record = mCache.get(resultType, id);
            if (record == null) {
                throw new IOException("Couldn't fetch entity from server.");
            }
            mCache.put(resultType, id, record);
        }
        return record;
    }

    public <T> List<T> storeRecords(JsonObject object, Class<T> resultType, boolean isBackgroundRequest)
            throws IOException {
        return storeRecords(object, resultType, -1, isBackgroundRequest);
    }

    public <T> List<T> storeRecords(JsonObject object, Class<T> resultType, int requestType,
            boolean isBackgroundRequest) throws IOException {
        return storeRecords(object, resultType, requestType, isBackgroundRequest, null);
    }

    public <T> List<T> storeRecords(JsonObject object, Class<T> resultType, int requestType,
            boolean isBackgroundRequest, QueryParams params) throws IOException {
        List<T> results = new ArrayList<>();
        JsonElement elements = object.get("images");

        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    Image image = mCache.get(Image.class, id);
                    if (image == null) {
                        String url = getAsString(o, "url");
                        int width = getAsInt(o, "width");
                        int height = getAsInt(o, "height");
                        image = Image.get(url, true, width, height);
                        mCache.put(Image.class, id, image);
                    }
                    if (resultType == Image.class) {
                        results.add((T) image);
                    }
                }
            }
        }
        elements = object.get("artists");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    Artist artist = mCache.get(Artist.class, id);
                    if (artist == null) {
                        String name = getAsString(o, "name");
                        String wiki = getAsString(o, "wikiabstract");
                        artist = Artist.get(name);
                        artist.setBio(new ListItemString(wiki));
                        JsonElement images = get(o, "images");
                        if (images instanceof JsonArray && ((JsonArray) images).size() > 0) {
                            String imageId = ((JsonArray) images).get(0).getAsString();
                            Image image = findRecord(imageId, Image.class, isBackgroundRequest);
                            artist.setImage(image);
                        }
                        mCache.put(Artist.class, id, artist);
                    }

                    if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_ARTISTS_TOPHITSANDALBUMS) {
                        JsonElement rawAlbums = get(o, "albums");
                        if (rawAlbums instanceof JsonObject && resultType == Album.class) {
                            results.addAll(storeRecords((JsonObject) rawAlbums, resultType, isBackgroundRequest));
                        }

                        JsonElement rawTopHits = get(o, "topHits");
                        if (rawTopHits instanceof JsonObject && resultType == Query.class) {
                            List<Chart> chartItems = storeRecords((JsonObject) rawTopHits, Chart.class,
                                    isBackgroundRequest);
                            List<Query> topHits = new ArrayList<>();
                            if (chartItems != null && chartItems.size() > 0) {
                                for (ChartItem item : chartItems.get(0).getChartItems()) {
                                    topHits.add(item.getQuery());
                                }
                            }
                            results.addAll((List<T>) topHits);
                        }
                    }
                    if (resultType == Artist.class) {
                        results.add((T) artist);
                    }
                }
            }
        }
        elements = object.get("albums");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    Album album = mCache.get(Album.class, id);
                    if (album == null) {
                        String name = getAsString(o, "name");
                        String artistId = getAsString(o, "artist");
                        Artist artist = findRecord(artistId, Artist.class, isBackgroundRequest);
                        album = Album.get(name, artist);
                        JsonElement images = get(o, "images");
                        if (images instanceof JsonArray && ((JsonArray) images).size() > 0) {
                            String imageId = ((JsonArray) images).get(0).getAsString();
                            Image image = findRecord(imageId, Image.class, isBackgroundRequest);
                            album.setImage(image);
                        }
                        String releaseType = getAsString(o, "releaseType");
                        album.setReleaseType(releaseType);
                        mCache.put(Album.class, id, album);
                    }

                    if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_ALBUMS_TRACKS) {
                        JsonElement rawTracks = get(o, "tracks");
                        if (rawTracks instanceof JsonObject && resultType == Query.class) {
                            results.addAll(storeRecords((JsonObject) rawTracks, resultType, isBackgroundRequest));
                        }
                    }
                    if (resultType == Album.class) {
                        results.add((T) album);
                    }
                }
            }
        }
        elements = object.get("tracks");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    Query query = mCache.get(Query.class, id);
                    if (query == null) {
                        String name = getAsString(o, "name");
                        String artistId = getAsString(o, "artist");
                        Artist artist = findRecord(artistId, Artist.class, isBackgroundRequest);
                        query = Query.get(name, null, artist.getName(), false, true);
                        mCache.put(Query.class, id, query);
                    }
                    if (resultType == Query.class) {
                        results.add((T) query);
                    }
                }
            }
        }
        elements = object.get("users");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    User user = User.get(id);
                    String name = getAsString(o, "name");
                    user.setName(name);
                    String about = getAsString(o, "about");
                    user.setAbout(about);
                    int followersCount = getAsInt(o, "followersCount");
                    user.setFollowersCount(followersCount);
                    int followCount = getAsInt(o, "followCount");
                    user.setFollowCount(followCount);
                    String nowplayingId = getAsString(o, "nowplaying");
                    if (nowplayingId != null) {
                        Query nowplaying = findRecord(nowplayingId, Query.class, isBackgroundRequest);
                        user.setNowPlaying(nowplaying);
                    }
                    String nowplayingtimestamp = getAsString(o, "nowplayingtimestamp");
                    user.setNowPlayingTimeStamp(ISO8601Utils.parse(nowplayingtimestamp));
                    String avatar = getAsString(o, "avatar");
                    if (avatar != null) {
                        Image image = findRecord(avatar, Image.class, isBackgroundRequest);
                        user.setImage(image);
                    }

                    if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_PLAYLISTS) {
                        JsonElement rawPlaylists = get(o, "playlists");
                        if (rawPlaylists instanceof JsonObject) {
                            List<Playlist> playlists = storeRecords((JsonObject) rawPlaylists, Playlist.class,
                                    isBackgroundRequest);
                            Collections.sort(playlists, new PlaylistComparator());
                            user.setPlaylists(playlists);
                        }
                    } else if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_LOVEDITEMS) {
                        JsonElement rawLovedItems = get(o, "lovedItems");
                        if (rawLovedItems instanceof JsonObject) {
                            List<Playlist> playlists = storeRecords((JsonObject) rawLovedItems, Playlist.class,
                                    isBackgroundRequest);
                            if (playlists != null && playlists.size() > 0) {
                                user.setFavorites(playlists.get(0));
                            }
                        }
                    } else if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_LOVEDALBUMS) {
                        JsonElement rawLovedAlbums = get(o, "lovedAlbums");
                        if (rawLovedAlbums instanceof JsonObject) {
                            List<Relationship> relationships = storeRecords((JsonObject) rawLovedAlbums,
                                    Relationship.class, isBackgroundRequest);
                            List<Album> albums = new ArrayList<>();
                            for (Relationship relationship : relationships) {
                                albums.add(relationship.getAlbum());
                            }
                            user.setStarredAlbums(albums);
                        }
                    } else if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_LOVEDARTISTS) {
                        JsonElement rawLovedArtists = get(o, "lovedArtists");
                        if (rawLovedArtists instanceof JsonObject) {
                            List<Relationship> relationships = storeRecords((JsonObject) rawLovedArtists,
                                    Relationship.class, isBackgroundRequest);
                            List<Artist> artists = new ArrayList<>();
                            for (Relationship relationship : relationships) {
                                artists.add(relationship.getArtist());
                            }
                            user.setStarredArtists(artists);
                        }
                    } else if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_PLAYBACKLOG) {
                        JsonElement rawPlaybackLog = get(o, "playbacklog");
                        if (rawPlaybackLog instanceof JsonObject) {
                            List<Playlist> playlists = storeRecords((JsonObject) rawPlaybackLog, Playlist.class,
                                    isBackgroundRequest);
                            if (playlists != null && playlists.size() > 0) {
                                user.setPlaybackLog(playlists.get(0));
                            }
                        }
                    } else if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_FOLLOWS
                            || requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_FOLLOWERS) {
                        boolean isFollows = requestType == InfoRequestData.INFOREQUESTDATA_TYPE_USERS_FOLLOWS;
                        JsonElement rawFollows = get(o, isFollows ? "follows" : "followers");
                        if (rawFollows instanceof JsonObject) {
                            JsonObject follows = (JsonObject) rawFollows;
                            storeRecords(follows, null, isBackgroundRequest);
                            JsonElement relationships = get(follows, "relationships");
                            if (relationships instanceof JsonArray) {
                                TreeMap<User, String> followsMap = new TreeMap<>(new AlphaComparator());
                                for (JsonElement relationship : (JsonArray) relationships) {
                                    JsonObject relationshipObj = (JsonObject) relationship;
                                    String relationshipId = getAsString(relationshipObj, "id");
                                    String userId = getAsString(relationshipObj, isFollows ? "targetUser" : "user");
                                    User followedUser = findRecord(userId, User.class, isBackgroundRequest);
                                    followsMap.put(followedUser, relationshipId);
                                }
                                if (isFollows) {
                                    user.setFollowings(followsMap);
                                } else {
                                    user.setFollowers(followsMap);
                                }
                            }
                        }
                    }
                    mCache.put(User.class, id, user);
                    if (resultType == User.class) {
                        results.add((T) user);
                    }
                }
            }
        }
        elements = object.get("playlistEntries");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    PlaylistEntry entry = mCache.get(PlaylistEntry.class, id);
                    if (entry == null) {
                        String trackId = getAsString(o, "track");
                        Query query = findRecord(trackId, Query.class, isBackgroundRequest);
                        String playlistId = getAsString(o, "playlist");
                        entry = PlaylistEntry.get(playlistId, query, id);
                        mCache.put(PlaylistEntry.class, id, entry);
                    }
                    if (resultType == PlaylistEntry.class) {
                        results.add((T) entry);
                    }
                }
            }
        }
        elements = object.get("playlists");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    String title = getAsString(o, "title");
                    String currentrevision = getAsString(o, "currentrevision");
                    Playlist playlist = null;
                    if (requestType == InfoRequestData.INFOREQUESTDATA_TYPE_PLAYLISTS_PLAYLISTENTRIES) {
                        JsonElement rawEntries = get(o, "playlistEntries");
                        if (rawEntries instanceof JsonObject) {
                            List<PlaylistEntry> entries = storeRecords((JsonObject) rawEntries, PlaylistEntry.class,
                                    isBackgroundRequest);
                            if (entries != null) {
                                playlist = Playlist.fromEntryList(id, null, null, entries);
                                playlist.setFilled(true);
                            }
                        }
                    } else {
                        JsonElement entryIds = o.get("playlistEntries");
                        if (entryIds instanceof JsonArray) {
                            List<PlaylistEntry> entries = new ArrayList<>();
                            for (JsonElement entryId : (JsonArray) entryIds) {
                                PlaylistEntry entry = findRecord(entryId.getAsString(), PlaylistEntry.class,
                                        isBackgroundRequest);
                                entries.add(entry);
                            }
                            playlist = Playlist.fromEntryList(id, null, null, entries);
                            playlist.setFilled(true);
                        }
                    }
                    if (playlist == null) {
                        playlist = Playlist.get(id);
                    }
                    playlist.setName(title);
                    playlist.setCurrentRevision(currentrevision);
                    playlist.setHatchetId(id);
                    int entryCount = getAsInt(o, "entryCount");
                    playlist.setCount(entryCount);
                    String userId = getAsString(o, "user");
                    playlist.setUserId(userId);
                    JsonElement popularArtists = get(o, "popularArtists");
                    if (popularArtists instanceof JsonArray) {
                        ArrayList<String> topArtistNames = new ArrayList<>();
                        for (JsonElement popularArtist : (JsonArray) popularArtists) {
                            String artistId = popularArtist.getAsString();
                            Artist artist = findRecord(artistId, Artist.class, isBackgroundRequest);
                            if (artist != null) {
                                topArtistNames.add(artist.getName());
                            }
                        }
                        playlist.setTopArtistNames(topArtistNames.toArray(new String[topArtistNames.size()]));
                    }
                    mCache.put(Playlist.class, id, playlist);
                    if (resultType == Playlist.class) {
                        results.add((T) playlist);
                    }
                }
            }
        }
        elements = object.get("playbacklogEntries");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    PlaybackLogEntry logEntry = mCache.get(PlaybackLogEntry.class, id);
                    if (logEntry == null) {
                        String trackId = getAsString(o, "track");
                        Query query = findRecord(trackId, Query.class, isBackgroundRequest);
                        String timestamp = getAsString(o, "timestamp");
                        Date date = ISO8601Utils.parse(timestamp);
                        logEntry = new PlaybackLogEntry(query, date);
                        mCache.put(PlaybackLogEntry.class, id, logEntry);
                    }
                    if (resultType == PlaybackLogEntry.class) {
                        results.add((T) logEntry);
                    }
                }
            }
        }
        elements = object.get("playbacklogs");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    JsonArray playbacklogEntries = get(o, "playbacklogEntries").getAsJsonArray();
                    ArrayList<PlaylistEntry> entries = new ArrayList<>();
                    for (JsonElement entry : playbacklogEntries) {
                        String entryId = entry.getAsString();
                        PlaybackLogEntry logEntry = findRecord(entryId, PlaybackLogEntry.class,
                                isBackgroundRequest);
                        PlaylistEntry e = PlaylistEntry.get(id, logEntry.getQuery(), entryId);
                        entries.add(e);
                    }
                    Playlist playlist = Playlist.fromEntryList(id, "Playbacklog", null, entries);
                    playlist.setHatchetId(id);
                    playlist.setFilled(true);
                    mCache.put(Playlist.class, id, playlist);
                    if (resultType == Playlist.class) {
                        results.add((T) playlist);
                    }
                }
            }
        }
        elements = object.get("socialActions");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    SocialAction socialAction = mCache.get(SocialAction.class, id);
                    if (socialAction == null) {
                        socialAction = SocialAction.get(id);
                        String action = getAsString(o, "action");
                        socialAction.setAction(action);
                        String date = getAsString(o, "date");
                        socialAction.setDate(ISO8601Utils.parse(date));
                        String actionType = getAsString(o, "type");
                        socialAction.setType(actionType);
                        String trackId = getAsString(o, "track");
                        if (trackId != null) {
                            Query query = findRecord(trackId, Query.class, isBackgroundRequest);
                            socialAction.setQuery(query);
                        }
                        String artistId = getAsString(o, "artist");
                        if (artistId != null) {
                            Artist artist = findRecord(artistId, Artist.class, isBackgroundRequest);
                            socialAction.setArtist(artist);
                        }
                        String albumId = getAsString(o, "album");
                        if (albumId != null) {
                            Album album = findRecord(albumId, Album.class, isBackgroundRequest);
                            socialAction.setAlbum(album);
                        }
                        String userId = getAsString(o, "user");
                        if (userId != null) {
                            User user = findRecord(userId, User.class, isBackgroundRequest);
                            socialAction.setUser(user);
                        }
                        String targetId = getAsString(o, "target");
                        if (targetId != null) {
                            User target = findRecord(targetId, User.class, isBackgroundRequest);
                            socialAction.setTarget(target);
                        }
                        String playlistId = getAsString(o, "playlist");
                        if (playlistId != null) {
                            Playlist playlist = findRecord(playlistId, Playlist.class, isBackgroundRequest);
                            socialAction.setPlaylist(playlist);
                        }
                        mCache.put(SocialAction.class, id, socialAction);
                    }
                    if (resultType == SocialAction.class) {
                        results.add((T) socialAction);
                    }
                }
            }
            if (params != null) {
                User user = findRecord(params.userid, User.class, false);
                if (user != null && resultType == SocialAction.class) {
                    if (HatchetInfoPlugin.HATCHET_SOCIALACTION_PARAMTYPE_FRIENDSFEED.equals(params.type)) {
                        user.setFriendsFeed((List<SocialAction>) results, params.before_date);
                    } else {
                        user.setSocialActions((List<SocialAction>) results, params.before_date);
                    }
                }
            }
        }
        elements = object.get("searchResults");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    SearchResult searchResult = mCache.get(SearchResult.class, id);
                    if (searchResult == null) {
                        float score = getAsFloat(o, "score");
                        String trackId = getAsString(o, "track");
                        if (trackId != null) {
                            Query query = findRecord(trackId, Query.class, isBackgroundRequest);
                            searchResult = new SearchResult(score, query);
                        }
                        String artistId = getAsString(o, "artist");
                        if (artistId != null) {
                            Artist artist = findRecord(artistId, Artist.class, isBackgroundRequest);
                            searchResult = new SearchResult(score, artist);
                        }
                        String albumId = getAsString(o, "album");
                        if (albumId != null) {
                            Album album = findRecord(albumId, Album.class, isBackgroundRequest);
                            searchResult = new SearchResult(score, album);
                        }
                        String userId = getAsString(o, "user");
                        if (userId != null) {
                            User user = findRecord(userId, User.class, isBackgroundRequest);
                            searchResult = new SearchResult(score, user);
                        }
                        String playlistId = getAsString(o, "playlist");
                        if (playlistId != null) {
                            Playlist playlist = findRecord(playlistId, Playlist.class, isBackgroundRequest);
                            searchResult = new SearchResult(score, playlist);
                        }
                        if (searchResult == null) {
                            throw new IOException("searchResult contained no actual result object!");
                        }
                        mCache.put(SearchResult.class, id, searchResult);
                    }
                    if (resultType == SearchResult.class) {
                        results.add((T) searchResult);
                    }
                }
            }
        }
        elements = object.get("searches");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    JsonArray rawSearchResults = get(o, "searchResults").getAsJsonArray();
                    ArrayList<SearchResult> searchResults = new ArrayList<>();
                    for (JsonElement rawSearchResult : rawSearchResults) {
                        String resultId = rawSearchResult.getAsString();
                        SearchResult searchResult = findRecord(resultId, SearchResult.class, isBackgroundRequest);
                        searchResults.add(searchResult);
                    }
                    Search search = new Search(searchResults);
                    mCache.put(Search.class, id, search);
                    if (resultType == Search.class) {
                        results.add((T) search);
                    }
                }
            }
        }
        elements = object.get("relationships");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    String type = getAsString(o, "type");
                    if (type.equals(HatchetInfoPlugin.HATCHET_RELATIONSHIPS_TYPE_LOVE)) {
                        String userId = getAsString(o, "user");
                        User user = findRecord(userId, User.class, isBackgroundRequest);
                        String dateString = getAsString(o, "date");
                        Date date = null;
                        if (dateString != null) {
                            date = ISO8601Utils.parse(dateString);
                        }
                        Relationship relationship = Relationship.get(id, type, user, date);
                        String targetId;
                        if ((targetId = getAsString(o, "targetTrack")) != null) {
                            Query query = findRecord(targetId, Query.class, isBackgroundRequest);
                            relationship.setQuery(query);
                            user.putRelationship(query, relationship);
                        } else if ((targetId = getAsString(o, "targetAlbum")) != null) {
                            Album album = findRecord(targetId, Album.class, isBackgroundRequest);
                            relationship.setAlbum(album);
                            user.putRelationship(album, relationship);
                        } else if ((targetId = getAsString(o, "targetArtist")) != null) {
                            Artist artist = findRecord(targetId, Artist.class, isBackgroundRequest);
                            relationship.setArtist(artist);
                            user.putRelationship(artist, relationship);
                        }
                        mCache.put(Relationship.class, id, relationship);
                        if (resultType == Relationship.class) {
                            results.add((T) relationship);
                        }
                    }
                }
            }
        }
        elements = object.get("chartItems");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    ChartItem item = mCache.get(ChartItem.class, id);
                    if (item == null) {
                        String trackid = getAsString(o, "track");
                        Query query = findRecord(trackid, Query.class, isBackgroundRequest);
                        int plays = getAsInt(o, "plays");
                        int listeners = getAsInt(o, "listeners");
                        item = new ChartItem(query, plays, listeners);
                        mCache.put(ChartItem.class, id, item);
                    }
                    if (resultType == ChartItem.class) {
                        results.add((T) item);
                    }
                }
            }
        }
        elements = object.get("chart");
        if (elements instanceof JsonArray) {
            for (JsonElement element : (JsonArray) elements) {
                if (element instanceof JsonObject) {
                    JsonObject o = (JsonObject) element;
                    String id = getAsString(o, "id");
                    Chart chart = mCache.get(Chart.class, id);
                    if (chart == null) {
                        List<ChartItem> items = new ArrayList<>();
                        JsonElement chartItems = get(o, "chartItems");
                        if (chartItems instanceof JsonArray) {
                            for (JsonElement chartItemid : (JsonArray) chartItems) {
                                String itemId = chartItemid.getAsString();
                                ChartItem chartItem = findRecord(itemId, ChartItem.class, isBackgroundRequest);
                                items.add(chartItem);
                            }
                        }
                        chart = new Chart(items);
                        mCache.put(Chart.class, id, chart);
                    }
                    if (resultType == Chart.class) {
                        results.add((T) chart);
                    }
                }
            }
        }
        return results;
    }

    public int getAsInt(JsonObject object, String memberName) throws IOException {
        JsonElement element = get(object, memberName);
        if (element != null && element.isJsonPrimitive()) {
            return element.getAsInt();
        }
        return -1;
    }

    public float getAsFloat(JsonObject object, String memberName) throws IOException {
        JsonElement element = get(object, memberName);
        if (element != null && element.isJsonPrimitive()) {
            return element.getAsFloat();
        }
        return -1;
    }

    public String getAsString(JsonObject object, String memberName) throws IOException {
        JsonElement element = get(object, memberName);
        if (element != null && element.isJsonPrimitive()) {
            return element.getAsString();
        }
        return null;
    }

    public JsonElement get(JsonObject object, String memberName) throws IOException {
        JsonElement element = object.get(memberName);
        if (element == null) {
            JsonObject links = object.getAsJsonObject("links");
            if (links != null && links.has(memberName)) {
                Request request = new Request.Builder().url(HATCHET_BASE_URL + links.get(memberName).getAsString())
                        .build();
                Log.d(TAG, "following link: " + request.urlString());
                Response response = mOkHttpClient.newCall(request).execute();
                if (!response.isSuccessful()) {
                    throw new IOException("API request with URL '" + request.urlString()
                            + "' not successful. Code was " + response.code());
                }
                try {
                    element = GsonHelper.get().fromJson(response.body().charStream(), JsonElement.class);
                } catch (JsonIOException | JsonSyntaxException e) {
                    throw new IOException(e);
                } finally {
                    response.body().close();
                }
            }
        }
        return element;
    }
}