com.nttec.everychan.ui.presentation.Subscriptions.java Source code

Java tutorial

Introduction

Here is the source code for com.nttec.everychan.ui.presentation.Subscriptions.java

Source

/*
 * Everychan Android (Meta Imageboard Client)
 * Copyright (C) 2014-2016  miku-nyan <https://github.com/miku-nyan>
 *     
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 */

package com.nttec.everychan.ui.presentation;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.regex.Matcher;

import org.apache.commons.lang3.StringEscapeUtils;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.provider.BaseColumns;
import com.nttec.everychan.api.models.UrlPageModel;
import com.nttec.everychan.api.util.RegexUtils;
import com.nttec.everychan.cache.SerializablePage;
import com.nttec.everychan.common.Logger;
import com.nttec.everychan.common.MainApplication;

public class Subscriptions {
    private static final String TAG = "Subscriptions";
    private SubscriptionsDB database;
    private Object[] cached;
    private Object[] waitingOwnPost;

    public Subscriptions(Context context) {
        database = new SubscriptionsDB(context);
    }

    /**
     *   ? ? (? ?)
     */
    public long getCurrentCount() {
        return database.getNumEntries();
    }

    /**
     * , ?    ?   ? ?
     * @param page ?
     * @param startPostIndex  ? ( ?)  ?, ? ?  ?? ?
     * @return ? (  ?)    ? ?, ?  ?,   ? -1 
     */
    public int checkSubscriptions(SerializablePage page, int startPostIndex) {
        if (!MainApplication.getInstance().settings.isSubscriptionsEnabled())
            return -1;
        if (page.pageModel == null || page.pageModel.type != UrlPageModel.TYPE_THREADPAGE || page.posts == null)
            return -1;
        String[] subscriptions = getSubscriptions(page.pageModel.chanName, page.pageModel.boardName,
                page.pageModel.threadNumber);
        if (subscriptions == null)
            return -1;
        if (startPostIndex < page.posts.length && MainApplication.getInstance().settings.subscribeThreads()
                && Arrays.binarySearch(subscriptions, page.pageModel.threadNumber) >= 0)
            return startPostIndex;
        for (int i = startPostIndex; i < page.posts.length; ++i) {
            String comment = page.posts[i].comment;
            if (comment == null)
                continue;
            Matcher m = PresentationItemModel.REPLY_LINK_FULL_PATTERN.matcher(comment);
            while (m.find())
                if (Arrays.binarySearch(subscriptions, m.group(1)) >= 0)
                    return i;
        }
        return -1;
    }

    /**
     *  ? ?
     */
    public void addSubscription(String chan, String board, String thread, String post) {
        database.put(chan, board, thread, post);
        Object[] tuple = cached;
        if (tuple != null && tuple[0].equals(chan) && tuple[1].equals(board) && tuple[2].equals(thread))
            cached = null;
    }

    /**
     * , ????  ? ?
     */
    public boolean hasSubscription(String chan, String board, String thread, String post) {
        return database.hasSubscription(chan, board, thread, post);
    }

    /**
     *  ? ?
     */
    public void removeSubscription(String chan, String board, String thread, String post) {
        database.remove(chan, board, thread, post);
        Object[] tuple = cached;
        if (tuple != null && tuple[0].equals(chan) && tuple[1].equals(board) && tuple[2].equals(thread))
            cached = null;
    }

    /**
     *  ? ?? ? ?   
     * @return ??, ?  ?? java.lang.String
     */
    public String[] getSubscriptions(String chan, String board, String thread) {
        Object[] tuple = cached;
        if (tuple != null && tuple[0].equals(chan) && tuple[1].equals(board) && tuple[2].equals(thread))
            return (String[]) tuple[3];
        String[] result = database.getSubscriptions(chan, board, thread);
        if (result == null || result.length == 0)
            result = null;
        else
            Arrays.sort(result);
        cached = new Object[] { chan, board, thread, result };
        return result;
    }

