src/SSC/Internal/XPathLikePathParser.cs
実行済みCoberturaのhit数が1以上の行です。
未実行カバレッジ対象ですがhit数が0の行です。
対象外Coberturaに実行対象として記録されていない行です。
| 行 | 行状態 | Hits | Source |
|---|---|---|---|
| 1 | 対象外 | — | namespace SSC.Internal; |
| 2 | 対象外 | — | |
| 3 | 対象外 | — | internal enum XPathLikePathSelectorKind |
| 4 | 対象外 | — | { |
| 5 | 対象外 | — | Key, |
| 6 | 対象外 | — | Ordinal, |
| 7 | 対象外 | — | } |
| 8 | 対象外 | — | |
| 9 | 対象外 | — | internal sealed class XPathLikePath |
| 10 | 対象外 | — | { |
| 11 | 実行済み | 164 | public XPathLikePath(string? rootName, IReadOnlyList<XPathLikePathSegment> segments) |
| 12 | 対象外 | — | { |
| 13 | 実行済み | 164 | RootName = rootName; |
| 14 | 実行済み | 164 | Segments = segments; |
| 15 | 実行済み | 164 | } |
| 16 | 対象外 | — | |
| 17 | 実行済み | 3 | public string? RootName { get; } |
| 18 | 対象外 | — | |
| 19 | 実行済み | 363 | public IReadOnlyList<XPathLikePathSegment> Segments { get; } |
| 20 | 対象外 | — | } |
| 21 | 対象外 | — | |
| 22 | 対象外 | — | internal sealed class XPathLikePathSegment |
| 23 | 対象外 | — | { |
| 24 | 実行済み | 334 | public XPathLikePathSegment(string memberName, XPathLikePathSelector? selector) |
| 25 | 対象外 | — | { |
| 26 | 実行済み | 334 | MemberName = memberName; |
| 27 | 実行済み | 334 | Selector = selector; |
| 28 | 実行済み | 334 | } |
| 29 | 対象外 | — | |
| 30 | 実行済み | 266 | public string MemberName { get; } |
| 31 | 対象外 | — | |
| 32 | 実行済み | 376 | public XPathLikePathSelector? Selector { get; } |
| 33 | 対象外 | — | } |
| 34 | 対象外 | — | |
| 35 | 対象外 | — | internal sealed class XPathLikePathSelector |
| 36 | 対象外 | — | { |
| 37 | 実行済み | 145 | private XPathLikePathSelector(XPathLikePathSelectorKind kind, string? keyText, int? ordinal) |
| 38 | 対象外 | — | { |
| 39 | 実行済み | 145 | Kind = kind; |
| 40 | 実行済み | 145 | KeyText = keyText; |
| 41 | 実行済み | 145 | Ordinal = ordinal; |
| 42 | 実行済み | 145 | } |
| 43 | 対象外 | — | |
| 44 | 実行済み | 120 | public XPathLikePathSelectorKind Kind { get; } |
| 45 | 対象外 | — | |
| 46 | 実行済み | 95 | public string? KeyText { get; } |
| 47 | 対象外 | — | |
| 48 | 実行済み | 8 | public int? Ordinal { get; } |
| 49 | 対象外 | — | |
| 50 | 対象外 | — | public static XPathLikePathSelector Key(string keyText) |
| 51 | 対象外 | — | { |
| 52 | 実行済み | 132 | return new XPathLikePathSelector(XPathLikePathSelectorKind.Key, keyText, null); |
| 53 | 対象外 | — | } |
| 54 | 対象外 | — | |
| 55 | 対象外 | — | public static XPathLikePathSelector FromOrdinal(int ordinal) |
| 56 | 対象外 | — | { |
| 57 | 実行済み | 13 | return new XPathLikePathSelector(XPathLikePathSelectorKind.Ordinal, null, ordinal); |
| 58 | 対象外 | — | } |
| 59 | 対象外 | — | } |
| 60 | 対象外 | — | |
| 61 | 対象外 | — | internal static class XPathLikePathParser |
| 62 | 対象外 | — | { |
| 63 | 対象外 | — | public static bool TryParse(string path, out XPathLikePath? parsedPath) |
| 64 | 対象外 | — | { |
| 65 | 実行済み | 81 | return TryParse(path, rootName: null, allowsEmptyKeySelector: false, out parsedPath); |
| 66 | 対象外 | — | } |
| 67 | 対象外 | — | |
| 68 | 対象外 | — | public static bool TryParse(string path, string? rootName, out XPathLikePath? parsedPath) |
| 69 | 対象外 | — | { |
| 70 | 実行済み | 40 | return TryParse(path, rootName, allowsEmptyKeySelector: false, out parsedPath); |
| 71 | 対象外 | — | } |
| 72 | 対象外 | — | |
| 73 | 対象外 | — | /// <summary> |
| 74 | 対象外 | — | /// 既存の <c>GetDiffEntries()</c> が空文字列の比較 key に対して生成する legacy 空 selector を、空 key selector として解析します。 |
| 75 | 対象外 | — | /// </summary> |
| 76 | 対象外 | — | /// <param name="path">解析する既存差分 path。</param> |
| 77 | 対象外 | — | /// <param name="parsedPath">解析に成功した場合の構造化 path。</param> |
| 78 | 対象外 | — | /// <returns>解析に成功した場合は <see langword="true"/>、それ以外は <see langword="false"/>。</returns> |
| 79 | 対象外 | — | internal static bool TryParseLegacyEmptyKeySelectorPath(string path, out XPathLikePath? parsedPath) |
| 80 | 対象外 | — | { |
| 81 | 実行済み | 60 | return TryParse(path, rootName: null, allowsEmptyKeySelector: true, out parsedPath); |
| 82 | 対象外 | — | } |
| 83 | 対象外 | — | |
| 84 | 対象外 | — | private static bool TryParse( |
| 85 | 対象外 | — | string path, |
| 86 | 対象外 | — | string? rootName, |
| 87 | 対象外 | — | bool allowsEmptyKeySelector, |
| 88 | 対象外 | — | out XPathLikePath? parsedPath) |
| 89 | 対象外 | — | { |
| 90 | 実行済み | 181 | parsedPath = null; |
| 91 | 実行済み | 181 | if (string.IsNullOrEmpty(path)) |
| 92 | 対象外 | — | { |
| 93 | 実行済み | 1 | return false; |
| 94 | 対象外 | — | } |
| 95 | 対象外 | — | |
| 96 | 実行済み | 180 | if (!TrySplitSegments(path, out var rawSegments)) |
| 97 | 対象外 | — | { |
| 98 | 実行済み | 7 | return false; |
| 99 | 対象外 | — | } |
| 100 | 対象外 | — | |
| 101 | 実行済み | 173 | var parsedRootName = GetRootName(rawSegments, rootName); |
| 102 | 実行済み | 173 | var segmentStartIndex = parsedRootName is null ? 0 : 1; |
| 103 | 実行済み | 173 | var segments = new List<XPathLikePathSegment>(rawSegments.Count - segmentStartIndex); |
| 104 | 実行済み | 1014 | for (var index = segmentStartIndex; index < rawSegments.Count; index++) |
| 105 | 対象外 | — | { |
| 106 | 実行済み | 343 | if (!TryParseSegment(rawSegments[index], allowsEmptyKeySelector, out var segment)) |
| 107 | 対象外 | — | { |
| 108 | 実行済み | 9 | return false; |
| 109 | 対象外 | — | } |
| 110 | 対象外 | — | |
| 111 | 実行済み | 334 | segments.Add(segment); |
| 112 | 対象外 | — | } |
| 113 | 対象外 | — | |
| 114 | 実行済み | 164 | if (segments.Count == 0) |
| 115 | 対象外 | — | { |
| 116 | 未実行 | 0 | return false; |
| 117 | 対象外 | — | } |
| 118 | 対象外 | — | |
| 119 | 実行済み | 164 | parsedPath = new XPathLikePath(parsedRootName, segments); |
| 120 | 実行済み | 164 | return true; |
| 121 | 対象外 | — | } |
| 122 | 対象外 | — | |
| 123 | 対象外 | — | private static string? GetRootName(IReadOnlyList<string> rawSegments, string? rootName) |
| 124 | 対象外 | — | { |
| 125 | 実行済み | 173 | if (rootName is null |
| 126 | 実行済み | 173 | || rawSegments.Count <= 1 |
| 127 | 実行済み | 173 | || rawSegments[0].Contains('[', StringComparison.Ordinal) |
| 128 | 実行済み | 173 | || !string.Equals(rawSegments[0], rootName, StringComparison.Ordinal)) |
| 129 | 対象外 | — | { |
| 130 | 実行済み | 166 | return null; |
| 131 | 対象外 | — | } |
| 132 | 対象外 | — | |
| 133 | 実行済み | 7 | return rootName; |
| 134 | 対象外 | — | } |
| 135 | 対象外 | — | |
| 136 | 対象外 | — | private static bool TrySplitSegments(string path, out List<string> segments) |
| 137 | 対象外 | — | { |
| 138 | 実行済み | 180 | segments = []; |
| 139 | 実行済み | 180 | var segmentStart = 0; |
| 140 | 実行済み | 180 | var inSelector = false; |
| 141 | 実行済み | 180 | var escaping = false; |
| 142 | 実行済み | 180 | var selectorClosedInSegment = false; |
| 143 | 対象外 | — | |
| 144 | 実行済み | 6092 | for (var index = 0; index < path.Length; index++) |
| 145 | 対象外 | — | { |
| 146 | 実行済み | 2870 | var current = path[index]; |
| 147 | 実行済み | 2870 | if (inSelector) |
| 148 | 対象外 | — | { |
| 149 | 実行済み | 600 | if (escaping) |
| 150 | 対象外 | — | { |
| 151 | 実行済み | 22 | escaping = false; |
| 152 | 実行済み | 22 | continue; |
| 153 | 対象外 | — | } |
| 154 | 対象外 | — | |
| 155 | 実行済み | 578 | if (current == '\\') |
| 156 | 対象外 | — | { |
| 157 | 実行済み | 22 | escaping = true; |
| 158 | 実行済み | 22 | continue; |
| 159 | 対象外 | — | } |
| 160 | 対象外 | — | |
| 161 | 実行済み | 556 | if (current == ']') |
| 162 | 対象外 | — | { |
| 163 | 実行済み | 157 | inSelector = false; |
| 164 | 実行済み | 157 | selectorClosedInSegment = true; |
| 165 | 対象外 | — | } |
| 166 | 対象外 | — | |
| 167 | 実行済み | 157 | continue; |
| 168 | 対象外 | — | } |
| 169 | 対象外 | — | |
| 170 | 実行済み | 2270 | if (current == '[') |
| 171 | 対象外 | — | { |
| 172 | 実行済み | 161 | if (selectorClosedInSegment) |
| 173 | 対象外 | — | { |
| 174 | 実行済み | 2 | return false; |
| 175 | 対象外 | — | } |
| 176 | 対象外 | — | |
| 177 | 実行済み | 159 | inSelector = true; |
| 178 | 実行済み | 159 | continue; |
| 179 | 対象外 | — | } |
| 180 | 対象外 | — | |
| 181 | 実行済み | 2109 | if (current == ']') |
| 182 | 対象外 | — | { |
| 183 | 実行済み | 1 | return false; |
| 184 | 対象外 | — | } |
| 185 | 対象外 | — | |
| 186 | 実行済み | 2108 | if (current != '.') |
| 187 | 対象外 | — | { |
| 188 | 対象外 | — | continue; |
| 189 | 対象外 | — | } |
| 190 | 対象外 | — | |
| 191 | 実行済み | 180 | if (index == segmentStart) |
| 192 | 対象外 | — | { |
| 193 | 実行済み | 1 | return false; |
| 194 | 対象外 | — | } |
| 195 | 対象外 | — | |
| 196 | 実行済み | 179 | segments.Add(path[segmentStart..index]); |
| 197 | 実行済み | 179 | segmentStart = index + 1; |
| 198 | 実行済み | 179 | selectorClosedInSegment = false; |
| 199 | 対象外 | — | } |
| 200 | 対象外 | — | |
| 201 | 実行済み | 176 | if (inSelector || escaping || segmentStart == path.Length) |
| 202 | 対象外 | — | { |
| 203 | 実行済み | 3 | return false; |
| 204 | 対象外 | — | } |
| 205 | 対象外 | — | |
| 206 | 実行済み | 173 | segments.Add(path[segmentStart..]); |
| 207 | 実行済み | 173 | return true; |
| 208 | 対象外 | — | } |
| 209 | 対象外 | — | |
| 210 | 対象外 | — | private static bool TryParseSegment( |
| 211 | 対象外 | — | string rawSegment, |
| 212 | 対象外 | — | bool allowsEmptyKeySelector, |
| 213 | 対象外 | — | out XPathLikePathSegment segment) |
| 214 | 対象外 | — | { |
| 215 | 実行済み | 343 | segment = null!; |
| 216 | 実行済み | 343 | if (string.IsNullOrEmpty(rawSegment)) |
| 217 | 対象外 | — | { |
| 218 | 未実行 | 0 | return false; |
| 219 | 対象外 | — | } |
| 220 | 対象外 | — | |
| 221 | 実行済み | 343 | var selectorStart = rawSegment.IndexOf('[', StringComparison.Ordinal); |
| 222 | 実行済み | 343 | if (selectorStart < 0) |
| 223 | 対象外 | — | { |
| 224 | 実行済み | 189 | segment = new XPathLikePathSegment(rawSegment, null); |
| 225 | 実行済み | 189 | return true; |
| 226 | 対象外 | — | } |
| 227 | 対象外 | — | |
| 228 | 実行済み | 154 | if (selectorStart == 0 || !rawSegment.EndsWith(']')) |
| 229 | 対象外 | — | { |
| 230 | 未実行 | 0 | return false; |
| 231 | 対象外 | — | } |
| 232 | 対象外 | — | |
| 233 | 実行済み | 154 | var memberName = rawSegment[..selectorStart]; |
| 234 | 実行済み | 154 | var selectorText = rawSegment[(selectorStart + 1)..^1]; |
| 235 | 実行済み | 154 | if (memberName.Length == 0 || (selectorText.Length == 0 && !allowsEmptyKeySelector)) |
| 236 | 対象外 | — | { |
| 237 | 実行済み | 5 | return false; |
| 238 | 対象外 | — | } |
| 239 | 対象外 | — | |
| 240 | 実行済み | 149 | if (!TryParseSelector(selectorText, allowsEmptyKeySelector, out var selector)) |
| 241 | 対象外 | — | { |
| 242 | 実行済み | 4 | return false; |
| 243 | 対象外 | — | } |
| 244 | 対象外 | — | |
| 245 | 実行済み | 145 | segment = new XPathLikePathSegment(memberName, selector); |
| 246 | 実行済み | 145 | return true; |
| 247 | 対象外 | — | } |
| 248 | 対象外 | — | |
| 249 | 対象外 | — | private static bool TryParseSelector( |
| 250 | 対象外 | — | string selectorText, |
| 251 | 対象外 | — | bool allowsEmptyKeySelector, |
| 252 | 対象外 | — | out XPathLikePathSelector selector) |
| 253 | 対象外 | — | { |
| 254 | 実行済み | 149 | selector = null!; |
| 255 | 実行済み | 149 | if (selectorText.Length == 0 && allowsEmptyKeySelector) |
| 256 | 対象外 | — | { |
| 257 | 実行済み | 2 | selector = XPathLikePathSelector.Key(string.Empty); |
| 258 | 実行済み | 2 | return true; |
| 259 | 対象外 | — | } |
| 260 | 対象外 | — | |
| 261 | 実行済み | 147 | if (selectorText[0] == '#') |
| 262 | 対象外 | — | { |
| 263 | 実行済み | 15 | if (selectorText.Length == 1 || !selectorText[1..].All(char.IsDigit)) |
| 264 | 対象外 | — | { |
| 265 | 実行済み | 2 | return false; |
| 266 | 対象外 | — | } |
| 267 | 対象外 | — | |
| 268 | 実行済み | 13 | if (!int.TryParse(selectorText[1..], out var ordinal)) |
| 269 | 対象外 | — | { |
| 270 | 未実行 | 0 | return false; |
| 271 | 対象外 | — | } |
| 272 | 対象外 | — | |
| 273 | 実行済み | 13 | selector = XPathLikePathSelector.FromOrdinal(ordinal); |
| 274 | 実行済み | 13 | return true; |
| 275 | 対象外 | — | } |
| 276 | 対象外 | — | |
| 277 | 実行済み | 132 | if (!TryUnescapeKey(selectorText, out var keyText) || keyText.Length == 0) |
| 278 | 対象外 | — | { |
| 279 | 実行済み | 2 | return false; |
| 280 | 対象外 | — | } |
| 281 | 対象外 | — | |
| 282 | 実行済み | 130 | selector = XPathLikePathSelector.Key(keyText); |
| 283 | 実行済み | 130 | return true; |
| 284 | 対象外 | — | } |
| 285 | 対象外 | — | |
| 286 | 対象外 | — | private static bool TryUnescapeKey(string selectorText, out string keyText) |
| 287 | 対象外 | — | { |
| 288 | 実行済み | 132 | var builder = new System.Text.StringBuilder(selectorText.Length); |
| 289 | 実行済み | 1032 | for (var index = 0; index < selectorText.Length; index++) |
| 290 | 対象外 | — | { |
| 291 | 実行済み | 386 | var current = selectorText[index]; |
| 292 | 実行済み | 386 | if (current != '\\') |
| 293 | 対象外 | — | { |
| 294 | 実行済み | 364 | builder.Append(current); |
| 295 | 実行済み | 364 | continue; |
| 296 | 対象外 | — | } |
| 297 | 対象外 | — | |
| 298 | 実行済み | 22 | if (index + 1 >= selectorText.Length) |
| 299 | 対象外 | — | { |
| 300 | 未実行 | 0 | keyText = string.Empty; |
| 301 | 未実行 | 0 | return false; |
| 302 | 対象外 | — | } |
| 303 | 対象外 | — | |
| 304 | 実行済み | 22 | var escaped = selectorText[++index]; |
| 305 | 実行済み | 22 | if (escaped is not (']' or '\\' or '#')) |
| 306 | 対象外 | — | { |
| 307 | 実行済み | 2 | keyText = string.Empty; |
| 308 | 実行済み | 2 | return false; |
| 309 | 対象外 | — | } |
| 310 | 対象外 | — | |
| 311 | 実行済み | 20 | builder.Append(escaped); |
| 312 | 対象外 | — | } |
| 313 | 対象外 | — | |
| 314 | 実行済み | 130 | keyText = builder.ToString(); |
| 315 | 実行済み | 130 | return true; |
| 316 | 対象外 | — | } |
| 317 | 対象外 | — | } |