11dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua/*
21dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * Copyright (C) 2012 The Android Open Source Project
31dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua *
41dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * Licensed under the Apache License, Version 2.0 (the "License");
51dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * you may not use this file except in compliance with the License.
61dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * You may obtain a copy of the License at
71dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua *
81dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua *      http://www.apache.org/licenses/LICENSE-2.0
91dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua *
101dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * Unless required by applicable law or agreed to in writing, software
111dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * distributed under the License is distributed on an "AS IS" BASIS,
121dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
131dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * See the License for the specific language governing permissions and
141dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua * limitations under the License.
151dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua */
161dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
171dd8ef56681617db46caec7776c9bf416f01d8ddWei Huapackage android.bordeaux.services;
181dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
191dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.content.ContentValues;
201dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.content.Context;
211dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.database.Cursor;
221dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.database.SQLException;
231dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.database.sqlite.SQLiteDatabase;
241dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.database.sqlite.SQLiteOpenHelper;
251dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport android.util.Log;
261dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
271dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.lang.System;
281dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaimport java.util.concurrent.ConcurrentHashMap;
291dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
301dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua// This class manages the database for storing the session data.
311dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua//
321dd8ef56681617db46caec7776c9bf416f01d8ddWei Huaclass BordeauxSessionStorage {
331dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
341dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private static final String TAG = "BordeauxSessionStorage";
351dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    // unique key for the session
361dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    public static final String COLUMN_KEY = "key";
371dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    // name of the learning class
381dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    public static final String COLUMN_CLASS = "class";
391dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    // data of the learning model
401dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    public static final String COLUMN_MODEL = "model";
411dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    // last update time
421dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    public static final String COLUMN_TIME = "time";
431dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
441dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private static final String DATABASE_NAME = "bordeaux";
451dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private static final String SESSION_TABLE = "sessions";
461dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private static final int DATABASE_VERSION = 1;
471dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private static final String DATABASE_CREATE =
481dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        "create table " + SESSION_TABLE + "( " + COLUMN_KEY +
491dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        " TEXT primary key, " + COLUMN_CLASS + " TEXT, " +
501dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        COLUMN_MODEL + " BLOB, " + COLUMN_TIME + " INTEGER);";
511dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
521dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private SessionDBHelper mDbHelper;
531dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private SQLiteDatabase mDbSessions;
541dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
551dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    BordeauxSessionStorage(final Context context) {
561dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        try {
571dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            mDbHelper = new SessionDBHelper(context);
581dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            mDbSessions = mDbHelper.getWritableDatabase();
591dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        } catch (SQLException e) {
601dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            throw new RuntimeException("Can't open session database");
611dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        }
621dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
631dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
641dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private class SessionDBHelper extends SQLiteOpenHelper {
651dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        SessionDBHelper(Context context) {
661dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            super(context, DATABASE_NAME, null, DATABASE_VERSION);
671dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        }
681dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
691dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        @Override
701dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        public void onCreate(SQLiteDatabase db) {
711dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            db.execSQL(DATABASE_CREATE);
721dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        }
731dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
741dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        @Override
751dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
761dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
771dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua                  + newVersion + ", which will destroy all old data");
781dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
791dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            db.execSQL("DROP TABLE IF EXISTS " + SESSION_TABLE);
801dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            onCreate(db);
811dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        }
821dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
831dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
841dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private ContentValues createSessionEntry(String key, Class learner, byte[] model) {
851dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        ContentValues entry = new ContentValues();
861dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        entry.put(COLUMN_KEY, key);
871dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        entry.put(COLUMN_TIME, System.currentTimeMillis());
881dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        entry.put(COLUMN_MODEL, model);
891dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        entry.put(COLUMN_CLASS, learner.getName());
901dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        return entry;
911dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
921dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
931dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    boolean saveSession(String key, Class learner, byte[] model) {
941dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        ContentValues content = createSessionEntry(key, learner, model);
951dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        long rowID =
961dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua                mDbSessions.insertWithOnConflict(SESSION_TABLE, null, content,
971dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua                                                 SQLiteDatabase.CONFLICT_REPLACE);
981dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        return rowID >= 0;
991dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
1001dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
1011dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    private BordeauxSessionManager.Session getSessionFromCursor(Cursor cursor) {
1021dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        BordeauxSessionManager.Session session = new BordeauxSessionManager.Session();
1031dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        String className = cursor.getString(cursor.getColumnIndex(COLUMN_CLASS));
1041dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        try {
1051dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            session.learnerClass = Class.forName(className);
1061dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            session.learner = (IBordeauxLearner) session.learnerClass.getConstructor().newInstance();
1071dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        } catch (Exception e) {
1081dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            throw new RuntimeException("Can't instantiate class: " + className);
1091dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        }
1101dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        byte[] model = cursor.getBlob(cursor.getColumnIndex(COLUMN_MODEL));
1111dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        session.learner.setModel(model);
1121dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        return session;
1131dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
1141dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
1151dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    BordeauxSessionManager.Session getSession(String key) {
1161dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        Cursor cursor = mDbSessions.query(true, SESSION_TABLE,
1171dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua                new String[]{COLUMN_KEY, COLUMN_CLASS, COLUMN_MODEL, COLUMN_TIME},
1181dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua                COLUMN_KEY + "=\"" + key + "\"", null, null, null, null, null);
119984e52f31d596840cfa51b1238e1c43d2e1918f8saberian        if ((cursor == null) | (cursor.getCount() == 0)) {
120984e52f31d596840cfa51b1238e1c43d2e1918f8saberian            cursor.close();
121984e52f31d596840cfa51b1238e1c43d2e1918f8saberian            return null;
122984e52f31d596840cfa51b1238e1c43d2e1918f8saberian        }
1231dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        if (cursor.getCount() > 1) {
124984e52f31d596840cfa51b1238e1c43d2e1918f8saberian            cursor.close();
1251dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            throw new RuntimeException("Unexpected duplication in session table for key:" + key);
1261dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        }
1271dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        cursor.moveToFirst();
128984e52f31d596840cfa51b1238e1c43d2e1918f8saberian        BordeauxSessionManager.Session s = getSessionFromCursor(cursor);
129984e52f31d596840cfa51b1238e1c43d2e1918f8saberian        cursor.close();
130984e52f31d596840cfa51b1238e1c43d2e1918f8saberian        return s;
1311dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
1321dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
1331dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    void getAllSessions(ConcurrentHashMap<String, BordeauxSessionManager.Session> sessions) {
1341dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        Cursor cursor = mDbSessions.rawQuery("select * from ?;", new String[]{SESSION_TABLE});
1351dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        if (cursor == null) return;
1361dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        do {
1371dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            String key = cursor.getString(cursor.getColumnIndex(COLUMN_KEY));
1381dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            BordeauxSessionManager.Session session = getSessionFromCursor(cursor);
1391dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua            sessions.put(key, session);
1401dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        } while (cursor.moveToNext());
1411dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
1421dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua
1431dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    // remove all sessions that have the key that matches the given sql regular
1441dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    // expression.
1451dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    int removeSessions(String reKey) {
1461dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        int nDeleteRows = mDbSessions.delete(SESSION_TABLE, "? like \"?\"",
1471dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua                                             new String[]{COLUMN_KEY, reKey});
1481dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        Log.i(TAG, "Number of rows in session table deleted: " + nDeleteRows);
1491dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua        return nDeleteRows;
1501dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua    }
1511dd8ef56681617db46caec7776c9bf416f01d8ddWei Hua}
152