    /**
     * ? ? ? (? ?)
     */
    public void reset() {
        database.resetDB();
        cached = null;
    }

    /**
     * ?  ? ? (? ,     ? ? ? )
     */
    public void detectOwnPost(String chan, String board, String thread, String comment) {
        if (chan == null || board == null || thread == null || comment == null)
            return;
        List<String> words = commentToWordsList(comment);
        //Logger.d(TAG, "set detector; words: " + words);
        waitingOwnPost = new Object[] { chan, board, thread, words };
    }

    /**
     * ,    ? ? ?, ?  {@link #detectOwnPost(String, String, String, String)},
     * ? ?,  ?  ? (?)
     * @param page ?
     * @param startPostIndex  ? ( ?)  ?, ? ?  ?? ?
     */
    @SuppressWarnings("unchecked")
    public void checkOwnPost(SerializablePage page, int startPostIndex) {
        if (page.pageModel == null || page.pageModel.type != UrlPageModel.TYPE_THREADPAGE || page.posts == null)
            return;
        String chan = page.pageModel.chanName;
        String board = page.pageModel.boardName;
        String thread = page.pageModel.threadNumber;
        Object[] tuple = waitingOwnPost;
        if (tuple != null && tuple[0].equals(chan) && tuple[1].equals(board) && tuple[2].equals(thread)) {
            waitingOwnPost = null;
            int postCount = page.posts.length - startPostIndex;
            if (postCount <= 1) {
                if (postCount == 1 && page.posts[startPostIndex] != null)
                    addSubscription(chan, board, thread, page.posts[startPostIndex].number);
                return;
            }
            List<int[]> result = new ArrayList<>(postCount);
            List<String> waitingWords = (List<String>) tuple[3];
            for (int i = startPostIndex; i < page.posts.length; ++i) {
                if (page.posts[i] == null || page.posts[i].comment == null)
                    continue;
                HashSet<String> postWords = new HashSet<>(commentToWordsList(htmlToComment(page.posts[i].comment)));
                //Logger.d(TAG, "checking post i=" + i + "\ncomment: " + page.posts[i].comment+"\nwords:" + postWords);
                int wordsCount = 0;
                for (String waitingWord : waitingWords)
                    if (postWords.remove(waitingWord))
                        ++wordsCount;
                result.add(new int[] { i, wordsCount, postWords.size() });
                //Logger.d(TAG, "result: overlap=" + wordsCount + "; remained=" + postWords.size());
            }
            if (result.size() == 0)
                return;
            Collections.sort(result, new Comparator<int[]>() {
                @Override
                public int compare(int[] lhs, int[] rhs) {
                    int result = compareInt(rhs[1], lhs[1]);
                    if (result == 0)
                        result = compareInt(lhs[2], rhs[2]);
                    if (result == 0)
                        result = compareInt(lhs[0], rhs[0]);
                    return result;
                }

                private int compareInt(int lhs, int rhs) {
                    return lhs < rhs ? -1 : (lhs == rhs ? 0 : 1);
                }
            });
            //for (int[] entry : result) Logger.d(TAG, "[" + entry[0] + ";" + entry[1] + ";" + entry[2] + "]");
            addSubscription(chan, board, thread, page.posts[result.get(0)[0]].number);
        }
    }

    private static String htmlToComment(String html) {
        return StringEscapeUtils.unescapeHtml4(RegexUtils.removeHtmlTags(html.replaceAll("<(br|p)/?>", " ")));
    }

    private static List<String> commentToWordsList(String comment) {
        return Arrays.asList(comment.replaceAll("[\\*%_]", "").replaceAll("\\[[^\\]]*\\]", "")
                .replaceAll("[^\\w\\d\\s]", " ").trim().split("\\s+"));
    }

    private static class SubscriptionsDB {
        private static final int DB_VERSION = 1000;
        private static final String DB_NAME = "subscriptions.db";

        private static final String TABLE_NAME = "subscriptions";
        private static final String COL_CHAN = "chan";
        private static final String COL_BOARD = "board";
        private static final String COL_THREAD = "thread";
        private static final String COL_POST = "post";

