Vuo  2.4.0
VuoStringUtilities.cc
Go to the documentation of this file.
1
10#include <iomanip>
11#include <sstream>
12#include <CommonCrypto/CommonDigest.h>
13#include <CoreFoundation/CoreFoundation.h>
14#include "VuoException.hh"
15#include "VuoStringUtilities.hh"
16
17extern "C" {
18#include "mkdio.h"
19}
20
24bool VuoStringUtilities::beginsWith(string wholeString, string beginning)
25{
26 return wholeString.length() >= beginning.length() && wholeString.substr(0, beginning.length()) == beginning;
27}
28
32bool VuoStringUtilities::endsWith(string wholeString, string ending)
33{
34 if (wholeString.length() < ending.length())
35 return false;
36
37 return wholeString.compare(wholeString.length()-ending.length(), ending.length(), ending) == 0;
38}
39
44string VuoStringUtilities::substrAfter(string wholeString, string beginning)
45{
46 if (! beginsWith(wholeString, beginning))
47 return "";
48
49 return wholeString.substr(beginning.length());
50}
51
56string VuoStringUtilities::substrBefore(string wholeString, string ending)
57{
58 if (! endsWith(wholeString, ending))
59 return "";
60
61 return wholeString.substr(0, wholeString.length()-ending.length());
62}
63
68string VuoStringUtilities::replaceAll(string wholeString, char originalChar, char replacementChar)
69{
70 string outString = wholeString;
71 size_t pos = 0;
72 while ((pos = wholeString.find_first_of(originalChar, pos)) != string::npos)
73 {
74 outString[pos] = replacementChar;
75 pos = pos + 1;
76 }
77 return outString;
78}
79
85size_t VuoStringUtilities::replaceAll(string &wholeString, string originalSubstring, string replacementSubstring)
86{
87 size_t replacementCount = 0;
88 size_t startPos = 0;
89 while ((startPos = wholeString.find(originalSubstring, startPos)) != string::npos)
90 {
91 wholeString.replace(startPos, originalSubstring.length(), replacementSubstring);
92 startPos += replacementSubstring.length();
93 ++replacementCount;
94 }
95 return replacementCount;
96}
97
101vector<string> VuoStringUtilities::split(const string &wholeString, char delimiter)
102{
103 vector<string> tokens;
104 istringstream iss(wholeString);
105 string token;
106 while( getline(iss, token, delimiter) )
107 tokens.push_back(token);
108 return tokens;
109}
110
114string VuoStringUtilities::join(vector<string> partialStrings, char delimiter)
115{
116 string delimiterStr(1, delimiter);
117 return join(partialStrings, delimiterStr);
118}
119
123string VuoStringUtilities::join(vector<string> partialStrings, string delimiter)
124{
125 string wholeString;
126 for (vector<string>::iterator i = partialStrings.begin(); i != partialStrings.end(); )
127 {
128 wholeString += *i;
129 if (++i != partialStrings.end())
130 wholeString += delimiter;
131 }
132 return wholeString;
133}
134
138string VuoStringUtilities::join(set<string> partialStrings, string delimiter)
139{
140 string wholeString;
141 for (set<string>::iterator i = partialStrings.begin(); i != partialStrings.end(); )
142 {
143 wholeString += *i;
144 if (++i != partialStrings.end())
145 wholeString += delimiter;
146 }
147 return wholeString;
148}
149
157string VuoStringUtilities::trim(string originalString)
158{
159 string whitespace = " \t\v\n\r\f";
160
161 string::size_type begin = originalString.find_first_not_of(whitespace);
162 if (begin == std::string::npos)
163 return "";
164
165 string::size_type end = originalString.find_last_not_of(whitespace);
166
167 return originalString.substr(begin, end - begin + 1);
168}
169
176string VuoStringUtilities::buildCompositionIdentifier(const string &parentCompositionIdentifier, const string &nodeIdentifier)
177{
178 return parentCompositionIdentifier + "/" + nodeIdentifier;
179}
180
185string VuoStringUtilities::buildPortIdentifier(const string &nodeIdentifier, const string &portName)
186{
187 return nodeIdentifier + ":" + portName;
188}
189
194string VuoStringUtilities::prefixSymbolName(string symbolName, string moduleKey)
195{
196 return transcodeToIdentifier(moduleKey) + "__" + symbolName;
197}
198
206{
207 CFMutableStringRef strCF = CFStringCreateMutable(NULL, 0);
208 CFStringAppendCString(strCF, str.c_str(), kCFStringEncodingUTF8);
209
210 CFStringNormalize(strCF, kCFStringNormalizationFormD); // decomposes combining characters, so accents/diacritics are separated from their letters
211
212 CFIndex strLength = CFStringGetLength(strCF);
213 UniChar *strBuf = (UniChar *)malloc(strLength * sizeof(UniChar));
214 if (!strBuf)
215 {
216 CFRelease(strCF);
217 return string();
218 }
219 CFStringGetCharacters(strCF, CFRangeMake(0, strLength), strBuf);
220
221 CFStringRef empty = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
222 CFStringRef underscore = CFStringCreateWithCString(NULL, "_", kCFStringEncodingUTF8);
223 CFStringRef doubleUnderscore = CFStringCreateWithCString(NULL, "__", kCFStringEncodingUTF8);
224 if (!empty || !underscore || !doubleUnderscore)
225 {
226 CFRelease(strCF);
227 if (empty)
228 CFRelease(empty);
229 if (underscore)
230 CFRelease(underscore);
231 if (doubleUnderscore)
232 CFRelease(doubleUnderscore);
233 return string();
234 }
235
236 for (CFIndex i = strLength-1; i >= 0; --i)
237 {
238 UniChar c = strBuf[i];
239
240 CFStringRef replacement = NULL;
241 if (c > 127) // non-ASCII
242 replacement = empty;
243 else if (c == '.' || isspace(c))
244 replacement = underscore;
245 else if (c == '/' || c == ':')
246 replacement = doubleUnderscore;
247 else if (! isValidCharInIdentifier(c))
248 replacement = empty;
249
250 if (replacement)
251 CFStringReplace(strCF, CFRangeMake(i, 1), replacement);
252 }
253
254 string ret = makeFromCFString(strCF);
255
256 CFRelease(strCF);
257 CFRelease(empty);
258 CFRelease(underscore);
259 CFRelease(doubleUnderscore);
260 free(strBuf);
261
262 return ret;
263}
264
270{
271 return isalnum(ch) || ch == '_';
272}
273
277string VuoStringUtilities::transcodeToGraphvizIdentifier(const string &originalString)
278{
279 string escapedString = originalString;
280 for (string::size_type i = 0; (i = escapedString.find("\\", i)) != std::string::npos; i += 2)
281 escapedString.replace(i, 1, "\\\\");
282 for (string::size_type i = 0; (i = escapedString.find("\"", i)) != std::string::npos; i += 2)
283 escapedString.replace(i, 1, "\\\"");
284 for (string::size_type i = 0; (i = escapedString.find("{", i)) != std::string::npos; i += 2)
285 escapedString.replace(i, 1, "\\{");
286 for (string::size_type i = 0; (i = escapedString.find("}", i)) != std::string::npos; i += 2)
287 escapedString.replace(i, 1, "\\}");
288 for (string::size_type i = 0; (i = escapedString.find("<", i)) != std::string::npos; i += 2)
289 escapedString.replace(i, 1, "\\<");
290 for (string::size_type i = 0; (i = escapedString.find(">", i)) != std::string::npos; i += 2)
291 escapedString.replace(i, 1, "\\>");
292 for (string::size_type i = 0; (i = escapedString.find("|", i)) != std::string::npos; i += 2)
293 escapedString.replace(i, 1, "\\|");
294 for (string::size_type i = 0; (i = escapedString.find(" ", i)) != std::string::npos; i += 3)
295 escapedString.replace(i, 2, " \\ ");
296 return escapedString;
297}
298
302string VuoStringUtilities::transcodeFromGraphvizIdentifier(const string &graphvizIdentifier)
303{
304 string unescapedString;
305 bool inEscape = false;
306 for (string::const_iterator i = graphvizIdentifier.begin(); i != graphvizIdentifier.end(); ++i)
307 {
308 if (inEscape)
309 {
310 inEscape = false;
311 unescapedString += *i;
312 continue;
313 }
314
315 if (*i == '\\')
316 {
317 inEscape = true;
318 continue;
319 }
320
321 unescapedString += *i;
322 }
323 return unescapedString;
324}
325
331string VuoStringUtilities::formUniqueIdentifier(set<string> &takenIdentifiers,
332 const string &preferredIdentifier, const string &identifierPrefix)
333{
334 auto isIdentifierAvailable = [&takenIdentifiers] (const string &identifier)
335 {
336 return takenIdentifiers.find(identifier) == takenIdentifiers.end();
337 };
338
339 string uniqueIdentifier = formUniqueIdentifier(isIdentifierAvailable, preferredIdentifier, identifierPrefix);
340 takenIdentifiers.insert(uniqueIdentifier);
341 return uniqueIdentifier;
342}
343
349string VuoStringUtilities::formUniqueIdentifier(std::function<bool(const string &)> isIdentifierAvailable,
350 const string &preferredIdentifier, const string &identifierPrefix)
351{
352 string unique = preferredIdentifier;
353 string prefix = (! identifierPrefix.empty() ? identifierPrefix : preferredIdentifier);
354 int suffix = 2;
355
356 while (! isIdentifierAvailable(unique))
357 {
358 ostringstream oss;
359 oss << prefix << suffix++;
360 unique = oss.str();
361 }
362
363 return unique;
364}
365
371string VuoStringUtilities::generateHtmlFromMarkdown(const string &markdownString)
372{
373 MMIOT *doc = mkd_string(markdownString.c_str(), markdownString.length(), MKD_NOPANTS);
374 mkd_compile(doc, 0);
375 char *html;
376 mkd_document(doc, &html);
377 string htmlString(html);
378 mkd_cleanup(doc);
379
380 // Remove the final linebreak from code blocks,
381 // since Qt (unlike typical browser rendering engines) considers that whitespace significant.
382 replaceAll(htmlString, "\n</code></pre>", "</code></pre>");
383
384 return htmlString;
385}
386
392string VuoStringUtilities::generateHtmlFromMarkdownLine(const string &markdownString)
393{
394 size_t length = markdownString.length();
395 if (!length)
396 return "";
397
398 char *html;
399 mkd_line((char *)markdownString.c_str(), length, &html, MKD_NOPANTS);
400 string htmlString(html);
401 free(html);
402 return htmlString;
403}
404
418string VuoStringUtilities::convertToCamelCase(const string &originalString,
419 bool forceFirstLetterToUpper, bool forceFirstLetterToLower, bool forceInterveningLettersToLower,
420 bool allowSeparatorDots)
421{
422 string camelCaseString;
423 bool first = true;
424 bool uppercaseNext = forceFirstLetterToUpper;
425 bool lowercaseNext = forceFirstLetterToLower;
426 bool previousWasDot = false;
427 for (string::const_iterator i = originalString.begin(); i != originalString.end(); ++i)
428 {
429 if (first && !isalpha(*i))
430 continue;
431 first = false;
432
433 bool isDot = *i == '.';
434 if (allowSeparatorDots && isDot)
435 {
436 if (previousWasDot)
437 continue;
438 uppercaseNext = false;
439 }
440 else if (!isalnum(*i))
441 {
442 uppercaseNext = true;
443 continue;
444 }
445
446 if (uppercaseNext)
447 camelCaseString += toupper(*i);
448 else if (lowercaseNext)
449 camelCaseString += tolower(*i);
450 else
451 camelCaseString += *i;
452
453 uppercaseNext = false;
454 lowercaseNext = forceInterveningLettersToLower;
455 previousWasDot = isDot;
456 }
457
458 // Trim trailing dots.
459 if (allowSeparatorDots)
460 while (endsWith(camelCaseString, "."))
461 camelCaseString = substrBefore(camelCaseString, ".");
462
463 return camelCaseString;
464}
465
472string VuoStringUtilities::expandCamelCase(string camelCaseString)
473{
474 // Only apply these transformations if the whole string matches,
475 // since they may appear as substrings in contexts where they shouldn't be all-caps.
476 if (camelCaseString == "x")
477 return "X";
478 else if (camelCaseString == "y")
479 return "Y";
480 else if (camelCaseString == "z")
481 return "Z";
482 else if (camelCaseString == "w")
483 return "W";
484 else if (camelCaseString == "xy")
485 return "XY";
486 else if (camelCaseString == "osc")
487 return "OSC";
488
489 string out;
490 out += toupper(camelCaseString[0]);
491
492 size_t length = camelCaseString.length();
493 for (int i = 1; i < length; ++i)
494 {
495 char c = camelCaseString[i];
496 if (isupper(c) || (isdigit(c) && !isdigit(camelCaseString[i-1])))
497 out += " ";
498 out += c;
499 }
500
501 string allCaps[]{
502 "2d",
503 "3d",
504 "4d",
505 "Xyzw",
506 "Xyz",
507 "Rgbaw",
508 "Rgba",
509 "Rgbw",
510 "Rgb",
511 "Wwcw",
512 "Cmy",
513 "Hsl",
514 "Hdmi",
515 "Sdi",
516 "Ntsc",
517// "Pal", // Appears in Leap Motion "Palm Velocity".
518 "Url",
519 "Midi",
520 "Rss",
521 "Csv",
522 "Tsv",
523 "Ascii",
524 "Json",
525 "Xml",
526 "Dmx",
527 };
528 for (auto lower : allCaps)
529 {
530 string upper = lower;
531 std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
532 VuoStringUtilities::replaceAll(out, lower, upper);
533 }
534
535 return out;
536}
537
542{
543 if (!cfs)
544 return "";
545
546 CFStringRef cfString = (CFStringRef)cfs;
547
548 // https://stackoverflow.com/questions/1609565/whats-the-cfstring-equiv-of-nsstrings-utf8string
549 const char *utf8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
550 if (utf8StringPtr)
551 return utf8StringPtr;
552 else
553 {
554 CFIndex maxBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfString), kCFStringEncodingUTF8) + 1;
555 char *t = (char *)calloc(1, maxBytes);
556 CFStringGetCString(cfString, t, maxBytes, kCFStringEncodingUTF8);
557 string s(t);
558 free(t);
559 return s;
560 }
561}
562
567{
568 static const char alphanum[] =
569 "0123456789"
570 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
571 "abcdefghijklmnopqrstuvwxyz";
572
573 string hash(length, 0);
574 for (int i = 0; i < length; ++i)
575 hash[i] = alphanum[arc4random_uniform(sizeof(alphanum)-1)];
576
577 return hash;
578}
579
580const std::locale VuoStringUtilities::locale;
581const std::collate<char> &VuoStringUtilities::collate = std::use_facet<std::collate<char> >(VuoStringUtilities::locale);
582
589{
590 unsigned char hash[CC_SHA256_DIGEST_LENGTH];
591 if (!CC_SHA256(s.c_str(), s.length(), hash))
592 throw VuoException("Error: CC_SHA256 failed.");
593
594 ostringstream oss;
595 oss << setfill('0') << hex;
596 for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i)
597 oss << setw(2) << (int)hash[i];
598 return oss.str();
599}