Dont index style nodes.
[beagle.git] / beagled / TextCache.cs
blob5bcfab9551b83e2b66c31e430e1bc6a3c8b1b538
1 //
2 // TextCache.cs
3 //
4 // Copyright (C) 2004 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
28 using System;
29 using System.Collections;
30 using System.IO;
31 using System.Threading;
33 using Mono.Data.SqliteClient;
34 using ICSharpCode.SharpZipLib.GZip;
36 using Beagle.Util;
38 namespace Beagle.Daemon {
40 public class TextCache {
42 static public bool Debug = false;
44 public const string SELF_CACHE_TAG = "*self*";
46 private string text_cache_dir;
47 private SqliteConnection connection;
49 private enum TransactionState {
50 None,
51 Requested,
52 Started
54 private TransactionState transaction_state;
56 private static TextCache user_cache = null;
58 public static TextCache UserCache {
59 get {
60 if (user_cache == null)
61 user_cache = new TextCache (PathFinder.StorageDir);
63 return user_cache;
67 public TextCache (string storage_dir) : this (storage_dir, false) { }
69 public TextCache (string storage_dir, bool read_only)
71 text_cache_dir = Path.Combine (storage_dir, "TextCache");
72 if (! Directory.Exists (text_cache_dir)) {
73 Directory.CreateDirectory (text_cache_dir);
75 // Create our cache subdirectories.
76 for (int i = 0; i < 256; ++i) {
77 string subdir = i.ToString ("x");
78 if (i < 16)
79 subdir = "0" + subdir;
80 subdir = Path.Combine (text_cache_dir, subdir);
81 Directory.CreateDirectory (subdir);
85 // Create our Sqlite database
86 string db_filename = Path.Combine (text_cache_dir, "TextCache.db");
87 bool create_new_db = false;
88 if (! File.Exists (db_filename))
89 create_new_db = true;
91 // Funky logic here to deal with sqlite versions.
93 // When sqlite 3 tries to open an sqlite 2 database,
94 // it will throw an SqliteException with SqliteError
95 // NOTADB when trying to execute a command.
97 // When sqlite 2 tries to open an sqlite 3 database,
98 // it will throw an ApplicationException when it
99 // tries to open the database.
101 try {
102 connection = Open (db_filename);
103 } catch (ApplicationException) {
104 Logger.Log.Warn ("Likely sqlite database version mismatch trying to open {0}. Purging.", db_filename);
105 create_new_db = true;
108 if (!create_new_db) {
109 // Run a dummy query to see if we get a NOTADB error. Sigh.
110 SqliteCommand command;
111 SqliteDataReader reader = null;
113 command = new SqliteCommand ();
114 command.Connection = connection;
115 command.CommandText =
116 "SELECT filename FROM uri_index WHERE uri='blah'";
118 try {
119 reader = SqliteUtils.ExecuteReaderOrWait (command);
120 } catch (ApplicationException ex) {
121 Logger.Log.Warn ("Likely sqlite database version mismatch trying to read from {0}. Purging.", db_filename);
122 create_new_db = true;
125 if (reader != null)
126 reader.Dispose ();
127 command.Dispose ();
130 if (create_new_db) {
131 if (connection != null)
132 connection.Dispose ();
134 if (read_only)
135 throw new UnauthorizedAccessException (String.Format ("Unable to create read only text cache {0}", db_filename));
137 File.Delete (db_filename);
138 try{
139 connection = Open (db_filename);
140 }catch (Exception e){
141 Logger.Log.Debug ( e.Message);
143 SqliteUtils.DoNonQuery (connection,
144 "CREATE TABLE uri_index ( " +
145 " uri STRING UNIQUE NOT NULL, " +
146 " filename STRING NOT NULL " +
147 ")");
151 private SqliteConnection Open (string db_filename)
153 SqliteConnection connection = new SqliteConnection ();
154 connection.ConnectionString = "version=" + ExternalStringsHack.SqliteVersion
155 + ",encoding=UTF-8,URI=file:" + db_filename;
156 connection.Open ();
157 return connection;
160 private static string UriToString (Uri uri)
162 return UriFu.UriToSerializableString (uri).Replace ("'", "''");
165 private SqliteCommand NewCommand (string format, params object [] args)
167 SqliteCommand command;
168 command = new SqliteCommand ();
169 command.Connection = connection;
170 command.CommandText = String.Format (format, args);
171 return command;
174 private void Insert (Uri uri, string filename)
176 lock (connection) {
177 MaybeStartTransaction_Unlocked ();
178 SqliteUtils.DoNonQuery (connection,
179 "INSERT OR REPLACE INTO uri_index (uri, filename) VALUES ('{0}', '{1}')",
180 UriToString (uri), filename);
184 private string LookupPathRawUnlocked (Uri uri, bool create_if_not_found)
186 SqliteCommand command;
187 SqliteDataReader reader = null;
188 string path = null;
190 command = NewCommand ("SELECT filename FROM uri_index WHERE uri='{0}'",
191 UriToString (uri));
192 reader = SqliteUtils.ExecuteReaderOrWait (command);
193 if (SqliteUtils.ReadOrWait (reader))
194 path = reader.GetString (0);
195 reader.Close ();
196 command.Dispose ();
198 if (path == null && create_if_not_found) {
199 string guid = Guid.NewGuid ().ToString ();
200 path = Path.Combine (guid.Substring (0, 2), guid.Substring (2));
201 Insert (uri, path);
204 if (path == SELF_CACHE_TAG)
205 return SELF_CACHE_TAG;
207 return path != null ? Path.Combine (text_cache_dir, path) : null;
210 // Don't do this unless you know what you are doing! If you
211 // do anything to the path you get back other than open and
212 // read the file, you will almost certainly break something.
213 // And it will be evidence that you are a bad person and that
214 // you deserve whatever horrible fate befalls you.
215 public string LookupPathRaw (Uri uri)
217 lock (connection)
218 return LookupPathRawUnlocked (uri, false);
221 private string LookupPath (Uri uri, bool create_if_not_found)
223 lock (connection) {
224 string path = LookupPathRawUnlocked (uri, create_if_not_found);
225 if (path == SELF_CACHE_TAG) {
226 // FIXME: How do we handle URI remapping for self-cached items?
227 #if false
228 if (uri_remapper != null)
229 uri = uri_remapper (uri);
230 #endif
231 if (! uri.IsFile) {
232 string msg = String.Format ("Non-file uri {0} flagged as self-cached", uri);
233 throw new Exception (msg);
235 return uri.LocalPath;
237 return path;
241 public void MarkAsSelfCached (Uri uri)
243 lock (connection)
244 Insert (uri, SELF_CACHE_TAG);
247 private bool world_readable = false;
248 public bool WorldReadable {
249 get { return this.world_readable; }
250 set { this.world_readable = value; }
253 public TextWriter GetWriter (Uri uri)
255 // FIXME: Uri remapping?
256 string path = LookupPath (uri, true);
258 FileStream fs;
259 fs = new FileStream (path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
261 // We don't expect to need this again in the near future.
262 FileAdvise.FlushCache (fs);
264 GZipOutputStream stream;
265 stream = new GZipOutputStream (fs);
267 if (! world_readable) {
268 // Make files only readable by the owner.
269 Mono.Unix.Native.Syscall.chmod (path, (Mono.Unix.Native.FilePermissions) 384);
272 StreamWriter writer;
273 writer = new StreamWriter (stream);
274 return writer;
277 public void WriteFromReader (Uri uri, TextReader reader)
279 TextWriter writer;
280 writer = GetWriter (uri);
281 string line;
282 while ((line = reader.ReadLine ()) != null)
283 writer.WriteLine (line);
284 writer.Close ();
287 public void WriteFromString (Uri uri, string str)
289 if (str == null) {
290 Delete (uri);
291 return;
294 TextWriter writer;
295 writer = GetWriter (uri);
296 writer.WriteLine (str);
297 writer.Close ();
300 // FIXME: Uri remapping?
301 public TextReader GetReader (Uri uri)
303 string path = LookupPath (uri, false);
304 if (path == null)
305 return null;
307 return GetReader (path);
310 public TextReader GetReader (string path)
312 FileStream file_stream;
313 try {
314 file_stream = new FileStream (path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
315 } catch (FileNotFoundException ex) {
316 return null;
319 StreamReader reader = null;
320 try {
321 Stream stream = new GZipInputStream (file_stream);
322 reader = new StreamReader (stream);
324 // This throws an exception if the file isn't compressed as follows:
325 // 1.) IOException on older versions of SharpZipLib
326 // 2.) GZipException on newer versions of SharpZipLib
327 // FIXME: Change this to GZipException when we depend
328 // on a higer version of SharpZipLib
329 reader.Peek ();
330 } catch (Exception ex) {
331 reader = new StreamReader (file_stream);
334 return reader;
337 public void Delete (Uri uri)
339 lock (connection) {
340 string path = LookupPathRawUnlocked (uri, false);
341 if (path != null) {
342 MaybeStartTransaction_Unlocked ();
343 SqliteUtils.DoNonQuery (connection,
344 "DELETE FROM uri_index WHERE uri='{0}' AND filename='{1}'",
345 UriToString (uri), path);
346 if (path != SELF_CACHE_TAG)
347 File.Delete (path);
352 private void MaybeStartTransaction_Unlocked ()
354 if (transaction_state == TransactionState.Requested)
355 SqliteUtils.DoNonQuery (connection, "BEGIN");
356 transaction_state = TransactionState.Started;
359 public void BeginTransaction ()
361 lock (connection) {
362 if (transaction_state == TransactionState.None)
363 transaction_state = TransactionState.Requested;
367 public void CommitTransaction ()
369 lock (connection) {
370 if (transaction_state == TransactionState.Started)
371 SqliteUtils.DoNonQuery (connection, "COMMIT");
372 transaction_state = TransactionState.None;