Line data Source code
1 0 : /* 2 : 3 : OOAIStateMachineVerifierStage.m 4 : 5 : 6 : Oolite 7 : Copyright (C) 2004-2013 Giles C Williams and contributors 8 : 9 : This program is free software; you can redistribute it and/or 10 : modify it under the terms of the GNU General Public License 11 : as published by the Free Software Foundation; either version 2 12 : of the License, or (at your option) any later version. 13 : 14 : This program is distributed in the hope that it will be useful, 15 : but WITHOUT ANY WARRANTY; without even the implied warranty of 16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 : GNU General Public License for more details. 18 : 19 : You should have received a copy of the GNU General Public License 20 : along with this program; if not, write to the Free Software 21 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, 22 : MA 02110-1301, USA. 23 : 24 : */ 25 : 26 : #import "OOAIStateMachineVerifierStage.h" 27 : #import "OOCollectionExtractors.h" 28 : #import "OOPListParsing.h" 29 : 30 : #if OO_OXP_VERIFIER_ENABLED 31 : 32 : #import "ResourceManager.h" 33 : 34 0 : static NSString * const kStageName = @"Validating AIs"; 35 : 36 : 37 : @interface OOAIStateMachineVerifierStage (Private) 38 : 39 0 : - (void) validateAI:(NSString *)aiName; 40 : 41 : @end 42 : 43 : 44 : @implementation OOAIStateMachineVerifierStage 45 : 46 0 : - (void) dealloc 47 : { 48 : [_whitelist release]; 49 : [_usedAIs release]; 50 : 51 : [super dealloc]; 52 : } 53 : 54 : 55 0 : - (NSString *) name 56 : { 57 : return kStageName; 58 : } 59 : 60 : 61 0 : - (BOOL) shouldRun 62 : { 63 : return [_usedAIs count] != 0; 64 : } 65 : 66 : 67 0 : - (void) run 68 : { 69 : NSArray *aiNames = nil; 70 : NSEnumerator *aiEnum = nil; 71 : NSString *aiName = nil; 72 : NSMutableSet *whitelist = nil; 73 : 74 : // Build whitelist. Note that we merge in aliases since the distinction doesn't matter when just validating. 75 : whitelist = [[NSMutableSet alloc] init]; 76 : [whitelist addObjectsFromArray:[[ResourceManager whitelistDictionary] oo_arrayForKey:@"ai_methods"]]; 77 : [whitelist addObjectsFromArray:[[ResourceManager whitelistDictionary] oo_arrayForKey:@"ai_and_action_methods"]]; 78 : [whitelist addObjectsFromArray:[[[ResourceManager whitelistDictionary] oo_dictionaryForKey:@"ai_method_aliases"] allKeys]]; 79 : _whitelist = [whitelist copy]; 80 : [whitelist release]; 81 : 82 : aiNames = [[_usedAIs allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)]; 83 : for (aiEnum = [aiNames objectEnumerator]; (aiName = [aiEnum nextObject]); ) 84 : { 85 : [self validateAI:aiName]; 86 : } 87 : 88 : [_whitelist release]; 89 : _whitelist = nil; 90 : } 91 : 92 : 93 : + (NSString *) nameForReverseDependencyForVerifier:(OOOXPVerifier *)verifier 94 : { 95 : return kStageName; 96 : } 97 : 98 : 99 : - (void) stateMachineNamed:(NSString *)name usedByShip:(NSString *)shipName 100 : { 101 : OOFileScannerVerifierStage *fileScanner = nil; 102 : 103 : if (name == nil) return; 104 : if ([_usedAIs containsObject:name]) return; 105 : if (_usedAIs == nil) _usedAIs = [[NSMutableSet alloc] init]; 106 : [_usedAIs addObject:name]; 107 : 108 : fileScanner = [[self verifier] fileScannerStage]; 109 : if (![fileScanner fileExists:name 110 : inFolder:@"AIs" 111 : referencedFrom:[NSString stringWithFormat:@"shipdata.plist entry \"%@\"", shipName] 112 : checkBuiltIn:YES]) 113 : { 114 : OOLog(@"verifyOXP.validateAI.notFound", @"----- WARNING: AI state machine \"%@\" referenced in shipdata.plist entry \"%@\" could not be found in %@ or in Oolite.", name, shipName, [[self verifier] oxpDisplayName]); 115 : } 116 : } 117 : 118 : @end 119 : 120 : 121 : @implementation OOAIStateMachineVerifierStage (Private) 122 : 123 : - (void) validateAI:(NSString *)aiName 124 : { 125 : NSString *path = nil; 126 : NSDictionary *aiStateMachine = nil; 127 : NSEnumerator *stateEnum = nil; 128 : NSString *stateKey = nil; 129 : NSDictionary *stateHandlers = nil; 130 : NSEnumerator *handlerEnum = nil; 131 : NSString *handlerKey = nil; 132 : NSArray *handlerActions = nil; 133 : NSEnumerator *actionEnum = nil; 134 : NSString *action = nil; 135 : NSRange spaceRange; 136 : NSString *selector = nil; 137 : NSMutableSet *badSelectors = nil; 138 : NSString *badSelectorDesc = nil; 139 : NSUInteger index = 0; 140 : 141 : OOLog(@"verifyOXP.verbose.validateAI", @"- Validating AI \"%@\".", aiName); 142 : OOLogIndentIf(@"verifyOXP.verbose.validateAI"); 143 : 144 : // Attempt to load AI. 145 : path = [[[self verifier] fileScannerStage] pathForFile:aiName inFolder:@"AIs" referencedFrom:@"AI list" checkBuiltIn:NO]; 146 : if (path == nil) return; 147 : 148 : badSelectors = [NSMutableSet set]; 149 : 150 : aiStateMachine = OODictionaryFromFile(path); 151 : if (aiStateMachine == nil) 152 : { 153 : OOLog(@"verifyOXP.validateAI.failed.notDictPlist", @"***** ERROR: could not interpret \"%@\" as a dictionary.", path); 154 : return; 155 : } 156 : 157 : // Validate each state. 158 : for (stateEnum = [aiStateMachine keyEnumerator]; (stateKey = [stateEnum nextObject]); ) 159 : { 160 : stateHandlers = [aiStateMachine objectForKey:stateKey]; 161 : if (![stateHandlers isKindOfClass:[NSDictionary class]]) 162 : { 163 : OOLog(@"verifyOXP.validateAI.failed.invalidFormat.state", @"***** ERROR: state \"%@\" in AI \"%@\" is not a dictionary.", stateKey, aiName); 164 : continue; 165 : } 166 : 167 : // Verify handlers for this state. 168 : for (handlerEnum = [stateHandlers keyEnumerator]; (handlerKey = [handlerEnum nextObject]); ) 169 : { 170 : handlerActions = [stateHandlers objectForKey:handlerKey]; 171 : if (![handlerActions isKindOfClass:[NSArray class]]) 172 : { 173 : OOLog(@"verifyOXP.validateAI.failed.invalidFormat.handler", @"***** ERROR: handler \"%@\" for state \"%@\" in AI \"%@\" is not an array, ignoring.", handlerKey, stateKey, aiName); 174 : continue; 175 : } 176 : 177 : // Verify commands for this handler. 178 : index = 0; 179 : for (actionEnum = [handlerActions objectEnumerator]; (action = [actionEnum nextObject]); ) 180 : { 181 : index++; 182 : if (![action isKindOfClass:[NSString class]]) 183 : { 184 : OOLog(@"verifyOXP.validateAI.failed.invalidFormat.action", @"***** ERROR: action %lu in handler \"%@\" for state \"%@\" in AI \"%@\" is not a string, ignoring.", index - 1, handlerKey, stateKey, aiName); 185 : continue; 186 : } 187 : 188 : // Trim spaces from beginning and end. 189 : action = [action stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]; 190 : 191 : // Cut off parameters. 192 : spaceRange = [action rangeOfString:@" "]; 193 : if (spaceRange.location == NSNotFound) selector = action; 194 : else selector = [action substringToIndex:spaceRange.location]; 195 : 196 : // Check against whitelist. 197 : if (![_whitelist containsObject:selector]) 198 : { 199 : [badSelectors addObject:selector]; 200 : } 201 : } 202 : } 203 : } 204 : 205 : if ([badSelectors count] != 0) 206 : { 207 : badSelectorDesc = [[[badSelectors allObjects] sortedArrayUsingSelector:@selector(caseInsensitiveCompare:)] componentsJoinedByString:@", "]; 208 : OOLog(@"verifyOXP.validateAI.failed.badSelector", @"***** ERROR: the AI \"%@\" uses %lu unpermitted method%s: %@", aiName, [badSelectors count], ([badSelectors count] == 1) ? "" : "s", badSelectorDesc); 209 : } 210 : 211 : OOLogOutdentIf(@"verifyOXP.verbose.validateAI"); 212 : } 213 : 214 : @end 215 : 216 : #endif