Oolite 1.91.0.7604-240417-a536cbe
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 NSEnumerator *extEnum = nil;
346 NSString *extension = nil;
347
348 for (extEnum = [extensions objectEnumerator]; (extension = [extEnum nextObject]); )
349 {
350 if ([[self pathExtension] caseInsensitiveCompare:extension] == NSOrderedSame) return YES;
351 }
352
353 return NO;
354}
355
356@end
357
358
359NSArray *ComponentsFromVersionString(NSString *string)
360{
361 NSArray *stringComponents = nil;
362 NSMutableArray *result = nil;
363 NSUInteger i, count;
364 int value;
365 id component;
366
367 stringComponents = [string componentsSeparatedByString:@" "];
368 stringComponents = [[stringComponents objectAtIndex:0] componentsSeparatedByString:@"-"];
369 stringComponents = [[stringComponents objectAtIndex:0] componentsSeparatedByString:@"."];
370 count = [stringComponents count];
371 result = [NSMutableArray arrayWithCapacity:count];
372
373 for (i = 0; i != count; ++i)
374 {
375 component = [stringComponents objectAtIndex:i];
376 if ([component respondsToSelector:@selector(intValue)]) value = MAX([component intValue], 0);
377 else value = 0;
378
379 [result addObject:[NSNumber numberWithUnsignedInt:value]];
380 }
381
382 return result;
383}
384
385
386NSComparisonResult CompareVersions(NSArray *version1, NSArray *version2)
387{
388 NSEnumerator *leftEnum = nil,
389 *rightEnum = nil;
390 NSNumber *leftComponent = nil,
391 *rightComponent = nil;
392 unsigned leftValue,
393 rightValue;
394
395 leftEnum = [version1 objectEnumerator];
396 rightEnum = [version2 objectEnumerator];
397
398 for (;;)
399 {
400 leftComponent = [leftEnum nextObject];
401 rightComponent = [rightEnum nextObject];
402
403 if (leftComponent == nil && rightComponent == nil) break; // End of both versions
404
405 // We'll get 0 if the component is nil, which is what we want.
406 leftValue = [leftComponent unsignedIntValue];
407 rightValue = [rightComponent unsignedIntValue];
408
409 if (leftValue < rightValue) return NSOrderedAscending;
410 if (leftValue > rightValue) return NSOrderedDescending;
411 }
412
413 // If there was a difference, we'd have returned already.
414 return NSOrderedSame;
415}
416
417
418NSString *ClockToString(double clock, BOOL adjusting)
419{
420 int days, hrs, mins, secs;
421 NSString *format = nil;
422
423 days = floor(clock / 86400.0);
424 secs = floor(clock - days * 86400.0);
425 hrs = floor(secs / 3600.0);
426 secs %= 3600;
427 mins = floor(secs / 60.0);
428 secs %= 60;
429
430 if (adjusting) format = DESC(@"clock-format-adjusting");
431 else format = DESC(@"clock-format");
432
433 return [NSString stringWithFormat:format, days, hrs, mins, secs];
434}
435
436
437#if DEBUG_GRAPHVIZ
438
439NSString *EscapedGraphVizString(NSString *string)
440{
441 NSString * const srcStrings[] =
442 {
443 //Note: backslash must be first.
444 @"\\", @"\"", @"\'", @"\r", @"\n", @"\t", nil
445 };
446 NSString * const subStrings[] =
447 {
448 //Note: must be same order.
449 @"\\\\", @"\\\"", @"\\\'", @"\\r", @"\\n", @"\\t", nil
450 };
451
452 NSString * const * src = srcStrings;
453 NSString * const * sub = subStrings;
454 NSMutableString *mutable = nil;
455 NSString *result = nil;
456
457 mutable = [string mutableCopy];
458 while (*src != nil)
459 {
460 [mutable replaceOccurrencesOfString:*src++
461 withString:*sub++
462 options:0
463 range:(NSRange){ 0, [mutable length] }];
464 }
465
466 if ([mutable length] == [string length])
467 {
468 result = string;
469 }
470 else
471 {
472 result = [[mutable copy] autorelease];
473 }
474 [mutable release];
475 return result;
476}
477
478
479static BOOL NameIsTaken(NSString *name, NSSet *uniqueSet);
480
481NSString *GraphVizTokenString(NSString *string, NSMutableSet *uniqueSet)
482{
483 NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
484
485 BOOL lastWasUnderscore = NO;
486 NSUInteger i, length = [string length], ri = 0;
487 unichar result[length];
488 NSString *token = nil;
489
490 if (length > 0)
491 {
492 // Special case for first char - can't be digit.
493 unichar c = [string characterAtIndex:0];
494 if (!isalpha(c))
495 {
496 c = '_';
497 lastWasUnderscore = YES;
498 }
499 result[ri++] = c;
500
501 for (i = 1; i < length; i++)
502 {
503 c = [string characterAtIndex:i];
504 if (!isalnum(c))
505 {
506 if (lastWasUnderscore) continue;
507 c = '_';
508 lastWasUnderscore = YES;
509 }
510 else
511 {
512 lastWasUnderscore = NO;
513 }
514
515 result[ri++] = c;
516 }
517
518 token = [NSString stringWithCharacters:result length:ri];
519 }
520 else
521 {
522 token = @"_";
523 }
524
525 if (NameIsTaken(token, uniqueSet))
526 {
527 if (!lastWasUnderscore) token = [token stringByAppendingString:@"_"];
528 NSString *uniqueToken = nil;
529 unsigned uniqueID = 2;
530
531 for (;;)
532 {
533 uniqueToken = [NSString stringWithFormat:@"%@%u", token, uniqueID];
534 if (!NameIsTaken(uniqueToken, uniqueSet)) break;
535 }
536 token = uniqueToken;
537 }
538 [uniqueSet addObject:token];
539
540 [token retain];
541 [pool release];
542 return [token autorelease];
543}
544
545
546static BOOL NameIsTaken(NSString *name, NSSet *uniqueSet)
547{
548 if ([uniqueSet containsObject:name]) return YES;
549
550 static NSSet *keywords = nil;
551 if (keywords == nil) keywords = [[NSSet alloc] initWithObjects:@"node", @"edge", @"graph", @"digraph", @"subgraph", @"strict", nil];
552
553 return [keywords containsObject:[name lowercaseString]];
554}
555
556#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:839
OOJavaScriptEngine * sharedEngine()
const Random_Seed kNilRandomSeed