matchVCardPrefixedField static method
Implementation
static List<List<String>>? matchVCardPrefixedField(
String prefix,
String rawText,
bool trim,
bool parseFieldDivider,
) {
List<List<String>>? matches;
int i = 0;
final max = rawText.length;
final reg = RegExp('(?:^|\n)$prefix(?:;([^:]*))?:', caseSensitive: false);
while (i < max) {
// At start or after newline, match prefix, followed by optional metadata
// (led by ;) ultimately ending in colon
final regMatches = reg.allMatches(rawText, i);
if (regMatches.isEmpty) break;
final matcher = regMatches.first;
i = matcher.end; // group 0 = whole pattern; end(0) is past final colon
if (i > 0) {
// i--; // Find from i-1 not i since looking at the preceding character
}
final metadataString = matcher.group(1); // group 1 = metadata substring
List<String>? metadata;
bool quotedPrintable = false;
String? quotedPrintableCharset;
String? valueType;
if (metadataString != null) {
for (String metadatum in metadataString.split(_semicolon)) {
metadata ??= [];
metadata.add(metadatum);
final metadatumTokens = metadatum.split(_equal); // todo , 2
if (metadatumTokens.length > 1) {
final key = metadatumTokens[0];
final value = metadatumTokens[1];
if ('ENCODING' == key.toUpperCase() &&
'QUOTED-PRINTABLE' == value.toUpperCase()) {
quotedPrintable = true;
} else if ('CHARSET' == key.toUpperCase()) {
quotedPrintableCharset = value;
} else if ('VALUE' == key.toUpperCase()) {
valueType = value;
}
}
}
}
final matchStart = i; // Found the start of a match here
while ((i = rawText.indexOf('\n', i)) >= 0) {
// Really, end in \r\n
if (i < rawText.length - 1 && // But if followed by tab or space,
(rawText[i + 1] == ' ' || // this is only a continuation
rawText[i + 1] == '\t')) {
i += 2; // Skip \n and continutation whitespace
} else if (quotedPrintable && // If preceded by = in quoted printable
((i >= 1 && rawText[i - 1] == '=') || // this is a continuation
(i >= 2 && rawText[i - 2] == '='))) {
i++; // Skip \n
} else {
break;
}
}
if (i < 0) {
// No terminating end character? uh, done. Set i such that loop terminates and break
i = max;
} else if (i > matchStart) {
// found a match
matches ??= [];
if (i >= 1 && rawText[i - 1] == '\r') {
i--; // Back up over \r, which really should be there
}
String element = rawText.substring(matchStart, i);
if (trim) {
element = element.trim();
}
if (quotedPrintable) {
element = _decodeQuotedPrintable(element, quotedPrintableCharset);
if (parseFieldDivider) {
element = element.replaceAll(_unescapedSemicolons, '\n').trim();
}
} else {
if (parseFieldDivider) {
element = element.replaceAll(_unescapedSemicolons, '\n').trim();
}
element = element.replaceAll(_crLfSpaceTab, '');
element = element.replaceAll(_newlineEscape, '\n');
element = element.replaceAllMapped(_vcardEscapes, (m) => '${m[1]}');
}
// Only handle VALUE=uri specially
if ('uri' == valueType) {
// Don't actually support dereferencing URIs, but use scheme-specific part not URI
// as value, to support tel: and mailto:
try {
element = Uri.parse(element).path;
} catch (_) {
// IllegalArgumentException
// ignore
}
}
if (metadata == null) {
final match = <String>[];
match.add(element);
matches.add(match);
} else {
metadata.insert(0, element);
matches.add(metadata);
}
i++;
} else {
i++;
}
}
return matches;
}