1 |
// |
2 |
// BSThreadListUpdateTask.m |
3 |
// BathyScaphe |
4 |
// |
5 |
// Created by Hori,Masaki on 06/03/29. |
6 |
// Copyright 2006 __MyCompanyName__. All rights reserved. |
7 |
// |
8 |
|
9 |
#import "BSThreadListUpdateTask.h" |
10 |
#import "BSDBThreadList.h" |
11 |
#import "BoardListItem.h" |
12 |
#import "DatabaseManager.h" |
13 |
#import "AppDefaults.h" |
14 |
|
15 |
@implementation BSThreadListUpdateTask |
16 |
|
17 |
+ (id)taskWithBSDBThreadList:(BSDBThreadList *)threadList |
18 |
{ |
19 |
return [[[[self class] alloc] initWithBSDBThreadList:threadList] autorelease]; |
20 |
} |
21 |
- (id)initWithBSDBThreadList:(BSDBThreadList *)threadList |
22 |
{ |
23 |
if(self = [super init]) { |
24 |
target = threadList; //[threadList retain]; |
25 |
progress = YES; |
26 |
userCanceled = NO; |
27 |
} |
28 |
|
29 |
return self; |
30 |
} |
31 |
- (void)dealloc |
32 |
{ |
33 |
// [target release]; |
34 |
|
35 |
[super dealloc]; |
36 |
} |
37 |
|
38 |
- (id) identifier |
39 |
{ |
40 |
return [NSValue valueWithPointer:self]; |
41 |
} |
42 |
|
43 |
- (NSString *) title |
44 |
{ |
45 |
return [[target boardListItem] representName]; |
46 |
} |
47 |
- (NSString *) message |
48 |
{ |
49 |
return [NSString stringWithFormat:@"Updating -- %@", [[target boardListItem] representName]]; |
50 |
} |
51 |
- (BOOL) isInProgress |
52 |
{ |
53 |
return progress; |
54 |
} |
55 |
|
56 |
// from 0.0 to 100.0 (or -1: Indeterminate) |
57 |
- (double) amount |
58 |
{ |
59 |
return ([self isInProgress]) ? 0 : -1; |
60 |
} |
61 |
|
62 |
- (IBAction) cancel : (id) sender |
63 |
{ |
64 |
userCanceled = YES; |
65 |
} |
66 |
|
67 |
|
68 |
//- (id)copyWithZone:(NSZone *)zone |
69 |
//{ |
70 |
// BSThreadListUpdateTask *result = [[self class] taskWithBSDBThreadList:target]; |
71 |
// if(result) { |
72 |
// result->progress = progress; |
73 |
// result->userCanceled = userCanceled; |
74 |
// } |
75 |
// |
76 |
// return [result retain]; |
77 |
//} |
78 |
|
79 |
#pragma mark- |
80 |
|
81 |
static inline NSArray *componentsSeparatedByWhiteSpace(NSString *string) |
82 |
{ |
83 |
NSMutableArray *result = [NSMutableArray array]; |
84 |
NSScanner *s = [NSScanner scannerWithString : string]; |
85 |
NSCharacterSet *cs = [NSCharacterSet whitespaceCharacterSet]; |
86 |
NSString *str; |
87 |
|
88 |
while ([s scanUpToCharactersFromSet : cs intoString : &str]) { |
89 |
[result addObject : str]; |
90 |
} |
91 |
|
92 |
if ([result count] == 0) { |
93 |
return nil; |
94 |
} |
95 |
|
96 |
return result; |
97 |
} |
98 |
static inline NSString *whereClauseFromSearchString(NSString *searchString) |
99 |
{ |
100 |
NSMutableString *clause; |
101 |
NSArray *searchs; |
102 |
NSEnumerator *searchsEnum; |
103 |
NSString *token; |
104 |
|
105 |
NSString *p = @""; |
106 |
|
107 |
searchs = componentsSeparatedByWhiteSpace(searchString); |
108 |
|
109 |
if (!searchs || [searchs count] == 0) { |
110 |
return nil; |
111 |
} |
112 |
|
113 |
clause = [NSMutableString stringWithFormat : @" WHERE "]; |
114 |
|
115 |
searchsEnum = [searchs objectEnumerator]; |
116 |
while (token = [searchsEnum nextObject]) { |
117 |
if ([token hasPrefix : @"!"]) { |
118 |
if ([token length] == 1) continue; |
119 |
|
120 |
[clause appendFormat : @"%@NOT %@ LIKE '%%%@%%' ", |
121 |
p, ThreadNameColumn, [token substringFromIndex : 1]]; |
122 |
} else { |
123 |
[clause appendFormat : @"%@%@ LIKE '%%%@%%' ", |
124 |
p, ThreadNameColumn, token]; |
125 |
} |
126 |
p = @"AND "; |
127 |
} |
128 |
|
129 |
return clause; |
130 |
} |
131 |
|
132 |
enum { |
133 |
kNewerThreadType, // ������������ |
134 |
kOlderThreadType, // ��������������� |
135 |
kAllThreadType, // ��������� |
136 |
}; |
137 |
|
138 |
// filter ��������� |
139 |
// ������������������������������������������������������������������������������DB��������������������������� |
140 |
// WHERE��������������� |
141 |
static inline NSString *conditionFromStatusAndType( int status, int type ) |
142 |
{ |
143 |
NSMutableString *result = [NSMutableString string]; |
144 |
NSString *brankOrAnd = @""; |
145 |
|
146 |
if(status & ThreadLogCachedStatus && |
147 |
(type == kOlderThreadType || !(status & ThreadNewCreatedStatus))) { |
148 |
// ������/������������������������������������������ ��������������������������������� |
149 |
[result appendFormat : @"NOT %@ IS NULL\n", NumberOfReadColumn]; |
150 |
brankOrAnd = @" AND "; |
151 |
} else if(status & ThreadNoCacheStatus) { |
152 |
// ��������������������� |
153 |
[result appendFormat : @"%@ IS NULL\n", NumberOfReadColumn]; |
154 |
brankOrAnd = @" AND "; |
155 |
} else if(status & ThreadNewCreatedStatus && type == kOlderThreadType) { |
156 |
// ������������������������������������������������������������ boardID ���������������������������0��������� |
157 |
[result appendFormat : @"%@ < 0\n",BoardIDColumn]; |
158 |
brankOrAnd = @" AND "; |
159 |
} |
160 |
|
161 |
switch(type) { |
162 |
case kNewerThreadType: |
163 |
[result appendFormat : @"%@%@ = %u\n", |
164 |
brankOrAnd, ThreadStatusColumn, ThreadNewCreatedStatus]; |
165 |
break; |
166 |
case kOlderThreadType: |
167 |
[result appendFormat : @"%@%@ != %u\n", |
168 |
brankOrAnd, ThreadStatusColumn, ThreadNewCreatedStatus]; |
169 |
break; |
170 |
case kAllThreadType: |
171 |
// Do nothing. |
172 |
break; |
173 |
default: |
174 |
UTILUnknownCSwitchCase(type); |
175 |
break; |
176 |
} |
177 |
|
178 |
return result; |
179 |
} |
180 |
static inline NSString *orderBy( NSString *sortKey, BOOL isAscending ) |
181 |
{ |
182 |
NSString *result = nil; |
183 |
NSString *sortCol = nil; |
184 |
NSString *ascending = @""; |
185 |
|
186 |
if (!isAscending) ascending = @"DESC"; |
187 |
|
188 |
if ([sortKey isEqualTo : CMRThreadTitleKey]) { |
189 |
sortCol = ThreadNameColumn; |
190 |
} else if ([sortKey isEqualTo : CMRThreadLastLoadedNumberKey]) { |
191 |
sortCol = NumberOfReadColumn; |
192 |
} else if ([sortKey isEqualTo : CMRThreadNumberOfMessagesKey]) { |
193 |
sortCol = NumberOfAllColumn; |
194 |
} else if ([sortKey isEqualTo : CMRThreadNumberOfUpdatedKey]) { |
195 |
sortCol = NumberOfDifferenceColumn; |
196 |
} else if ([sortKey isEqualTo : CMRThreadSubjectIndexKey]) { |
197 |
sortCol = TempThreadThreadNumberColumn; |
198 |
} else if ([sortKey isEqualTo : CMRThreadStatusKey]) { |
199 |
sortCol = ThreadStatusColumn; |
200 |
} else if ([sortKey isEqualTo : CMRThreadModifiedDateKey]) { |
201 |
sortCol = ModifiedDateColumn; |
202 |
} else if ([sortKey isEqualTo : ThreadPlistIdentifierKey]) { |
203 |
sortCol = ThreadIDColumn; |
204 |
} else if ([sortKey isEqualTo : ThreadPlistBoardNameKey]) { |
205 |
sortCol = BoardNameColumn; |
206 |
} |
207 |
|
208 |
// if(sortCol) { |
209 |
// result = [NSString stringWithFormat : @"ORDER BY %@ %@",sortCol, ascending]; |
210 |
// } |
211 |
// |
212 |
// return result; |
213 |
return [sortCol lowercaseString]; |
214 |
} |
215 |
- (NSString *) sqlForListForType : (int) type |
216 |
{ |
217 |
NSString *targetTable = [[target boardListItem] query]; |
218 |
NSMutableString *sql; |
219 |
NSString *whereOrAnd = @" WHERE "; |
220 |
NSString *searchCondition; |
221 |
NSString *filterCondition; |
222 |
NSString *order; |
223 |
|
224 |
sql = [NSMutableString stringWithFormat : @"SELECT * FROM (%@) ",targetTable]; |
225 |
|
226 |
if ([target searchString] && ![[target searchString] isEmpty]) { |
227 |
searchCondition = whereClauseFromSearchString([target searchString]); |
228 |
if (searchCondition) { |
229 |
[sql appendString : searchCondition]; |
230 |
whereOrAnd = @" AND "; |
231 |
} |
232 |
} |
233 |
|
234 |
filterCondition = conditionFromStatusAndType( [target status], type); |
235 |
if(filterCondition && [filterCondition length] != 0) { |
236 |
[sql appendFormat : @"%@ %@\n", whereOrAnd, filterCondition]; |
237 |
// whereOrAnd = @" AND "; |
238 |
} |
239 |
|
240 |
// order = orderBy( [target sortKey], [target isAscending]); |
241 |
// if(order) { |
242 |
// [sql appendString : order]; |
243 |
// } |
244 |
|
245 |
return sql; |
246 |
} |
247 |
- (id)cursor |
248 |
{ |
249 |
id result = nil; |
250 |
|
251 |
[self postTaskWillStartNotification]; |
252 |
|
253 |
SQLiteDB *db = [[DatabaseManager defaultManager] databaseForCurrentThread]; |
254 |
NSString *newersSQL = nil; |
255 |
NSString *sql; |
256 |
id <SQLiteMutableCursor> newerCursor = nil; |
257 |
id <SQLiteMutableCursor> olderCursor = nil; |
258 |
|
259 |
UTILAssertNotNil(db); |
260 |
|
261 |
if( [CMRPref collectByNew] ) { |
262 |
if(userCanceled) goto final; |
263 |
newersSQL = [self sqlForListForType : kNewerThreadType]; |
264 |
if(userCanceled) goto final; |
265 |
sql = [self sqlForListForType : kOlderThreadType]; |
266 |
} else { |
267 |
sql = [self sqlForListForType : kAllThreadType]; |
268 |
} |
269 |
|
270 |
// if(userCanceled) goto final; |
271 |
// sql = [sql stringByAppendingString:@"\nLIMIT 5000"]; |
272 |
// newersSQL = [newersSQL stringByAppendingString:@"\nLIMIT 5000"]; |
273 |
|
274 |
do { |
275 |
if(userCanceled) goto final; |
276 |
olderCursor = [db cursorForSQL : sql]; |
277 |
if ([db lastErrorID] != 0) { |
278 |
NSLog(@"sql error on %s line %d.\n\tReason : %@", __FILE__, __LINE__, [db lastError]); |
279 |
olderCursor = nil; |
280 |
break; |
281 |
} |
282 |
if(userCanceled) goto final; |
283 |
if(newersSQL) { |
284 |
newerCursor = [db cursorForSQL : newersSQL]; |
285 |
if([db lastErrorID] != 0) { |
286 |
NSLog(@"sql error on %s line %d.\n\tReason : %@", __FILE__, __LINE__, [db lastError]); |
287 |
newerCursor = nil; |
288 |
break; |
289 |
} |
290 |
} |
291 |
|
292 |
if(userCanceled) goto final; |
293 |
id sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:orderBy([target sortKey], 0) |
294 |
ascending:[target isAscending] |
295 |
selector:@selector(numericCompare:)] autorelease]; |
296 |
id sortDescriptors = [NSArray arrayWithObject:sortDescriptor]; |
297 |
if(newerCursor) { |
298 |
NSArray *data = [newerCursor arrayForTableView]; |
299 |
NSArray *col = [newerCursor columnNames]; |
300 |
NSArrayController *acon = [[[NSArrayController alloc] initWithContent:data] autorelease]; |
301 |
|
302 |
[acon setSortDescriptors:sortDescriptors]; |
303 |
data = [acon arrangeObjects:data]; |
304 |
|
305 |
newerCursor = [NSDictionary dictionaryWithObjectsAndKeys:data, @"Values", col, @"ColumnNames", nil]; |
306 |
} |
307 |
if(olderCursor) { |
308 |
NSArray *data = [olderCursor arrayForTableView]; |
309 |
NSArray *col = [olderCursor columnNames]; |
310 |
NSArrayController *acon = [[[NSArrayController alloc] initWithContent:data] autorelease]; |
311 |
|
312 |
[acon setSortDescriptors:sortDescriptors]; |
313 |
data = [acon arrangeObjects:data]; |
314 |
|
315 |
olderCursor = [NSDictionary dictionaryWithObjectsAndKeys:data, @"Values", col, @"ColumnNames", nil]; |
316 |
} |
317 |
|
318 |
if(userCanceled) goto final; |
319 |
if(newerCursor && [newerCursor rowCount]) { |
320 |
[newerCursor appendCursor : olderCursor]; |
321 |
olderCursor = nil; |
322 |
} |
323 |
} while( NO ); |
324 |
|
325 |
if(olderCursor || newerCursor) { |
326 |
if(olderCursor) { |
327 |
result = olderCursor; |
328 |
} else { |
329 |
result = newerCursor; |
330 |
} |
331 |
} |
332 |
|
333 |
final: |
334 |
[self postTaskDidFinishNotification]; |
335 |
|
336 |
return result; |
337 |
} |
338 |
|
339 |
@end |
340 |
|
341 |
@implementation BSThreadListUpdateTask(TaskNotification) |
342 |
- (void) postTaskWillStartNotification |
343 |
{ |
344 |
NSNotificationCenter *nc_; |
345 |
|
346 |
nc_ = [NSNotificationCenter defaultCenter]; |
347 |
[nc_ postNotificationName : CMRTaskWillStartNotification |
348 |
object : self]; |
349 |
} |
350 |
|
351 |
- (void) postTaskDidFinishNotification |
352 |
{ |
353 |
NSNotificationCenter *nc_; |
354 |
|
355 |
progress = NO; |
356 |
|
357 |
progress = NO; |
358 |
|
359 |
nc_ = [NSNotificationCenter defaultCenter]; |
360 |
[nc_ postNotificationName : CMRTaskDidFinishNotification |
361 |
object : self]; |
362 |
} |
363 |
@end |
364 |
|
365 |
@implementation NSString(BSThreadListUpdateTaskAddition) |
366 |
- (NSComparisonResult)numericCompare:(NSString *)string |
367 |
{ |
368 |
return [self compare:string options:NSNumericSearch]; |
369 |
} |
370 |
@end |
371 |
@implementation NSNumber(BSThreadListUpdateTaskAddition) |
372 |
- (NSComparisonResult)numericCompare:(id)obj |
373 |
{ |
374 |
return [self compare:obj]; |
375 |
} |
376 |
@end |