root/Trunk/AILogSizeSort.m @ 18

Revision 18, 6.9 KB (checked in by jon, 16 years ago)

Re-implemented caching in a less stupid way. Still want to get it intercepting content updates before I declare #2 closed, though. Also did some modest refactoring.

  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 *
4 * Adium is the legal property of its developers, whose names are listed in the copyright file included
5 * with this source distribution.
6 *
7 * This plugin is copyright (c) 2008 Jon Chambers.  The plugin's official site is:
8 * http://projects.eatthepath.com/sort-by-log-size-plugin/
9 *
10 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License as published by the Free Software Foundation; either version 2 of the License,
12 * or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
15 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
16 * Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along with this program; if not,
19 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 */
21
22#import "AILogSizeSort.h"
23#import "AILoggerPlugin.h"
24
25#import <Adium/AISharedAdium.h>
26
27#import <AIUtilities/AITigerCompatibility.h>
28#import <AIUtilities/AIStringUtilities.h>
29
30#import <Adium/AIContactControllerProtocol.h>
31#import <Adium/AIListObject.h>
32#import <Adium/AIMetaContact.h>
33
34#import <Adium/ESDebugAILog.h>
35
36@implementation AILogSizeSort
37
38/*!
39 * @brief Did become active first time
40 *
41 * Called only once; gives the sort controller an opportunity to set defaults and load preferences lazily.
42 */
43- (void)didBecomeActiveFirstTime
44{
45        AILog(@"Sort by log size controller became active for first time.");
46        logSizeCache = [[NSMutableDictionary alloc] init];
47        AILog(@"%@", logSizeCache);
48}
49
50/*!
51 * @brief Non-localized identifier
52 */
53- (NSString *)identifier{
54    return @"Log size";
55}
56
57/*!
58 * @brief Localized display name
59 */
60- (NSString *)displayName{
61    return AILocalizedString(@"Sort Contacts by Log Size",nil);
62}
63
64/*!
65 * @brief Properties which, when changed, should trigger a resort
66 */
67- (NSSet *)statusKeysRequiringResort{
68        return nil;
69}
70
71/*!
72 * @brief Attribute keys which, when changed, should trigger a resort
73 */
74- (NSSet *)attributeKeysRequiringResort{
75        return nil;
76}
77
78#pragma mark Configuration
79/*!
80 * @brief Window title when configuring the sort
81 *
82 * Subclasses should provide a title for configuring the sort only if configuration is possible.
83 * @result Localized title. If nil, the menu item will be disabled.
84 */
85- (NSString *)configureSortWindowTitle{
86        return nil;
87}
88
89/*!
90 * @brief Nib name for configuration
91 */
92- (NSString *)configureNibName{
93        return nil;
94}
95
96/*!
97 * @brief View did load
98 */
99- (void)viewDidLoad{
100}
101
102/*!
103 * @brief Preference changed
104 *
105 * Sort controllers should live update as preferences change.
106 */
107- (IBAction)changePreference:(id)sender
108{
109}
110
111/*!
112 * @brief Allow users to manually sort groups
113 */
114-(BOOL)canSortManually
115{
116        return YES;
117}
118
119-(unsigned long long)getCachedLogSize:(AIListContact *)listContact
120{
121        AILogWithSignature(@"Getting cached log size for %@/%@.", [[listContact account] explicitFormattedUID], [listContact UID]);
122       
123        if([logSizeCache valueForKey:[[listContact account] explicitFormattedUID]] == nil)
124        {
125                [logSizeCache setValue:[[NSMutableDictionary alloc] init] forKey:[[listContact account] explicitFormattedUID]];
126        }
127       
128        NSMutableDictionary *accountDictionary = [logSizeCache valueForKey:[[listContact account] explicitFormattedUID]];
129       
130        if([accountDictionary valueForKey:[listContact UID]] == nil)
131        {
132                AILogWithSignature(@"\tNo cache hit.");
133                [accountDictionary setValue:[NSNumber numberWithUnsignedLongLong:[AILogSizeSort getContactLogSize:listContact]] forKey: [listContact UID]];
134        }
135       
136        AILogWithSignature(@"\tLog file size: %@", [accountDictionary valueForKey:[listContact UID]]);
137        return [[accountDictionary valueForKey:[listContact UID]] unsignedLongLongValue];
138}
139
140/*!
141 * @brief Returns the total aggregate log size for a contact
142 *
143 * Returns the total aggregate log size for a contact.  For meta-contacts, the
144 * total log file size of all sub-contacts is returned.  If no log exists or if
145 * something else goes wrong, 0 is returned.
146 *
147 * @param listContact an AIListContact for which to retrieve a total log file size
148 * @return the total log file size in bytes or 0 if an error occurred
149 */
150+(unsigned long long)getContactLogSize:(AIListContact *)listContact
151{
152        NSFileManager *fileManager = [NSFileManager defaultManager];
153       
154        if([listContact isMemberOfClass:[AIMetaContact class]])
155        {
156                // Recurse through all sub-contacts
157                id contact;
158                unsigned long long size = 0;
159               
160                NSEnumerator *contactEnumerator = [[(AIMetaContact *)listContact listContacts] objectEnumerator];
161
162                while(contact = [contactEnumerator nextObject])
163                {
164                        size += [AILogSizeSort getContactLogSize:contact];
165                }
166               
167                return size;
168        }
169        else
170        {
171                // Find the path to the directory containing the log files for this contact
172                NSString *path = [[AILoggerPlugin logBasePath] stringByAppendingPathComponent:[AILoggerPlugin relativePathForLogWithObject:[listContact UID] onAccount: [listContact account]]];
173               
174                // Grab an enumerator for all log files for this contact
175                NSDirectoryEnumerator *dirEnum = [[NSFileManager defaultManager] enumeratorAtPath:path];
176                NSString *file;
177               
178                unsigned long long size = 0;
179               
180                while(file = [dirEnum nextObject])
181                {
182                        NSDictionary *fileAttributes = [fileManager fileAttributesAtPath:[path stringByAppendingPathComponent:file] traverseLink:YES];
183                       
184                        if (fileAttributes != nil)
185                        {
186                                NSNumber *fileSize;
187                                if(fileSize = [fileAttributes objectForKey:NSFileSize])
188                                {
189                                        size += [fileSize unsignedLongLongValue];
190                                }
191                        }
192                }
193               
194                return size;
195        }
196}
197
198#pragma mark Sorting
199/*!
200 * @brief Sort by log size
201 */
202int logSizeSort(id objectA, id objectB, BOOL groups)
203{
204        if(groups)
205        {
206                // Keep groups in manual order (borrowed from ESStatusSort)
207                if ([objectA orderIndex] > [objectB orderIndex])
208                {
209                        return NSOrderedDescending;
210                }
211                else
212                {
213                        return NSOrderedAscending;
214                }
215        }
216       
217        // Get a reference to one and only AILogSizeSort instance.  If this sorting method is being
218        // called, it should always be the case that AILogSizeSort is the active sort controller.
219        AISortController *sortController = [[adium contactController] activeSortController];
220       
221        unsigned long long sizeA = 0;
222        unsigned long long sizeB = 0;
223       
224        if([sortController isMemberOfClass:[AILogSizeSort class]])
225        {
226                sizeA = [(AILogSizeSort *)sortController getCachedLogSize:objectA];
227                sizeB = [(AILogSizeSort *)sortController getCachedLogSize:objectB];
228        }
229        else
230        {
231                sizeA = [AILogSizeSort getContactLogSize:objectA];
232                sizeB = [AILogSizeSort getContactLogSize:objectB];
233        }
234
235        if(sizeB == sizeA)
236        {
237                // Fall back to basic alphabetical sorting in the event of a tie.
238                return [[objectA displayName] caseInsensitiveCompare:[objectB displayName]];
239        }
240        else if(sizeA > sizeB)
241        {
242                // There's a clear winner; run with it.
243                return NSOrderedAscending;
244        }
245        else
246        {
247                return NSOrderedDescending;
248        }
249}
250
251/*!
252 * @brief Sort function
253 */
254- (sortfunc)sortFunction{
255        return &logSizeSort;
256}
257@end
Note: See TracBrowser for help on using the browser.