Ejemplo n.º 1
0
 /// <summary>
 /// Construct new <see cref="VotePartition"/>
 /// </summary>
 /// <param name="voteLine">The vote line that the partition is composed of.</param>
 /// <exception cref="ArgumentNullException"/>
 public VotePartition(VoteLine voteLine, VoteType voteType)
 {
     VoteType = voteType;
     AddLine(voteLine);
 }
Ejemplo n.º 2
0
        /// <summary>
        /// Take the initial value of a post, and substitute plans and
        /// proxy vote lines as appropriate.
        /// </summary>
        /// <param name="post">The original post.</param>
        /// <returns>Returns a list of VoteLines to use for the purpose of processing the vote.</returns>
        public static List <VotePartition> GetVotePartitions(Post post, IQuest quest)
        {
            if (post == null)
            {
                throw new ArgumentNullException(nameof(post));
            }
            if (quest == null)
            {
                throw new ArgumentNullException(nameof(quest));
            }

            List <VotePartition> results   = new List <VotePartition>();
            VotePartition        partition = null;
            VoteLine             parent    = null;

            // Move through the vote lines by index so we can modify the
            // index pointer directly when doing comparisons.
            var voteLineArray = post.Vote.VoteLines.ToArray();
            int index         = 0;

            while (index < voteLineArray.Length)
            {
                // Current line.
                VoteLine line = voteLineArray[index];

                // First check if the line is a proxy vote for a plan or a user
                Match m = startsWithPlan.Match(line.ComparableContent);

                // "Plan XYZ"
                if (m.Success)
                {
                    string planname = m.Groups["planname"].Value;

                    // Plan has priority, in case the plan is named after the user.
                    if (VotingRecords.Instance.HasPlanName(planname))
                    {
                        // Get the collection of variants of this plan name.
                        var  plans = VotingRecords.Instance.GetPlans(planname);
                        Plan plan  = null;

                        // Make sure we don't go over the limit of the remaining post vote lines.
                        int remainingLines = voteLineArray.Length - index - 1;

                        if (remainingLines == 0)
                        {
                            // If there are no remaining lines in the current post, we
                            // just take the provided variant 0.
                            plan = plans.First(p => p.Identity.Number == 0);
                        }
                        else
                        {
                            // OK, there are lines that may potentially be copied from the plan.
                            // Limit the plans we're looking at to those that can fit in our remaining line space.
                            var limitedPlans = plans.Where(p => p.Content.VoteLines.Count <= remainingLines).ToList();

                            if (limitedPlans.Any())
                            {
                                foreach (var p in limitedPlans)
                                {
                                    // In each of the plans that can fit in the remaining vote space,
                                    // compare them with an equal-sized segment of the vote.
                                    // If it matches, that's a copy of the plan, and we can skip past it.
                                    var segment = new ArraySegment <VoteLine>(voteLineArray, index, p.Content.VoteLines.Count);

                                    if (segment.SequenceEqual(p.Content.VoteLines))
                                    {
                                        plan = p;
                                        break;
                                    }
                                }

                                // However if no sequence match was found, we only have the naming line to
                                // copy, and we can move on to the next line of the post.
                                if (plan == null)
                                {
                                    plan = limitedPlans.First(p => p.Identity.Number == 0);
                                }
                                else
                                {
                                    // If we did find the full match, update the index so that we increment past that.
                                    index += plan.Content.VoteLines.Count - 1;
                                }
                            }
                            else
                            {
                                // None of the selected plans can fit in the remaining lines of the post.
                                // Thus we don't need to try to match, and just take the named line.
                                plan = plans.First(p => p.Identity.Number == 0);
                            }
                        }

                        // If we found a plan, add its contents to the results and go to move next.
                        // Otherwise, drop out and treat this as a normal line.
                        if (plan != null)
                        {
                            List <VotePartition> planPartitions = VotingRecords.Instance.GetPartitionsForIdentity(plan.Identity);

                            results.AddRange(planPartitions);

                            goto moveNext;
                        }
                    }
                    // Otherwise check if it's a user proxy.  Usernames are unlikely to be allowed to be greater
                    // than 20 characters, but giving a fair buffer just in case.  Don't check for very long strings
                    // as usernames.  Also check global options in case proxy votes are disabled.
                    else if (planname.Length <= 40 && !AdvancedOptions.Instance.DisableProxyVotes)
                    {
                        List <VotePartition> proxyPartitions = GetProxyPartitions(planname, false, post.Identity);

                        // There are no matching identities.  Treat as a normal line.
                        if (proxyPartitions == null)
                        {
                            goto normalLine;
                        }

                        // If there are no recorded partitions for this identity, it hasn't been
                        // processed yet, and this is a future reference, or a past vote that itself
                        // has future references and hasn't completed processing yet.
                        if (!proxyPartitions.Any())
                        {
                            // If we aren't forcing the processing of this post, just bail and return null.
                            // If we *are* forcing it, we have to treat this as a normal line.
                            if (post.ForceProcess)
                            {
                                goto normalLine;
                            }
                            else
                            {
                                VotingRecords.Instance.NoteFutureReference(post);
                                return(null);
                            }
                        }

                        // Add whatever proxy partitions we got to he results and move on.
                        results.AddRange(proxyPartitions);

                        goto moveNext;
                    }
                }
                // If it doesn't start with 'plan', also check if it's a (possibly pinned) username proxy.
                // Usernames are unlikely to be allowed to be greater than 20 characters, but giving
                // a fair buffer just in case.  Don't check for very long strings as usernames.
                // Also check global options in case proxy votes are disabled.
                else
                {
                    // Proxy regex might start with ^ to indicate a pinned name, and have a
                    // username length of no more than 40 characters.
                    m = proxyRegex.Match(line.ComparableContent);

                    if (m.Success && !AdvancedOptions.Instance.DisableProxyVotes)
                    {
                        bool   pin      = m.Groups["pin"].Success;
                        string username = m.Groups["username"].Value;

                        List <VotePartition> proxyPartitions = GetProxyPartitions(username, pin, post.Identity);

                        // There are no matching identities.  Treat as a normal line.
                        if (proxyPartitions == null)
                        {
                            goto normalLine;
                        }

                        // If there are no recorded partitions for this identity, it hasn't been
                        // processed yet, and this is a future reference, or a past vote that itself
                        // has future references and hasn't completed processing yet.
                        if (!proxyPartitions.Any())
                        {
                            // If we aren't forcing the processing of this post, just bail and return null.
                            // If we *are* forcing it, we have to treat this as a normal line.
                            if (post.ForceProcess)
                            {
                                goto normalLine;
                            }
                            else
                            {
                                VotingRecords.Instance.NoteFutureReference(post);
                                return(null);
                            }
                        }

                        // Add whatever proxy partitions we got to he results and move on.
                        results.AddRange(proxyPartitions);

                        goto moveNext;
                    }
                    // If we have a plan name that doesn't have the 'plan' prefix, redo the logic
                    // of skipping past any copied content.
                    else if (VotingRecords.Instance.HasPlanName(line.ComparableContent))
                    {
                        string planname = line.ComparableContent;

                        // Get the collection of variants of this plan name.
                        var  plans = VotingRecords.Instance.GetPlans(planname);
                        Plan plan  = null;

                        // Make sure we don't go over the limit of the remaining post vote lines.
                        int remainingLines = voteLineArray.Length - index - 1;

                        if (remainingLines == 0)
                        {
                            // If there are no remaining lines in the current post, we
                            // just take the provided variant 0.
                            plan = plans.First(p => p.Identity.Number == 0);
                        }
                        else
                        {
                            // OK, there are lines that may potentially be copied from the plan.
                            // Limit the plans we're looking at to those that can fit in our remaining line space.
                            var limitedPlans = plans.Where(p => p.Content.VoteLines.Count <= remainingLines).ToList();

                            if (limitedPlans.Any())
                            {
                                foreach (var p in limitedPlans)
                                {
                                    // In each of the plans that can fit in the remaining vote space,
                                    // compare them with an equal-sized segment of the vote.
                                    // If it matches, that's a copy of the plan, and we can skip past it.
                                    var segment = new ArraySegment <VoteLine>(voteLineArray, index, p.Content.VoteLines.Count);

                                    if (segment.SequenceEqual(p.Content.VoteLines))
                                    {
                                        plan = p;
                                        break;
                                    }
                                }

                                // However if no sequence match was found, we only have the naming line to
                                // copy, and we can move on to the next line of the post.
                                if (plan == null)
                                {
                                    plan = limitedPlans.First(p => p.Identity.Number == 0);
                                }
                                else
                                {
                                    // If we did find the full match, update the index so that we increment past that.
                                    index += plan.Content.VoteLines.Count - 1;
                                }
                            }
                            else
                            {
                                // None of the selected plans can fit in the remaining lines of the post.
                                // Thus we don't need to try to match, and just take the named line.
                                plan = plans.First(p => p.Identity.Number == 0);
                            }
                        }

                        // If we found a plan, add its contents to the results and go to move next.
                        // Otherwise, drop out and treat this as a normal line.
                        if (plan != null)
                        {
                            List <VotePartition> planPartitions = VotingRecords.Instance.GetPartitionsForIdentity(plan.Identity);

                            results.AddRange(planPartitions);

                            goto moveNext;
                        }
                    }
                }


                // Not a plan proxy, and not a user proxy. Handle normal partitioning.
normalLine:

                if (partition == null)
                {
                    partition = new VotePartition();
                }
                if (parent == null)
                {
                    parent = line;
                }

                switch (quest.PartitionMode)
                {
                case PartitionMode.None:
                    partition.AddLine(line);
                    break;

                case PartitionMode.ByLine:
                    if (partition.VoteLines.Count > 0)
                    {
                        results.Add(partition);
                    }
                    partition = new VotePartition(line, VoteType.Vote);
                    break;

                case PartitionMode.ByLineTask:
                    if (partition.VoteLines.Count > 0)
                    {
                        results.Add(partition);
                    }

                    if (line.Prefix.Length == 0)
                    {
                        parent    = line;
                        partition = new VotePartition(line, VoteType.Vote);
                    }
                    else if (parent.Task != string.Empty && line.Task == string.Empty)
                    {
                        var mLine = line.Modify(task: parent.Task);
                        partition = new VotePartition(mLine, VoteType.Vote);
                    }
                    else
                    {
                        partition = new VotePartition(line, VoteType.Vote);
                    }
                    break;

                case PartitionMode.ByBlock:
                case PartitionMode.ByBlockAll:
                    if (line.Prefix.Length == 0)
                    {
                        if (partition.VoteLines.Count > 0)
                        {
                            results.Add(partition);
                        }
                        partition = new VotePartition(line, VoteType.Vote);
                    }
                    else
                    {
                        partition.AddLine(line);
                    }
                    break;

                default:
                    break;
                }

                // Exit point for next enumerator line.
moveNext:

                index++;
            }

            if (partition != null && partition.VoteLines.Count > 0)
            {
                results.Add(partition);
            }

            return(results);
        }