File: AdunElementSelection.m

package info (click to toggle)
adun.app 0.81-13
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 16,600 kB
  • sloc: objc: 70,912; ansic: 6,662; yacc: 394; python: 75; cpp: 36; makefile: 33; xml: 15; awk: 3
file content (424 lines) | stat: -rw-r--r-- 13,428 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
/*
 Project: Adun
 
 Copyright (C) 2008 Michael Johnston & Jordi Villa-Freixa
 
 Author: Michael Johnston
 
 Created: 2008-07-23 by michael johnston
 
 This application is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public
 License as published by the Free Software Foundation; either
 version 2 of the License, or (at your option) any later version.
 
 This application is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 Library General Public License for more details.
 
 You should have received a copy of the GNU General Public
 License along with this library; if not, write to the Free
 Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
*/
#include <AdunKernel/AdunElementSelection.h>
#include <AdunKernel/AdIndexSetConversions.h>

//Keys of a specifier dictionary.
const NSString* AdStringSpecifier = @"AdStringSpecifier";
const NSString* AdIndexSpecifier = @"AdIndexSpecifier";
const NSString* AdSpecifierType = @"AdSpecifierType";
const NSString* AdSpecifierData = @"AdSpecifierData";

//Keys of the dictionary that is passed to the designated initialiser
const NSString* AdGroupCategory = @"AdGroupCategory";
const NSString* AdGroupSubcategory = @"AdGroupSubcategory";
const NSString* AdElementCategory = @"AdElementCategory";
const NSString* AdElementSubcategory = @"AdElementSubcategory";

@implementation NSString (AdElementSelectionExtensions)

- (NSRange) rangeOfCharactersFromSet: (NSCharacterSet*) characterSet
{
	unsigned int i;
	NSRange range;
	
	//First check if any of the characters are present
	range = [self rangeOfCharacterFromSet: characterSet];
	if(range.location != NSNotFound)
	{
		for(i=range.location+1; i<[self length]; i++)
		{
			if(![characterSet characterIsMember: [self characterAtIndex: i]])
				break;
		}
		
		range.length = i - range.location;
	}
		
	return range;
}

@end

@implementation AdElementSelection

- (NSMutableDictionary*) _processSpecifier: (NSString*) specifier
{
	int location, length;
	NSRange range;
	NSCharacterSet *integerRangeSet, *alphanumericSet;
	NSArray* indices;
	NSIndexSet* indexSet;
	NSMutableDictionary* resultsDict = [NSMutableDictionary dictionary];
	
	integerRangeSet = [NSCharacterSet characterSetWithCharactersInString: @"1234567890-"];
	alphanumericSet = [NSCharacterSet alphanumericCharacterSet];
	
	range = NSMakeRange(0,[specifier length]);
	
	//Specifiers can either be integer ranges or alphanumeric
	//First check is it a range specifier then a string specifier
	if(NSEqualRanges(range, [specifier rangeOfCharactersFromSet: integerRangeSet]))
	{
		indices = [specifier componentsSeparatedByString: @"-"];
		if([indices count] > 2)
		{
			[NSException raise: NSInvalidArgumentException
				    format: @"Invalid range specifier - %@", specifier];
		}
		else
		{
			if([indices count] == 1)
			{
				indexSet = [NSIndexSet indexSetFromArray: indices];
			}
			else
			{
				location = [[indices objectAtIndex:0] intValue];
				length = [[indices objectAtIndex:1] intValue] - location;
				range = NSMakeRange(location, length);
				indexSet = [NSIndexSet indexSetWithIndexesInRange: range];
			}
			
			[resultsDict setObject: AdIndexSpecifier forKey: AdSpecifierType];
			[resultsDict setObject: indexSet forKey: AdSpecifierData];
		}
	}
	else if(NSEqualRanges(range, [specifier rangeOfCharactersFromSet: alphanumericSet]))
	{
		[resultsDict setObject: AdStringSpecifier forKey: AdSpecifierType];
		[resultsDict setObject: specifier forKey: AdSpecifierData];
	}
	else
	{
		[NSException raise: NSInvalidArgumentException
			    format: @"Specifier - %@ - contains invalid characters", specifier];
	}
	
	return resultsDict;
}

+ (id) biomolecularSelectionWithString: (NSString*) aString
{
	NSDictionary* dictionary;

	dictionary = [NSDictionary dictionaryWithObjectsAndKeys:
				@"Residue Name", @"AdGroupCategory",
				@"PDBName", @"AdElementCategory", nil];
				
	return [[[AdElementSelection alloc] 
			initWithSelectionCategories: dictionary 
			selectionString: aString] autorelease];
}

- (id) init
{
	return [self initWithSelectionCategories: nil selectionString: nil];
}

