Oolite
Loading...
Searching...
No Matches
OOStringParsing.m
Go to the documentation of this file.
1/*
2
3OOStringParsing.m
4
5Oolite
6Copyright (C) 2004-2013 Giles C Williams and contributors
7
8This program is free software; you can redistribute it and/or
9modify it under the terms of the GNU General Public License
10as published by the Free Software Foundation; either version 2
11of the License, or (at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program; if not, write to the Free Software
20Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
21MA 02110-1301, USA.
22
23*/
24
25#import "OOStringParsing.h"
26#import "OOLogging.h"
28#import "legacy_random.h"
29#import "Universe.h"
30#import "PlayerEntity.h"
34#import "ResourceManager.h"
35#import "HeadUpDisplay.h"
36
39
40
41static NSString * const kOOLogStringVectorConversion = @"strings.conversion.vector";
42static NSString * const kOOLogStringQuaternionConversion = @"strings.conversion.quaternion";
43static NSString * const kOOLogStringRandomSeedConversion = @"strings.conversion.randomSeed";
44
45
46NSMutableArray *ScanTokensFromString(NSString *values)
47{
48 NSMutableArray *result = nil;
49 NSScanner *scanner = nil;
50 NSString *token = nil;
51 static NSCharacterSet *space_set = nil;
52
53 // Note: Shark suggests we're getting a lot of early exits, but testing showed a pretty steady 2% early exit rate.
54 if (EXPECT_NOT(values == nil)) return [NSMutableArray array];
55 if (EXPECT_NOT(space_set == nil)) space_set = [[NSCharacterSet whitespaceAndNewlineCharacterSet] retain];
56
57 result = [NSMutableArray array];
58 scanner = [NSScanner scannerWithString:values];
59
60 while (![scanner isAtEnd])
61 {
62 [scanner ooliteScanCharactersFromSet:space_set intoString:NULL];
63 if ([scanner ooliteScanUpToCharactersFromSet:space_set intoString:&token])
64 {
65 [result addObject:token];
66 }
67 }
68
69 return result;
70}
71
72
73BOOL ScanVectorFromString(NSString *xyzString, Vector *outVector)
74{
75 GLfloat xyz[] = {0.0, 0.0, 0.0};
76 int i = 0;
77 NSString *error = nil;
78 NSScanner *scanner = nil;
79
80 assert(outVector != NULL);
81 if (xyzString == nil) return NO;
82
83 if (!error) scanner = [NSScanner scannerWithString:xyzString];
84 while (![scanner isAtEnd] && i < 3 && !error)
85 {
86 if (![scanner scanFloat:&xyz[i++]]) error = @"could not scan a float value.";
87 }
88
89 if (!error && i < 3) error = @"found less than three float values.";
90
91 if (!error)
92 {
93 *outVector = make_vector(xyz[0], xyz[1], xyz[2]);
94 return YES;
95 }
96 else
97 {
98 OOLogERR(kOOLogStringVectorConversion, @"cannot make vector from '%@': %@", xyzString, error);
99 return NO;
100 }
101}
102
103BOOL ScanHPVectorFromString(NSString *xyzString, HPVector *outVector)
104{
105 Vector scanVector;
106 assert(outVector != NULL);
107 BOOL result = ScanVectorFromString(xyzString, &scanVector);
108 if (!result)
109 {
110 return NO;
111 }
112 *outVector = vectorToHPVector(scanVector);
113 return YES;
114}
115
116BOOL ScanQuaternionFromString(NSString *wxyzString, Quaternion *outQuaternion)
117{
118 GLfloat wxyz[] = {1.0, 0.0, 0.0, 0.0};
119 int i = 0;
120 NSString *error = nil;
121 NSScanner *scanner = nil;
122
123 assert(outQuaternion != NULL);
124 if (wxyzString == nil) return NO;
125
126 if (!error) scanner = [NSScanner scannerWithString:wxyzString];
127 while (![scanner isAtEnd] && i < 4 && !error)
128 {
129 if (![scanner scanFloat:&wxyz[i++]]) error = @"could not scan a float value.";
130 }
131
132 if (!error && i < 4) error = @"found less than four float values.";
133
134 if (!error)
135 {
136 outQuaternion->w = wxyz[0];
137 outQuaternion->x = wxyz[1];
138 outQuaternion->y = wxyz[2];
139 outQuaternion->z = wxyz[3];
140 quaternion_normalize(outQuaternion);
141 return YES;
142 }
143 else
144 {
145 OOLogERR(kOOLogStringQuaternionConversion, @"cannot make quaternion from '%@': %@", wxyzString, error);
146 return NO;
147 }
148}
149
150
151BOOL ScanVectorAndQuaternionFromString(NSString *xyzwxyzString, Vector *outVector, Quaternion *outQuaternion)
152{
153 GLfloat xyzwxyz[] = { 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0};
154 int i = 0;
155 NSString *error = nil;
156 NSScanner *scanner = nil;
157
158 assert(outVector != NULL && outQuaternion != NULL);
159 if (xyzwxyzString == nil) return NO;
160
161 if (!error) scanner = [NSScanner scannerWithString:xyzwxyzString];
162 while (![scanner isAtEnd] && i < 7 && !error)
163 {
164 if (![scanner scanFloat:&xyzwxyz[i++]]) error = @"Could not scan a float value.";
165 }
166
167 if (!error && i < 7) error = @"Found less than seven float values.";
168
169 if (error)
170 {
171 OOLogERR(kOOLogStringQuaternionConversion, @"cannot make vector and quaternion from '%@': %@", xyzwxyzString, error);
172 return NO;
173 }
174
175 outVector->x = xyzwxyz[0];
176 outVector->y = xyzwxyz[1];
177 outVector->z = xyzwxyz[2];
178 outQuaternion->w = xyzwxyz[3];
179 outQuaternion->x = xyzwxyz[4];
180 outQuaternion->y = xyzwxyz[5];
181 outQuaternion->z = xyzwxyz[6];
182
183 return YES;
184}
185
186
187Vector VectorFromString(NSString *xyzString, Vector defaultValue)
188{
189 Vector result;
190 if (!ScanVectorFromString(xyzString, &result)) result = defaultValue;
191 return result;
192}
193
194
195Quaternion QuaternionFromString(NSString *wxyzString, Quaternion defaultValue)
196{
197 Quaternion result;
198 if (!ScanQuaternionFromString(wxyzString, &result)) result = defaultValue;
199 return result;
200}
201
202
203NSString *StringFromPoint(NSPoint point)
204{
205 return [NSString stringWithFormat:@"%f %f", point.x, point.y];
206}
207
208
209NSPoint PointFromString(NSString *xyString)
210{
211 NSArray *tokens = ScanTokensFromString(xyString);
212 NSPoint result = NSZeroPoint;
213
214 NSUInteger n_tokens = [tokens count];
215 if (n_tokens == 2)
216 {
217 result.x = [[tokens objectAtIndex:0] doubleValue];
218 result.y = [[tokens objectAtIndex:1] doubleValue];
219 }
220 return result;
221}
222
223
224Random_Seed RandomSeedFromString(NSString *abcdefString)
225{
226 Random_Seed result;
227 int abcdef[] = { 0, 0, 0, 0, 0, 0};
228 int i = 0;
229 NSString *error = nil;
230 NSScanner *scanner = [NSScanner scannerWithString:abcdefString];
231
232 while (![scanner isAtEnd] && i < 6 && !error)
233 {
234 if (![scanner scanInt:&abcdef[i++]]) error = @"could not scan a int value.";
235 }
236
237 if (!error && i < 6) error = @"found less than six int values.";
238
239 if (!error)
240 {
241 result.a = abcdef[0];
242 result.b = abcdef[1];
243 result.c = abcdef[2];
244 result.d = abcdef[3];
245 result.e = abcdef[4];
246 result.f = abcdef[5];
247 }
248 else
249 {
250 OOLogERR(kOOLogStringRandomSeedConversion, @"cannot make Random_Seed from '%@': %@", abcdefString, error);
251 result = kNilRandomSeed;
252 }
253
254 return result;
255}
256
257
259{
260 return [NSString stringWithFormat: @"%d %d %d %d %d %d", seed.a, seed.b, seed.c, seed.d, seed.e, seed.f];
261}
262
263
264NSString *OOPadStringToEms(NSString * string, float padEms)
265{
266 NSString *result = string;
267 float numEms = padEms - OOStringWidthInEm(result);
268 if (numEms>0)
269 {
270 numEms /= OOStringWidthInEm(@" "); // start with wide space
271 result=[[@"" stringByPaddingToLength:(NSUInteger)numEms withString: @" " startingAtIndex:0] stringByAppendingString: result];
272 }
273 // most of the way there, so switch to narrow space
274 numEms = padEms - OOStringWidthInEm(result);
275 if (numEms>0)
276 {
277 numEms /= OOStringWidthInEm(@"\037"); // 037 is narrow space
278 result=[[@"" stringByPaddingToLength:(NSUInteger)numEms withString: @"\037" startingAtIndex:0] stringByAppendingString: result];
279 }
280 return result;
281}
282
283
284NSString *OOStringFromDeciCredits(OOCreditsQuantity tenthsOfCredits, BOOL includeDecimal, BOOL includeSymbol)
285{
286 JSContext *context = OOJSAcquireContext();
287 JSObject *global = [[OOJavaScriptEngine sharedEngine] globalObject];
288 JSObject *fakeRoot;
289 jsval method;
290 jsval rval;
291 NSString *result = nil;
292 jsval exception;
293 BOOL hadException;
294
295 /* Because the |cr etc. formatting operators call this, and the
296 implementation may use string expansion, we need to ensure recursion
297 can't happen.
298 */
299 static BOOL reentrancyLock;
300 if (reentrancyLock) return [NSString stringWithFormat:@"%0.1f", tenthsOfCredits * 0.1];
301
302 reentrancyLock = YES;
303
304 hadException = JS_GetPendingException(context, &exception);
305 JS_ClearPendingException(context);
306
307 if (JS_GetMethodById(context, global, OOJSID("formatCredits"), &fakeRoot, &method))
308 {
309 jsval args[3];
310 if (JS_NewNumberValue(context, tenthsOfCredits * 0.1, &args[0]))
311 {
312 args[1] = OOJSValueFromBOOL(includeDecimal);
313 args[2] = OOJSValueFromBOOL(includeSymbol);
314
316 JS_CallFunctionValue(context, global, method, 3, args, &rval);
318
319 result = OOStringFromJSValue(context, rval);
320 }
321 }
322
323 if (hadException) JS_SetPendingException(context, exception);
324
325 OOJSRelinquishContext(context);
326
327 if (EXPECT_NOT(result == nil)) result = [NSString stringWithFormat:@"%li", (long)(tenthsOfCredits) / 10];
328
329 reentrancyLock = NO;
330
331 return result;
332}
333
334
335@implementation NSString (OOUtilities)
336
337- (BOOL)pathHasExtension:(NSString *)extension
338{
339 return [[self pathExtension] caseInsensitiveCompare:extension] == NSOrderedSame;
340}
341
342
343- (BOOL)pathHasExtensionInArray:(NSArray *)extensions
344{
345 NSString *extension = nil;
346
347 foreach (extension, extensions)
348 {
349 if ([[self pathExtension] caseInsensitiveCompare:extension] == NSOrderedSame) return YES;
350 }
351
352 return NO;
353}
354
355@end
356
357
358NSArray *ComponentsFromVersionString(NSString *string)
359{
360 NSArray *stringComponents = nil;
361 NSMutableArray *result = nil;
362 NSUInteger i, count;
363 int value;
364 id component;
365
366 stringComponents = [string componentsSeparatedByString:@" "];
367 stringComponents = [[stringComponents objectAtIndex:0] componentsSeparatedByString:@"-"];
368 stringComponents = [[stringComponents objectAtIndex:0] componentsSeparatedByString:@"."];
369 count = [stringComponents count];
370 result = [NSMutableArray arrayWithCapacity:count];
371
372 for (i = 0; i != count; ++i)
373 {
374 component = [stringComponents objectAtIndex:i];
375 if ([component respondsToSelector:@selector(intValue)]) value = MAX([component intValue], 0);
376 else value = 0;
377
378 [result addObject:[NSNumber numberWithUnsignedInt:value]];
379 }
380
381 return result;
382}
383
384
385NSComparisonResult CompareVersions(NSArray *version1, NSArray *version2)
386{
387 NSEnumerator *leftEnum = nil,
388 *rightEnum = nil;
389 NSNumber *leftComponent = nil,
390 *rightComponent = nil;
391 unsigned leftValue,
392 rightValue;
393
394 leftEnum = [version1 objectEnumerator];
395 rightEnum = [version2 objectEnumerator];
396
397 for (;;)
398 {
399 leftComponent = [leftEnum nextObject];
400 rightComponent = [rightEnum nextObject];
401
402 if (leftComponent == nil && rightComponent == nil) break; // End of both versions
403
404 // We'll get 0 if the component is nil, which is what we want.
405 leftValue = [leftComponent unsignedIntValue];
406 rightValue = [rightComponent unsignedIntValue];
407
408 if (leftValue < rightValue) return NSOrderedAscending;
409 if (leftValue > rightValue) return NSOrderedDescending;
410 }
411
412 // If there was a difference, we'd have returned already.
413 return NSOrderedSame;
414}
415
416
417NSString *ClockToString(double clock, BOOL adjusting)
418{
419 int days, hrs, mins, secs;
420 NSString *format = nil;
421
422 days = floor(clock / 86400.0);
423 secs = floor(clock - days * 86400.0);
424 hrs = floor(secs / 3600.0);
425 secs %= 3600;
426 mins = floor(secs / 60.0);
427 secs %= 60;
428
429 if (adjusting) format = DESC(@"clock-format-adjusting");
430 else format = DESC(@"clock-format");
431
432 return [NSString stringWithFormat:format, days, hrs, mins, secs];
433}
434
435
436#if DEBUG_GRAPHVIZ
437
438NSString *EscapedGraphVizString(NSString *string)
439{
440 NSString * const srcStrings[] =
441 {
442 //Note: backslash must be first.
443 @"\\", @"\"", @"\'", @"\r", @"\n", @"\t", nil
444 };
445 NSString * const subStrings[] =
446 {
447 //Note: must be same order.
448 @"\\\\", @"\\\"", @"\\\'", @"\\r", @"\\n", @"\\t", nil
449 };
450
451 NSString * const * src = srcStrings;
452 NSString * const * sub = subStrings;
453 NSMutableString *mutable = nil;
454 NSString *result = nil;
455
456 mutable = [string mutableCopy];
457 while (*src != nil)
458 {
459 [mutable replaceOccurrencesOfString:*src++
460 withString:*sub++
461 options:0
462 range:(NSRange){ 0, [mutable length] }];
463 }
464
465 if ([mutable length] == [string length])
466 {
467 result = string;
468 }
469 else
470 {
471 result = [[mutable copy] autorelease];
472 }
473 [mutable release];
474 return result;
475}
476
477
478static BOOL NameIsTaken(NSString *name, NSSet *uniqueSet);
479
480NSString *GraphVizTokenString(NSString *string, NSMutableSet *uniqueSet)
481{
482 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
483
484 BOOL lastWasUnderscore = NO;
485 NSUInteger i, length = [string length], ri = 0;
486 unichar result[length];
487 NSString *token = nil;
488
489 if (length > 0)
490 {
491 // Special case for first char - can't be digit.
492 unichar c = [string characterAtIndex:0];
493 if (!isalpha(c))
494 {
495 c = '_';
496 lastWasUnderscore = YES;
497 }
498 result[ri++] = c;
499
500 for (i = 1; i < length; i++)
501 {
502 c = [string characterAtIndex:i];
503 if (!isalnum(c))
504 {
505 if (lastWasUnderscore) continue;
506 c = '_';
507 lastWasUnderscore = YES;
508 }
509 else
510 {
511 lastWasUnderscore = NO;
512 }
513
514 result[ri++] = c;
515 }
516
517 token = [NSString stringWithCharacters:result length:ri];
518 }
519 else
520 {
521 token = @"_";
522 }
523
524 if (NameIsTaken(token, uniqueSet))
525 {
526 if (!lastWasUnderscore) token = [token stringByAppendingString:@"_"];
527 NSString *uniqueToken = nil;
528 unsigned uniqueID = 2;
529
530 for (;;)
531 {
532 uniqueToken = [NSString stringWithFormat:@"%@%u", token, uniqueID];
533 if (!NameIsTaken(uniqueToken, uniqueSet)) break;
534 }
535 token = uniqueToken;
536 }
537 [uniqueSet addObject:token];
538
539 [token retain];
540 [pool release];
541 return [token autorelease];
542}
543
544
545static BOOL NameIsTaken(NSString *name, NSSet *uniqueSet)
546{
547 if ([uniqueSet containsObject:name]) return YES;
548
549 static NSSet *keywords = nil;
550 if (keywords == nil) keywords = [[NSSet alloc] initWithObjects:@"node", @"edge", @"graph", @"digraph", @"subgraph", @"strict", nil];
551
552 return [keywords containsObject:[name lowercaseString]];
553}
554
555#endif //DEBUG_GRAPHVIZ
CGFloat OOStringWidthInEm(NSString *text)
#define EXPECT_NOT(x)
#define OOJSStopTimeLimiter()
#define OOJSStartTimeLimiter()
#define OOJSID(str)
Definition OOJSPropID.h:38
NSString * OOStringFromJSValue(JSContext *context, jsval value)
OOINLINE JSContext * OOJSAcquireContext(void)
OOINLINE jsval OOJSValueFromBOOL(int b) INLINE_CONST_FUNC
OOINLINE void OOJSRelinquishContext(JSContext *context)
#define OOLogERR(class, format,...)
Definition OOLogging.h:112
#define MAX(A, B)
Definition OOMaths.h:114
unsigned count
return nil
NSArray * ComponentsFromVersionString(NSString *string)
NSString * ClockToString(double clock, BOOL adjusting)
NSComparisonResult CompareVersions(NSArray *version1, NSArray *version2)
NSMutableArray * ScanTokensFromString(NSString *values)
Quaternion QuaternionFromString(NSString *wxyzString, Quaternion defaultValue)
static NSString *const kOOLogStringVectorConversion
static NSString *const kOOLogStringRandomSeedConversion
static NSString *const kOOLogStringQuaternionConversion
BOOL ScanVectorAndQuaternionFromString(NSString *xyzwxyzString, Vector *outVector, Quaternion *outQuaternion)
NSPoint PointFromString(NSString *xyString)
Vector VectorFromString(NSString *xyzString, Vector defaultValue)
NSString * OOPadStringToEms(NSString *string, float padEms)
NSString * StringFromRandomSeed(Random_Seed seed)
Random_Seed RandomSeedFromString(NSString *abcdefString)
BOOL ScanHPVectorFromString(NSString *xyzString, HPVector *outVector)
BOOL ScanQuaternionFromString(NSString *wxyzString, Quaternion *outQuaternion)
NSString * OOStringFromDeciCredits(OOCreditsQuantity tenthsOfCredits, BOOL includeDecimal, BOOL includeSymbol)
BOOL ScanVectorFromString(NSString *xyzString, Vector *outVector)
NSString * StringFromPoint(NSPoint point)
uint64_t OOCreditsQuantity
Definition OOTypes.h:182
#define DESC(key)
Definition Universe.h:848
OOJavaScriptEngine * sharedEngine()
const Random_Seed kNilRandomSeed