/// <summary>Dispatch <paramref name="events"/></summary> /// <param name="events"></param> public void DispatchEvents(ref StructList12 <IEvent> events) { // Errors StructList4 <Exception> errors = new StructList4 <Exception>(); for (int i = 0; i < events.Count; i++) { IEvent e = events[i]; try { e?.Observer?.Observer?.OnNext(e); } catch (Exception error) { if (errorHandler != null) { errorHandler(this, e, error); } else { errors.Add(error); } } } if (errors.Count > 0) { throw new AggregateException(errors.ToArray()); } }
/// <summary> /// Get all non-canonical keys as parameterName,parameterValue in order of from root towards tail. /// </summary> /// <param name="line">line to read parameters of</param> /// <param name="parameterInfos">(optional) map of infos for determining if parameter is key</param> /// <returns>dictionary of keys</returns> public static KeyValuePair <string, string>[] GetNonCanonicalsArray(this ILine line, IParameterInfos parameterInfos = null) { StructList12 <KeyValuePair <string, string> > result = new StructList12 <KeyValuePair <string, string> >(); line.GetNonCanonicalKeyPairs <StructList12 <KeyValuePair <string, string> > >(ref result, parameterInfos); return(result.ToReverseArray()); }
/// <summary> /// Try parse <paramref name="str"/> into parameters. /// </summary> /// <param name="str"></param> /// <param name="parameters"></param> /// <returns></returns> public override bool TryParse(string str, ref StructList12 <KeyValuePair <string, string> > parameters) { if (str == null) { return(false); } MatchCollection matches = ParsePattern.Matches(str); foreach (Match m in matches) { if (!m.Success) { return(false); } Group k_key = m.Groups["key"], k_value = m.Groups["value"]; if (!k_key.Success || !k_value.Success) { return(false); } string parameterName = UnescapeLiteral(k_key.Value); string parameterValue = UnescapeLiteral(k_value.Value); parameters.Add(new KeyValuePair <string, string>(parameterName, parameterValue)); } return(true); }
/// <summary>Dispatch <paramref name="events"/></summary> /// <param name="events"></param> public void DispatchEvents(ref StructList12 <IEvent> events) { if (events.Count == 1) { taskFactory.StartNew(processEventsAction, events[0]); } else if (events.Count >= 2) { taskFactory.StartNew(processEventsAction, events.ToArray()); } }
/// <summary> /// Send <paramref name="events"/> to observers. /// </summary> /// <param name="events"></param> public void DispatchEvents(ref StructList12 <IEvent> events) { // Don't send events anymore if (IsDisposing) { return; } // Nothing to do if (events.Count == 0) { return; } // Get first dispatcher var _dispatcher = events[0].Observer.Dispatcher ?? EventDispatcher.Instance; // Dispatch one event if (events.Count == 1) { _dispatcher.DispatchEvent(events[0]); return; } // All same dispatcher? bool allSameDispatcher = true; for (int i = 1; i < events.Count; i++) { var __dispatcher = events[i].Observer.Dispatcher ?? EventDispatcher.Instance; if (__dispatcher != _dispatcher) { allSameDispatcher = false; break; } } // All events use same dispatcher if (allSameDispatcher) { // Send with struct list if (_dispatcher is IEventDispatcherExtended ext) { ext.DispatchEvents(ref events); return; } // Convert to array _dispatcher.DispatchEvents(events.ToArray()); } else // Events use different dispatchers { // Dispatch each separately with different dispatchers for (int i = 0; i < events.Count; i++) { (events[i].Observer.Dispatcher ?? EventDispatcher.Instance).DispatchEvent(events[i]); } } }
/// <summary> /// Append a sequence of key,value pairs to a string. /// /// The format is as following: /// parameterKey:parameterValue:parameterKey:parameterValue:... /// /// Escape character is backslash. /// \unnnn /// \xnnnn /// \: /// \\ /// </summary> /// <param name="parameters"></param> /// <param name="sb"></param> /// <returns></returns> public override void Print(StructList12 <ILineParameter> parameters, StringBuilder sb) { for (int i = parameters.Count - 1; i >= 0; i--) { if (i < parameters.Count - 1) { sb.Append(':'); } ILineParameter parameter = parameters[i]; sb.Append(EscapeLiteral(parameter.ParameterName)); sb.Append(':'); sb.Append(EscapeLiteral(parameter.ParameterValue)); } }
/// <summary> /// Calculate <paramref name="line"/>'s hashcode. /// </summary> /// <param name="line"></param> /// <returns>hashcode or 0 if <paramref name="line"/> was null</returns> public int CalculateHashCode(ILine line) { int result = FNVHashBasis; // Non-canonical hashing foreach (var comparer in comparers) { // hash in non-canonical comparer result ^= comparer.GetHashCode(line); } // Canonical hashing if (canonicalComparers.Count > 0) { StructList12 <ILineParameter> list = new StructList12 <ILineParameter>(); line.GetCanonicalKeys <StructList12 <ILineParameter> >(ref list, parameterInfos); for (int i = 0; i < list.Count; i++) { var key = list[i]; // hash in canonical comparer foreach (var comparer in canonicalComparers) { // Use canonical comparer (the order of canonical comparisons should not be hashed in) result ^= comparer.GetHashCode(key); } } } // Parameter hashing if (parameterComparers.Count > 0) { StructList12 <ILineParameter> list = new StructList12 <ILineParameter>(); line.GetParameterParts <StructList12 <ILineParameter> >(ref list); for (int i = 0; i < list.Count; i++) { var key = list[i]; // hash in canonical comparer foreach (var comparer in parameterComparers) { result ^= comparer.GetHashCode(key); result *= FNVHashPrime; } } } return(result); }
/// <summary> /// Qualify <paramref name="line"/> against the qualifier rules. /// </summary> /// <param name="qualifier">(optional) qualifier</param> /// <param name="line"></param> /// <returns>true if line is qualified, false if disqualified</returns> public static bool Qualify(this ILineQualifier qualifier, ILine line) { // Evaluate whole line if (qualifier is ILineQualifierEvaluatable eval) { return(eval.Qualify(line)); } // Evaluate argument if (qualifier is ILineArgumentQualifier argumentQualifier) { if (argumentQualifier.NeedsOccuranceIndex) { // Break key into effective parameters with occurance index StructList12 <(ILineArgument, int)> list1 = new StructList12 <(ILineArgument, int)>(); line.GetArgumentPartsWithOccurance(ref list1); for (int i = 0; i < list1.Count; i++) { if (!argumentQualifier.QualifyArgument(list1[i].Item1, list1[i].Item2)) { return(false); } } } else { // Break key into parameters StructList12 <ILineArgument> list2 = new StructList12 <ILineArgument>(); line.GetArgumentParts(ref list2); for (int i = 0; i < list2.Count; i++) { if (!argumentQualifier.QualifyArgument(list2[i], -1)) { return(false); } } } } // no criteria, accept all return(true); }
/// <summary> /// Prune out arguments that are disqualified by <paramref name="qualifier"/>. /// </summary> /// <param name="line"></param> /// <param name="qualifier">Argument qualifier that is used for determining which parts to keep in the line</param> /// <param name="lineFactory">(optional) extra line factory</param> /// <returns>a modified <paramref name="line"/></returns> /// <exception cref="LineException"></exception> public static ILine Prune(this ILine line, ILineQualifier qualifier, ILineFactory lineFactory = null) { // Qualified parts to append. Order: tail to root. StructList12 <ILineArgument> list = new StructList12 <ILineArgument>(); ILineArgumentQualifier lineArgumentQualifier = qualifier as ILineArgumentQualifier; // Earliest qualified line part. The start tail, where to start appending qualified parts ILine startTail = line; // Line part's arguments. Order: root to tail StructList8 <ILineArgument> tmp = new StructList8 <ILineArgument>(); // Start tail buffered args. Order: root to tail StructList8 <ILineArgument> startTailArgsBuffer = new StructList8 <ILineArgument>(); // Add parts for (ILine l = line; l != null; l = l.GetPreviousPart()) { tmp.Clear(); if (l is ILineArgumentEnumerable lineArguments) { foreach (ILineArgument lineArgument in lineArguments) { tmp.Add(lineArgument); } } if (l is ILineArgument argument) { tmp.Add(argument); } // Now qualify bool linePartQualifies = true; string parameterName, parameterValue; for (int i = tmp.Count - 1; i >= 0; i--) { ILineArgument a = tmp[i]; bool argumentQualifies = true; if (lineArgumentQualifier != null) { // Qualify as an argument. if (!a.IsNonCanonicalKey()) { argumentQualifies = lineArgumentQualifier.QualifyArgument(a); } // Qualify as non-canonical parameter else if (a.TryGetParameter(out parameterName, out parameterValue)) { // Calculate occurance index int occIx = -1; if (lineArgumentQualifier.NeedsOccuranceIndex) { occIx = 0; for (int j = i - 1; j >= 0; j--) { ILineArgument b = list[j]; string parameterName2, parameterValue2; if (b.TryGetParameter(out parameterName2, out parameterValue2)) { continue; } if (parameterValue2 != null && parameterName == parameterName2) { occIx++; } } } argumentQualifies = lineArgumentQualifier.QualifyArgument(a, occIx); } } if (!argumentQualifies) { tmp.RemoveAt(i); } linePartQualifies &= argumentQualifies; } // This part didn't qualify if (!linePartQualifies) { // Append previous start tail to append args if (startTailArgsBuffer.Count > 0) { for (int i = 0; i < startTailArgsBuffer.Count; i++) { list.Add(startTailArgsBuffer[i]); } startTailArgsBuffer.Clear(); startTail = null; } // Add parts that did qualify to append list for (int i = 0; i < tmp.Count; i++) { list.Add(tmp[i]); } // preceding part might be better for start tail startTail = l.GetPreviousPart(); } else // This part qualified { // Add to start tail buffer, in case preceding startTail fails qualifications for (int i = 0; i < tmp.Count; i++) { startTailArgsBuffer.Add(tmp[i]); } } } // Append qualified parts. ILineFactory appender1 = null; line.TryGetAppender(out appender1); // Nothing qualified, no start, create dummy if (startTail == null && list.Count == 0) { // Create dummy ILineFactory appender2 = null; line.TryGetAppender(out appender2); ILinePart dummy = null; if (lineFactory == null || !lineFactory.TryCreate(null, out dummy)) { if (appender2 == null || !appender2.TryCreate(null, out dummy)) { throw new LineException(line, $"LineFactory doesn't have capability to create {nameof(ILinePart)}"); } } return(dummy); } // Append parts ILine result = startTail; for (int i = list.Count - 1; i >= 0; i--) { ILineArgument arg = list[i]; if (lineFactory == null || !lineFactory.TryCreate(result, arg, out result)) { if (appender1 == null || !appender1.TryCreate(result, arg, out result)) { throw new LineException(line, $"LineFactory doesn't have capability to concat {arg}"); } } } return(result); }
/// <summary> /// Forward event /// </summary> /// <param name="sender"></param> void OnEvent(object sender) { var _observer = Observer; if (_observer == null) { return; } // Create new token if (!IsDisposing) { this.changeToken = FileProvider.Watch(Filter); } // Get new snapshot DateTimeOffset time = DateTimeOffset.UtcNow; Dictionary <string, IEntry> newSnapshot = ReadSnapshot(); // List of events StructList12 <IEvent> events = new StructList12 <IEvent>(); // Find adds foreach (KeyValuePair <string, IEntry> newEntry in newSnapshot) { string path = newEntry.Key; // Find matching previous entry IEntry prevEntry; if (previousSnapshot.TryGetValue(path, out prevEntry)) { // Send change event if (!EntryComparer.PathDateLengthTypeEqualityComparer.Equals(newEntry.Value, prevEntry)) { events.Add(new ChangeEvent(this, time, path)); } } // Send create event else { events.Add(new CreateEvent(this, time, path)); } } // Find Deletes foreach (KeyValuePair <string, IEntry> oldEntry in previousSnapshot) { string path = oldEntry.Key; if (!newSnapshot.ContainsKey(path)) { events.Add(new DeleteEvent(this, time, path)); } } // Replace entires previousSnapshot = newSnapshot; // Dispatch events if (events.Count > 0) { ((FileSystemBase)this.FileSystem).DispatchEvents(ref events); } // Start watching again if (!IsDisposing) { this.watcher = changeToken.RegisterChangeCallback(OnEvent, this); } }
/// <summary> /// Compare <paramref name="x"/> and <paramref name="y"/> for equality. /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <returns>true if equal</returns> public bool Equals(ILine x, ILine y) { bool xIsNull = x == null, yIsNull = y == null; if (xIsNull && yIsNull) { return(true); } if (xIsNull || yIsNull) { return(false); } if (Object.ReferenceEquals(x, y)) { return(true); } // Test if cached hash-codes mismatch. if (this == LineComparer.key && x is ILineDefaultHashCode x_code && y is ILineDefaultHashCode y_code) { if (x_code.GetDefaultHashCode() != y_code.GetDefaultHashCode()) { return(false); } } // Regular comparers foreach (var comparer in comparers) { if (!comparer.Equals(x, y)) { return(false); } } // Canonical key part comparers if (canonicalComparers.Count > 0) { StructList12 <ILineParameter> x_canonicals = new StructList12 <ILineParameter>(), y_canonicals = new StructList12 <ILineParameter>(); x.GetCanonicalKeys <StructList12 <ILineParameter> >(ref x_canonicals, parameterInfos); y.GetCanonicalKeys <StructList12 <ILineParameter> >(ref y_canonicals, parameterInfos); if (x_canonicals.Count != y_canonicals.Count) { return(false); } for (int i = 0; i < x_canonicals.Count; i++) { ILineParameter x_key = x_canonicals[i], y_key = y_canonicals[i]; // Run comparers for (int j = 0; j < canonicalComparers.Count; j++) { if (!canonicalComparers[j].Equals(x_key, y_key)) { return(false); } } } } // Parameter part comparers if (parameterComparers.Count > 0) { StructList12 <ILineParameter> x_parameters = new StructList12 <ILineParameter>(), y_parameters = new StructList12 <ILineParameter>(); x.GetParameterParts <StructList12 <ILineParameter> >(ref x_parameters); y.GetParameterParts <StructList12 <ILineParameter> >(ref y_parameters); if (x_parameters.Count != y_parameters.Count) { return(false); } for (int i = 0; i < x_parameters.Count; i++) { ILineParameter x_key = x_parameters[i], y_key = y_parameters[i]; // Run comparers for (int j = 0; j < parameterComparers.Count; j++) { if (!parameterComparers[j].Equals(x_key, y_key)) { return(false); } } } } return(true); }
/// <summary> /// Evaluate placeholders into string values. /// </summary> /// <param name="line">original requesting line</param> /// <param name="resolvedLine">(optional Line that was matched from IAsset or inlines</param> /// <param name="pluralLine">(optional) Line that was matched from IAsset or inlines for plural value</param> /// <param name="placeholders"></param> /// <param name="features">contextual data</param> /// <param name="placeholder_values">collection where strings are placed, one for each placeholder</param> /// <param name="culture">the culture in which to evaluate</param> void EvaluatePlaceholderValues(ILine line, ILine resolvedLine, ILine pluralLine, IPlaceholder[] placeholders, ref LineFeatures features, ref StructList12 <string> placeholder_values, CultureInfo culture) { PlaceholderExpressionEvaluator placeholder_evaluator = new PlaceholderExpressionEvaluator(); placeholder_evaluator.Args = features.ValueArgs; placeholder_evaluator.FunctionEvaluationCtx.Culture = culture; placeholder_evaluator.FunctionEvaluationCtx.Line = line; placeholder_evaluator.FunctionEvaluationCtx.ResolvedLine = resolvedLine; placeholder_evaluator.FunctionEvaluationCtx.PluralLine = pluralLine; placeholder_evaluator.FunctionEvaluationCtx.StringResolver = this; placeholder_evaluator.FunctionEvaluationCtx.EnumResolver = EnumResolver; if (features.FormatProviders.Count == 1) { placeholder_evaluator.FunctionEvaluationCtx.FormatProvider = features.FormatProviders[0]; } else if (features.FormatProviders.Count > 1) { placeholder_evaluator.FunctionEvaluationCtx.FormatProvider = new FormatProviderComposition(features.FormatProviders.ToArray()); } if (features.Functions.Count == 1) { placeholder_evaluator.FunctionEvaluationCtx.Functions = features.Functions[0]; } else if (features.Functions.Count > 1) { placeholder_evaluator.FunctionEvaluationCtx.Functions = new FunctionsMap(features.Functions); } for (int i = 0; i < placeholders.Length; i++) { try { // Get placeholder IPlaceholder ph = placeholders[i]; // Evaluate value string ph_value = placeholder_evaluator.toString(placeholder_evaluator.Evaluate(ph.Expression)); // Add to array placeholder_values.Add(ph_value); // Update code features.Status.UpPlaceholder(placeholder_evaluator.Status); } catch (Exception e) { // Log exceptions features.Log(e); // Mark error features.Status.UpPlaceholder(LineStatus.PlaceholderErrorExpressionEvaluation); // Put empty value placeholder_values.Add(null); } } }
/// <summary> /// Resolve <paramref name="key"/> into <see cref="IString"/>, but without applying format arguments. /// /// If the <see cref="IString"/> contains plural categories, then matches into the applicable plurality case. /// </summary> /// <param name="key"></param> /// <returns>format string</returns> public IString ResolveFormatString(ILine key) { // Extract parameters from line LineFeatures features = new LineFeatures { Resolvers = Resolvers }; // Scan features try { features.ScanFeatures(key); } catch (Exception e) { features.Log(e); features.Status.UpResolve(LineStatus.ResolveFailedException); return(new StatusString(null, features.Status)); } // Resolve key to line CultureInfo culture = features.Culture; ILine line = ResolveKeyToLine(key, ref features, ref culture); // No line or value if (line == null || !features.HasValue) { features.Status.UpResolve(LineStatus.ResolveFailedNoValue); LineString str = new LineString(key, (Exception)null, features.Status); features.Log(str); return(new StatusString(null, features.Status)); } // Parse value IString value = features.EffectiveString; features.Status.Up(value.Status); // Value has error if (value.Parts == null || value.Status.Failed()) { LineString str = new LineString(key, (Exception)null, features.Status); features.Log(str); return(new StatusString(null, features.Status)); } // Plural Rules if (value.HasPluralRules()) { if (features.PluralRules != null) { // Evaluate expressions in placeholders into strings StructList12 <string> placeholder_values = new StructList12 <string>(); CultureInfo culture_for_format = features.Culture; if (culture_for_format == null && features.CulturePolicy != null) { CultureInfo[] cultures = features.CulturePolicy.Cultures; if (cultures != null && cultures.Length > 0) { culture_for_format = cultures[0]; } } if (culture_for_format == null) { culture_for_format = CultureInfo.InvariantCulture; } EvaluatePlaceholderValues(key, line, null, value.Placeholders, ref features, ref placeholder_values, culture_for_format); // Create permutation configuration PluralCasePermutations permutations = new PluralCasePermutations(line); for (int i = 0; i < value.Placeholders.Length; i++) { // Get placeholder IPlaceholder placeholder = value.Placeholders[i]; // No plural category in this placeholder if (placeholder.PluralCategory == null) { continue; } // Placeholder value after evaluation string ph_value = placeholder_values[i]; // Placeholder evaluated value IPluralNumber placeholderValue = ph_value == null ? DecimalNumber.Empty : new DecimalNumber.Text(ph_value?.ToString(), culture); // Add placeholder to permutation configuration permutations.AddPlaceholder(placeholder, placeholderValue, features.PluralRules, culture?.Name ?? ""); } // Find first value that matches permutations features.CulturePolicy = null; features.String = null; features.StringText = null; for (int i = 0; i < permutations.Count - 1; i++) { // Create key with plurality cases ILine key_with_plurality = permutations[i]; // Search line with the key ILine line_for_plurality_arguments = ResolveKeyToLine(key_with_plurality, ref features, ref culture); // Got no match if (line_for_plurality_arguments == null) { continue; } // Parse value IString value_for_plurality = line_for_plurality_arguments.GetString(Resolvers); // Add status from parsing the value features.Status.Up(value_for_plurality.Status); // Value has error if (value_for_plurality.Parts == null || value_for_plurality.Status.Failed()) { LineString str = new LineString(key, (Exception)null, features.Status); features.Log(str); return(new StatusString(null, features.Status)); } // Return with match features.Status.UpPlurality(LineStatus.PluralityOkMatched); // Update status codes features.Status.Up(value_for_plurality.Status); // Return values value = value_for_plurality; line = line_for_plurality_arguments; break; } } else { // Plural rules were not found features.Status.Up(LineStatus.PluralityErrorRulesNotFound); } } else { // Plurality feature was not used. features.Status.UpPlurality(LineStatus.PluralityOkNotUsed); } return(value); }
/// <summary> /// Resolve <paramref name="key"/> into <see cref="LineString"/> with format arguments applied. /// </summary> /// <param name="key"></param> /// <returns></returns> public LineString ResolveString(ILine key) { // Extract parameters from line LineFeatures features = new LineFeatures { Resolvers = Resolvers }; // Scan features try { features.ScanFeatures(key); } catch (Exception e) { features.Log(e); features.Status.UpResolve(LineStatus.ResolveFailedException); return(new LineString(key, e, features.Status)); } try { // Resolve key to line CultureInfo culture = features.Culture; ILine line = ResolveKeyToLine(key, ref features, ref culture); // No line or value if (line == null || !features.HasValue) { features.Status.UpResolve(LineStatus.ResolveFailedNoValue); LineString str = new LineString(key, (Exception)null, features.Status); features.Log(str); return(str); } // Parse value IString value = features.EffectiveString; features.Status.Up(value.Status); // Value has error if (value.Parts == null || value.Status.Failed()) { LineString str = new LineString(key, (Exception)null, features.Status); features.Log(str); return(str); } // Evaluate expressions in placeholders into strings StructList12 <string> placeholder_values = new StructList12 <string>(); CultureInfo culture_for_format = features.Culture; if (culture_for_format == null && features.CulturePolicy != null) { CultureInfo[] cultures = features.CulturePolicy.Cultures; if (cultures != null && cultures.Length > 0) { culture_for_format = cultures[0]; } } if (culture_for_format == null) { culture_for_format = CultureInfo.InvariantCulture; } EvaluatePlaceholderValues(key, line, null, value.Placeholders, ref features, ref placeholder_values, culture_for_format); // Plural Rules if (value.HasPluralRules()) { if (features.PluralRules != null) { // Create permutation configuration PluralCasePermutations permutations = new PluralCasePermutations(line); for (int i = 0; i < value.Placeholders.Length; i++) { // Get placeholder IPlaceholder placeholder = value.Placeholders[i]; // No plural category in this placeholder if (placeholder.PluralCategory == null) { continue; } // Placeholder value after evaluation string ph_value = placeholder_values[i]; // Placeholder evaluated value IPluralNumber placeholderValue = ph_value == null ? DecimalNumber.Empty : new DecimalNumber.Text(ph_value?.ToString(), culture); // Add placeholder to permutation configuration permutations.AddPlaceholder(placeholder, placeholderValue, features.PluralRules, culture?.Name ?? ""); } if (permutations.ArgumentCount <= MaxPluralArguments) { // Find first value that matches permutations features.CulturePolicy = null; features.String = null; features.StringText = null; for (int i = 0; i < permutations.Count - 1; i++) { // Create key with plurality cases ILine key_with_plurality = permutations[i]; // Search line with the key ILine line_for_plurality_arguments = ResolveKeyToLine(key_with_plurality, ref features, ref culture); // Got no match if (line_for_plurality_arguments == null) { continue; } // Scan value try { features.ScanValueFeature(line_for_plurality_arguments); } catch (Exception e) { features.Log(e); features.Status.Up(LineStatus.FailedUnknownReason); return(new LineString(key, e, features.Status)); } // Parse value IString value_for_plurality = features.EffectiveString; // Add status from parsing the value features.Status.Up(value_for_plurality.Status); // Value has error if (value_for_plurality.Parts == null || value_for_plurality.Status.Failed()) { LineString str = new LineString(key, (Exception)null, features.Status); features.Log(str); return(str); } // Return with match features.Status.UpPlurality(LineStatus.PluralityOkMatched); // Evaluate placeholders again if (!EqualPlaceholders(value, value_for_plurality)) { placeholder_values.Clear(); EvaluatePlaceholderValues(key, line, line_for_plurality_arguments, value_for_plurality.Placeholders, ref features, ref placeholder_values, culture); } // Update status codes features.Status.Up(value_for_plurality.Status); // Return values value = value_for_plurality; line = line_for_plurality_arguments; break; } } else { features.Status.UpPlaceholder(LineStatus.PluralityErrorMaxPluralArgumentsExceeded); } } else { // Plural rules were not found features.Status.Up(LineStatus.PluralityErrorRulesNotFound); } } else { // Plurality feature was not used. features.Status.UpPlurality(LineStatus.PluralityOkNotUsed); } // Put string together string text = null; if (value == null || value.Parts == null) { text = null; } // Only one part else if (value.Parts.Length == 1) { if (value.Parts[0].Kind == StringPartKind.Text) { text = value.Parts[0].Text; features.Status.UpStringFormat(LineStatus.StringFormatOkString); } else if (value.Parts[0].Kind == StringPartKind.Placeholder) { text = placeholder_values[0]; features.Status.UpStringFormat(LineStatus.StringFormatOkString); } } // Compile multiple parts else { // Calculate length int length = 0; for (int i = 0; i < value.Parts.Length; i++) { IStringPart part = value.Parts[i]; string partText = part.Kind switch { StringPartKind.Text => part.Text, StringPartKind.Placeholder => placeholder_values[((IPlaceholder)part).PlaceholderIndex], _ => null }; if (partText != null) { length += partText.Length; } } // Copy characters char[] arr = new char[length]; int ix = 0; for (int i = 0; i < value.Parts.Length; i++) { IStringPart part = value.Parts[i]; string str = part.Kind switch { StringPartKind.Text => part.Text, StringPartKind.Placeholder => placeholder_values[((IPlaceholder)part).PlaceholderIndex], _ => null }; if (str != null) { str.CopyTo(0, arr, ix, str.Length); ix += str.Length; } } // String text = new string(arr); features.Status.UpStringFormat(LineStatus.StringFormatOkString); } // Create result LineString result = new LineString(key, text, features.Status); // Log features.Log(result); // Return return(result); } catch (Exception e) { // Capture unexpected error features.Log(e); features.Status.UpResolve(LineStatus.ResolveFailedException); LineString lineString = new LineString(key, e, features.Status); features.Log(lineString); return(lineString); } }