- (id) initWithSelectionCategories: (NSDictionary*) categories
		selectionString: (NSString*) aString
{
	if(self == [super init])
	{	
		groupCategory = [categories objectForKey: AdGroupCategory];
		elementCategory = [categories objectForKey: AdElementCategory];
		specifierDict = [NSMutableDictionary new];
		
		if(groupCategory == nil || elementCategory == nil)
		{
			[self release];
			[NSException raise: NSInvalidArgumentException
				format: @"Missing required field in category dictionary %@", categories];
		}

		if(aString != nil)
			[self setSelectionString: aString];
	}
	
	return self;
}

- (void) dealloc
{
	[selectionString release];
	[specifierDict release];
	[super dealloc];
}

- (void) setSelectionString: (NSString*) aString
{
	int count;
	NSCharacterSet* categorySet;
	NSScanner* categoryScanner;
	NSMutableDictionary *result;
	NSString *categoryString, *categoryCode;
	NSArray *specifiers, *splitSpecifier;
	NSMutableArray* processedSpecifiers;
	NSEnumerator* specifierEnum;
	id specifier, subspecifier;
	
	[specifierDict removeAllObjects];
	
	categorySet = [NSCharacterSet characterSetWithCharactersInString: @":@"];
	categoryScanner = [NSScanner scannerWithString: aString];
	
	//Scan up to the first category character - this must be either : or @
	//Only two categories are possible 
	count = 0;
	while(![categoryScanner isAtEnd] && count < 2)
	{
		//Check the string begins with a category code
		if(![categoryScanner scanCharactersFromSet: categorySet intoString: &categoryCode])
		{
			[NSException raise: NSInvalidArgumentException
				format: @"Invalid initial character - %c", [aString characterAtIndex: 0]];
		}
		
		NSDebugLLog(@"AdElementSelection", @"Count %d. Category Code %@", count, categoryCode);
		
		//Scan everything for this category into a string
		//If nothing is provided this is an error
		if([categoryScanner scanUpToCharactersFromSet: categorySet 
			intoString: &categoryString])
		{	
			NSDebugLLog(@"AdElementSelection", @"Category String %@", categoryString);
			//If we are here then categoryString has some characters in it
			//Split it by commas - then check each specifier for validity
			specifiers = [categoryString componentsSeparatedByString: @","];
			processedSpecifiers = [NSMutableArray array];
			specifierEnum = [specifiers objectEnumerator];
			while((specifier = [specifierEnum nextObject]))
			{
				//Check if there are subcategories
				splitSpecifier = [specifier componentsSeparatedByString: @"."];
				if([splitSpecifier count] == 1)
				{
					//No subcategories
					result = [self _processSpecifier: specifier];
					[processedSpecifiers addObject: result];
				}
				else if([splitSpecifier count] == 2)
				{
					//Subcategory
					result = [self _processSpecifier: [splitSpecifier objectAtIndex: 0]];
					subspecifier = [self _processSpecifier: [splitSpecifier objectAtIndex: 1]];
					[result setObject: subspecifier forKey: @"AdSubcategorySpecifier"];
					[processedSpecifiers addObject: result];
				}
				else
				{
					//Error - To many '.'
					[NSException raise: NSInvalidArgumentException
						format: @"Invalid specifier - %@ - Only one subcategory may be specified", 
						specifier];
				}
			}
			
			[specifierDict setObject: processedSpecifiers forKey: categoryCode];
			
			NSDebugLLog(@"AdElementSelection", @"SpecifierDict %@", specifierDict);
		}
		else
		{	
			[NSException raise: NSInvalidArgumentException
				format: @"Specifier string invalid - category present with no specifiers"];
		}
		
		categoryCode = nil;
		categoryString = nil;
		count++;
	}
	
	//Retain the selection string if we get this far
	selectionString = [aString copy];
	
	//FIXME: Consolidate ...
	//Combine index and string specifiers in a category if they have no subcategories.
	
	NSDebugLLog(@"AdElementSelection", @"Parsing done");
}

- (NSString*) selectionString
{
	return [[selectionString retain] autorelease];
}

