Vuo  2.2.1
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  return string();
226 
227  for (CFIndex i = strLength-1; i >= 0; --i)
228  {
229  UniChar c = strBuf[i];
230 
231  CFStringRef replacement = NULL;
232  if (c > 127) // non-ASCII
233  replacement = empty;
234  else if (c == '.' || isspace(c))
235  replacement = underscore;
236  else if (c == '/' || c == ':')
237  replacement = doubleUnderscore;
238  else if (! isValidCharInIdentifier(c))
239  replacement = empty;
240 
241  if (replacement)
242  CFStringReplace(strCF, CFRangeMake(i, 1), replacement);
243  }
244 
245  string ret = makeFromCFString(strCF);
246 
247  CFRelease(strCF);
248  CFRelease(empty);
249  CFRelease(underscore);
250  CFRelease(doubleUnderscore);
251 
252  return ret;
253 }
254 
260 {
261  return isalnum(ch) || ch == '_';
262 }
263 
267 string VuoStringUtilities::transcodeToGraphvizIdentifier(const string &originalString)
268 {
269  string escapedString = originalString;
270  for (string::size_type i = 0; (i = escapedString.find("\\", i)) != std::string::npos; i += 2)
271  escapedString.replace(i, 1, "\\\\");
272  for (string::size_type i = 0; (i = escapedString.find("\"", i)) != std::string::npos; i += 2)
273  escapedString.replace(i, 1, "\\\"");
274  for (string::size_type i = 0; (i = escapedString.find("{", i)) != std::string::npos; i += 2)
275  escapedString.replace(i, 1, "\\{");
276  for (string::size_type i = 0; (i = escapedString.find("}", i)) != std::string::npos; i += 2)
277  escapedString.replace(i, 1, "\\}");
278  for (string::size_type i = 0; (i = escapedString.find("<", i)) != std::string::npos; i += 2)
279  escapedString.replace(i, 1, "\\<");
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 += 3)
285  escapedString.replace(i, 2, " \\ ");
286  return escapedString;
287 }
288 
292 string VuoStringUtilities::transcodeFromGraphvizIdentifier(const string &graphvizIdentifier)
293 {
294  string unescapedString;
295  bool inEscape = false;
296  for (string::const_iterator i = graphvizIdentifier.begin(); i != graphvizIdentifier.end(); ++i)
297  {
298  if (inEscape)
299  {
300  inEscape = false;
301  unescapedString += *i;
302  continue;
303  }
304 
305  if (*i == '\\')
306  {
307  inEscape = true;
308  continue;
309  }
310 
311  unescapedString += *i;
312  }
313  return unescapedString;
314 }
315 
321 string VuoStringUtilities::formUniqueIdentifier(set<string> &takenIdentifiers,
322  const string &preferredIdentifier, const string &identifierPrefix)
323 {
324  auto isIdentifierAvailable = [&takenIdentifiers] (const string &identifier)
325  {
326  return takenIdentifiers.find(identifier) == takenIdentifiers.end();
327  };
328 
329  string uniqueIdentifier = formUniqueIdentifier(isIdentifierAvailable, preferredIdentifier, identifierPrefix);
330  takenIdentifiers.insert(uniqueIdentifier);
331  return uniqueIdentifier;
332 }
333 
339 string VuoStringUtilities::formUniqueIdentifier(std::function<bool(const string &)> isIdentifierAvailable,
340  const string &preferredIdentifier, const string &identifierPrefix)
341 {
342  string unique = preferredIdentifier;
343  string prefix = (! identifierPrefix.empty() ? identifierPrefix : preferredIdentifier);
344  int suffix = 2;
345 
346  while (! isIdentifierAvailable(unique))
347  {
348  ostringstream oss;
349  oss << prefix << suffix++;
350  unique = oss.str();
351  }
352 
353  return unique;
354 }
355 
361 string VuoStringUtilities::generateHtmlFromMarkdown(const string &markdownString)
362 {
363  MMIOT *doc = mkd_string(markdownString.c_str(), markdownString.length(), MKD_NOPANTS);
364  mkd_compile(doc, 0);
365  char *html;
366  mkd_document(doc, &html);
367  string htmlString(html);
368  mkd_cleanup(doc);
369 
370  // Remove the final linebreak from code blocks,
371  // since Qt (unlike typical browser rendering engines) considers that whitespace significant.
372  replaceAll(htmlString, "\n</code></pre>", "</code></pre>");
373 
374  return htmlString;
375 }
376 
382 string VuoStringUtilities::generateHtmlFromMarkdownLine(const string &markdownString)
383 {
384  size_t length = markdownString.length();
385  if (!length)
386  return "";
387 
388  char *html;
389  mkd_line((char *)markdownString.c_str(), length, &html, MKD_NOPANTS);
390  string htmlString(html);
391  free(html);
392  return htmlString;
393 }
394 
408 string VuoStringUtilities::convertToCamelCase(const string &originalString,
409  bool forceFirstLetterToUpper, bool forceFirstLetterToLower, bool forceInterveningLettersToLower,
410  bool allowSeparatorDots)
411 {
412  string camelCaseString;
413  bool first = true;
414  bool uppercaseNext = forceFirstLetterToUpper;
415  bool lowercaseNext = forceFirstLetterToLower;
416  bool previousWasDot = false;
417  for (string::const_iterator i = originalString.begin(); i != originalString.end(); ++i)
418  {
419  if (first && !isalpha(*i))
420  continue;
421  first = false;
422 
423  bool isDot = *i == '.';
424  if (allowSeparatorDots && isDot)
425  {
426  if (previousWasDot)
427  continue;
428  uppercaseNext = false;
429  }
430  else if (!isalnum(*i))
431  {
432  uppercaseNext = true;
433  continue;
434  }
435 
436  if (uppercaseNext)
437  camelCaseString += toupper(*i);
438  else if (lowercaseNext)
439  camelCaseString += tolower(*i);
440  else
441  camelCaseString += *i;
442 
443  uppercaseNext = false;
444  lowercaseNext = forceInterveningLettersToLower;
445  previousWasDot = isDot;
446  }
447 
448  // Trim trailing dots.
449  if (allowSeparatorDots)
450  while (endsWith(camelCaseString, "."))
451  camelCaseString = substrBefore(camelCaseString, ".");
452 
453  return camelCaseString;
454 }
455 
462 string VuoStringUtilities::expandCamelCase(string camelCaseString)
463 {
464  // Only apply these transformations if the whole string matches,
465  // since they may appear as substrings in contexts where they shouldn't be all-caps.
466  if (camelCaseString == "x")
467  return "X";
468  else if (camelCaseString == "y")
469  return "Y";
470  else if (camelCaseString == "z")
471  return "Z";
472  else if (camelCaseString == "w")
473  return "W";
474  else if (camelCaseString == "xy")
475  return "XY";
476  else if (camelCaseString == "osc")
477  return "OSC";
478 
479  string out;
480  out += toupper(camelCaseString[0]);
481 
482  size_t length = camelCaseString.length();
483  for (int i = 1; i < length; ++i)
484  {
485  char c = camelCaseString[i];
486  if (isupper(c) || (isdigit(c) && !isdigit(camelCaseString[i-1])))
487  out += " ";
488  out += c;
489  }
490 
491  string allCaps[]{
492  "2d",
493  "3d",
494  "4d",
495  "Xyzw",
496  "Xyz",
497  "Rgbaw",
498  "Rgba",
499  "Rgbw",
500  "Rgb",
501  "Wwcw",
502  "Cmy",
503  "Hsl",
504  "Hdmi",
505  "Sdi",
506  "Ntsc",
507 // "Pal", // Appears in Leap Motion "Palm Velocity".
508  "Url",
509  "Midi",
510  "Rss",
511  "Csv",
512  "Tsv",
513  "Ascii",
514  "Json",
515  "Xml",
516  "Dmx",
517  };
518  for (auto lower : allCaps)
519  {
520  string upper = lower;
521  std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
522  VuoStringUtilities::replaceAll(out, lower, upper);
523  }
524 
525  return out;
526 }
527 
532 {
533  if (!cfs)
534  return "";
535 
536  CFStringRef cfString = (CFStringRef)cfs;
537 
538  // https://stackoverflow.com/questions/1609565/whats-the-cfstring-equiv-of-nsstrings-utf8string
539  const char *utf8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
540  if (utf8StringPtr)
541  return utf8StringPtr;
542  else
543  {
544  CFIndex maxBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfString), kCFStringEncodingUTF8) + 1;
545  char *t = (char *)calloc(1, maxBytes);
546  CFStringGetCString(cfString, t, maxBytes, kCFStringEncodingUTF8);
547  string s(t);
548  free(t);
549  return s;
550  }
551 }
552 
557 {
558  static const char alphanum[] =
559  "0123456789"
560  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
561  "abcdefghijklmnopqrstuvwxyz";
562 
563  string hash(length, 0);
564  for (int i = 0; i < length; ++i)
565  hash[i] = alphanum[arc4random_uniform(sizeof(alphanum)-1)];
566 
567  return hash;
568 }
569 
570 const std::locale VuoStringUtilities::locale;
571 const std::collate<char> &VuoStringUtilities::collate = std::use_facet<std::collate<char> >(VuoStringUtilities::locale);
572 
578 string VuoStringUtilities::calculateSHA256(const string &s)
579 {
580  unsigned char hash[CC_SHA256_DIGEST_LENGTH];
581  if (!CC_SHA256(s.c_str(), s.length(), hash))
582  throw VuoException("Error: CC_SHA256 failed.");
583 
584  ostringstream oss;
585  oss << setfill('0') << hex;
586  for (int i = 0; i < CC_SHA256_DIGEST_LENGTH; ++i)
587  oss << setw(2) << (int)hash[i];
588  return oss.str();
589 }