        private final DBHelper dbHelper;

        public SubscriptionsDB(Context context) {
            dbHelper = new DBHelper(context);
        }

        public boolean hasSubscription(String chan, String board, String thread, String post) {
            Cursor c = dbHelper.getReadableDatabase().query(TABLE_NAME, null,
                    COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_THREAD + " = ? AND " + COL_POST + " = ?",
                    new String[] { chan, board, thread, post }, null, null, null);
            boolean result = false;
            if (c != null && c.moveToFirst())
                result = true;
            if (c != null)
                c.close();
            return result;
        }

        public void put(String chan, String board, String thread, String post) {
            if (hasSubscription(chan, board, thread, post)) {
                Logger.d(TAG, "entry is already exists");
                return;
            }
            ContentValues value = new ContentValues(4);
            value.put(COL_CHAN, chan);
            value.put(COL_BOARD, board);
            value.put(COL_THREAD, thread);
            value.put(COL_POST, post);
            dbHelper.getWritableDatabase().insert(TABLE_NAME, null, value);
        }

        public void remove(String chan, String board, String thread, String post) {
            dbHelper.getWritableDatabase().delete(TABLE_NAME,
                    COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_THREAD + " = ? AND " + COL_POST + " = ?",
                    new String[] { chan, board, thread, post });
        }

        public String[] getSubscriptions(String chan, String board, String thread) {
            Cursor c = dbHelper.getReadableDatabase().query(TABLE_NAME, null,
                    COL_CHAN + " = ? AND " + COL_BOARD + " = ? AND " + COL_THREAD + " = ?",
                    new String[] { chan, board, thread }, null, null, null);
            String[] result = null;
            if (c != null && c.moveToFirst()) {
                int postIndex = c.getColumnIndex(COL_POST);
                int count = c.getCount();
                result = new String[count];
                int i = 0;
                do
                    result[i++] = c.getString(postIndex);
                while (i < count && c.moveToNext());
                if (i < count) {
                    Logger.e(TAG, "result size < cursor getCount()");
                    String[] tmp = new String[i];
                    System.arraycopy(result, 0, tmp, 0, i);
                    result = tmp;
                }
            }
            if (c != null)
                c.close();
            return result;
        }

        public void resetDB() {
            dbHelper.resetDB();
        }

        public long getNumEntries() {
            return DatabaseUtils.queryNumEntries(dbHelper.getReadableDatabase(), TABLE_NAME);
        }

        private static class DBHelper extends SQLiteOpenHelper implements BaseColumns {
            public DBHelper(Context context) {
                super(context, DB_NAME, null, DB_VERSION);
            }

            @Override
            public void onCreate(SQLiteDatabase db) {
                db.execSQL(
                        createTable(TABLE_NAME, new String[] { COL_CHAN, COL_BOARD, COL_THREAD, COL_POST }, null));
            }

            @Override
            public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                if (oldVersion < newVersion) {
                    db.execSQL(dropTable(TABLE_NAME));
                    onCreate(db);
                }
            }

            @Override
            public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
                onUpgrade(db, oldVersion, newVersion);
            }

            private static String createTable(String tableName, String[] columns, String[] types) {
                StringBuilder sql = new StringBuilder(110).append("create table ").append(tableName).append(" (")
                        .append(_ID).append(" integer primary key autoincrement,");
                for (int i = 0; i < columns.length; ++i) {
                    sql.append(columns[i]).append(' ').append(types == null ? "text" : types[i]).append(',');
                }
                sql.setCharAt(sql.length() - 1, ')');
                return sql.append(';').toString();
            }

            private static String dropTable(String tableName) {
                return "DROP TABLE IF EXISTS " + tableName;
            }

            private void resetDB() {
                SQLiteDatabase db = getWritableDatabase();
                db.execSQL(dropTable(TABLE_NAME));
                onCreate(db);
            }
        }
    }

}