10using System.Collections.Generic;
18 internal static class ExternalLinkFormulaUtils
23 internal static bool DetectExternalLinkId(
string expression)
25 return DetectExternalLinkId(expression,
null);
32 internal static bool DetectExternalLinkId(
string expression,
string targetId)
34 if (targetId !=
null && !ParserUtils.IsValidExternalLinkId(targetId))
36 throw new ArgumentException(
37 $
"The target ID '{targetId}' is not a valid external link ID.",
41 if (
string.IsNullOrEmpty(expression))
46 int firstOpeningBracket = expression.IndexOf(
'[');
47 if (firstOpeningBracket < 0)
52 if (targetId !=
null && expression.IndexOf(targetId, StringComparison.Ordinal) < 0)
57 int firstQuote = expression.IndexOf(
'"');
58 int scanStart = firstQuote >= 0 && firstQuote < firstOpeningBracket
60 : firstOpeningBracket;
61 bool insideStringConstant =
false;
63 for (
int i = scanStart; i < expression.Length; i++)
65 char current = expression[i];
68 if (insideStringConstant && i + 1 < expression.Length && expression[i + 1] ==
'"')
73 insideStringConstant = !insideStringConstant;
77 if (insideStringConstant || current !=
'[' ||
78 !ParserUtils.TryReadExternalLinkId(expression, i, out
int identifierLength))
83 if (targetId ==
null ||
84 (identifierLength == targetId.Length &&
85 string.CompareOrdinal(expression, i, targetId, 0, identifierLength) == 0))
90 i += identifierLength - 1;
100 internal static bool TryFindUnresolvedExternalLink(
string expression, out
string token)
103 if (
string.IsNullOrEmpty(expression) || expression.IndexOf(
'[') < 0)
107 bool insideStringConstant =
false;
108 for (
int i = 0; i < expression.Length; i++)
110 char current = expression[i];
113 if (insideStringConstant && i + 1 < expression.Length && expression[i + 1] ==
'"')
118 insideStringConstant = !insideStringConstant;
121 if (insideStringConstant || current !=
'[')
125 int closingBracket = expression.IndexOf(
']', i + 1);
126 if (closingBracket <= i + 1)
130 string bracketToken = expression.Substring(i, closingBracket - i + 1);
131 if (ParserUtils.IsValidExternalLinkId(bracketToken) || HasStructuredReferencePrefix(expression, i))
136 if (FormsExternalReference(expression, closingBracket))
138 token = bracketToken;
151 internal static string ReplaceExternalLinkId(
string expression, Dictionary<string, ExternalLink> links)
153 if (expression ==
null || expression.Length == 0 || links ==
null || links.Count == 0)
157 int firstOpeningBracket = expression.IndexOf(
'[');
158 if (firstOpeningBracket < 0)
162 bool insideStringConstant =
false;
163 int unchangedSectionStart = 0;
164 StringBuilder builder =
null;
166 for (
int i = firstOpeningBracket; i < expression.Length; i++)
168 char current = expression[i];
171 if (insideStringConstant && i + 1 < expression.Length && expression[i + 1] ==
'"')
176 insideStringConstant = !insideStringConstant;
180 if (insideStringConstant || current !=
'[' ||
181 !ParserUtils.TryReadExternalLinkId(expression, i, out
int identifierLength))
186 string identifier = expression.Substring(i, identifierLength);
187 if (!links.TryGetValue(identifier, out ExternalLink externalLink))
191 if (externalLink ==
null)
193 throw new ArgumentException($
"The external link '{identifier}' is null.", nameof(links));
196 string replacement = externalLink.ReadableReferenceToken;
197 if (
string.IsNullOrEmpty(replacement))
199 throw new ArgumentException(
200 $
"The external link '{identifier}' does not provide a usable URI.",
206 builder =
new StringBuilder(expression.Length + replacement.Length);
208 builder.Append(expression, unchangedSectionStart, i - unchangedSectionStart);
209 builder.Append(replacement);
211 i += identifierLength - 1;
212 unchangedSectionStart = i + 1;
219 builder.Append(expression, unchangedSectionStart, expression.Length - unchangedSectionStart);
220 return builder.ToString();
223 private static bool HasStructuredReferencePrefix(
string expression,
int openingBracket)
225 if (openingBracket == 0)
229 char previous = expression[openingBracket - 1];
230 return char.IsLetterOrDigit(previous) || previous ==
'_' || previous ==
'.';
233 private static bool FormsExternalReference(
string expression,
int closingBracket)
235 bool hasWorksheetName =
false;
236 for (
int i = closingBracket + 1; i < expression.Length; i++)
238 char current = expression[i];
241 return hasWorksheetName || i == closingBracket + 1;
243 if (IsReferenceTerminator(current))
247 if (!
char.IsWhiteSpace(current) && current !=
'\'')
249 hasWorksheetName =
true;
255 private static bool IsReferenceTerminator(
char character)