Vuo  2.3.2
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 
17 extern "C" {
18 #include "mkdio.h"
19 }
20 
24 bool VuoStringUtilities::beginsWith(string wholeString, string beginning)
25 {
26  return wholeString.length() >= beginning.length() && wholeString.substr(0, beginning.length()) == beginning;
27 }
28 
32 bool 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 
44 string VuoStringUtilities::substrAfter(string wholeString, string beginning)
45 {
46  if (! beginsWith(wholeString, beginning))
47  return "";
48 
49  return wholeString.substr(beginning.length());
50 }
51 
56 string 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 
68 string 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 
85 size_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 
101 vector<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 
114 string VuoStringUtilities::join(vector<string> partialStrings, char delimiter)
115 {
116  string delimiterStr(1, delimiter);
117  return join(partialStrings, delimiterStr);
118 }
119 
123 string 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 
138 string 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 
157 string 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 
176 string VuoStringUtilities::buildCompositionIdentifier(const string &parentCompositionIdentifier, const string &nodeIdentifier)
177 {
178  return parentCompositionIdentifier + "/" + nodeIdentifier;
179 }
180 
185 string VuoStringUtilities::buildPortIdentifier(const string &nodeIdentifier, const string &portName)
186 {
187  return nodeIdentifier + ":" + portName;
188 }
189 
194 string 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 
277 string 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 
302 string 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 
331 string 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 
349 string 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 
371 string 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 
392 string 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 
418 string 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 
472 string 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 
580 const std::locale VuoStringUtilities::locale;
581 const std::collate<char> &VuoStringUtilities::collate = std::use_facet<std::collate<char> >(VuoStringUtilities::locale);
582 
588 string VuoStringUtilities::calculateSHA256(const string &s)
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 }