/// <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);
                        }
                    }
                }
            }
        }
Пример #2
0
        /// <summary>
        /// Compare two keys for all their non-canonical part parameter key-values.
        ///
        /// Only the left-most value of each parameter name is considered effective.
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <returns>true if keys are equals in terms of non-canonical parameters</returns>
        public bool Equals(ILine x, ILine y)
        {
            StructList8 <KeyValuePair <string, string> > x_parameters = new StructList8 <KeyValuePair <string, string> >(KeyValuePairEqualityComparer <string, string> .Default);
            StructList8 <KeyValuePair <string, string> > y_parameters = new StructList8 <KeyValuePair <string, string> >(KeyValuePairEqualityComparer <string, string> .Default);

            x.GetNonCanonicalKeyPairs <StructList8 <KeyValuePair <string, string> > >(ref x_parameters, parameterInfos);
            y.GetNonCanonicalKeyPairs <StructList8 <KeyValuePair <string, string> > >(ref y_parameters, parameterInfos);

            // Compare count
            if (x_parameters.Count != y_parameters.Count)
            {
                return(false);
            }

            // Sort lists
            sorter.Sort(ref x_parameters);
            sorter.Sort(ref y_parameters);

            // Pair comparison of the sorted lists
            for (int i = 0; i < x_parameters.Count; i++)
            {
                KeyValuePair <string, string> x_parameter = x_parameters[i], y_parameter = y_parameters[i];
                if (x_parameter.Key != y_parameter.Key || x_parameter.Value != y_parameter.Value)
                {
                    return(false);
                }
            }

            return(true);
        }
Пример #3
0
            /// <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);
                    }
        /// <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);
            }
        }
Пример #5
0
        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());
        }
Пример #6
0
        /// <summary>
        /// Calculate hash-code of every non-canonical parameters.
        ///
        /// Only the left-most value of each parameter name is considered effective.
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        public int GetHashCode(ILine line)
        {
            StructList8 <KeyValuePair <string, string> > parameters = new StructList8 <KeyValuePair <string, string> >(KeyValuePairEqualityComparer <string, string> .Default);

            line.GetNonCanonicalKeyPairs <StructList8 <KeyValuePair <string, string> > >(ref parameters, parameterInfos);
            int hash = 0;

            for (int i = 0; i < parameters.Count; i++)
            {
                var param = parameters[i];
                // Hash in only if value is non-""
                if (!String.IsNullOrEmpty(param.Value))
                {
                    hash ^= param.Key.GetHashCode();
                    hash ^= param.Value.GetHashCode();
                }
            }
            return(hash);
        }
Пример #7
0
        /// <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);
        }
Пример #8
0
 /// <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]));
     }
 }
Пример #9
0
        /// <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);
        }
Пример #10
0
        /// <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;
                }
            }
        }