Develop and Download Open Source Software

Browse Subversion Repository

Annotation of /XspfManager/XspfMRule.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 338 - (hide annotations) (download)
Sun Feb 14 15:18:05 2010 UTC (14 years, 2 months ago) by masaki
Original Path: XspfManager/XspfMRuleEditorRow.m
File size: 16886 byte(s)
[Mod] ライブラリの条件変更に必要な項目を一カ所に集約した。
1 masaki 120 //
2     // XspfMRuleEditorRow.m
3     // XspfManager
4     //
5     // Created by Hori,Masaki on 09/11/29.
6     // Copyright 2009 masakih. All rights reserved.
7     //
8    
9     #import "XspfMRuleEditorRow.h"
10 masaki 170 #import "XspfMRule_private.h"
11 masaki 120
12 masaki 243 #import "XspfMLabelField.h"
13     #import "XspfMLabelCell.h"
14 masaki 120
15 masaki 243
16 masaki 152 @implementation XspfMRule (XspfMAccessor)
17     - (void)setChildren:(NSArray *)newChildren
18 masaki 120 {
19 masaki 158 if(!newChildren) newChildren = [NSMutableArray array];
20 masaki 152
21     [children autorelease];
22 masaki 158 children = [[NSMutableArray alloc] initWithArray:newChildren copyItems:YES];
23 masaki 120 }
24 masaki 152 - (void)addChild:(XspfMRule *)child
25     {
26     [children addObject:child];
27     }
28     - (void)setPredicateParts:(NSDictionary *)parts
29     {
30     [predicateHints autorelease];
31     predicateHints = [parts mutableCopy];
32     }
33     - (void)setExpression:(id)expression forKey:(id)key
34     {
35     [predicateHints setObject:expression forKey:key];
36     }
37     - (void)setValue:(NSString *)newValue
38     {
39 masaki 153 if([_value isEqualToString:newValue]) return;
40 masaki 152
41 masaki 153 [_value autorelease];
42     _value = [newValue copy];
43 masaki 152 }
44 masaki 153 - (NSString *)value { return _value; }
45 masaki 143 @end
46 masaki 152
47     @implementation XspfMRule
48     @dynamic value;
49    
50     - (NSInteger)numberOfChildren
51 masaki 120 {
52 masaki 152 return [children count];
53 masaki 120 }
54 masaki 152 - (id)childAtIndex:(NSInteger)index
55 masaki 120 {
56 masaki 152 return [children objectAtIndex:index];
57     }
58     - (id)displayValueForRuleEditor:(NSRuleEditor *)ruleEditor inRow:(NSInteger)row
59     {
60 masaki 153 return _value;
61 masaki 152 }
62     - (NSDictionary *)predicatePartsWithDisplayValue:(id)displayValue forRuleEditor:(NSRuleEditor *)ruleEditor inRow:(NSInteger)row
63     {
64     id result = [NSMutableDictionary dictionary];
65 masaki 142
66 masaki 152 NSRuleEditorRowType rowType = [ruleEditor rowTypeForRow:row];
67     if(rowType == NSRuleEditorRowTypeCompound) {
68     return predicateHints;
69     }
70    
71     if([predicateHints valueForKey:@"XspfMIgnoreExpression"]) return nil;
72    
73 masaki 166 id operatorType = [predicateHints valueForKey:NSRuleEditorPredicateOperatorType];
74     id option = [predicateHints valueForKey:NSRuleEditorPredicateOptions];
75     id leftExp = [predicateHints valueForKey:NSRuleEditorPredicateLeftExpression];
76     id rightExp = [predicateHints valueForKey:NSRuleEditorPredicateRightExpression];
77 masaki 152 id customRightExp = [predicateHints valueForKey:@"XspfMPredicateRightExpression"];
78    
79     if(operatorType) {
80     [result setValue:operatorType forKey:NSRuleEditorPredicateOperatorType];
81     }
82     if(option) {
83     [result setValue:option forKey:NSRuleEditorPredicateOptions];
84     }
85     if(leftExp) {
86     id exp = nil;
87     if([leftExp isEqual:@"value"]) {
88     exp = [NSExpression expressionForKeyPath:displayValue];
89     } else {
90     exp = [NSExpression expressionForKeyPath:leftExp];
91     }
92     if(exp) {
93     [result setValue:exp forKey:NSRuleEditorPredicateLeftExpression];
94     }
95     }
96     if(rightExp) {
97     SEL selector = NSSelectorFromString(rightExp);
98     id exp = nil;
99     if(selector) {
100     exp = [NSExpression expressionForConstantValue:[displayValue performSelector:selector]];
101     } else {
102     exp = [NSExpression expressionForConstantValue:rightExp];
103     }
104     if(exp) {
105     [result setValue:exp forKey:NSRuleEditorPredicateRightExpression];
106     }
107     }
108     if(customRightExp) {
109     SEL selector = NSSelectorFromString(customRightExp);
110     id arg01 = [predicateHints valueForKey:@"XspfMRightExpressionArg01"];
111     id arg02 = [predicateHints valueForKey:@"XspfMRightExpressionArg02"];
112    
113    
114     if(arg02 && arg01) {
115     if([arg01 isEqual:@"displayValues"]) {
116     arg01 = [ruleEditor displayValuesForRow:row];
117     }
118     if([arg02 isEqual:@"displayValues"]) {
119     arg02 = [ruleEditor displayValuesForRow:row];
120     }
121 masaki 163 id rhs = [self performSelector:selector withObject:arg01 withObject:arg02];
122     [result setValue:rhs forKey:NSRuleEditorPredicateRightExpression];
123 masaki 152 } else if(arg01) {
124     if([arg01 isEqual:@"displayValues"]) {
125     arg01 = [ruleEditor displayValuesForRow:row];
126     }
127 masaki 163 id rhs = [self performSelector:selector withObject:arg01];
128     [result setValue:rhs forKey:NSRuleEditorPredicateRightExpression];
129 masaki 152 } else {
130 masaki 163 id rhs = [self performSelector:selector];
131     [result setValue:rhs forKey:NSRuleEditorPredicateRightExpression];
132 masaki 152 }
133     }
134    
135 masaki 163 NSString *selName = [predicateHints valueForKey:@"XspfMCustomSelector"];
136     if(selName) {
137 masaki 154 id args = nil;
138     NSString *argSelName = [predicateHints valueForKey:@"XspfMCustomSelectorArgumentsCteator"];
139     if(argSelName) {
140     SEL argSel = NSSelectorFromString(argSelName);
141     id argSelArg01 = [predicateHints valueForKey:@"XspfMCustomSelectorArgumentsCteatorArg01"];
142     if([argSelArg01 isEqual:@"displayValues"]) {
143     argSelArg01 = [ruleEditor displayValuesForRow:row];
144     }
145     id argSelArg02 = [predicateHints valueForKey:@"XspfMCustomSelectorArgumentsCteatorArg02"];
146     if([argSelArg02 isEqual:@"displayValues"]) {
147     argSelArg02 = [ruleEditor displayValuesForRow:row];
148     }
149     if(argSelArg02) {
150     args = [self performSelector:argSel withObject:argSelArg01 withObject:argSelArg02];
151     } else if(argSelArg01) {
152     args = [self performSelector:argSel withObject:argSelArg01];
153     } else {
154     args = [self performSelector:argSel];
155     }
156     } else {
157     id arg01 = [predicateHints valueForKey:@"XspfMCustomSelectorArg01"];
158     args = [NSArray arrayWithObjects:[NSExpression expressionForConstantValue:arg01], nil];
159     }
160 masaki 152
161 masaki 165 id target = [NSExpression expressionForConstantValue:[[[[self class] alloc] init] autorelease]];
162     id rhs = [NSExpression expressionForFunction:target selectorName:selName arguments:args];
163 masaki 166 [result setValue:rhs forKey:NSRuleEditorPredicateRightExpression];
164 masaki 152 }
165    
166 masaki 187 // HMLog(HMLogLevelDebug, @"predicate\tcriterion -> %@, value -> %@, row -> %d, result -> %@", predicateHints, displayValue, row, result);
167 masaki 152
168     return result;
169 masaki 142 }
170 masaki 152
171 masaki 153 - (id)displayValue { return _value; }
172 masaki 120
173 masaki 338
174     #pragma mark#### Variables for add/change criteria of Library. ####
175     + (NSArray *)leftKeys
176     {
177     static NSArray *leftKeys = nil;
178     if(!leftKeys) {
179     leftKeys = [[NSArray arrayWithObjects:@"title",
180     @"lastPlayDate", @"modificationDate", @"creationDate",
181     @"rating",
182     @"label",
183     @"information.voiceActorsList",
184     @"information.productsList",
185     nil] retain];
186     }
187     return leftKeys;
188     }
189     static inline NSArray *dateKeys()
190     {
191     static NSArray *dateKeys = nil;
192     if(!dateKeys) {
193     dateKeys = [[NSArray arrayWithObjects:@"lastPlayDate", @"modificationDate", @"creationDate", nil] retain];
194     }
195     return dateKeys;
196     }
197     static inline BOOL isDateKeyPath(NSString *keyPath)
198     {
199     return [dateKeys() containsObject:keyPath];
200     }
201     - (BOOL)isDateKeyPath:(NSString *)keyPath
202     {
203     return isDateKeyPath(keyPath);
204     }
205     - (BOOL)isStringKeyPath:(NSString *)keyPath
206     {
207     static NSArray *stringExpressionLeftKeys = nil;
208     if(!stringExpressionLeftKeys) {
209     stringExpressionLeftKeys = [[NSArray arrayWithObjects:
210     @"title",
211     @"information.voiceActorsList",
212     @"information.productsList",
213     nil] retain];
214     }
215     return [stringExpressionLeftKeys containsObject:keyPath];
216     }
217     - (BOOL)isRateKeyPath:(NSString *)keyPath
218     {
219     return [keyPath isEqualToString:@"rating"];
220     }
221     - (BOOL)isLabelKeyPath:(NSString *)keyPath
222     {
223     return [keyPath isEqualToString:@"label"];
224     }
225     + (NSString *)templateKeyForLeftKeyPath:(NSString *)leftKeypath
226     {
227     NSString *key = nil;
228     if([leftKeypath isEqualToString:@"title"]) {
229     key = @"String";
230     } else if([leftKeypath isEqualToString:@"rating"]) {
231     key = @"Rate";
232     } else if(isDateKeyPath(leftKeypath)) {
233     key = @"AbDate";
234     } else if([leftKeypath isEqualToString:@"label"]) {
235     key = @"Label";
236     } else if([leftKeypath isEqualToString:@"information.voiceActorsList"]) {
237     key = @"VoiceActors";
238     } else if([leftKeypath isEqualToString:@"information.productsList"]) {
239     key = @"Products";
240     }
241    
242     return key;
243     }
244     - (NSString *)templateKeyForLeftKeyPath:(NSString *)leftKeypath
245     {
246     return [[self class] templateKeyForLeftKeyPath:leftKeypath];
247     }
248    
249 masaki 164 #pragma mark == NSCopying Protocol ==
250 masaki 152 - (id)copyWithZone:(NSZone *)zone
251 masaki 142 {
252 masaki 152 XspfMRule *result = [[[self class] allocWithZone:zone] init];
253 masaki 158 [result setChildren:children];
254     [result setPredicateParts:predicateHints];
255     [result setValue:_value];
256 masaki 152
257     return result;
258 masaki 142 }
259 masaki 152
260 masaki 164 #pragma mark == NSCoding Protocol ==
261     static NSString *const XspfMRuleChildrenKey = @"XspfMRuleChildrenKey";
262     static NSString *const XspfMRulePredicateHintsKey = @"XspfMRulePredicateHintsKey";
263     static NSString *const XspfMRuleValueKey = @"XspfMRuleValueKey";
264     - (id)initWithCoder:(NSCoder *)decoder
265     {
266     self = [self init];
267    
268     [self setChildren:[decoder decodeObjectForKey:XspfMRuleChildrenKey]];
269     [self setPredicateParts:[decoder decodeObjectForKey:XspfMRulePredicateHintsKey]];
270     [self setValue:[decoder decodeObjectForKey:XspfMRuleValueKey]];
271    
272     return self;
273     }
274     - (void)encodeWithCoder:(NSCoder *)encoder
275     {
276     [encoder encodeObject:children forKey:XspfMRuleChildrenKey];
277     [encoder encodeObject:predicateHints forKey:XspfMRulePredicateHintsKey];
278     [encoder encodeObject:_value forKey:XspfMRuleValueKey];
279     }
280    
281 masaki 152 - (BOOL)isEqual:(id)other
282 masaki 120 {
283 masaki 152 if([super isEqual:other]) return YES;
284     if(![other isKindOfClass:[XspfMRule class]]) return NO;
285 masaki 120
286 masaki 152 XspfMRule *o = other;
287 masaki 153 if(![_value isEqualToString:o->_value]) return NO;
288 masaki 152 // if(![children isEqual:o->children]) return NO;
289     // if(![predicateHints isEqual:o->predicateHints]) return NO;
290    
291 masaki 120 return YES;
292     }
293 masaki 152 - (NSUInteger)hash
294     {
295 masaki 153 return _value ? [_value hash] : [super hash];
296 masaki 152 }
297    
298 masaki 120 - (id)description
299     {
300 masaki 152 return [NSString stringWithFormat:@"%@ {\n\t%@ = %@;\n\t%@ = %@;\n\t%@ = %@;}",
301     NSStringFromClass([self class]),
302 masaki 153 @"value", _value,
303 masaki 152 @"hints", predicateHints,
304     @"children", children,
305     nil];
306 masaki 120 }
307     @end
308    
309 masaki 152 @implementation XspfMRule (XspfMCreation)
310 masaki 120
311 masaki 152 - (id)init
312 masaki 120 {
313 masaki 152 [super init];
314    
315     children = [[NSMutableArray array] retain];
316     predicateHints = [[NSMutableDictionary dictionary] retain];
317    
318     return self;
319 masaki 120 }
320 masaki 152
321     - (id)initWithValue:(NSString *)newValue children:(NSArray *)newChildren predicateHints:(NSDictionary *)parts
322 masaki 120 {
323 masaki 152 [self init];
324 masaki 120
325 masaki 152 if([newValue isEqualToString:@"separator"]) {
326     [self release];
327     return [[XspfMSeparatorRule alloc] initSparetorRule];
328     }
329    
330 masaki 170 id fieldRule = [XspfMFieldRule fieldRuleWithValue:newValue];
331     if(fieldRule) {
332 masaki 152 [self release];
333 masaki 170 self = [fieldRule retain];
334 masaki 152 }
335    
336     [self setValue:newValue];
337     [self setChildren:newChildren];
338     [self setPredicateParts:parts];
339    
340 masaki 120 return self;
341     }
342 masaki 152 + (id)ruleWithValue:(NSString *)newValue children:(NSArray *)newChildren predicateHints:(NSDictionary *)parts
343 masaki 120 {
344 masaki 152 return [[[self alloc] initWithValue:newValue children:newChildren predicateHints:parts] autorelease];
345 masaki 120 }
346 masaki 152
347     + (NSArray *)compoundRule
348 masaki 120 {
349 masaki 152 id comp = [self ruleWithValue:@"of the following are true" children:nil predicateHints:[NSDictionary dictionary]];
350    
351     id allExp = [NSNumber numberWithUnsignedInt:NSAndPredicateType];
352     id all = [self ruleWithValue:@"All"
353     children:[NSArray arrayWithObject:comp]
354     predicateHints:[NSDictionary dictionaryWithObject:allExp forKey:NSRuleEditorPredicateCompoundType]];
355    
356     id anyExp = [NSNumber numberWithUnsignedInt:NSOrPredicateType];
357     id any = [self ruleWithValue:@"Any"
358     children:[NSArray arrayWithObject:comp]
359     predicateHints:[NSDictionary dictionaryWithObject:anyExp forKey:NSRuleEditorPredicateCompoundType]];
360    
361     return [NSArray arrayWithObjects:all, any, nil];
362 masaki 120 }
363 masaki 152
364     - (NSDictionary *)predicateHintsWithPlist:(NSDictionary *)plist
365 masaki 120 {
366 masaki 152 NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:plist];
367     [result removeObjectForKey:@"criteria"];
368     [result removeObjectForKey:@"value"];
369    
370     return result;
371 masaki 120 }
372 masaki 152
373     + (id)ruleWithPlist:(id)plist
374 masaki 120 {
375 masaki 152 return [[[self alloc] initWithPlist:plist] autorelease];
376 masaki 120 }
377 masaki 152 - (id)initWithPlist:(id)plist
378 masaki 120 {
379 masaki 152 if(![plist isKindOfClass:[NSDictionary class]]) {
380     [self init];
381     [self release];
382     return nil;
383     }
384    
385     id pValue = [plist valueForKey:@"value"];
386     id criteria = [plist valueForKey:@"criteria"];
387     id pChildren = [NSMutableArray array];
388     for(id criterion in criteria) {
389     id c = [[self class] ruleWithPlist:criterion];
390     if(c) [pChildren addObject:c];
391     }
392     id hints = [self predicateHintsWithPlist:plist];
393    
394     return [self initWithValue:pValue children:pChildren predicateHints:hints];
395 masaki 120 }
396 masaki 152
397     - (void)dealloc
398 masaki 120 {
399 masaki 152 [children release];
400     [predicateHints release];
401 masaki 153 [_value release];
402 masaki 152
403     [super dealloc];
404 masaki 120 }
405 masaki 152
406 masaki 120 @end
407    
408 masaki 153 @implementation XspfMRule (XspfMPrivate)
409 masaki 142
410 masaki 153 - (NSView *)textField
411     {
412     id text = [[[NSTextField alloc] initWithFrame:NSMakeRect(0,0,100,19)] autorelease];
413     [[text cell] setControlSize:NSSmallControlSize];
414     [text setFont:[NSFont controlContentFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
415     [text setStringValue:@"1234567890"];
416     [text sizeToFit];
417     [text setStringValue:@""];
418     [text setDelegate:self];
419    
420     return text;
421     }
422     - (NSView *)datePicker
423     {
424     id date = [[[NSDatePicker alloc] initWithFrame:NSMakeRect(0,0,100,19)] autorelease];
425     [[date cell] setControlSize:NSSmallControlSize];
426     [date setFont:[NSFont controlContentFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
427     [date setDatePickerElements:NSYearMonthDayDatePickerElementFlag];
428     [date setDrawsBackground:YES];
429     [date setDateValue:[NSDate dateWithTimeIntervalSinceNow:0.0]];
430     [date sizeToFit];
431     [date setDelegate:self];
432    
433     return date;
434     }
435     - (NSView *)ratingIndicator
436     {
437     id rate = [[[NSLevelIndicator alloc] initWithFrame:NSMakeRect(0,0,100,19)] autorelease];
438     id cell = [rate cell];
439     [cell setControlSize:NSSmallControlSize];
440     [rate setFont:[NSFont controlContentFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
441     [rate setMinValue:0];
442     [rate setMaxValue:5];
443     [cell setLevelIndicatorStyle:NSRatingLevelIndicatorStyle];
444     [cell setEditable:YES];
445     [rate sizeToFit];
446    
447     return rate;
448     }
449     - (NSView *)numberField
450     {
451     id text = [[[NSTextField alloc] initWithFrame:NSMakeRect(0,0,100,19)] autorelease];
452     [[text cell] setControlSize:NSSmallControlSize];
453     [text setFont:[NSFont controlContentFontOfSize:[NSFont systemFontSizeForControlSize:NSSmallControlSize]]];
454     [text setStringValue:@"123"];
455     NSNumberFormatter *formatter = [[[NSNumberFormatter alloc] init] autorelease];
456     [formatter setNumberStyle:NSNumberFormatterDecimalStyle];
457     [formatter setMinimum:[NSNumber numberWithInt:0]];
458     [text setFormatter:formatter];
459     [text sizeToFit];
460     [text setStringValue:@"1"];
461     [text setDelegate:self];
462    
463     return text;
464     }
465 masaki 243 - (NSView *)labelField
466     {
467     HMLog(HMLogLevelDebug, @"Enter -> %@", NSStringFromSelector(_cmd));
468     id label = [[[XspfMLabelField alloc] initWithFrame:NSMakeRect(0,0,100,19)] autorelease];
469     [label sizeToFit];
470 masaki 251 [label setLabelStyle:XspfMSquareStyle];
471     [label setDrawX:YES];
472 masaki 243
473     return label;
474     }
475 masaki 153 @end
476 masaki 152 @implementation XspfMRule (XspfMExpressionBuilder)
477 masaki 156 - (NSArray *)twoNumberAndUnitArgs:(NSArray *)displayValues
478     {
479     id value03 = [displayValues objectAtIndex:2];
480     id arg01 = [NSNumber numberWithInt:[[value03 objectValue] intValue]];
481    
482     id value05 = [displayValues objectAtIndex:4];
483     id arg02 = [NSNumber numberWithInt:[[value05 objectValue] intValue]];
484    
485     id value06 = [displayValues objectAtIndex:5];
486     id arg03 = nil;
487     if([value06 isEqualToString:@"Days"]) {
488 masaki 163 arg03 = [NSNumber numberWithInt:XspfMDaysUnitType];
489 masaki 156 } else if([value06 isEqualToString:@"Weeks"]) {
490 masaki 163 arg03 = [NSNumber numberWithInt:XpsfMWeeksUnitType];
491 masaki 156 } else if([value06 isEqualToString:@"Months"]) {
492 masaki 163 arg03 = [NSNumber numberWithInt:XspfMMonthsUnitType];
493 masaki 156 } else if([value06 isEqualToString:@"Years"]) {
494 masaki 163 arg03 = [NSNumber numberWithInt:XspfMYearsUnitType];
495 masaki 156 }
496    
497     if([arg01 compare:arg02] == NSOrderedDescending) {
498     id t = arg01;
499     arg01 = arg02;
500     arg02 = t;
501     }
502    
503     return [NSArray arrayWithObjects:[NSExpression expressionForConstantValue:arg01],
504     [NSExpression expressionForConstantValue:arg02],
505     [NSExpression expressionForConstantValue:arg03],
506     nil];
507     }
508 masaki 154 - (NSArray *)numberAndUnitArgs:(NSArray *)displayValues
509     {
510     id value03 = [displayValues objectAtIndex:2];
511     id arg01 = [NSNumber numberWithInt:[[value03 objectValue] intValue]];
512    
513     id value04 = [displayValues objectAtIndex:3];
514     id arg02 = nil;
515     if([value04 isEqualToString:@"Days"]) {
516 masaki 163 arg02 = [NSNumber numberWithInt:XspfMDaysUnitType];
517 masaki 154 } else if([value04 isEqualToString:@"Weeks"]) {
518 masaki 163 arg02 = [NSNumber numberWithInt:XpsfMWeeksUnitType];
519 masaki 154 } else if([value04 isEqualToString:@"Months"]) {
520 masaki 163 arg02 = [NSNumber numberWithInt:XspfMMonthsUnitType];
521 masaki 154 } else if([value04 isEqualToString:@"Years"]) {
522 masaki 163 arg02 = [NSNumber numberWithInt:XspfMYearsUnitType];
523 masaki 154 }
524    
525     return [NSArray arrayWithObjects:[NSExpression expressionForConstantValue:arg01],
526     [NSExpression expressionForConstantValue:arg02], nil];
527     }
528 masaki 152 - (NSExpression *)rangeDateFromDisplayValues:(NSArray *)displayValues
529     {
530     id field01 = nil;
531     id field02 = nil;
532    
533     Class datepickerclass = [NSDatePicker class];
534     for(id v in displayValues) {
535     if([v isKindOfClass:datepickerclass]) {
536     if([v tag] == XspfMPrimaryDateFieldTag) {
537     field01 = v;
538     } else {
539     field02 = v;
540     }
541     }
542 masaki 142 }
543    
544 masaki 152 if(!field01 || !field02) return nil;
545    
546     id value01, value02;
547     value01 = [field01 dateValue]; value02 = [field02 dateValue];
548     if([value01 compare:value02] == NSOrderedDescending) {
549     id t = value02;
550     value02 = value01;
551     value01 = t;
552 masaki 142 }
553    
554 masaki 152 id expression01, expression02;
555     expression01 = [NSExpression expressionForConstantValue:value01];
556     expression02 = [NSExpression expressionForConstantValue:value02];
557    
558     return [NSExpression expressionForAggregate:[NSArray arrayWithObjects:expression01, expression02, nil]];
559 masaki 142 }
560 masaki 120 @end
561 masaki 152

Back to OSDN">Back to OSDN
ViewVC Help
Powered by ViewVC 1.1.26