Vuo  2.0.1
VuoStringUtilities.cc
Go to the documentation of this file.
1 
10 #include <sstream>
11 #include <CoreFoundation/CoreFoundation.h>
12 #include "VuoStringUtilities.hh"
13 
14 extern "C" {
15 #include "mkdio.h"
16 }
17 
21 bool VuoStringUtilities::beginsWith(string wholeString, string beginning)
22 {
23  return wholeString.length() >= beginning.length() && wholeString.substr(0, beginning.length()) == beginning;
24 }
25 
29 bool VuoStringUtilities::endsWith(string wholeString, string ending)
30 {
31  if (wholeString.length() < ending.length())
32  return false;
33 
34  return wholeString.compare(wholeString.length()-ending.length(), ending.length(), ending) == 0;
35 }
36 
41 string VuoStringUtilities::substrAfter(string wholeString, string beginning)
42 {
43  if (! beginsWith(wholeString, beginning))
44  return "";
45 
46  return wholeString.substr(beginning.length());
47 }
48 
53 string VuoStringUtilities::substrBefore(string wholeString, string ending)
54 {
55  if (! endsWith(wholeString, ending))
56  return "";
57 
58  return wholeString.substr(0, wholeString.length()-ending.length());
59 }
60 
65 string VuoStringUtilities::replaceAll(string wholeString, char originalChar, char replacementChar)
66 {
67  string outString = wholeString;
68  size_t pos = 0;
69  while ((pos = wholeString.find_first_of(originalChar, pos)) != string::npos)
70  {
71  outString[pos] = replacementChar;
72  pos = pos + 1;
73  }
74  return outString;
75 }
76 
82 size_t VuoStringUtilities::replaceAll(string &wholeString, string originalSubstring, string replacementSubstring)
83 {
84  size_t replacementCount = 0;
85  size_t startPos = 0;
86  while ((startPos = wholeString.find(originalSubstring, startPos)) != string::npos)
87  {
88  wholeString.replace(startPos, originalSubstring.length(), replacementSubstring);
89  startPos += replacementSubstring.length();
90  ++replacementCount;
91  }
92  return replacementCount;
93 }
94 
98 vector<string> VuoStringUtilities::split(const string &wholeString, char delimiter)
99 {
100  vector<string> tokens;
101  istringstream iss(wholeString);
102  string token;
103  while( getline(iss, token, delimiter) )
104  tokens.push_back(token);
105  return tokens;
106 }
107 
111 string VuoStringUtilities::join(vector<string> partialStrings, char delimiter)
112 {
113  string delimiterStr(1, delimiter);
114  return join(partialStrings, delimiterStr);
115 }
116 
120 string VuoStringUtilities::join(vector<string> partialStrings, string delimiter)
121 {
122  string wholeString;
123  for (vector<string>::iterator i = partialStrings.begin(); i != partialStrings.end(); )
124  {
125  wholeString += *i;
126  if (++i != partialStrings.end())
127  wholeString += delimiter;
128  }
129  return wholeString;
130 }
131 
135 string VuoStringUtilities::join(set<string> partialStrings, string delimiter)
136 {
137  string wholeString;
138  for (set<string>::iterator i = partialStrings.begin(); i != partialStrings.end(); )
139  {
140  wholeString += *i;
141  if (++i != partialStrings.end())
142  wholeString += delimiter;
143  }
144  return wholeString;
145 }
146 
154 string VuoStringUtilities::trim(string originalString)
155 {
156  string whitespace = " \t\v\n\r\f";
157 
158  string::size_type begin = originalString.find_first_not_of(whitespace);
159  if (begin == std::string::npos)
160  return "";
161 
162  string::size_type end = originalString.find_last_not_of(whitespace);
163 
164  return originalString.substr(begin, end - begin + 1);
165 }
166 
173 string VuoStringUtilities::buildCompositionIdentifier(const string &parentCompositionIdentifier, const string &nodeIdentifier)
174 {
175  return parentCompositionIdentifier + "/" + nodeIdentifier;
176 }
177 
182 string VuoStringUtilities::buildPortIdentifier(const string &nodeIdentifier, const string &portName)
183 {
184  return nodeIdentifier + ":" + portName;
185 }
186 
191 string VuoStringUtilities::prefixSymbolName(string symbolName, string moduleKey)
192 {
193  return transcodeToIdentifier(moduleKey) + "__" + symbolName;
194 }
195 
203 {
204  CFMutableStringRef strCF = CFStringCreateMutable(NULL, 0);
205  CFStringAppendCString(strCF, str.c_str(), kCFStringEncodingUTF8);
206 
207  CFStringNormalize(strCF, kCFStringNormalizationFormD); // decomposes combining characters, so accents/diacritics are separated from their letters
208 
209  CFIndex strLength = CFStringGetLength(strCF);
210  UniChar *strBuf = (UniChar *)malloc(strLength * sizeof(UniChar));
211  if (!strBuf)
212  {
213  CFRelease(strCF);
214  return string();
215  }
216  CFStringGetCharacters(strCF, CFRangeMake(0, strLength), strBuf);
217 
218  CFStringRef empty = CFStringCreateWithCString(NULL, "", kCFStringEncodingUTF8);
219  CFStringRef underscore = CFStringCreateWithCString(NULL, "_", kCFStringEncodingUTF8);
220  CFStringRef doubleUnderscore = CFStringCreateWithCString(NULL, "__", kCFStringEncodingUTF8);
221  if (!empty || !underscore || !doubleUnderscore)
222  return string();
223 
224  for (CFIndex i = strLength-1; i >= 0; --i)
225  {
226  UniChar c = strBuf[i];
227 
228  CFStringRef replacement = NULL;
229  if (c > 127) // non-ASCII
230  replacement = empty;
231  else if (c == '.' || isspace(c))
232  replacement = underscore;
233  else if (c == '/' || c == ':')
234  replacement = doubleUnderscore;
235  else if (! isValidCharInIdentifier(c))
236  replacement = empty;
237 
238  if (replacement)
239  CFStringReplace(strCF, CFRangeMake(i, 1), replacement);
240  }
241 
242  string ret = makeFromCFString(strCF);
243 
244  CFRelease(strCF);
245  CFRelease(empty);
246  CFRelease(underscore);
247  CFRelease(doubleUnderscore);
248 
249  return ret;
250 }
251 
257 {
258  return isalnum(ch) || ch == '_';
259 }
260 
264 string VuoStringUtilities::transcodeToGraphvizIdentifier(const string &originalString)
265 {
266  string escapedString = originalString;
267  for (string::size_type i = 0; (i = escapedString.find("\\", i)) != std::string::npos; i += 2)
268  escapedString.replace(i, 1, "\\\\");
269  for (string::size_type i = 0; (i = escapedString.find("\"", i)) != std::string::npos; i += 2)
270  escapedString.replace(i, 1, "\\\"");
271  for (string::size_type i = 0; (i = escapedString.find("{", i)) != std::string::npos; i += 2)
272  escapedString.replace(i, 1, "\\{");
273  for (string::size_type i = 0; (i = escapedString.find("}", i)) != std::string::npos; i += 2)
274  escapedString.replace(i, 1, "\\}");
275  for (string::size_type i = 0; (i = escapedString.find("<", i)) != std::string::npos; i += 2)
276  escapedString.replace(i, 1, "\\<");
277  for (string::size_type i = 0; (i = escapedString.find(">", i)) != std::string::npos; i += 2)
278  escapedString.replace(i, 1, "\\>");
279  for (string::size_type i = 0; (i = escapedString.find("|", i)) != std::string::npos; i += 2)
280  escapedString.replace(i, 1, "\\|");
281  for (string::size_type i = 0; (i = escapedString.find(" ", i)) != std::string::npos; i += 3)
282  escapedString.replace(i, 2, " \\ ");
283  return escapedString;
284 }
285 
289 string VuoStringUtilities::transcodeFromGraphvizIdentifier(const string &graphvizIdentifier)
290 {
291  string unescapedString;
292  bool inEscape = false;
293  for (string::const_iterator i = graphvizIdentifier.begin(); i != graphvizIdentifier.end(); ++i)
294  {
295  if (inEscape)
296  {
297  inEscape = false;
298  unescapedString += *i;
299  continue;
300  }
301 
302  if (*i == '\\')
303  {
304  inEscape = true;
305  continue;
306  }
307 
308  unescapedString += *i;
309  }
310  return unescapedString;
311 }
312 
318 string VuoStringUtilities::formUniqueIdentifier(set<string> &takenIdentifiers,
319  const string &preferredIdentifier, const string &identifierPrefix)
320 {
321  auto isIdentifierAvailable = [&takenIdentifiers] (const string &identifier)
322  {
323  return takenIdentifiers.find(identifier) == takenIdentifiers.end();
324  };
325 
326  string uniqueIdentifier = formUniqueIdentifier(isIdentifierAvailable, preferredIdentifier, identifierPrefix);
327  takenIdentifiers.insert(uniqueIdentifier);
328  return uniqueIdentifier;
329 }
330 
336 string VuoStringUtilities::formUniqueIdentifier(std::function<bool(const string &)> isIdentifierAvailable,
337  const string &preferredIdentifier, const string &identifierPrefix)
338 {
339  string unique = preferredIdentifier;
340  string prefix = (! identifierPrefix.empty() ? identifierPrefix : preferredIdentifier);
341  int suffix = 2;
342 
343  while (! isIdentifierAvailable(unique))
344  {
345  ostringstream oss;
346  oss << prefix << suffix++;
347  unique = oss.str();
348  }
349 
350  return unique;
351 }
352 
358 string VuoStringUtilities::generateHtmlFromMarkdown(const string &markdownString)
359 {
360  MMIOT *doc = mkd_string(markdownString.c_str(), markdownString.length(), MKD_NOPANTS);
361  mkd_compile(doc, 0);
362  char *html;
363  mkd_document(doc, &html);
364  string htmlString(html);
365  mkd_cleanup(doc);
366 
367  // Remove the final linebreak from code blocks,
368  // since Qt (unlike typical browser rendering engines) considers that whitespace significant.
369  replaceAll(htmlString, "\n</code></pre>", "</code></pre>");
370 
371  return htmlString;
372 }
373 
379 string VuoStringUtilities::generateHtmlFromMarkdownLine(const string &markdownString)
380 {
381  size_t length = markdownString.length();
382  if (!length)
383  return "";
384 
385  char *html;
386  mkd_line((char *)markdownString.c_str(), length, &html, MKD_NOPANTS);
387  string htmlString(html);
388  free(html);
389  return htmlString;
390 }
391 
405 string VuoStringUtilities::convertToCamelCase(const string &originalString,
406  bool forceFirstLetterToUpper, bool forceFirstLetterToLower, bool forceInterveningLettersToLower,
407  bool allowSeparatorDots)
408 {
409  string camelCaseString;
410  bool first = true;
411  bool uppercaseNext = forceFirstLetterToUpper;
412  bool lowercaseNext = forceFirstLetterToLower;
413  bool previousWasDot = false;
414  for (string::const_iterator i = originalString.begin(); i != originalString.end(); ++i)
415  {
416  if (first && !isalpha(*i))
417  continue;
418  first = false;
419 
420  bool isDot = *i == '.';
421  if (allowSeparatorDots && isDot)
422  {
423  if (previousWasDot)
424  continue;
425  uppercaseNext = false;
426  }
427  else if (!isalnum(*i))
428  {
429  uppercaseNext = true;
430  continue;
431  }
432 
433  if (uppercaseNext)
434  camelCaseString += toupper(*i);
435  else if (lowercaseNext)
436  camelCaseString += tolower(*i);
437  else
438  camelCaseString += *i;
439 
440  uppercaseNext = false;
441  lowercaseNext = forceInterveningLettersToLower;
442  previousWasDot = isDot;
443  }
444 
445  // Trim trailing dots.
446  if (allowSeparatorDots)
447  while (endsWith(camelCaseString, "."))
448  camelCaseString = substrBefore(camelCaseString, ".");
449 
450  return camelCaseString;
451 }
452 
459 string VuoStringUtilities::expandCamelCase(string camelCaseString)
460 {
461  // Only apply these transformations if the whole string matches,
462  // since they may appear as substrings in contexts where they shouldn't be all-caps.
463  if (camelCaseString == "x")
464  return "X";
465  else if (camelCaseString == "y")
466  return "Y";
467  else if (camelCaseString == "z")
468  return "Z";
469  else if (camelCaseString == "w")
470  return "W";
471  else if (camelCaseString == "xy")
472  return "XY";
473  else if (camelCaseString == "osc")
474  return "OSC";
475 
476  string out;
477  out += toupper(camelCaseString[0]);
478 
479  size_t length = camelCaseString.length();
480  for (int i = 1; i < length; ++i)
481  {
482  char c = camelCaseString[i];
483  if (isupper(c) || (isdigit(c) && !isdigit(camelCaseString[i-1])))
484  out += " ";
485  out += c;
486  }
487 
488  string allCaps[]{
489  "2d",
490  "3d",
491  "4d",
492  "Xyzw",
493  "Xyz",
494  "Rgbaw",
495  "Rgba",
496  "Rgbw",
497  "Rgb",
498  "Wwcw",
499  "Cmy",
500  "Hsl",
501  "Hdmi",
502  "Sdi",
503  "Ntsc",
504 // "Pal", // Appears in Leap Motion "Palm Velocity".
505  "Url",
506  "Midi",
507  "Rss",
508  "Csv",
509  "Tsv",
510  "Ascii",
511  "Json",
512  "Xml",
513  "Dmx",
514  };
515  for (auto lower : allCaps)
516  {
517  string upper = lower;
518  std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
519  VuoStringUtilities::replaceAll(out, lower, upper);
520  }
521 
522  return out;
523 }
524 
529 {
530  if (!cfs)
531  return "";
532 
533  CFStringRef cfString = (CFStringRef)cfs;
534 
535  // https://stackoverflow.com/questions/1609565/whats-the-cfstring-equiv-of-nsstrings-utf8string
536  const char *utf8StringPtr = CFStringGetCStringPtr(cfString, kCFStringEncodingUTF8);
537  if (utf8StringPtr)
538  return utf8StringPtr;
539  else
540  {
541  CFIndex maxBytes = CFStringGetMaximumSizeForEncoding(CFStringGetLength(cfString), kCFStringEncodingUTF8) + 1;
542  char *t = (char *)calloc(1, maxBytes);
543  CFStringGetCString(cfString, t, maxBytes, kCFStringEncodingUTF8);
544  string s(t);
545  free(t);
546  return s;
547  }
548 }
549 
554 {
555  static const char alphanum[] =
556  "0123456789"
557  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
558  "abcdefghijklmnopqrstuvwxyz";
559 
560  string hash(length, 0);
561  for (int i = 0; i < length; ++i)
562  hash[i] = alphanum[arc4random_uniform(sizeof(alphanum)-1)];
563 
564  return hash;
565 }
566 
567 const std::locale VuoStringUtilities::locale;
568 const std::collate<char> &VuoStringUtilities::collate = std::use_facet<std::collate<char> >(VuoStringUtilities::locale);