/WebVox/src/com/marvin/webvox/ScriptDatabase.java

http://eyes-free.googlecode.com/ · 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. package com.marvin.webvox;
  17. import java.io.InputStream;
  18. import java.util.HashMap;
  19. import java.util.LinkedList;
  20. import java.util.List;
  21. import java.util.Map;
  22. import java.util.regex.Matcher;
  23. import java.util.regex.Pattern;
  24. import org.json.JSONArray;
  25. import org.json.JSONObject;
  26. import android.content.ContentValues;
  27. import android.content.Context;
  28. import android.content.res.Resources;
  29. import android.database.Cursor;
  30. import android.database.sqlite.SQLiteDatabase;
  31. import android.database.sqlite.SQLiteOpenHelper;
  32. import android.util.Log;
  33. public class ScriptDatabase extends SQLiteOpenHelper {
  34. public final static String TAG = ScriptDatabase.class.toString();
  35. public final static String DB_NAME = "scripts";
  36. public final static int DB_VERSION = 3;
  37. public final static String TABLE_SCRIPTS = "scripts";
  38. public final static String FIELD_SCRIPT_NAME = "title";
  39. public final static String FIELD_SCRIPT_AUTHOR = "author";
  40. public final static String FIELD_SCRIPT_DESCRIP = "descrip";
  41. public final static String FIELD_SCRIPT_DOMAINREGEX = "domain";
  42. public final static String FIELD_SCRIPT_CONTENT = "content";
  43. public final static String FIELD_SCRIPT_ENABLED = "enabled";
  44. private final Resources res;
  45. public ScriptDatabase(Context context) {
  46. super(context, DB_NAME, null, DB_VERSION);
  47. this.res = context.getResources();
  48. }
  49. public void onCreate(SQLiteDatabase db) {
  50. db.execSQL("CREATE TABLE " + TABLE_SCRIPTS
  51. + " (_id INTEGER PRIMARY KEY, "
  52. + FIELD_SCRIPT_NAME + " TEXT, "
  53. + FIELD_SCRIPT_AUTHOR + " TEXT, "
  54. + FIELD_SCRIPT_DESCRIP + " TEXT, "
  55. + FIELD_SCRIPT_DOMAINREGEX + " TEXT, "
  56. + FIELD_SCRIPT_CONTENT + " TEXT, "
  57. + FIELD_SCRIPT_ENABLED + " INTEGER)");
  58. // populate database with a few example scripts
  59. try {
  60. this.insertScript(db, Util.getRawString(res, R.raw.mgws));
  61. this.insertScript(db, Util.getRawString(res, R.raw.mgmail));
  62. this.insertScript(db, Util.getRawString(res, R.raw.basic));
  63. //this.insertScript(db, Util.getRawString(res, R.raw.email));
  64. //this.insertScript(db, Util.getRawString(res, R.raw.halfscan));
  65. //this.insertScript(db, Util.getRawString(res, R.raw.sharedigg));
  66. //this.insertScript(db, Util.getRawString(res, R.raw.wikipedia));
  67. //this.insertScript(db, Util.getRawString(res, R.raw.slashdot));
  68. //this.insertScript(db, Util.getRawString(res, R.raw.userscripts));
  69. } catch(Exception e) {
  70. Log.e(TAG, "Problem while inserting default scripts", e);
  71. }
  72. }
  73. public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  74. int currentVersion = oldVersion;
  75. if(currentVersion != newVersion) {
  76. db.execSQL("DROP TABLE IF EXISTS " + TABLE_SCRIPTS);
  77. onCreate(db);
  78. }
  79. }
  80. public static Pattern headerRegex = Pattern.compile("^// @([^\\s]+)\\s+(.+?)$", Pattern.MULTILINE);
  81. public final static String UNKNOWN = "Unknown";
  82. /**
  83. * Format a domain "include" statement so it behaves nicely as a Java regex.
  84. */
  85. public static String formatDomain(String domain) {
  86. // escape any "." and replace "*" with generic matcher
  87. domain = domain.replace(".", "\\.");
  88. domain = domain.replace("*", ".*");
  89. return domain;
  90. }
  91. /**
  92. * Import the given script into our database.
  93. * @throws Exception if problem parsing script
  94. */
  95. public long insertScript(SQLiteDatabase db, String raw) throws Exception {
  96. if(db == null) db = this.getWritableDatabase();
  97. // extract metadata from script comments
  98. String name = UNKNOWN, author = UNKNOWN, descrip = UNKNOWN;
  99. StringBuilder include = new StringBuilder();
  100. include.append('(');
  101. try {
  102. Matcher matcher = headerRegex.matcher(raw);
  103. while(matcher.find()) {
  104. String key = matcher.group(1),
  105. value = matcher.group(2);
  106. Log.d(TAG, String.format("found header %s=%s", key, value));
  107. if("name".equals(key)) {
  108. name = value;
  109. } else if("author".equals(key)) {
  110. author = value;
  111. } else if("description".equals(key)) {
  112. descrip = value;
  113. } else if("include".equals(key)) {
  114. include.append(formatDomain(value));
  115. include.append('|');
  116. }
  117. }
  118. } catch(Exception e) {
  119. Log.e(TAG, "Problem while parsing script header", e);
  120. }
  121. String domainregex = "";
  122. if(include.length() > 0) {
  123. // replace last '|' with a closing bracket
  124. include.setCharAt(include.length() - 1, ')');
  125. domainregex = include.toString();
  126. }
  127. // script is valid if has name and parsable domain regex
  128. if(UNKNOWN.equals(name))
  129. throw new Exception("No name found in script");
  130. try {
  131. Pattern.compile(domainregex);
  132. } catch(Exception e) {
  133. throw new Exception("Problem parsing domain regex", e);
  134. }
  135. //Log.d(TAG, String.format("domainregex=%s", domainregex));
  136. ContentValues values = new ContentValues();
  137. values.put(FIELD_SCRIPT_NAME, name);
  138. values.put(FIELD_SCRIPT_AUTHOR, author);
  139. values.put(FIELD_SCRIPT_DESCRIP, descrip);
  140. values.put(FIELD_SCRIPT_DOMAINREGEX, domainregex);
  141. values.put(FIELD_SCRIPT_CONTENT, raw);
  142. values.put(FIELD_SCRIPT_ENABLED, 1);
  143. return db.insert(TABLE_SCRIPTS, null, values);
  144. }
  145. /**
  146. * Return cursor across all scripts.
  147. */
  148. public Cursor getScripts() {
  149. SQLiteDatabase db = this.getReadableDatabase();
  150. return db.query(TABLE_SCRIPTS, new String[] { "_id", FIELD_SCRIPT_NAME,
  151. FIELD_SCRIPT_AUTHOR, FIELD_SCRIPT_DESCRIP, FIELD_SCRIPT_DOMAINREGEX,
  152. FIELD_SCRIPT_ENABLED }, null, null, null, null, null);
  153. }
  154. /**
  155. * Delete a specific script.
  156. */
  157. public void deleteScript(long id) {
  158. SQLiteDatabase db = this.getWritableDatabase();
  159. db.delete(TABLE_SCRIPTS, "_id = ?", new String[] { Long.toString(id) });
  160. }
  161. /**
  162. * Toggle the enabled state of the given script.
  163. */
  164. public void toggleEnabled(long id) {
  165. SQLiteDatabase db = this.getWritableDatabase();
  166. // first read the existing status
  167. Cursor cur = db.query(TABLE_SCRIPTS, new String[] { FIELD_SCRIPT_ENABLED }, "_id = ?", new String[] { Long.toString(id) }, null, null, null);
  168. if(cur == null || !cur.moveToFirst()) return;
  169. int value = cur.getInt(cur.getColumnIndex(FIELD_SCRIPT_ENABLED));
  170. cur.close();
  171. // update to have the opposite value
  172. ContentValues values = new ContentValues();
  173. values.put(FIELD_SCRIPT_ENABLED, (value == 0) ? 1 : 0);
  174. db.update(TABLE_SCRIPTS, values, "_id = ?", new String[] { Long.toString(id) });
  175. }
  176. private Map<Pattern,String> cache = new HashMap<Pattern,String>();
  177. private void validateCache() {
  178. SQLiteDatabase db = this.getReadableDatabase();
  179. Cursor cur = db.query(TABLE_SCRIPTS, new String[] { FIELD_SCRIPT_DOMAINREGEX,
  180. FIELD_SCRIPT_CONTENT }, FIELD_SCRIPT_ENABLED + " = 1", null, null, null, null);
  181. int COL_DOMAINREGEX = cur.getColumnIndex(FIELD_SCRIPT_DOMAINREGEX),
  182. COL_CONTENT = cur.getColumnIndex(FIELD_SCRIPT_CONTENT);
  183. cache.clear();
  184. while(cur.moveToNext()) {
  185. String domainregex = cur.getString(COL_DOMAINREGEX),
  186. content = cur.getString(COL_CONTENT);
  187. cache.put(Pattern.compile(domainregex), content);
  188. }
  189. cur.close();
  190. }
  191. /**
  192. * Return Javascript (CONTENT) for all scripts that should be active given
  193. * this specific url.
  194. */
  195. public List<String> getActive(String url) {
  196. List<String> active = new LinkedList<String>();
  197. this.validateCache();
  198. for(Pattern trial : cache.keySet()) {
  199. if(trial.matcher(url).matches())
  200. active.add(cache.get(trial));
  201. }
  202. return active;
  203. }
  204. }