- (NSMutableIndexSet*) _rowsInMatrix: (AdDataMatrix*) aMatrix
			matchingSpecifiers: (NSArray*) specifiers
			categoryColumn: (NSString*) categoryColumn
			subcategoryColumn: (NSString*) subcategoryColumn
			restrictToRows: (NSIndexSet*) restrictRows
{
	unsigned int i, numberRows, index;
	NSUInteger *buffer;
	NSRange matrixRange;
	NSString *name;
	NSArray *types, *categoryData;
	NSMutableArray* stringSpecifiers = [NSMutableArray array];
	NSMutableIndexSet *selectedRows = [NSMutableIndexSet indexSet];
	NSMutableIndexSet *nonSelectedRows, *ignoreRows;
	NSDictionary* stringSpecifier;
	NSEnumerator* specifierEnum;
	id type, data;
		
	matrixRange = NSMakeRange(0, [aMatrix numberOfRows]);
	nonSelectedRows = [NSMutableIndexSet indexSetWithIndexesInRange: matrixRange];
	
	//If no specifier is given create one which selects every row in the matrix
	if(specifiers == nil || [specifiers count] == 0)
	{
		specifiers = [NSArray arrayWithObject:
				[NSDictionary dictionaryWithObjectsAndKeys:
					AdIndexSpecifier, AdSpecifierType,
					[[nonSelectedRows copy] autorelease], AdSpecifierData,
					nil]];
	}

	//restrictRows contains the indexes of the rows of the matrix
	//that the filtering should be restricted to.
	//ignoreRows is the inverse of this index set.
	if(restrictRows == nil)
	{
		ignoreRows = [NSMutableIndexSet indexSet];
	}	
	else	
	{	
		ignoreRows = [[nonSelectedRows mutableCopy] autorelease];
		[ignoreRows removeIndexes: restrictRows];
	}
	
	//Identify the AdIndexSpecifier's and collect those indexes into the variable 'indexes'.
	//Remove those indexes from nonSelectedIndexes.
	//This last part is done so the selected indexes won't be
	//searched during the string search.
	types = [specifiers valueForKey: (NSString*)AdSpecifierType];
	for(i=0; i<[types count]; i++)
	{
		type = [types objectAtIndex: i];
		data = [specifiers objectAtIndex: i];
		if([type isEqual: AdIndexSpecifier])
		{
			[selectedRows addIndexes: 
				[data objectForKey: AdSpecifierData]];
			[nonSelectedRows removeIndexes: 
				[data objectForKey: AdSpecifierData]];
		}
		else
		{
			[stringSpecifiers addObject: data];
		}
	}
	
	NSDebugLLog(@"AdElementSelection", @"Selected rows %@", selectedRows);
	
	//Remove from selectedRows any indexes which are in ignoreRows
	//Do the same for nonSelectedRows
	[selectedRows removeIndexes: ignoreRows];
	[nonSelectedRows removeIndexes: ignoreRows];
				
	//Put all non selected indexes into a buffer for speed
	numberRows = [nonSelectedRows count];
	buffer = [[AdMemoryManager appMemoryManager] 
			allocateArrayOfSize: numberRows*sizeof(NSUInteger)];
	[nonSelectedRows getIndexes: buffer maxCount: numberRows inIndexRange: NULL];
	
	//Iterate through the string specifiers and find which of the groups match them.
	//FIXME: Invert iteration
	categoryData = [aMatrix columnWithHeader: categoryColumn];
	specifierEnum = [stringSpecifiers objectEnumerator];
	while((stringSpecifier = [specifierEnum nextObject]))
	{
		name = [stringSpecifier objectForKey: AdSpecifierData];
		name = [name uppercaseString];
		for(i=0; i<numberRows; i++)
		{
			index = buffer[i];
			if([[categoryData objectAtIndex: index] isEqual: name])
				[selectedRows addIndex: index];	
		}
	}
	
	[[AdMemoryManager appMemoryManager] freeArray: buffer];
	
	return selectedRows;
}

- (NSIndexSet*) matchingGroupsInDataSource: (AdDataSource*) dataSource
{
	AdDataMatrix* groupProperties;

	groupProperties = [dataSource groupProperties];
	NSDebugLLog(@"AdElementSelection", @"Specifiers %@", specifierDict);
	return [self _rowsInMatrix: groupProperties 
			matchingSpecifiers: [specifierDict objectForKey: @":"]
			categoryColumn: groupCategory
			subcategoryColumn: nil 
			restrictToRows: nil];
}

- (NSIndexSet*) matchingElementsInDataSource: (AdDataSource*) dataSource
{
	int i, numberResidues;
	NSRange range;
	NSArray *elementsPerResidue;
	NSIndexSet *groupIndexes;
	NSMutableIndexSet *elementIndexes = [NSMutableIndexSet indexSet];
	AdDataMatrix *groupProperties, *elementProperties;

	groupIndexes = [self matchingGroupsInDataSource: dataSource];
		
	groupProperties = [dataSource groupProperties];
	numberResidues = [groupProperties numberOfRows];
	elementProperties = [dataSource elementProperties];
	range = NSMakeRange(0,0);
	
	/*
	 * FIXME: Need to have a column containing ranges
	 * That is need to have a column that defines the mapping between elements and groups.
	 * 'Atoms' will only work for groups which subdivide all the elements
	 * and only contain contiguous elements.
	 * However this also requires that multiple types of group properties matrix exist
	 * and the one to use can be specified (since Atoms is okay for the only current 
	 * type of group matrix).
	 */
	elementsPerResidue = [groupProperties columnWithHeader: @"Atoms"];
	for(i=0; i<numberResidues; i++)
	{
		range.location = NSMaxRange(range);
		range.length = [[elementsPerResidue objectAtIndex: i] intValue];
		if([groupIndexes containsIndex: i])
			[elementIndexes addIndexesInRange: range];
	}
		
	elementIndexes = [self _rowsInMatrix: elementProperties
				matchingSpecifiers: [specifierDict objectForKey: @"@"]
				categoryColumn: elementCategory
				subcategoryColumn: nil
				restrictToRows: elementIndexes];
				
	return elementIndexes;
}

@end