Пример #1
0
        private void AssertStringFieldIsMerged(ProgramInformationMergeFieldsFlags field, Action <TestProgramInformation> otherSourceFieldSetter, Func <IProgramInformation, string> informationFieldGetter)
        {
            var emptyInformation = new TestProgramInformation();

            Assert.Null(informationFieldGetter(emptyInformation));
            var otherInformationSource = new TestProgramInformation();

            otherSourceFieldSetter(otherInformationSource);
            Assert.NotNull(informationFieldGetter(otherInformationSource));

            var mergedInformation = emptyInformation.Merge(field, new Tuple <IProgramInformation, ProgramInformationMergeFieldsFlags>(otherInformationSource, field));

            var expectedValue = informationFieldGetter(otherInformationSource);
            var actualValue   = informationFieldGetter(mergedInformation);

            Assert.Equal(expectedValue, actualValue);
        }
Пример #2
0
        /// <summary>
        /// Merges the given IProgramInformation data to form a new, combined version of the information.
        /// </summary>
        /// <param name="programInformation">The "primary" source of information.</param>
        /// <param name="fieldsToMerge">Identifies which fields to merge.</param>
        /// <param name="otherSources">The other information sources to merge, and how to merge them. See Remarks.</param>
        /// <returns>The merged program information.</returns>
        /// <remarks>Each of the additional information sources in the merge will be combined with the primary. The newly
        /// merged information is initialized using the data from <paramref name="programInformation"/>. The
        /// other sources should be ordered such that the first entry is the "most important" and the final entry the
        /// "least important". Each entry also describes which fields it is allowed to override in the merge.
        /// This is to offer a means of conflict resolution in situations arising from multiple sources containing
        /// the same information and attempting to override the default. For example, consider the case in which
        /// <paramref name="programInformation"/> and two entries are provided via <paramref name="otherSources"/>,
        /// all three of which define a value for <see cref="IProgramInformation.Title"/>. Here are some ways in which
        /// this can be configured:
        /// <code>
        /// fieldsToMerge = ProgramInformationMergeFieldsFlags.None;
        /// </code>
        /// In this case, the information in <paramref name="programInformation"/> will not be changed. A copy of the
        /// data within it will be returned. Note, however, that the specific implementation of <see cref="IProgramInformation"/>
        /// used to deliver the resulting data may be different than the original!
        /// Now, consider this scenario:
        /// <code>
        /// fieldsToMerge = ProgramInformationMergeFieldsFlags.Title;
        /// otherSources[0].Item2 = ProgramInformationMergeFieldsFlags.Title;
        /// otherSources[1].Item2 = ProgramInformationMergeFieldsFlags.Title;
        /// </code>
        /// In this case, each entry allows 'Title' to be set. Because <paramref name="programInformation"/> is
        /// treated as the highest priority, its value will be retained unless it is not set (in this case, a <c>null</c>
        /// or empty string). So, if programInformation.Title has no value, but otherSources[0].Item1.Title does, then
        /// the final result contains otherSources[0].Item1.Title, regardless of the value of otherSources[1].Item1.Title.
        /// By carefully considering these flags, you can have a reasonably rich order-of-precedence defined for establishing
        /// the data in the merged result.
        /// </remarks>
        /// <exception cref="System.ArgumentException">Thrown if unrecognized flags are provided in <paramref name="fieldsToMerge"/> or via <paramref name="otherSources"/>.</exception>
        public static IProgramInformation Merge(this IProgramInformation programInformation, ProgramInformationMergeFieldsFlags fieldsToMerge, params Tuple <IProgramInformation, ProgramInformationMergeFieldsFlags>[] otherSources)
        {
            // Grr.... PCLs (at least in .NET 4.0) don't support Enum.GetValues()
            var flagsToProcess = new[] { ProgramInformationMergeFieldsFlags.Title, ProgramInformationMergeFieldsFlags.Vendor, ProgramInformationMergeFieldsFlags.Year, ProgramInformationMergeFieldsFlags.Features, ProgramInformationMergeFieldsFlags.ShortName, ProgramInformationMergeFieldsFlags.Crcs };

            var unknownFlagsForMerge = otherSources.Select(s => s.Item2).Concat(new[] { fieldsToMerge }).Except(flagsToProcess).Except(new[] { ProgramInformationMergeFieldsFlags.None, ProgramInformationMergeFieldsFlags.All });

            if (unknownFlagsForMerge.Any())
            {
                var unknownFlags = unknownFlagsForMerge.Aggregate((all, flag) => all | flag);
                throw new ArgumentException(string.Format(Resources.Strings.ProgramInformation_InvalidFieldFormat, unknownFlags));
            }

            var mergedProgramInformation = new UserSpecifiedProgramInformation(programInformation);
            var mergedPriorityFlags      = fieldsToMerge; // accumulates if a "higher priority" info has already claimed a field

            foreach (var otherSource in otherSources)
            {
                var otherInformation = otherSource.Item1;
                var updateFlags      = otherSource.Item2;
                foreach (var flag in flagsToProcess)
                {
                    if (updateFlags.HasFlag(flag))
                    {
                        switch (flag)
                        {
                        case ProgramInformationMergeFieldsFlags.Title:
                            if (string.IsNullOrEmpty(mergedProgramInformation.Title) || !mergedPriorityFlags.HasFlag(flag))
                            {
                                mergedProgramInformation.Title = otherInformation.Title;
                                if (!string.IsNullOrEmpty(mergedProgramInformation.Title))
                                {
                                    mergedPriorityFlags |= flag;
                                }
                            }
                            break;

                        case ProgramInformationMergeFieldsFlags.Vendor:
                            if (string.IsNullOrEmpty(mergedProgramInformation.Vendor) || !mergedPriorityFlags.HasFlag(flag))
                            {
                                mergedProgramInformation.Vendor = otherInformation.Vendor;
                                if (!string.IsNullOrEmpty(mergedProgramInformation.Vendor))
                                {
                                    mergedPriorityFlags |= flag;
                                }
                            }
                            break;

                        case ProgramInformationMergeFieldsFlags.Year:
                            if (string.IsNullOrEmpty(mergedProgramInformation.Year) || !mergedPriorityFlags.HasFlag(flag))
                            {
                                mergedProgramInformation.Year = otherInformation.Year;
                                if (!string.IsNullOrEmpty(mergedProgramInformation.Year))
                                {
                                    mergedPriorityFlags |= flag;
                                }
                            }
                            break;

                        case ProgramInformationMergeFieldsFlags.Features:
                            // This runs the risk of combining conflicting flags...
                            mergedProgramInformation.Features = ProgramFeatures.Combine(mergedProgramInformation.Features, otherInformation.Features);
                            mergedPriorityFlags |= flag;
                            break;

                        case ProgramInformationMergeFieldsFlags.ShortName:
                            if (string.IsNullOrEmpty(mergedProgramInformation.ShortName) || !mergedPriorityFlags.HasFlag(flag))
                            {
                                mergedProgramInformation.ShortName = otherInformation.ShortName;
                                if (!string.IsNullOrEmpty(mergedProgramInformation.ShortName))
                                {
                                    mergedPriorityFlags |= flag;
                                }
                            }
                            break;

                        case ProgramInformationMergeFieldsFlags.Crcs:
                            var crcsToMerge = otherInformation.Crcs.Where(o => !mergedProgramInformation.Crcs.Any(m => m.Crc == o.Crc));
                            foreach (var crc in crcsToMerge)
                            {
                                mergedProgramInformation.AddCrc(crc.Crc, crc.Description, crc.Incompatibilities);
                                mergedPriorityFlags |= flag;
                            }
                            break;
                        }
                    }
                }
            }
            return(mergedProgramInformation);
        }