Vuo  2.0.0
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  // https://stackoverflow.com/questions/1609565/whats-the-cfstring-equiv-of-nsstrings-utf8string
243 
244  const char *useUTF8StringPtr = NULL;
245  char *freeUTF8StringPtr = NULL;
246 
247  if ((useUTF8StringPtr = CFStringGetCStringPtr(strCF, kCFStringEncodingUTF8)) == NULL)
248  {
249  CFIndex maxBytes = 4 * strLength + 1;
250  freeUTF8StringPtr = (char *)malloc(maxBytes);
251  CFStringGetCString(strCF, freeUTF8StringPtr, maxBytes, kCFStringEncodingUTF8);
252  useUTF8StringPtr = freeUTF8StringPtr;
253  }
254 
255  string ret = useUTF8StringPtr;
256 
257  if (freeUTF8StringPtr != NULL)
258  free(freeUTF8StringPtr);
259 
260  CFRelease(strCF);
261  CFRelease(empty);
262  CFRelease(underscore);
263  CFRelease(doubleUnderscore);
264  free(strBuf);
265 
266  return ret;
267 }
268 
274 {
275  return isalnum(ch) || ch == '_';
276 }
277 
281 string VuoStringUtilities::transcodeToGraphvizIdentifier(const string &originalString)
282 {
283  string escapedString = originalString;
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 += 2)
295  escapedString.replace(i, 1, "\\>");
296  for (string::size_type i = 0; (i = escapedString.find("|", i)) != std::string::npos; i += 2)
297  escapedString.replace(i, 1, "\\|");
298  for (string::size_type i = 0; (i = escapedString.find(" ", i)) != std::string::npos; i += 3)
299  escapedString.replace(i, 2, " \\ ");
300  return escapedString;
301 }
302 
306 string VuoStringUtilities::transcodeFromGraphvizIdentifier(const string &graphvizIdentifier)
307 {
308  string unescapedString;
309  bool inEscape = false;
310  for (string::const_iterator i = graphvizIdentifier.begin(); i != graphvizIdentifier.end(); ++i)
311  {
312  if (inEscape)
313  {
314  inEscape = false;
315  unescapedString += *i;
316  continue;
317  }
318 
319  if (*i == '\\')
320  {
321  inEscape = true;
322  continue;
323  }
324 
325  unescapedString += *i;
326  }
327  return unescapedString;
328 }
329 
335 string VuoStringUtilities::formUniqueIdentifier(set<string> &takenIdentifiers,
336  const string &preferredIdentifier, const string &identifierPrefix)
337 {
338  auto isIdentifierAvailable = [&takenIdentifiers] (const string &identifier)
339  {
340  return takenIdentifiers.find(identifier) == takenIdentifiers.end();
341  };
342 
343  string uniqueIdentifier = formUniqueIdentifier(isIdentifierAvailable, preferredIdentifier, identifierPrefix);
344  takenIdentifiers.insert(uniqueIdentifier);
345  return uniqueIdentifier;
346 }
347 
353 string VuoStringUtilities::formUniqueIdentifier(std::function<bool(const string &)> isIdentifierAvailable,
354  const string &preferredIdentifier, const string &identifierPrefix)
355 {
356  string unique = preferredIdentifier;
357  string prefix = (! identifierPrefix.empty() ? identifierPrefix : preferredIdentifier);
358  int suffix = 2;
359 
360  while (! isIdentifierAvailable(unique))
361  {
362  ostringstream oss;
363  oss << prefix << suffix++;
364  unique = oss.str();
365  }
366 
367  return unique;
368 }
369 
375 string VuoStringUtilities::generateHtmlFromMarkdown(const string &markdownString)
376 {
377  MMIOT *doc = mkd_string(markdownString.c_str(), markdownString.length(), MKD_NOPANTS);
378  mkd_compile(doc, 0);
379  char *html;
380  mkd_document(doc, &html);
381  string htmlString(html);
382  mkd_cleanup(doc);
383 
384  // Remove the final linebreak from code blocks,
385  // since Qt (unlike typical browser rendering engines) considers that whitespace significant.
386  replaceAll(htmlString, "\n</code></pre>", "</code></pre>");
387 
388  return htmlString;
389 }
390 
396 string VuoStringUtilities::generateHtmlFromMarkdownLine(const string &markdownString)
397 {
398  size_t length = markdownString.length();
399  if (!length)
400  return "";
401 
402  char *html;
403  mkd_line((char *)markdownString.c_str(), length, &html, MKD_NOPANTS);
404  string htmlString(html);
405  free(html);
406  return htmlString;
407 }
408 
422 string VuoStringUtilities::convertToCamelCase(const string &originalString,
423  bool forceFirstLetterToUpper, bool forceFirstLetterToLower, bool forceInterveningLettersToLower,
424  bool allowSeparatorDots)
425 {
426  string camelCaseString;
427  bool first = true;
428  bool uppercaseNext = forceFirstLetterToUpper;
429  bool lowercaseNext = forceFirstLetterToLower;
430  bool previousWasDot = false;
431  for (string::const_iterator i = originalString.begin(); i != originalString.end(); ++i)
432  {
433  if (first && !isalpha(*i))
434  continue;
435  first = false;
436 
437  bool isDot = *i == '.';
438  if (allowSeparatorDots && isDot)
439  {
440  if (previousWasDot)
441  continue;
442  uppercaseNext = false;
443  }
444  else if (!isalnum(*i))
445  {
446  uppercaseNext = true;
447  continue;
448  }
449 
450  if (uppercaseNext)
451  camelCaseString += toupper(*i);
452  else if (lowercaseNext)
453  camelCaseString += tolower(*i);
454  else
455  camelCaseString += *i;
456 
457  uppercaseNext = false;
458  lowercaseNext = forceInterveningLettersToLower;
459  previousWasDot = isDot;
460  }
461 
462  // Trim trailing dots.
463  if (allowSeparatorDots)
464  while (endsWith(camelCaseString, "."))
465  camelCaseString = substrBefore(camelCaseString, ".");
466 
467  return camelCaseString;
468 }
469 
476 string VuoStringUtilities::expandCamelCase(string camelCaseString)
477 {
478  // Only apply these transformations if the whole string matches,
479  // since they may appear as substrings in contexts where they shouldn't be all-caps.
480  if (camelCaseString == "x")
481  return "X";
482  else if (camelCaseString == "y")
483  return "Y";
484  else if (camelCaseString == "z")
485  return "Z";
486  else if (camelCaseString == "w")
487  return "W";
488  else if (camelCaseString == "xy")
489  return "XY";
490  else if (camelCaseString == "osc")
491  return "OSC";
492 
493  string out;
494  out += toupper(camelCaseString[0]);
495 
496  size_t length = camelCaseString.length();
497  for (int i = 1; i < length; ++i)
498  {
499  char c = camelCaseString[i];
500  if (isupper(c) || (isdigit(c) && !isdigit(camelCaseString[i-1])))
501  out += " ";
502  out += c;
503  }
504 
505  string allCaps[]{
506  "2d",
507  "3d",
508  "4d",
509  "Xyzw",
510  "Xyz",
511  "Rgbaw",
512  "Rgba",
513  "Rgbw",
514  "Rgb",
515  "Wwcw",
516  "Cmy",
517  "Hsl",
518  "Hdmi",
519  "Sdi",
520  "Ntsc",
521 // "Pal", // Appears in Leap Motion "Palm Velocity".
522  "Url",
523  "Midi",
524  "Rss",
525  "Csv",
526  "Tsv",
527  "Ascii",
528  "Json",
529  "Xml",
530  "Dmx",
531  };
532  for (auto lower : allCaps)
533  {
534  string upper = lower;
535  std::transform(upper.begin(), upper.end(), upper.begin(), ::toupper);
536  VuoStringUtilities::replaceAll(out, lower, upper);
537  }
538 
539  return out;
540 }
541 
546 {
547  static const char alphanum[] =
548  "0123456789"
549  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
550  "abcdefghijklmnopqrstuvwxyz";
551 
552  string hash(length, 0);
553  for (int i = 0; i < length; ++i)
554  hash[i] = alphanum[arc4random_uniform(sizeof(alphanum)-1)];
555 
556  return hash;
557 }
558 
559 const std::locale VuoStringUtilities::locale;
560 const std::collate<char> &VuoStringUtilities::collate = std::use_facet<std::collate<char> >(VuoStringUtilities::locale);