1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.bordeaux.services;
18
19import android.content.ContentValues;
20import android.content.Context;
21import android.database.Cursor;
22import android.database.SQLException;
23import android.database.sqlite.SQLiteDatabase;
24import android.database.sqlite.SQLiteOpenHelper;
25import android.util.Log;
26
27import java.lang.System;
28import java.util.concurrent.ConcurrentHashMap;
29
30// This class manages the database for storing the session data.
31//
32class BordeauxSessionStorage {
33
34    private static final String TAG = "BordeauxSessionStorage";
35    // unique key for the session
36    public static final String COLUMN_KEY = "key";
37    // name of the learning class
38    public static final String COLUMN_CLASS = "class";
39    // data of the learning model
40    public static final String COLUMN_MODEL = "model";
41    // last update time
42    public static final String COLUMN_TIME = "time";
43
44    private static final String DATABASE_NAME = "bordeaux";
45    private static final String SESSION_TABLE = "sessions";
46    private static final int DATABASE_VERSION = 1;
47    private static final String DATABASE_CREATE =
48        "create table " + SESSION_TABLE + "( " + COLUMN_KEY +
49        " TEXT primary key, " + COLUMN_CLASS + " TEXT, " +
50        COLUMN_MODEL + " BLOB, " + COLUMN_TIME + " INTEGER);";
51
52    private SessionDBHelper mDbHelper;
53    private SQLiteDatabase mDbSessions;
54
55    BordeauxSessionStorage(final Context context) {
56        try {
57            mDbHelper = new SessionDBHelper(context);
58            mDbSessions = mDbHelper.getWritableDatabase();
59        } catch (SQLException e) {
60            throw new RuntimeException("Can't open session database");
61        }
62    }
63
64    private class SessionDBHelper extends SQLiteOpenHelper {
65        SessionDBHelper(Context context) {
66            super(context, DATABASE_NAME, null, DATABASE_VERSION);
67        }
68
69        @Override
70        public void onCreate(SQLiteDatabase db) {
71            db.execSQL(DATABASE_CREATE);
72        }
73
74        @Override
75        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
76            Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
77                  + newVersion + ", which will destroy all old data");
78
79            db.execSQL("DROP TABLE IF EXISTS " + SESSION_TABLE);
80            onCreate(db);
81        }
82    }
83
84    private ContentValues createSessionEntry(String key, Class learner, byte[] model) {
85        ContentValues entry = new ContentValues();
86        entry.put(COLUMN_KEY, key);
87        entry.put(COLUMN_TIME, System.currentTimeMillis());
88        entry.put(COLUMN_MODEL, model);
89        entry.put(COLUMN_CLASS, learner.getName());
90        return entry;
91    }
92
93    boolean saveSession(String key, Class learner, byte[] model) {
94        ContentValues content = createSessionEntry(key, learner, model);
95        long rowID =
96                mDbSessions.insertWithOnConflict(SESSION_TABLE, null, content,
97                                                 SQLiteDatabase.CONFLICT_REPLACE);
98        return rowID >= 0;
99    }
100
101    private BordeauxSessionManager.Session getSessionFromCursor(Cursor cursor) {
102        BordeauxSessionManager.Session session = new BordeauxSessionManager.Session();
103        String className = cursor.getString(cursor.getColumnIndex(COLUMN_CLASS));
104        try {
105            session.learnerClass = Class.forName(className);
106            session.learner = (IBordeauxLearner) session.learnerClass.getConstructor().newInstance();
107        } catch (Exception e) {
108            throw new RuntimeException("Can't instantiate class: " + className);
109        }
110        byte[] model = cursor.getBlob(cursor.getColumnIndex(COLUMN_MODEL));
111        session.learner.setModel(model);
112        return session;
113    }
114
115    BordeauxSessionManager.Session getSession(String key) {
116        Cursor cursor = mDbSessions.query(true, SESSION_TABLE,
117                new String[]{COLUMN_KEY, COLUMN_CLASS, COLUMN_MODEL, COLUMN_TIME},
118                COLUMN_KEY + "=\"" + key + "\"", null, null, null, null, null);
119        if ((cursor == null) | (cursor.getCount() == 0)) {
120            cursor.close();
121            return null;
122        }
123        if (cursor.getCount() > 1) {
124            cursor.close();
125            throw new RuntimeException("Unexpected duplication in session table for key:" + key);
126        }
127        cursor.moveToFirst();
128        BordeauxSessionManager.Session s = getSessionFromCursor(cursor);
129        cursor.close();
130        return s;
131    }
132
133    void getAllSessions(ConcurrentHashMap<String, BordeauxSessionManager.Session> sessions) {
134        Cursor cursor = mDbSessions.rawQuery("select * from ?;", new String[]{SESSION_TABLE});
135        if (cursor == null) return;
136        cursor.moveToFirst();
137        do {
138            String key = cursor.getString(cursor.getColumnIndex(COLUMN_KEY));
139            BordeauxSessionManager.Session session = getSessionFromCursor(cursor);
140            sessions.put(key, session);
141        } while (cursor.moveToNext());
142    }
143
144    // remove all sessions that have the key that matches the given sql regular
145    // expression.
146    int removeSessions(String reKey) {
147        int nDeleteRows = mDbSessions.delete(SESSION_TABLE, "? like \"?\"",
148                                             new String[]{COLUMN_KEY, reKey});
149        Log.i(TAG, "Number of rows in session table deleted: " + nDeleteRows);
150        return nDeleteRows;
151    }
152}
153