1 |
// |
2 |
// BSDBThreadsListDBUpdateTask2.m |
3 |
// BathyScaphe |
4 |
// |
5 |
// Created by Hori,Masaki on 06/08/06. |
6 |
// Copyright 2006-2011 BathyScaphe Project. All rights reserved. |
7 |
// encoding="UTF-8" |
8 |
// |
9 |
|
10 |
#import "BSDBThreadsListDBUpdateTask2.h" |
11 |
|
12 |
#import <CocoaOniguruma/OnigRegexp.h> |
13 |
|
14 |
#import "DatabaseManager.h" |
15 |
#import "CMRHostHandler.h" |
16 |
#import "CMXTextParser.h" |
17 |
#import "CMRDocumentFileManager.h" |
18 |
|
19 |
//NSString *const BSDBThreadsListDBUpdateTask2DidFinishNotification = @"BSDBThreadsListDBUpdateTask2DidFinishNotification"; |
20 |
|
21 |
|
22 |
static inline id nilIfObjectIsNSNull(id obj) |
23 |
{ |
24 |
return (obj == [NSNull null]) ? nil : obj; |
25 |
} |
26 |
|
27 |
@implementation BSDBThreadsListDBUpdateTask2 |
28 |
|
29 |
static NSString *sSelectThreadTableQuery = nil; |
30 |
static NSString *sInsertQuery = nil; |
31 |
static NSString *sUpdateQuery = nil; |
32 |
static NSString *sInsertNumberQuery = nil; |
33 |
static OnigRegexp *gRegexp = nil; |
34 |
|
35 |
- (id)initWithBBSName:(NSString *)name data:(NSData *)data livedoor:(BOOL)isLivedoorFlag rebuilding:(BOOL)isRebuildingFlag |
36 |
{ |
37 |
if (self = [super init]) { |
38 |
bbsName = [name retain]; |
39 |
|
40 |
subjectData = [data retain]; |
41 |
isRebuilding = isRebuildingFlag; |
42 |
isLivedoor = isLivedoorFlag; |
43 |
} |
44 |
|
45 |
return self; |
46 |
} |
47 |
|
48 |
+ (id)taskWithBBSName:(NSString *)name data:(NSData *)data livedoor:(BOOL)isLivedoorFlag rebuilding:(BOOL)isRebuildingFlag |
49 |
{ |
50 |
return [[[self alloc] initWithBBSName:name data:data livedoor:isLivedoorFlag rebuilding:isRebuildingFlag] autorelease]; |
51 |
} |
52 |
|
53 |
- (BOOL)isRebuilding |
54 |
{ |
55 |
return isRebuilding; |
56 |
} |
57 |
|
58 |
- (BOOL)isLivedoor |
59 |
{ |
60 |
return isLivedoor; |
61 |
} |
62 |
|
63 |
- (NSError *)lastErrorWhileRebuilding |
64 |
{ |
65 |
return lastError; |
66 |
} |
67 |
|
68 |
- (void)dealloc |
69 |
{ |
70 |
[subjectData release]; |
71 |
[boardID release]; |
72 |
[bbsName release]; |
73 |
[lastError release]; |
74 |
|
75 |
[super dealloc]; |
76 |
} |
77 |
|
78 |
- (void)setBBSName:(NSString *)name |
79 |
{ |
80 |
NSArray *boradIDs = [[DatabaseManager defaultManager] boardIDsForName:name]; |
81 |
if (!boradIDs || [boradIDs count] == 0) { |
82 |
return; |
83 |
} |
84 |
boardID = [[boradIDs objectAtIndex:0] retain]; |
85 |
bbsName = [name retain]; |
86 |
} |
87 |
|
88 |
static inline SQLiteReservedQuery *reservedSelectThreadTable(SQLiteDB* db) |
89 |
{ |
90 |
return [SQLiteReservedQuery sqliteReservedQueryWithQuery:sSelectThreadTableQuery usingSQLiteDB:db]; |
91 |
} |
92 |
|
93 |
static inline SQLiteReservedQuery *reservedInsert(SQLiteDB* db) |
94 |
{ |
95 |
return [SQLiteReservedQuery sqliteReservedQueryWithQuery:sInsertQuery usingSQLiteDB:db]; |
96 |
} |
97 |
|
98 |
static inline SQLiteReservedQuery *reservedUpdate(SQLiteDB* db) |
99 |
{ |
100 |
return [SQLiteReservedQuery sqliteReservedQueryWithQuery:sUpdateQuery usingSQLiteDB:db]; |
101 |
} |
102 |
|
103 |
static inline SQLiteReservedQuery *reservedInsertNumber(SQLiteDB* db) |
104 |
{ |
105 |
return [SQLiteReservedQuery sqliteReservedQueryWithQuery:sInsertNumberQuery usingSQLiteDB:db]; |
106 |
} |
107 |
|
108 |
+ (BOOL)makeQuerys |
109 |
{ |
110 |
// ������������������ |
111 |
if (!sSelectThreadTableQuery) { |
112 |
sSelectThreadTableQuery = [NSString stringWithFormat: @"SELECT %@, %@, %@ FROM %@ WHERE %@ = ? AND %@ = ?", |
113 |
ThreadStatusColumn, NumberOfAllColumn, NumberOfReadColumn, |
114 |
ThreadInfoTableName, |
115 |
BoardIDColumn, ThreadIDColumn]; |
116 |
[sSelectThreadTableQuery retain]; |
117 |
} |
118 |
|
119 |
// ��������������������� |
120 |
if (!sInsertQuery) { |
121 |
sInsertQuery = [NSString stringWithFormat: @"INSERT INTO %@ ( %@, %@, %@, %@, %@ ) VALUES ( ?, ?, ?, ?, %ld )", |
122 |
ThreadInfoTableName, |
123 |
BoardIDColumn, ThreadIDColumn, ThreadNameColumn, NumberOfAllColumn, ThreadStatusColumn, |
124 |
(long)ThreadNewCreatedStatus]; |
125 |
[sInsertQuery retain]; |
126 |
} |
127 |
|
128 |
// ������������������������������ |
129 |
if (!sUpdateQuery) { |
130 |
sUpdateQuery = [NSString stringWithFormat: @"UPDATE %@ SET %@ = ?, %@ = ? WHERE %@ = ? AND %@ = ?", |
131 |
ThreadInfoTableName, |
132 |
NumberOfAllColumn, ThreadStatusColumn, |
133 |
BoardIDColumn, ThreadIDColumn]; |
134 |
[sUpdateQuery retain]; |
135 |
} |
136 |
|
137 |
// ��������������������������� |
138 |
if (!sInsertNumberQuery) { |
139 |
sInsertNumberQuery = [NSString stringWithFormat: @"INSERT INTO %@ ( %@, %@, %@ ) VALUES ( ?, ?, ? )", |
140 |
TempThreadNumberTableName, |
141 |
BoardIDColumn, ThreadIDColumn, TempThreadThreadNumberColumn]; |
142 |
[sInsertNumberQuery retain]; |
143 |
} |
144 |
|
145 |
return YES; |
146 |
} |
147 |
|
148 |
+ (BOOL)makeRegex |
149 |
{ |
150 |
if (!gRegexp) { |
151 |
gRegexp = [OnigRegexp compile:[NSString stringWithFormat:@"(\\d+)[^,<>]*(?:<>|,)(.*)\\s*(?:\\(|<>|%C)(\\d+)",0xFF08]]; |
152 |
[gRegexp retain]; |
153 |
} |
154 |
return YES; |
155 |
} |
156 |
|
157 |
- (BOOL)updateDB:(SQLiteDB *)db ID:(NSString *)datString title:(NSString *)title count:(NSInteger)countInt index:(NSInteger)index |
158 |
{ |
159 |
NSArray *bindValues; |
160 |
id<SQLiteCursor> cursor; |
161 |
|
162 |
// ������������������������������������������������������ |
163 |
// [cursor rowCount] ���0��������������������������������� |
164 |
bindValues = [NSArray arrayWithObjects: |
165 |
boardID, datString, nil]; |
166 |
|
167 |
cursor = [reservedSelectThreadTable(db) cursorForBindValues:bindValues]; |
168 |
if (!cursor) { |
169 |
return NO; |
170 |
} |
171 |
|
172 |
if ([cursor rowCount] == 0) { |
173 |
// ��������������������������������������������������������� |
174 |
SQLiteReservedQuery *rFirstQuery = reservedInsert(db); |
175 |
const char *format = F_NSNumberOfInt F_NSString F_NSString F_Int; |
176 |
[rFirstQuery cursorWithFormat:format, boardID, datString, title, countInt, nil]; |
177 |
if ([db lastErrorID] != SQLITE_OK) { |
178 |
NSLog(@"Fail INSERT. ErrorID -> %ld. Reason: %@", (long)[db lastErrorID], [db lastError]); |
179 |
} |
180 |
|
181 |
} else { |
182 |
// ��������������������������������������������������������������������������������������������������������������������� |
183 |
id<SQLiteRow> row = [cursor rowAtIndex:0]; |
184 |
|
185 |
NSUInteger currentNumber; |
186 |
NSUInteger currentStatus, newStatus; |
187 |
NSUInteger readNumber; |
188 |
|
189 |
currentNumber = [nilIfObjectIsNSNull([row valueForColumn:NumberOfAllColumn]) integerValue]; |
190 |
currentStatus = [nilIfObjectIsNSNull([row valueForColumn:ThreadStatusColumn]) integerValue]; |
191 |
readNumber = [nilIfObjectIsNSNull([row valueForColumn:NumberOfReadColumn]) integerValue]; |
192 |
|
193 |
if (readNumber == 0) { |
194 |
newStatus = ThreadNoCacheStatus; |
195 |
} else if (countInt <= readNumber) { |
196 |
newStatus = ThreadLogCachedStatus; |
197 |
} else { |
198 |
newStatus = ThreadUpdatedStatus;; |
199 |
} |
200 |
|
201 |
if (currentNumber != countInt || currentStatus != newStatus) { |
202 |
SQLiteReservedQuery *rUpdateQuery = reservedUpdate(db); |
203 |
const char *format2 = F_Int F_Int F_NSNumberOfInt F_NSString; |
204 |
[rUpdateQuery cursorWithFormat:format2, countInt, newStatus, boardID, datString, nil]; |
205 |
if ([db lastErrorID] != SQLITE_OK) { |
206 |
NSLog(@"Fail UPDATE. ErrorID -> %ld. Reason: %@", (long)[db lastErrorID], [db lastError]); |
207 |
} |
208 |
} |
209 |
} |
210 |
|
211 |
// ��������������������������������������������������������������������� |
212 |
SQLiteReservedQuery *rTempInsertQuery = reservedInsertNumber(db); |
213 |
const char *format3 = F_NSNumberOfInt F_NSString F_Int; |
214 |
[rTempInsertQuery cursorWithFormat:format3, boardID, datString, index, nil]; |
215 |
if ([db lastErrorID] != SQLITE_OK) { |
216 |
NSLog(@"Fail INSERT. ErrorID -> %ld. Reason: %@", (long)[db lastErrorID], [db lastError]); |
217 |
} |
218 |
|
219 |
return YES; |
220 |
} |
221 |
|
222 |
- (void)deleteUnusedInfomations:(SQLiteDB *)db |
223 |
{ |
224 |
BOOL isDelete = SGTemplateBool(@"System - Delete Unused Thread Informations"); |
225 |
if (!isDelete) { |
226 |
return; |
227 |
} |
228 |
|
229 |
if (db && [db beginTransaction]) { |
230 |
// ��������������������������� |
231 |
UTILDebugWrite(@"START DELETING"); |
232 |
NSString *query = [NSString stringWithFormat: |
233 |
@"DELETE FROM %@ " |
234 |
@"WHERE " |
235 |
@"%@ = %@ AND " |
236 |
@"%@ IS NULL AND " |
237 |
@"%@ NOT IN " |
238 |
@"(SELECT %@ FROM %@ WHERE %@ = %@)" |
239 |
, |
240 |
ThreadInfoTableName, |
241 |
BoardIDColumn, boardID, |
242 |
NumberOfReadColumn, |
243 |
ThreadIDColumn, |
244 |
ThreadIDColumn, TempThreadNumberTableName, BoardIDColumn, boardID]; |
245 |
[db performQuery:query]; |
246 |
if ([db lastErrorID] != 0) { |
247 |
NSLog(@"Fail DELETE. ErrorID -> %ld. Reason: %@", (long)[db lastErrorID], [db lastError] ); |
248 |
} |
249 |
UTILDebugWrite1(@" %d row(s) deleted.", sqlite3_changes([db rowDatabase])); |
250 |
[db commitTransaction]; |
251 |
UTILDebugWrite(@"END DELETEING"); |
252 |
} |
253 |
} |
254 |
|
255 |
- (BOOL)rebuildFromLogFiles:(NSError **)errorPtr |
256 |
{ |
257 |
NSString *folderPath = [[CMRDocumentFileManager defaultManager] directoryWithBoardName:bbsName]; |
258 |
|
259 |
return [[DatabaseManager defaultManager] rebuildFromLogFolder:folderPath boardID:boardID error:errorPtr]; |
260 |
} |
261 |
|
262 |
- (void)run |
263 |
{ |
264 |
NSString *str; |
265 |
NSArray *lines; |
266 |
NSUInteger count, i; |
267 |
NSString *line; |
268 |
NSString *datString; |
269 |
id title; |
270 |
NSString *numString; |
271 |
|
272 |
UTILDebugWrite(@"Start BSDBThreadsListDBUpdateTask2."); |
273 |
|
274 |
CFStringEncoding enc; |
275 |
DatabaseManager *dbm = [DatabaseManager defaultManager]; |
276 |
NSArray *array = [dbm boardIDsForName:bbsName]; |
277 |
NSString *urlStr; |
278 |
NSURL *url; |
279 |
CMRHostHandler *handler; |
280 |
|
281 |
if (!array || [array count] == 0) { |
282 |
NSLog(@"Can NOT found bbs named %@.",bbsName); |
283 |
return; |
284 |
} |
285 |
boardID = [[array objectAtIndex:0] retain]; |
286 |
|
287 |
urlStr = [dbm urlStringForBoardID:[boardID integerValue]]; |
288 |
url = [NSURL URLWithString:urlStr]; |
289 |
if (!url) { |
290 |
NSLog(@"Can NOT create url from bbs named %@.",bbsName); |
291 |
return; |
292 |
} |
293 |
handler = [CMRHostHandler hostHandlerForURL:url]; |
294 |
if (!handler) { |
295 |
NSLog(@"Can NOT create host handler from url %@.",url); |
296 |
return; |
297 |
} |
298 |
enc = [handler subjectEncoding]; |
299 |
str = [CMXTextParser stringWithData:subjectData CFEncoding:enc];//[NSString stringWithDataUsingTEC:subjectData encoding:enc]; |
300 |
// ��������� |
301 |
lines = [str componentsSeparatedByNewline]; |
302 |
|
303 |
if (![[self class] makeRegex]) { |
304 |
UTILDebugWrite(@"Can not create regular expression(BSDBThreadsListDBUpdateTask2.)"); |
305 |
return; |
306 |
} |
307 |
OnigResult *match; |
308 |
|
309 |
SQLiteDB *db = [dbm databaseForCurrentThread]; |
310 |
|
311 |
if (db && [db beginTransaction]) { |
312 |
if (![BSDBThreadsListDBUpdateTask2 makeQuerys]) { |
313 |
UTILDebugWrite(@"Can not create query string(BSDBThreadsListDBUpdateTask2.)"); |
314 |
goto abort; |
315 |
} |
316 |
|
317 |
if (isInterrupted) { |
318 |
goto abort; |
319 |
} |
320 |
// ��������������������������������������������� |
321 |
UTILDebugWrite(@"START CLEAR TEMP DATA"); |
322 |
id query = [NSString stringWithFormat:@"DELETE FROM %@ WHERE %@ = %@", |
323 |
TempThreadNumberTableName, |
324 |
BoardIDColumn, boardID]; |
325 |
[db performQuery:query]; |
326 |
UTILDebugWrite1(@" %d row(s) deleted.", sqlite3_changes([db rowDatabase])); |
327 |
UTILDebugWrite(@"END CLEAR TEMP DATA"); |
328 |
|
329 |
UTILDebugWrite(@"START REGISTER THREADS"); |
330 |
count = [lines count]; |
331 |
NSMutableSet *dats = isLivedoor ? [[NSMutableSet alloc] initWithCapacity:count] : nil; |
332 |
for (i = 0; i < count; i++) { |
333 |
if (isInterrupted) { |
334 |
[dats release]; |
335 |
goto abort; |
336 |
} |
337 |
line = [lines objectAtIndex:i]; |
338 |
match = [gRegexp search:line]; |
339 |
|
340 |
datString = [match stringAt:1]; |
341 |
title = [match stringAt:2]; |
342 |
numString = [match stringAt:3]; |
343 |
|
344 |
if (!numString) { |
345 |
continue; |
346 |
} |
347 |
if (isLivedoor) { |
348 |
if ([dats member:datString]) { |
349 |
continue; |
350 |
} else { |
351 |
[dats addObject:datString]; |
352 |
} |
353 |
} |
354 |
title = [[title mutableCopy] autorelease]; |
355 |
[CMXTextParser replaceEntityReferenceWithString:title]; |
356 |
|
357 |
// DB ��������� |
358 |
if(![self updateDB:db ID:datString title:title count:[numString integerValue] index:(i+1)]) { |
359 |
UTILDebugWrite(@"Abort in updateDB."); |
360 |
[dats release]; |
361 |
goto abort; |
362 |
} |
363 |
} |
364 |
[dats release]; |
365 |
[db commitTransaction]; |
366 |
UTILDebugWrite(@"END REGISTER THREADS"); |
367 |
} |
368 |
|
369 |
if (isRebuilding) { |
370 |
NSError *error = nil; |
371 |
[self rebuildFromLogFiles:&error]; |
372 |
if (error) { |
373 |
lastError = [error retain]; |
374 |
} |
375 |
} else { |
376 |
[self deleteUnusedInfomations:db]; |
377 |
} |
378 |
|
379 |
// [self postNotificationWithName:BSDBThreadsListDBUpdateTask2DidFinishNotification]; |
380 |
|
381 |
return; |
382 |
|
383 |
abort: |
384 |
NSLog(@"Fail Database operation. Reason: \n%@\nin %@(%@)", |
385 |
[db lastError], NSStringFromClass([self class]), NSStringFromSelector(_cmd)); |
386 |
[db rollbackTransaction]; |
387 |
|
388 |
// if (!isInterrupted) { |
389 |
// [self postNotificationWithName:BSDBThreadsListDBUpdateTask2DidFinishNotification]; |
390 |
// } |
391 |
} |
392 |
|
393 |
- (void)cancel:(id)sender |
394 |
{ |
395 |
// [self postNotificationWithName:BSDBThreadsListDBUpdateTask2DidFinishNotification]; |
396 |
isInterrupted = YES; |
397 |
} |
398 |
@end |
399 |
|
400 |
/* |
401 |
@implementation BSDBThreadsListDBUpdateTask2(TaskNotification) |
402 |
- (void)postNotificationWithName:(NSString *)name |
403 |
{ |
404 |
[[NSNotificationCenter defaultCenter] postNotificationName:name object:self]; |
405 |
UTILDebugWrite(@"End BSDBThreadsListDBUpdateTask2."); |
406 |
} |
407 |
@end |
408 |
*/ |