com.akop.bach.parser.PsnParser.java Source code

Java tutorial

Introduction

Here is the source code for com.akop.bach.parser.PsnParser.java

Source

/*
 * PsnParser.java 
 * Copyright (C) 2010-2014 Akop Karapetyan
 *
 * This file is part of Spark 360, the online gaming service client.
 *
 *  This program 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 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program 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 this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

package com.akop.bach.parser;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.HttpParams;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;

import com.akop.bach.BasicAccount;
import com.akop.bach.App;
import com.akop.bach.PSN.ComparedGameInfo;
import com.akop.bach.PSN.ComparedTrophyInfo;
import com.akop.bach.PSN.Friends;
import com.akop.bach.PSN.GameCatalogItem;
import com.akop.bach.PSN.GameCatalogItemDetails;
import com.akop.bach.PSN.GameCatalogList;
import com.akop.bach.PSN.GamerProfileInfo;
import com.akop.bach.PSN.Games;
import com.akop.bach.PSN.Profiles;
import com.akop.bach.PSN.Trophies;
import com.akop.bach.Preferences;
import com.akop.bach.PsnAccount;
import com.akop.bach.R;
import com.akop.bach.util.IgnorantHttpClient;
import com.akop.bach.util.rss.RssChannel;

public abstract class PsnParser extends Parser {
    protected static final Pattern PATTERN_PSN_DOWN = Pattern.compile("id=\"siteMessageEN\".*?<h2>([^<]*)</h2>",
            Pattern.DOTALL | Pattern.CASE_INSENSITIVE);
    protected static final Pattern PATTERN_TOS_UPDATED = Pattern.compile("href=\"/external/loadEula.action\">",
            Pattern.CASE_INSENSITIVE);

    protected static final String URL_LOGIN = "https://store.playstation.com/j_acegi_external_security_check?target=/external/login.action";

    protected static final int COLUMN_GAME_ID = 0;
    protected static final int COLUMN_GAME_PROGRESS = 1;
    protected static final int COLUMN_GAME_BRONZE = 2;
    protected static final int COLUMN_GAME_SILVER = 3;
    protected static final int COLUMN_GAME_GOLD = 4;
    protected static final int COLUMN_GAME_PLATINUM = 5;

    protected static final String[] GAMES_PROJECTION = new String[] { Games._ID, Games.PROGRESS,
            Games.UNLOCKED_BRONZE, Games.UNLOCKED_SILVER, Games.UNLOCKED_GOLD, Games.UNLOCKED_PLATINUM, };

    protected static final String[] FRIEND_ID_PROJECTION = { Friends._ID };

    public PsnParser(Context context) {
        super(context);

        HttpParams params = mHttpClient.getParams();
        //params.setParameter("http.useragent", USER_AGENT);
        params.setParameter("http.protocol.max-redirects", 0);
    }

    @Override
    protected DefaultHttpClient createHttpClient(Context context) {
        return new IgnorantHttpClient();
    }

    @Override
    protected String preparseResponse(String response) throws IOException {
        // Sony's stupid method of requesting re-authentication
        // This needs to be improved; too un-dependable

        if (response.startsWith("<script") && response.endsWith("</script>"))
            throw new ClientProtocolException();

        return super.preparseResponse(response);
    }

    protected String getOutageMessage(String response) {
        Matcher m;
        String message = null;

        if ((m = PATTERN_PSN_DOWN.matcher(response)).find())
            message = htmlDecode(m.group(1));

        if ((m = PATTERN_TOS_UPDATED.matcher(response)).find())
            message = mContext.getString(R.string.reaccept_tos);

        return message;
    }

    private void parseAccountSummary(PsnAccount account) throws ParserException, IOException {
        ContentValues cv = parseSummaryData(account);
        ContentResolver cr = mContext.getContentResolver();

        long accountId = account.getId();
        boolean newRecord = true;

        long started = System.currentTimeMillis();
        Cursor c = cr.query(Profiles.CONTENT_URI, new String[] { Profiles._ID },
                Profiles.ACCOUNT_ID + "=" + accountId, null, null);

        if (c != null) {
            if (c.moveToFirst())
                newRecord = false;
            c.close();
        }

        if (newRecord) {
            cv.put(Profiles.ACCOUNT_ID, account.getId());
            cv.put(Profiles.UUID, account.getUuid());

            cr.insert(Profiles.CONTENT_URI, cv);
        } else {
            cr.update(Profiles.CONTENT_URI, cv, Profiles.ACCOUNT_ID + "=" + accountId, null);
        }

        cr.notifyChange(Profiles.CONTENT_URI, null);

        if (App.getConfig().logToConsole())
            displayTimeTaken("Summary update", started);

        account.refresh(Preferences.get(mContext));
        account.setOnlineId(cv.getAsString(Profiles.ONLINE_ID));
        account.setIconUrl(cv.getAsString(Profiles.ICON_URL));
        account.setLastSummaryUpdate(System.currentTimeMillis());
        account.save(Preferences.get(mContext));
    }

    protected String getAvatarImage(String iconUrl) {
        int index = iconUrl.indexOf("=");
        if (index >= 0)
            return iconUrl.substring(index + 1);

        return iconUrl;
    }

    @Override
    protected abstract boolean onAuthenticate(BasicAccount account) throws IOException, ParserException;

    protected abstract ContentValues parseSummaryData(PsnAccount account) throws IOException, ParserException;

    protected abstract void parseGames(PsnAccount account) throws ParserException, IOException;

    protected abstract void parseTrophies(PsnAccount account, long gameId) throws ParserException, IOException;

    protected abstract void parseFriends(PsnAccount account) throws ParserException, IOException;

    protected abstract GamerProfileInfo parseGamerProfile(PsnAccount account, String onlineId)
            throws ParserException, IOException;

    protected abstract void parseFriendSummary(PsnAccount account, String friendOnlineId)
            throws ParserException, IOException;

    protected abstract ComparedGameInfo parseCompareGames(PsnAccount account, String friendId)
            throws ParserException, IOException;

    protected abstract ComparedTrophyInfo parseCompareTrophies(PsnAccount account, String friendId, String gameId)
            throws ParserException, IOException;

    public abstract RssChannel fetchBlog() throws ParserException;

    public void fetchSummary(PsnAccount account) throws AuthenticationException, IOException, ParserException {
        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                parseAccountSummary(account);
                saveSession(account);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }
    }

    public void fetchFriends(PsnAccount account) throws AuthenticationException, IOException, ParserException {
        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                parseFriends(account);
                saveSession(account);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }
    }

    public void fetchGames(PsnAccount account) throws AuthenticationException, IOException, ParserException {
        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                parseGames(account);
                saveSession(account);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }
    }

    public void fetchTrophies(PsnAccount account, long gameId)
            throws AuthenticationException, IOException, ParserException {
        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                parseTrophies(account, gameId);
                saveSession(account);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }
    }

    public void fetchFriendSummary(PsnAccount account, String friendId)
            throws AuthenticationException, IOException, ParserException {
        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                parseFriendSummary(account, friendId);
                saveSession(account);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }
    }

    public GameCatalogList fetchGameCatalog(PsnAccount account, int console, int page, int releaseStatus,
            int sortOrder) throws AuthenticationException, IOException, ParserException {
        return parseGameCatalog(console, page, releaseStatus, sortOrder);
    }

    public GameCatalogItemDetails fetchGameCatalogItemDetails(PsnAccount account, GameCatalogItem item)
            throws AuthenticationException, IOException, ParserException {
        return parseGameCatalogItemDetails(item);
    }

    protected abstract GameCatalogList parseGameCatalog(int console, int page, int releaseStatus, int sortOrder)
            throws AuthenticationException, IOException, ParserException;

    protected abstract GameCatalogItemDetails parseGameCatalogItemDetails(GameCatalogItem item)
            throws AuthenticationException, IOException, ParserException;

    protected ArrayList<HashMap<String, String>> parsePairsInSimpleXml(String document, String nodeName) {
        Pattern outerNode = Pattern.compile("<" + nodeName + "(?:\\s+[^>]*)?>(.*?)</" + nodeName + ">",
                Pattern.DOTALL);
        Pattern innerNode = Pattern.compile("<(\\w+)[^>]*>([^<]*)</\\1>");

        ArrayList<HashMap<String, String>> kvpList = new ArrayList<HashMap<String, String>>();

        Matcher nodeMatcher = outerNode.matcher(document);
        while (nodeMatcher.find()) {
            String content = nodeMatcher.group(1);

            Matcher m = innerNode.matcher(content);
            HashMap<String, String> items = new HashMap<String, String>();

            while (m.find()) {
                String innerNodeName = m.group(1);
                if (items.containsKey(innerNodeName))
                    continue;

                items.put(innerNodeName, m.group(2));
            }

            if (items.size() > 0)
                kvpList.add(items);
        }

        return kvpList;
    }

    public GamerProfileInfo fetchGamerProfile(PsnAccount account, String psnId)
            throws AuthenticationException, IOException, ParserException {
        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                GamerProfileInfo gpi = parseGamerProfile(account, psnId);
                saveSession(account);

                return gpi;
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
        }
    }

    public ComparedGameInfo compareGames(PsnAccount account, String friendId)
            throws AuthenticationException, IOException, ParserException {
        ComparedGameInfo cgi;

        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                cgi = parseCompareGames(account, friendId);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }

        return cgi;
    }

    public ComparedTrophyInfo compareTrophies(PsnAccount account, String friendId, String gameId)
            throws AuthenticationException, IOException, ParserException {
        ComparedTrophyInfo cti;

        for (int i = 0;; i++) {
            if (!authenticate(account, true))
                throw new AuthenticationException(
                        mContext.getString(R.string.error_invalid_credentials_f, account.getEmailAddress()));

            try {
                cti = parseCompareTrophies(account, friendId, gameId);
            } catch (ClientProtocolException e) {
                // We'll allow one ClientProtocolException, in case the 
                // session data is invalid and redirection has failed. 
                // If that happens, we re-authenticate
                if (i < 1) {
                    if (App.getConfig().logToConsole())
                        App.logv("Re-authenticating");

                    deleteSession(account);
                    continue;
                }

                throw e;
            }
            break;
        }

        return cti;
    }

    @Override
    public ContentValues validateAccount(BasicAccount account)
            throws AuthenticationException, IOException, ParserException {
        if (!authenticate(account, false))
            throw new AuthenticationException(
                    mContext.getString(R.string.error_invalid_credentials_f, account.getLogonId()));

        ContentValues cv = parseSummaryData((PsnAccount) account);
        cv.put(Profiles.ACCOUNT_ID, account.getId());
        cv.put(Profiles.UUID, account.getUuid());

        return cv;
    }

    @Override
    public void deleteAccount(BasicAccount account) {
        ContentResolver cr = mContext.getContentResolver();
        long accountId = account.getId();

        // Clear games & achievements
        StringBuffer buffer = new StringBuffer();
        Cursor c = cr.query(Games.CONTENT_URI, new String[] { Games._ID }, Games.ACCOUNT_ID + "=" + accountId, null,
                null);

        try {
            if (c != null) {
                while (c.moveToNext()) {
                    if (buffer.length() > 0)
                        buffer.append(",");
                    buffer.append(c.getLong(0));
                }
            }
        } finally {
            if (c != null)
                c.close();
        }

        // Clear trophies
        cr.delete(Trophies.CONTENT_URI, Trophies.GAME_ID + " IN (" + buffer.toString() + ")", null);

        // Clear rest of data
        cr.delete(Games.CONTENT_URI, Games.ACCOUNT_ID + "=" + accountId, null);
        cr.delete(Profiles.CONTENT_URI, Profiles.ACCOUNT_ID + "=" + accountId, null);
        cr.delete(Friends.CONTENT_URI, Friends.ACCOUNT_ID + "=" + accountId, null);

        // Send notifications
        cr.notifyChange(Profiles.CONTENT_URI, null);
        cr.notifyChange(Trophies.CONTENT_URI, null);
        cr.notifyChange(Games.CONTENT_URI, null);
        cr.notifyChange(Friends.CONTENT_URI, null);

        // Delete authenticated session
        deleteSession(account);
    }

    public void createAccount(BasicAccount account, ContentValues cv) {
        // Add profile to database
        ContentResolver cr = mContext.getContentResolver();
        cr.insert(Profiles.CONTENT_URI, cv);
        cr.notifyChange(Profiles.CONTENT_URI, null);

        PsnAccount psnAccount = (PsnAccount) account;

        // Save changes to preferences
        psnAccount.setOnlineId(cv.getAsString(Profiles.ONLINE_ID));
        psnAccount.setIconUrl(cv.getAsString(Profiles.ICON_URL));
        psnAccount.setLastSummaryUpdate(System.currentTimeMillis());

        account.save(Preferences.get(mContext));
    }
}