/// <summary> /// Get all the argument indices that are expressed in <paramref name="exp"/>. /// </summary> /// <param name="exp"></param> /// <param name="argumentIndices"></param> static void GetArguments(IExpression exp, ref StructList4 <int> argumentIndices) { StructList8 <IExpression> stack = new StructList8 <IExpression>(); stack.Add(exp); while (stack.Count > 0) { IExpression e = stack.Dequeue(); if (e is IArgumentIndexExpression arg) { argumentIndices.AddIfNew(arg.Index); } if (e is ICompositeExpression compositeExpression) { for (int i = 0; i < compositeExpression.ComponentCount; i++) { IExpression ce = compositeExpression.GetComponent(i); if (ce != null) { stack.Add(ce); } } } } }
/// <summary> /// Creates a line factory that is specialized for handling <see cref="ILineArgument"/> of specific implementing <paramref name="argumentType"/>. /// </summary> /// <param name="argumentType">Argument class that implements <see cref="ILineArgument"/> once or more than once</param> /// <returns>Factory that forwards calls for the specific <paramref name="argumentType"/> or null</returns> /// <exception cref="LineException">If adapter construction fails</exception> public ILineFactoryByArgument Build(Type argumentType) { try { StructList8 <ILineFactoryByArgument> factories = new StructList8 <ILineFactoryByArgument>(); Type[] intfs = argumentType.GetInterfaces(); foreach (Type intf in intfs) { if (!intf.IsGenericType) { continue; } Type intfGeneric = intf.GetGenericTypeDefinition(); if (intfGeneric == null) { continue; } Type[] typeArgs = intf.GetGenericArguments(); // ILineArgument<> if (intfGeneric == typeof(ILineArgument <>)) { factories.Add(Activator.CreateInstance(typeof(Adapter <>).MakeGenericType(typeArgs)) as ILineFactoryByArgument); } // ILineArgument<,> else if (intfGeneric == typeof(ILineArgument <,>)) { factories.Add(Activator.CreateInstance(typeof(Adapter <,>).MakeGenericType(typeArgs)) as ILineFactoryByArgument); } // ILineArgument<,,> else if (intfGeneric == typeof(ILineArgument <, ,>)) { factories.Add(Activator.CreateInstance(typeof(Adapter <, ,>).MakeGenericType(typeArgs)) as ILineFactoryByArgument); } // ILineArgument<,,,> else if (intfGeneric == typeof(ILineArgument <, , ,>)) { factories.Add(Activator.CreateInstance(typeof(Adapter <, ,>).MakeGenericType(typeArgs)) as ILineFactoryByArgument); } } if (factories.Count == 0) { return(null); } if (factories.Count == 1) { return(factories[0]); } return(new Factories(factories.ToArray())); } catch (Exception e) { throw new LineException(null, e.Message, e); } }
/// <summary> /// Parse string /// </summary> protected void Build() { StructList8 <IStringPart> parts = new StructList8 <IStringPart>(); // Create parser CSharpFormat.CSharpParser parser = new CSharpFormat.CSharpParser(this); // Read parts while (parser.HasMore) { IStringPart part = parser.ReadNext(); if (part != null) { parts.Add(part); } } // Unify text parts for (int i = 1; i < parts.Count;) { if (parts[i - 1] is TextPart left && parts[i] is TextPart right) { parts[i - 1] = TextPart.Unify(left, right); parts.RemoveAt(i); }
static CultureInfo[] MakeArray(CultureInfo culture) { StructList8 <CultureInfo> result = new StructList8 <CultureInfo>(); for (CultureInfo ci = culture; ci != null; ci = ci.Parent) { if (result.Contains(ci)) { break; } else { result.Add(ci); } } return(result.ToArray()); }
/// <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> /// Ensure that parameter does not exist (effectively) in the whole line. /// </summary> /// <param name="line"></param> /// <returns></returns> public bool Qualify(ILine line) { if (OccuranceIndex < 0) { bool matched = true; int count = 0; for (ILine l = line; l != null; l = l.GetPreviousPart()) { if (l is ILineParameterEnumerable lineParameters) { foreach (ILineParameter parameter in lineParameters) { if (parameter.ParameterName == ParameterName) { count++; matched &= QualifyValue(parameter.ParameterValue); } } } if (l is ILineParameter lineParameter) { if (lineParameter.ParameterName == ParameterName) { count++; matched &= QualifyValue(lineParameter.ParameterValue); } } if (!matched) { break; } } return(matched && count > 0); } else { StructList8 <string> list = new StructList8 <string>(); for (ILine l = line; l != null; l = l.GetPreviousPart()) { if (l is ILineParameterEnumerable lineParameters) { foreach (ILineParameter parameter in lineParameters) { if (parameter.ParameterName == ParameterName && (parameter.ParameterValue != null && parameter.ParameterValue != "")) { list.Add(parameter.ParameterValue); } } } if (l is ILineParameter lineParameter) { if (lineParameter.ParameterName == ParameterName && (lineParameter.ParameterValue != null && lineParameter.ParameterValue != "")) { list.Add(lineParameter.ParameterValue); } } } // Did not exist if (OccuranceIndex >= list.Count) { return(false); } return(QualifyValue(list[list.Count - 1 - OccuranceIndex])); } }
/// <summary> /// Filter rules by <paramref name="filterCriteria"/>. /// </summary> /// <param name="filterCriteria"></param> /// <returns><see cref="PluralRulesCasesEvaluatable"/> or <see cref="PluralRulesArray"/>, or null if content is empty</returns> public IPluralRulesEnumerable Query(PluralRuleInfo filterCriteria) { // Try get IPluralRulesEnumerable result = null; lock (m_lock) if (queries.TryGetValue(filterCriteria, out result)) { return(result); } // Query if (ruleSource is IPluralRulesQueryable queryable) { result = queryable.Query(filterCriteria); // Wrap into PluralRulesEvaluatable if (result != null && filterCriteria.Category != null && filterCriteria.Culture != null && filterCriteria.Case == null && filterCriteria.Optional == -1) { result = new PluralRulesCasesEvaluatable(result); } } else if (ruleSource is IEnumerable <IPluralRule> enumr) { // Filter rules StructList8 <IPluralRule> list = new StructList8 <IPluralRule>(); StructList8 <string> rulesets = new StructList8 <string>(); foreach (IPluralRule rule in enumr) { // Filter by criteria if (!filterCriteria.FilterMatch(rule.Info)) { continue; } // Gather a list of rulesets if (rule.Info.RuleSet != null && !rulesets.Contains(rule.Info.RuleSet)) { rulesets.Add(rule.Info.RuleSet); } // Add to list list.Add(rule); } // No result if (list.Count == 0) { result = null; } // Instantiate PluralRulesEvaluatable else if (rulesets.Count <= 1 && filterCriteria.Category != null && filterCriteria.Culture != null && filterCriteria.Case == null && filterCriteria.Optional == -1) { result = new PluralRulesCasesEvaluatable(list.ToArray()); } // Instantiate PluralRules. else { result = new PluralRulesArray(list.ToArray()); } } // Could not read source else { result = null; } // Write to cache, if is still new lock (m_lock) if (!queries.ContainsKey(filterCriteria)) { queries[filterCriteria] = result; } // Return result return(result); }
/// <summary> /// Print as string /// </summary> /// <param name="str"></param> /// <returns>string or null</returns> public LineString Print(IString str) { if (str is CSharpFormat && str.Text != null) { return(new LineString(null, str.Text, LineStatus.StringFormatOkString)); } StringBuilder sb = new StringBuilder(); LineStatus status = 0UL; foreach (var part in str.Parts) { // Escape '{' '}' and '\' to '\{' '\}' '\\' if (part.Kind == StringPartKind.Text) { string s = part.Text; for (int i = 0; i < s.Length; i++) { char c = s[i]; if (c == '{' || c == '}' || c == '\\') { sb.Append('\\'); } sb.Append(c); } continue; } // Placeholder if (part.Kind == StringPartKind.Placeholder && part is IPlaceholder placeholder) { int argumentIx = -1; string format = null; int alignment = 0; StructList8 <IExpression> queue = new StructList8 <IExpression>(); queue.Add(placeholder.Expression); while (queue.Count > 0) { IExpression e = queue.Dequeue(); if (e is IArgumentIndexExpression arg) { // Second argument index if (argumentIx >= 0) { status.Up(LineStatus.StringFormatErrorPrintNoCapabilityMultipleArguments); } else { argumentIx = arg.Index; } continue; } if (e is ICallExpression call) { if (call.Name == "Format" && call.Args != null && call.Args.Length == 2 && call.Args[1] is IConstantExpression formatExp && formatExp.Value is string formatStr) { queue.Add(call.Args[0]); format = formatStr; } else if (call.Name == "Alignment" && call.Args != null && call.Args.Length == 2 && call.Args[1] is IConstantExpression alignmentExp) { queue.Add(call.Args[0]); if (alignmentExp.Value is long longValue) { alignment = (int)longValue; } else if (alignmentExp.Value is int intValue) { alignment = intValue; } else { status.Up(LineStatus.StringFormatErrorPrintUnsupportedExpression); } } else { status.Up(LineStatus.StringFormatErrorPrintUnsupportedExpression); } continue; } status.Up(LineStatus.StringFormatErrorPrintUnsupportedExpression); } if (argumentIx >= 0) { sb.Append('{'); if (placeholder.PluralCategory != null) { sb.Append(placeholder.PluralCategory); sb.Append(':'); } sb.Append(argumentIx); if (alignment != 0) { sb.Append(','); sb.Append(alignment); } if (format != null) { sb.Append(":"); sb.Append(format); } sb.Append('}'); } continue; } // Malformed status.Up(LineStatus.StringFormatErrorMalformed); }
/// <summary> /// Add placeholder and cases that apply to that placeholder. /// </summary> /// <param name="placeholder"></param> /// <param name="placeholderValue"></param> /// <param name="pluralRules"></param> /// <param name="culture"></param> public void AddPlaceholder(IPlaceholder placeholder, IPluralNumber placeholderValue, IPluralRules pluralRules, string culture) { IExpression e = placeholder?.Expression; if (e == null) { return; } // Query cases PluralRuleInfo query = new PluralRuleInfo(null, placeholder.PluralCategory, culture, null, -1); IPluralRule[] cases = pluralRules.Evaluate(query, placeholderValue); if (cases == null || cases.Length == 0) { return; } int optionalCount = cases.Length == 1 ? 0 : cases.Length - 1; int requiredCount = cases.Length > 0 ? 1 : 0; // Scan arguments StructList4 <int> argIndices = new StructList4 <int>(); GetArguments(e, ref argIndices); // Add and unify cases for (int ix = 0; ix < argIndices.Count; ix++) { int argIx = argIndices[ix]; // Find if argument already exists int prevIx = -1; for (int j = 0; j < arguments.Count; j++) { if (arguments[j].ArgumentIndex == argIx) { prevIx = j; break; } } if (prevIx < 0) { // Add argument Entry entry = new Entry { Cases = cases, ArgumentIndex = argIx, OptionalCases = optionalCount, RequiredCases = requiredCount }; arguments.Add(entry); this.Count *= (1 + cases.Length); this.count1 *= cases.Length; } else { // Previous entry Entry entry = arguments[prevIx]; // Unify entries StructList8 <IPluralRule> optionalCases = new StructList8 <IPluralRule>(IPluralRuleComparer.Default); StructList8 <IPluralRule> requiredCases = new StructList8 <IPluralRule>(IPluralRuleComparer.Default); foreach (var c in entry.Cases) { if (c.Info.Optional == 1) { optionalCases.AddIfNew(c); } else if (c.Info.Optional == 0) { requiredCases.AddIfNew(c); } } foreach (var c in cases) { if (c.Info.Optional == 1) { optionalCases.AddIfNew(c); } else if (c.Info.Optional == 0) { requiredCases.AddIfNew(c); } } StructList8 <IPluralRule> allCases = new StructList8 <IPluralRule>(IPluralRuleComparer.Default); for (int i = 0; i < optionalCases.Count; i++) { allCases.Add(optionalCases[i]); } for (int i = 0; i < requiredCases.Count; i++) { allCases.Add(requiredCases[i]); } // Create new entry Entry newEntry = new Entry { Cases = allCases.ToArray(), ArgumentIndex = argIx, OptionalCases = optionalCases.Count, RequiredCases = requiredCases.Count }; // Update arguments[prevIx] = newEntry; this.Count /= (1 + entry.Cases.Length); this.Count *= (1 + newEntry.Cases.Length); this.count1 /= entry.Cases.Length; this.count1 *= newEntry.Cases.Length; } } }