/WebVox/src/com/marvin/webvox/ScriptDatabase.java
Java | 259 lines | 155 code | 55 blank | 49 comment | 19 complexity | 50298f2d38d9b823c97f39baa5669a2e MD5 | raw file
1/* 2 * Copyright (C) 2008 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * 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, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17package com.marvin.webvox; 18 19import java.io.InputStream; 20import java.util.HashMap; 21import java.util.LinkedList; 22import java.util.List; 23import java.util.Map; 24import java.util.regex.Matcher; 25import java.util.regex.Pattern; 26 27import org.json.JSONArray; 28import org.json.JSONObject; 29 30import android.content.ContentValues; 31import android.content.Context; 32import android.content.res.Resources; 33import android.database.Cursor; 34import android.database.sqlite.SQLiteDatabase; 35import android.database.sqlite.SQLiteOpenHelper; 36import android.util.Log; 37 38public class ScriptDatabase extends SQLiteOpenHelper { 39 40 public final static String TAG = ScriptDatabase.class.toString(); 41 42 public final static String DB_NAME = "scripts"; 43 public final static int DB_VERSION = 3; 44 45 public final static String TABLE_SCRIPTS = "scripts"; 46 public final static String FIELD_SCRIPT_NAME = "title"; 47 public final static String FIELD_SCRIPT_AUTHOR = "author"; 48 public final static String FIELD_SCRIPT_DESCRIP = "descrip"; 49 public final static String FIELD_SCRIPT_DOMAINREGEX = "domain"; 50 public final static String FIELD_SCRIPT_CONTENT = "content"; 51 public final static String FIELD_SCRIPT_ENABLED = "enabled"; 52 53 private final Resources res; 54 55 public ScriptDatabase(Context context) { 56 super(context, DB_NAME, null, DB_VERSION); 57 this.res = context.getResources(); 58 } 59 60 public void onCreate(SQLiteDatabase db) { 61 db.execSQL("CREATE TABLE " + TABLE_SCRIPTS 62 + " (_id INTEGER PRIMARY KEY, " 63 + FIELD_SCRIPT_NAME + " TEXT, " 64 + FIELD_SCRIPT_AUTHOR + " TEXT, " 65 + FIELD_SCRIPT_DESCRIP + " TEXT, " 66 + FIELD_SCRIPT_DOMAINREGEX + " TEXT, " 67 + FIELD_SCRIPT_CONTENT + " TEXT, " 68 + FIELD_SCRIPT_ENABLED + " INTEGER)"); 69 70 // populate database with a few example scripts 71 try { 72 this.insertScript(db, Util.getRawString(res, R.raw.mgws)); 73 this.insertScript(db, Util.getRawString(res, R.raw.mgmail)); 74 this.insertScript(db, Util.getRawString(res, R.raw.basic)); 75 //this.insertScript(db, Util.getRawString(res, R.raw.email)); 76 //this.insertScript(db, Util.getRawString(res, R.raw.halfscan)); 77 //this.insertScript(db, Util.getRawString(res, R.raw.sharedigg)); 78 //this.insertScript(db, Util.getRawString(res, R.raw.wikipedia)); 79 //this.insertScript(db, Util.getRawString(res, R.raw.slashdot)); 80 //this.insertScript(db, Util.getRawString(res, R.raw.userscripts)); 81 } catch(Exception e) { 82 Log.e(TAG, "Problem while inserting default scripts", e); 83 } 84 85 86 } 87 88 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 89 int currentVersion = oldVersion; 90 91 if(currentVersion != newVersion) { 92 db.execSQL("DROP TABLE IF EXISTS " + TABLE_SCRIPTS); 93 onCreate(db); 94 } 95 } 96 97 public static Pattern headerRegex = Pattern.compile("^// @([^\\s]+)\\s+(.+?)$", Pattern.MULTILINE); 98 public final static String UNKNOWN = "Unknown"; 99 100 /** 101 * Format a domain "include" statement so it behaves nicely as a Java regex. 102 */ 103 public static String formatDomain(String domain) { 104 105 // escape any "." and replace "*" with generic matcher 106 domain = domain.replace(".", "\\."); 107 domain = domain.replace("*", ".*"); 108 return domain; 109 110 } 111 112 /** 113 * Import the given script into our database. 114 * @throws Exception if problem parsing script 115 */ 116 public long insertScript(SQLiteDatabase db, String raw) throws Exception { 117 if(db == null) db = this.getWritableDatabase(); 118 119 // extract metadata from script comments 120 String name = UNKNOWN, author = UNKNOWN, descrip = UNKNOWN; 121 StringBuilder include = new StringBuilder(); 122 include.append('('); 123 124 try { 125 Matcher matcher = headerRegex.matcher(raw); 126 while(matcher.find()) { 127 String key = matcher.group(1), 128 value = matcher.group(2); 129 130 Log.d(TAG, String.format("found header %s=%s", key, value)); 131 132 if("name".equals(key)) { 133 name = value; 134 } else if("author".equals(key)) { 135 author = value; 136 } else if("description".equals(key)) { 137 descrip = value; 138 } else if("include".equals(key)) { 139 include.append(formatDomain(value)); 140 include.append('|'); 141 } 142 } 143 } catch(Exception e) { 144 Log.e(TAG, "Problem while parsing script header", e); 145 } 146 147 String domainregex = ""; 148 if(include.length() > 0) { 149 // replace last '|' with a closing bracket 150 include.setCharAt(include.length() - 1, ')'); 151 domainregex = include.toString(); 152 } 153 154 // script is valid if has name and parsable domain regex 155 if(UNKNOWN.equals(name)) 156 throw new Exception("No name found in script"); 157 158 try { 159 Pattern.compile(domainregex); 160 } catch(Exception e) { 161 throw new Exception("Problem parsing domain regex", e); 162 } 163 164 //Log.d(TAG, String.format("domainregex=%s", domainregex)); 165 166 ContentValues values = new ContentValues(); 167 values.put(FIELD_SCRIPT_NAME, name); 168 values.put(FIELD_SCRIPT_AUTHOR, author); 169 values.put(FIELD_SCRIPT_DESCRIP, descrip); 170 values.put(FIELD_SCRIPT_DOMAINREGEX, domainregex); 171 values.put(FIELD_SCRIPT_CONTENT, raw); 172 values.put(FIELD_SCRIPT_ENABLED, 1); 173 174 return db.insert(TABLE_SCRIPTS, null, values); 175 176 } 177 178 /** 179 * Return cursor across all scripts. 180 */ 181 public Cursor getScripts() { 182 SQLiteDatabase db = this.getReadableDatabase(); 183 return db.query(TABLE_SCRIPTS, new String[] { "_id", FIELD_SCRIPT_NAME, 184 FIELD_SCRIPT_AUTHOR, FIELD_SCRIPT_DESCRIP, FIELD_SCRIPT_DOMAINREGEX, 185 FIELD_SCRIPT_ENABLED }, null, null, null, null, null); 186 187 } 188 189 /** 190 * Delete a specific script. 191 */ 192 public void deleteScript(long id) { 193 SQLiteDatabase db = this.getWritableDatabase(); 194 db.delete(TABLE_SCRIPTS, "_id = ?", new String[] { Long.toString(id) }); 195 196 } 197 198 /** 199 * Toggle the enabled state of the given script. 200 */ 201 public void toggleEnabled(long id) { 202 SQLiteDatabase db = this.getWritableDatabase(); 203 204 // first read the existing status 205 Cursor cur = db.query(TABLE_SCRIPTS, new String[] { FIELD_SCRIPT_ENABLED }, "_id = ?", new String[] { Long.toString(id) }, null, null, null); 206 if(cur == null || !cur.moveToFirst()) return; 207 int value = cur.getInt(cur.getColumnIndex(FIELD_SCRIPT_ENABLED)); 208 cur.close(); 209 210 // update to have the opposite value 211 ContentValues values = new ContentValues(); 212 values.put(FIELD_SCRIPT_ENABLED, (value == 0) ? 1 : 0); 213 db.update(TABLE_SCRIPTS, values, "_id = ?", new String[] { Long.toString(id) }); 214 215 } 216 217 private Map<Pattern,String> cache = new HashMap<Pattern,String>(); 218 219 private void validateCache() { 220 221 SQLiteDatabase db = this.getReadableDatabase(); 222 Cursor cur = db.query(TABLE_SCRIPTS, new String[] { FIELD_SCRIPT_DOMAINREGEX, 223 FIELD_SCRIPT_CONTENT }, FIELD_SCRIPT_ENABLED + " = 1", null, null, null, null); 224 225 int COL_DOMAINREGEX = cur.getColumnIndex(FIELD_SCRIPT_DOMAINREGEX), 226 COL_CONTENT = cur.getColumnIndex(FIELD_SCRIPT_CONTENT); 227 228 cache.clear(); 229 230 while(cur.moveToNext()) { 231 String domainregex = cur.getString(COL_DOMAINREGEX), 232 content = cur.getString(COL_CONTENT); 233 cache.put(Pattern.compile(domainregex), content); 234 235 } 236 237 cur.close(); 238 239 } 240 241 /** 242 * Return Javascript (CONTENT) for all scripts that should be active given 243 * this specific url. 244 */ 245 public List<String> getActive(String url) { 246 List<String> active = new LinkedList<String>(); 247 248 this.validateCache(); 249 for(Pattern trial : cache.keySet()) { 250 if(trial.matcher(url).matches()) 251 active.add(cache.get(trial)); 252 } 253 254 return active; 255 } 256 257 258 259}