From 1fd8079566f4fef227a786b4650bd694e16b707a Mon Sep 17 00:00:00 2001 From: Glenn Watson Date: Wed, 10 Jul 2019 12:11:43 +1000 Subject: [PATCH] Azure build script, cake script, complying with code standards --- .editorconfig | 36 + .gitignore | 116 +- NuGet.Config | 18 - appveyor.yml | 126 -- azure-pipelines.yml | 23 + build.cake | 28 + build.cmd | 2 + build.config | 2 + build.ps1 | 112 + build.sh | 51 + cake.config | 7 + logo/dd-logo.png | Bin 0 -> 22604 bytes logo/dd-logo.svg | 115 + logo/dd-shapes.svg | 174 ++ src/Directory.Build.props | 64 +- .../Cache/SourceCache.cs | 7 +- .../DynamicData.Benchmarks.csproj | 2 +- src/DynamicData.Benchmarks/List/GroupAdd.cs | 6 +- .../List/GroupRemove.cs | 8 +- src/DynamicData.Benchmarks/List/SourceList.cs | 8 +- src/DynamicData.Benchmarks/Program.cs | 5 +- src/DynamicData.Profile/Allocations.cs | 6 +- src/DynamicData.Profile/AllocationsCount.cs | 6 +- .../CacheAllocationChecks.cs | 9 +- .../CheapAndDirtyAllocationTest.cs | 11 +- src/DynamicData.Profile/Person.cs | 6 +- .../Domain/Person.cs | 114 - .../Domain/RandomPersonGenerator.cs | 61 - .../DynamicData.ReactiveUI.Tests.csproj | 25 - .../Fixtures/BindChangeSetFixture.cs | 86 - .../Fixtures/BindFromObservableListFixture.cs | 86 - .../Fixtures/BindSortedChangeSetFixture.cs | 183 -- ...oObservableChangeSetWithoutINdexFixture.cs | 99 - .../Fixtures/ToObservableChangeSetFixture.cs | 98 - .../Fixtures/TransformManyFixture.cs | 178 -- ...ransformManyObservableCollectionFixture.cs | 190 -- ...mManyObservableCollectionWithKeyFixture.cs | 231 -- .../DynamicData.ReactiveUI.csproj | 27 - src/DynamicData.ReactiveUI/DynamicDataEx.cs | 315 --- .../ObservableCacheToReactiveListAdaptor.cs | 102 - .../ObservableListToReactiveListAdaptor.cs | 48 - src/DynamicData.ReactiveUI/ReactiveListEx.cs | 210 -- .../SortedReactiveListAdaptor.cs | 107 - .../AggregationTests/AggregationFixture.cs | 7 +- .../AggregationTests/AverageFixture.cs | 2 +- .../AggregationTests/MaxFixture.cs | 3 +- .../AggregationTests/SumFixture.cs | 2 +- src/DynamicData.Tests/AutoRefreshFilter.cs | 2 - .../Binding/BindingLIstBindListFixture.cs | 2 +- .../Binding/BindingListBindCacheFixture.cs | 2 +- .../BindingListBindCacheSortedFixture.cs | 8 +- ...eeplyNestedNotifyPropertyChangedFixture.cs | 67 +- .../Binding/NotifyPropertyChangedExFixture.cs | 2 +- .../ObservableCollectionBindCacheFixture.cs | 2 +- ...ervableCollectionBindCacheSortedFixture.cs | 6 +- .../ObservableCollectionBindListFixture.cs | 2 +- ...bleCollectionExtendedToChangeSetFixture.cs | 2 +- .../ObservableCollectionToChangeSetFixture.cs | 2 +- ...yObservableCollectionToChangeSetFixture.cs | 2 +- src/DynamicData.Tests/Cache/AndFixture.cs | 4 +- .../Cache/AutoRefreshFixture.cs | 2 +- src/DynamicData.Tests/Cache/BatchFixture.cs | 3 +- src/DynamicData.Tests/Cache/BatchIfFixture.cs | 3 - .../Cache/BatchIfWithTimeOutFixture.cs | 1 - .../Cache/BufferInitialFixture.cs | 4 +- .../Cache/ChangesReducerFixture.cs | 4 +- .../Cache/DeferUntilLoadedFixture.cs | 2 +- .../Cache/DisposeManyFixture.cs | 2 +- .../Cache/DistinctFixture.cs | 3 +- .../Cache/DynamicAndFixture.cs | 2 +- .../Cache/DynamicExceptFixture.cs | 2 +- .../Cache/DynamicOrFixture.cs | 2 +- .../Cache/DynamicXorFixture.cs | 2 +- .../Cache/EditDiffFixture.cs | 7 +- ...eObservableToObservableChangeSetFixture.cs | 2 +- src/DynamicData.Tests/Cache/ExceptFixture.cs | 4 +- .../Cache/ExpireAfterFixture.cs | 8 +- .../Cache/FilterControllerFixture.cs | 6 +- src/DynamicData.Tests/Cache/FilterFixture.cs | 2 +- .../Cache/FilterOnPropertyFixture.cs | 3 +- .../Cache/ForEachChangeFixture.cs | 2 +- .../Cache/FromAsyncFixture.cs | 3 +- .../Cache/FullJoinFixture.cs | 106 +- .../Cache/FullJoinManyFixture.cs | 14 +- .../Cache/GroupControllerFixture.cs | 18 +- .../GroupControllerForFilteredItemsFixture.cs | 18 +- src/DynamicData.Tests/Cache/GroupFixture.cs | 6 +- .../Cache/GroupImmutableFixture.cs | 6 +- .../Cache/GroupOnPropertyFixture.cs | 3 +- ...roupOnPropertyWithImmutableStateFixture.cs | 3 +- .../Cache/IgnoreUpdateFixture.cs | 2 +- .../Cache/IncludeUpdateFixture.cs | 2 +- .../Cache/InnerJoinFixture.cs | 101 +- .../Cache/InnerJoinManyFixture.cs | 19 +- .../Cache/LeftJoinFixture.cs | 101 +- .../Cache/LeftJoinManyFixture.cs | 17 +- .../Cache/MergeManyFixture.cs | 2 +- .../Cache/MergeManyItemsFixture.cs | 2 +- .../Cache/MergeManyWithKeyOverloadFixture.cs | 2 +- .../Cache/MonitorStatusFixture.cs | 2 +- .../Cache/ObservableChangeSetFixture.cs | 11 +- .../ObservableToObservableChangeSetFixture.cs | 5 - src/DynamicData.Tests/Cache/OnItemFixture.cs | 2 +- src/DynamicData.Tests/Cache/OrFixture.cs | 4 +- src/DynamicData.Tests/Cache/PageFixture.cs | 2 +- .../Cache/QueryWhenChangedFixture.cs | 2 +- .../Cache/RefCountFixture.cs | 2 +- .../Cache/RightJoinFixture.cs | 100 +- .../Cache/RightJoinManyFixture.cs | 19 +- .../Cache/SizeLimitFixture.cs | 2 +- src/DynamicData.Tests/Cache/SortFixture.cs | 8 +- .../Cache/SortObservableFixtureFixture.cs | 5 +- .../Cache/SubscribeManyFixture.cs | 2 +- src/DynamicData.Tests/Cache/SwitchFixture.cs | 6 +- .../Cache/TimeExpiryFixture.cs | 5 + .../Cache/ToObservableChangeSetFixture.cs | 2 +- ...bservableChangeSetFixtureWithCompletion.cs | 1 - .../Cache/ToSortedCollectionFixture.cs | 2 +- .../Cache/TransformAsyncFixture.cs | 4 +- .../Cache/TransformFixture.cs | 2 +- .../Cache/TransformManyFixture.cs | 4 +- .../TransformManyObservableCacheFixture.cs | 7 - .../Cache/TransformManyRefreshFixture.cs | 2 +- .../Cache/TransformManySimpleFixture.cs | 4 +- .../Cache/TransformSafeAsyncFixture.cs | 7 +- .../Cache/TransformSafeFixture.cs | 3 +- .../Cache/TransformSafeParallelFixture.cs | 3 +- .../Cache/TransformTreeFixture.cs | 30 +- .../Cache/TransformTreeWithRefreshFixture.cs | 32 +- .../Cache/TrueForAllFixture.cs | 3 +- .../Cache/TrueForAnyFixture.cs | 3 +- src/DynamicData.Tests/Cache/WatchFixture.cs | 2 +- src/DynamicData.Tests/Cache/WatcherFixture.cs | 5 +- src/DynamicData.Tests/Cache/XorFixture.cs | 4 +- .../Domain/ParentAndChildren.cs | 30 +- src/DynamicData.Tests/Domain/Person.cs | 79 +- .../Domain/PersonEmployment.cs | 24 +- src/DynamicData.Tests/Domain/PersonObs.cs | 79 +- .../Domain/PersonWithChildren.cs | 1 - .../Domain/PersonWithGender.cs | 5 + .../DynamicData.Tests.csproj | 2 +- .../Kernal/CacheUpdaterFixture.cs | 4 +- .../Kernal/DistinctUpdateFixture.cs | 2 +- src/DynamicData.Tests/Kernal/EnumerableEx.cs | 33 +- .../Kernal/KeyValueFixture.cs | 2 +- src/DynamicData.Tests/Kernal/OptionFixture.cs | 2 +- .../Kernal/SourceUpdaterFixture.cs | 1 - src/DynamicData.Tests/Kernal/UpdateFixture.cs | 2 +- src/DynamicData.Tests/List/AndFixture.cs | 5 +- .../List/AutoRefreshFixture.cs | 32 +- src/DynamicData.Tests/List/BatchFixture.cs | 2 +- src/DynamicData.Tests/List/BatchIfFixture.cs | 2 +- .../List/BatchIfWithTimeOutFixture.cs | 2 +- src/DynamicData.Tests/List/BufferFixture.cs | 2 +- .../List/BufferInitialFixture.cs | 2 + src/DynamicData.Tests/List/CastFixture.cs | 3 +- .../List/ChangeAwareListFixture.cs | 6 +- .../List/CloneChangesFixture.cs | 4 +- src/DynamicData.Tests/List/CloneFixture.cs | 3 +- .../List/CreationFixtures.cs | 3 +- .../List/DeferUntilLoadedFixture.cs | 2 +- .../List/DisposeManyFixture.cs | 2 +- .../List/DistinctValuesFixture.cs | 3 +- .../List/DynamicAndFixture.cs | 2 +- .../List/DynamicExceptFixture.cs | 2 +- .../List/DynamicOrFixture.cs | 3 +- .../List/DynamicXOrFixture.cs | 3 +- src/DynamicData.Tests/List/EditDiffFixture.cs | 3 +- src/DynamicData.Tests/List/ExceptFixture.cs | 6 +- .../List/ExpireAfterFixture.cs | 7 +- ...terControllerFixtureWithClearAndReplace.cs | 6 +- .../FilterControllerFixtureWithDiffSet.cs | 4 +- src/DynamicData.Tests/List/FilterFixture.cs | 3 +- .../List/FilterOnObservableFixture.cs | 2 - .../List/FilterOnPropertyFixture.cs | 4 +- .../List/FilterWithObservable.cs | 4 +- .../List/ForEachChangeFixture.cs | 2 +- .../List/FromAsyncFixture.cs | 4 +- .../List/GroupImmutableFixture.cs | 7 +- src/DynamicData.Tests/List/GroupOnFixture.cs | 3 +- .../List/GroupOnPropertyFixture.cs | 6 +- ...roupOnPropertyWithImmutableStateFixture.cs | 4 +- .../List/MergeManyFixture.cs | 2 +- src/DynamicData.Tests/List/OrFixture.cs | 6 +- src/DynamicData.Tests/List/PageFixture.cs | 5 +- .../List/QueryWhenChangedFixture.cs | 2 +- .../List/RecursiveTransformManyFixture.cs | 2 +- src/DynamicData.Tests/List/RefCountFixture.cs | 3 +- .../List/RemoveManyFixture.cs | 3 +- src/DynamicData.Tests/List/ReverseFixture.cs | 3 +- src/DynamicData.Tests/List/SelectFixture.cs | 2 +- .../List/SizeLimitFixture.cs | 3 +- .../List/SortMutableFixture.cs | 8 +- .../List/SortPrimitiveFixture.cs | 2 - .../List/SourceListPreviewFixture.cs | 3 +- .../List/SubscribeManyFixture.cs | 2 +- src/DynamicData.Tests/List/SwitchFixture.cs | 3 +- .../List/TransformAsyncFixture.cs | 3 +- .../List/TransformFixture.cs | 2 +- .../List/TransformManyFixture.cs | 10 +- ...ransformManyObservableCollectionFixture.cs | 10 +- .../List/VirtualisationFixture.cs | 2 +- src/DynamicData.Tests/List/XOrFixture.cs | 4 +- .../Utilities/SelectManyExtensions.cs | 11 +- src/DynamicData.sln | 42 +- .../Aggregation/AggregateEnumerator.cs | 10 + src/DynamicData/Aggregation/AggregateItem.cs | 39 +- src/DynamicData/Aggregation/AggregateType.cs | 5 +- src/DynamicData/Aggregation/AggregationEx.cs | 39 +- src/DynamicData/Aggregation/Avg.cs | 5 +- src/DynamicData/Aggregation/AvgEx.cs | 37 +- src/DynamicData/Aggregation/CountEx.cs | 6 +- .../Aggregation/IAggregateChangeSet.cs | 4 + src/DynamicData/Aggregation/MaxEx.cs | 39 +- src/DynamicData/Aggregation/StdDev.cs | 3 + src/DynamicData/Aggregation/StdDevEx.cs | 35 +- src/DynamicData/Aggregation/SumEx.cs | 112 +- src/DynamicData/Alias/ObservableCacheAlias.cs | 158 +- src/DynamicData/Alias/ObservableListAlias.cs | 56 +- src/DynamicData/Attributes.cs | 6 +- .../Binding/AbstractNotifyPropertyChanged.cs | 12 +- src/DynamicData/Binding/BindingListAdaptor.cs | 11 +- .../Binding/BindingListEventsSuspender.cs | 6 +- src/DynamicData/Binding/ExpressionBuilder.cs | 36 +- src/DynamicData/Binding/IEvaluateAware.cs | 3 + src/DynamicData/Binding/IIndexAware.cs | 5 +- .../INotifyCollectionChangedSuspender.cs | 4 + .../Binding/IObservableCollection.cs | 5 +- .../Binding/IObservableCollectionAdaptor.cs | 3 + .../ISortedObservableCollectionAdaptor.cs | 3 + .../Binding/NotifyPropertyChangedEx.cs | 237 +- .../Binding/ObservableCollectionAdaptor.cs | 19 + .../Binding/ObservableCollectionEx.cs | 50 +- .../Binding/ObservableCollectionExtended.cs | 42 +- .../Binding/ObservablePropertyFactory.cs | 20 +- .../Binding/ObservablePropertyFactoryCache.cs | 7 +- .../Binding/ObservablePropertyPart.cs | 4 + src/DynamicData/Binding/PropertyValue.cs | 30 +- src/DynamicData/Binding/SortDirection.cs | 5 +- src/DynamicData/Binding/SortExpression.cs | 6 +- .../Binding/SortExpressionComparer.cs | 46 +- .../Binding/SortedBindingListAdaptor.cs | 15 +- .../SortedObservableCollectionAdaptor.cs | 19 +- src/DynamicData/Cache/Change.cs | 30 +- src/DynamicData/Cache/ChangeAwareCache.cs | 55 +- src/DynamicData/Cache/ChangeSet.cs | 12 +- src/DynamicData/Cache/DistinctChangeSet.cs | 6 +- src/DynamicData/Cache/GroupChangeSet.cs | 6 +- src/DynamicData/Cache/ICache.cs | 6 +- src/DynamicData/Cache/ICacheUpdater.cs | 5 +- src/DynamicData/Cache/IChangeSet.cs | 6 +- src/DynamicData/Cache/IGrouping.cs | 6 +- src/DynamicData/Cache/IIntermediateCache.cs | 4 + src/DynamicData/Cache/IKeyValueCollection.cs | 6 +- src/DynamicData/Cache/IObservableCache.cs | 4 + src/DynamicData/Cache/IPagedChangeSet.cs | 6 +- src/DynamicData/Cache/IQuery.cs | 4 + src/DynamicData/Cache/ISourceCache.cs | 4 + src/DynamicData/Cache/ISourceUpdater.cs | 6 +- src/DynamicData/Cache/IndexedItem.cs | 23 +- src/DynamicData/Cache/IntermediateCache.cs | 10 +- .../Cache/Internal/AbstractFilter.cs | 18 +- .../Internal/AnonymousObservableCache.cs | 10 +- .../Cache/Internal/AnonymousQuery.cs | 4 + src/DynamicData/Cache/Internal/AutoRefresh.cs | 8 +- src/DynamicData/Cache/Internal/BatchIf.cs | 24 +- src/DynamicData/Cache/Internal/Cache.cs | 24 +- src/DynamicData/Cache/Internal/CacheEx.cs | 8 +- .../Cache/Internal/CacheUpdater.cs | 109 +- src/DynamicData/Cache/Internal/Cast.cs | 7 +- .../Cache/Internal/ChangesReducer.cs | 12 +- .../Cache/Internal/CombineOperator.cs | 5 +- src/DynamicData/Cache/Internal/Combiner.cs | 22 +- .../Cache/Internal/DeferUntilLoaded.cs | 10 +- .../Cache/Internal/DictionaryExtensions.cs | 8 +- src/DynamicData/Cache/Internal/DisposeMany.cs | 4 + .../Cache/Internal/DistinctCalculator.cs | 24 +- .../Cache/Internal/DynamicCombiner.cs | 34 +- .../Cache/Internal/DynamicFilter.cs | 6 +- src/DynamicData/Cache/Internal/EditDiff.cs | 6 +- .../Cache/Internal/ExpirableItem.cs | 12 +- src/DynamicData/Cache/Internal/FilterEx.cs | 54 +- .../Cache/Internal/FilterOnProperty.cs | 4 + .../Cache/Internal/FilteredIndexCalculator.cs | 28 +- src/DynamicData/Cache/Internal/FinallySafe.cs | 4 + src/DynamicData/Cache/Internal/FullJoin.cs | 8 +- .../Cache/Internal/FullJoinMany.cs | 4 + .../Cache/Internal/GroupImmutable.cs | 43 +- src/DynamicData/Cache/Internal/GroupOn.cs | 53 +- .../Cache/Internal/GroupOnProperty.cs | 10 +- .../GroupOnPropertyWithImmutableState.cs | 8 +- src/DynamicData/Cache/Internal/IFilter.cs | 6 +- .../Cache/Internal/IKeySelector.cs | 3 + .../Cache/Internal/ImmutableGroup.cs | 30 +- .../Cache/Internal/ImmutableGroupChangeSet.cs | 6 +- .../Cache/Internal/IndexAndNode.cs | 6 +- .../Cache/Internal/IndexCalculator.cs | 31 +- src/DynamicData/Cache/Internal/InnerJoin.cs | 7 +- .../Cache/Internal/InnerJoinMany.cs | 6 +- src/DynamicData/Cache/Internal/KeyComparer.cs | 6 +- src/DynamicData/Cache/Internal/KeySelector.cs | 6 + .../Cache/Internal/KeySelectorException.cs | 12 +- .../Cache/Internal/KeyValueCollection.cs | 4 + .../Cache/Internal/KeyValueComparer.cs | 8 +- src/DynamicData/Cache/Internal/LeftJoin.cs | 11 +- .../Cache/Internal/LeftJoinMany.cs | 4 + .../Cache/Internal/LockFreeObservableCache.cs | 41 +- .../Cache/Internal/ManagedGroup.cs | 18 +- src/DynamicData/Cache/Internal/MergeMany.cs | 9 +- .../Cache/Internal/MergeManyItems.cs | 9 +- .../Cache/Internal/ObservableWithValue.cs | 4 + src/DynamicData/Cache/Internal/Page.cs | 19 +- .../Cache/Internal/QueryWhenChanged.cs | 9 +- .../Cache/Internal/ReaderWriter.cs | 28 +- src/DynamicData/Cache/Internal/RefCount.cs | 13 +- .../Cache/Internal/RemoveKeyEnumerator.cs | 6 +- src/DynamicData/Cache/Internal/RightJoin.cs | 7 +- .../Cache/Internal/RightJoinMany.cs | 4 + src/DynamicData/Cache/Internal/SizeExpirer.cs | 9 +- src/DynamicData/Cache/Internal/SizeLimiter.cs | 6 +- src/DynamicData/Cache/Internal/Sort.cs | 18 +- .../Cache/Internal/SpecifiedGrouper.cs | 5 + .../Cache/Internal/StaticFilter.cs | 9 +- .../Cache/Internal/StatusMonitor.cs | 16 +- .../Cache/Internal/SubscribeMany.cs | 13 +- src/DynamicData/Cache/Internal/Switch.cs | 12 +- src/DynamicData/Cache/Internal/TimeExpirer.cs | 5 + .../Cache/Internal/ToObservableChangeSet.cs | 13 +- src/DynamicData/Cache/Internal/Transform.cs | 8 + .../Cache/Internal/TransformAsync.cs | 22 +- .../Cache/Internal/TransformMany.cs | 50 +- .../Internal/TransformWithForcedTransform.cs | 8 +- src/DynamicData/Cache/Internal/TreeBuilder.cs | 13 +- src/DynamicData/Cache/Internal/TrueFor.cs | 4 + src/DynamicData/Cache/Internal/Virtualiser.cs | 14 +- src/DynamicData/Cache/MissingKeyException.cs | 30 +- src/DynamicData/Cache/Node.cs | 59 +- src/DynamicData/Cache/ObservableCache.cs | 30 +- src/DynamicData/Cache/ObservableCacheEx.cs | 1990 +++++++++++++---- src/DynamicData/Cache/PageRequest.cs | 46 +- src/DynamicData/Cache/PageResponse.cs | 34 +- src/DynamicData/Cache/PagedChangeSet.cs | 23 +- src/DynamicData/Cache/SortOptimisations.cs | 12 +- src/DynamicData/Cache/SortedChangeSet.cs | 25 +- src/DynamicData/Cache/SourceCache.cs | 32 +- src/DynamicData/Cache/SourceCacheEx.cs | 11 +- .../Cache/Tests/ChangeSetAggregator.cs | 23 +- .../Tests/DistinctChangeSetAggregator.cs | 26 +- .../Cache/Tests/PagedChangeSetAggregator.cs | 25 +- .../Cache/Tests/SortedChangeSetAggregator.cs | 25 +- src/DynamicData/Cache/Tests/TestEx.cs | 30 +- .../Cache/Tests/VirtualChangeSetAggregator.cs | 23 +- src/DynamicData/Cache/VirtualChangeSet.cs | 38 +- src/DynamicData/Cache/VirtualRequest.cs | 30 +- src/DynamicData/Cache/VirtualResponse.cs | 30 +- .../Diagnostics/ChangeStatistics.cs | 39 +- src/DynamicData/Diagnostics/ChangeSummary.cs | 23 +- .../Diagnostics/DiagnosticOperators.cs | 18 +- src/DynamicData/DynamicData.csproj | 2 +- src/DynamicData/EnumerableEx.cs | 25 +- .../Experimental/ExperimentalEx.cs | 12 +- .../Experimental/ISubjectWithRefCount.cs | 6 +- src/DynamicData/Experimental/IWatcher.cs | 6 +- .../Experimental/SubjectWithRefCount.cs | 6 +- src/DynamicData/Experimental/Watcher.cs | 7 +- src/DynamicData/IChangeSet.cs | 5 +- src/DynamicData/Kernel/ConnectionStatus.cs | 3 + src/DynamicData/Kernel/DoubleCheck.cs | 13 +- src/DynamicData/Kernel/EnumerableEx.cs | 47 +- src/DynamicData/Kernel/EnumerableIList.cs | 7 +- src/DynamicData/Kernel/Error.cs | 32 +- src/DynamicData/Kernel/ISupportsCapcity.cs | 5 +- src/DynamicData/Kernel/InternalEx.cs | 20 +- src/DynamicData/Kernel/ItemWithIndex.cs | 11 +- src/DynamicData/Kernel/ItemWithValue.cs | 10 +- src/DynamicData/Kernel/OptionElse.cs | 17 +- src/DynamicData/Kernel/OptionExtensions.cs | 63 +- src/DynamicData/Kernel/Optional.cs | 39 +- src/DynamicData/Kernel/ParallelEx.cs | 26 +- .../Kernel/ReadOnlyCollectionLight.cs | 4 + .../Kernel/ReferenceEqualityComparer.cs | 4 + src/DynamicData/List/Change.cs | 50 +- src/DynamicData/List/ChangeAwareList.cs | 561 +++-- .../List/ChangeAwareListWithRefCounts.cs | 6 +- src/DynamicData/List/ChangeSet.cs | 27 +- src/DynamicData/List/ChangeSetEx.cs | 30 +- src/DynamicData/List/IChangeSet.cs | 6 +- src/DynamicData/List/IExtendedList.cs | 6 +- src/DynamicData/List/IGrouping.cs | 4 + src/DynamicData/List/IObservableList.cs | 4 + src/DynamicData/List/IPageChangeSet.cs | 4 + src/DynamicData/List/ISourceList.cs | 4 + .../List/Internal/AnonymousObservableList.cs | 12 +- src/DynamicData/List/Internal/AutoRefresh.cs | 7 +- src/DynamicData/List/Internal/BufferIf.cs | 12 +- src/DynamicData/List/Internal/Combiner.cs | 27 +- .../List/Internal/DeferUntilLoaded.cs | 4 + src/DynamicData/List/Internal/Distinct.cs | 67 +- .../List/Internal/DynamicCombiner.cs | 32 +- src/DynamicData/List/Internal/EditDiff.cs | 6 +- .../List/Internal/ExpirableItem.cs | 30 +- src/DynamicData/List/Internal/ExpireAfter.cs | 5 + src/DynamicData/List/Internal/Filter.cs | 65 +- .../List/Internal/FilterOnObservable.cs | 40 +- .../List/Internal/FilterOnProperty.cs | 6 +- src/DynamicData/List/Internal/FilterStatic.cs | 17 +- src/DynamicData/List/Internal/Group.cs | 34 +- src/DynamicData/List/Internal/GroupOn.cs | 65 +- .../List/Internal/GroupOnImmutable.cs | 74 +- .../List/Internal/GroupOnProperty.cs | 8 +- .../GroupOnPropertyWithImmutableState.cs | 8 +- .../List/Internal/ImmutableGroup.cs | 28 +- src/DynamicData/List/Internal/MergeMany.cs | 4 + src/DynamicData/List/Internal/OnBeingAdded.cs | 4 + .../List/Internal/OnBeingRemoved.cs | 5 + src/DynamicData/List/Internal/Pager.cs | 20 +- .../List/Internal/QueryWhenChanged.cs | 4 + src/DynamicData/List/Internal/ReaderWriter.cs | 35 +- src/DynamicData/List/Internal/RefCount.cs | 13 +- .../List/Internal/ReferenceCountTracker.cs | 6 +- src/DynamicData/List/Internal/SizeLimiter.cs | 6 + src/DynamicData/List/Internal/Sort.cs | 45 +- .../List/Internal/SubscribeMany.cs | 4 + src/DynamicData/List/Internal/Switch.cs | 9 +- .../List/Internal/ToObservableChangeSet.cs | 14 +- .../List/Internal/TransformAsync.cs | 80 +- .../List/Internal/TransformMany.cs | 47 +- src/DynamicData/List/Internal/Transformer.cs | 64 +- .../List/Internal/UnifiedChange.cs | 10 +- src/DynamicData/List/Internal/Virtualiser.cs | 21 +- src/DynamicData/List/ItemChange.cs | 11 +- src/DynamicData/List/Linq/AddKeyEnumerator.cs | 12 +- .../List/Linq/ItemChangeEnumerator.cs | 5 + .../List/Linq/ReverseEnumerator.cs | 12 +- .../List/Linq/UnifiedChangeEnumerator.cs | 4 + .../List/Linq/WithoutIndexEnumerator.cs | 4 + src/DynamicData/List/ListEx.cs | 197 +- src/DynamicData/List/ObservableListEx.cs | 690 ++++-- src/DynamicData/List/PageChangeSet.cs | 6 +- src/DynamicData/List/RangeChange.cs | 4 + src/DynamicData/List/SortException.cs | 19 + src/DynamicData/List/SortOptions.cs | 4 + src/DynamicData/List/SourceList.cs | 35 +- .../List/SourceListEditConvenienceEx.cs | 88 +- src/DynamicData/List/SourceListEx.cs | 18 +- .../List/Tests/ChangeSetAggregator.cs | 30 +- src/DynamicData/List/Tests/TestEx.cs | 6 +- .../List/UnspecifiedIndexException.cs | 10 + src/DynamicData/List/VirtualChangeSet.cs | 6 +- src/DynamicData/ObservableChangeSet.cs | 181 +- src/DynamicData/ObsoleteEx.cs | 6 +- .../Platforms/net45/PLinqFilteredUpdater.cs | 8 +- .../Platforms/net45/PSubscribeMany.cs | 6 +- src/DynamicData/Platforms/net45/PTransform.cs | 10 +- src/DynamicData/Platforms/net45/ParallelEx.cs | 10 +- .../Platforms/net45/ParallelOperators.cs | 89 +- .../Platforms/net45/ParallelisationOptions.cs | 12 +- src/DynamicData/Properties/Annotations.cs | 7 +- src/analyzers.ruleset | 285 +++ src/global.json | 5 +- version.json | 17 + 461 files changed, 8950 insertions(+), 4489 deletions(-) create mode 100644 .editorconfig delete mode 100644 NuGet.Config delete mode 100644 appveyor.yml create mode 100644 azure-pipelines.yml create mode 100644 build.cake create mode 100644 build.cmd create mode 100644 build.config create mode 100644 build.ps1 create mode 100644 build.sh create mode 100644 cake.config create mode 100644 logo/dd-logo.png create mode 100644 logo/dd-logo.svg create mode 100644 logo/dd-shapes.svg delete mode 100644 src/DynamicData.ReactiveUI.Tests/Domain/Person.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs delete mode 100644 src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs delete mode 100644 src/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj delete mode 100644 src/DynamicData.ReactiveUI/DynamicDataEx.cs delete mode 100644 src/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs delete mode 100644 src/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs delete mode 100644 src/DynamicData.ReactiveUI/ReactiveListEx.cs delete mode 100644 src/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs create mode 100644 src/analyzers.ruleset create mode 100644 version.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..18cbb7d4a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,36 @@ + +[*.{c,c++,cc,cp,cpp,cu,cuh,cxx,h,hh,hpp,hxx,inc,inl,ino,ipp,mpp,proto,tpp}] +indent_style=tab +indent_size=tab +tab_width=4 + +[*.{asax,ascx,aspx,cs,cshtml,css,htm,html,js,jsx,master,razor,skin,ts,tsx,vb,xaml,xamlx,xoml}] +indent_style=space +indent_size=4 +tab_width=4 + +[*.{appxmanifest,build,config,csproj,dbml,discomap,dtd,json,jsproj,lsproj,njsproj,nuspec,proj,props,resjson,resw,resx,StyleCop,targets,tasks,vbproj,xml,xsd}] +indent_style=space +indent_size=2 +tab_width=2 + +[*] + +# Microsoft .NET properties +csharp_new_line_before_members_in_object_initializers=false +csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion +csharp_style_var_elsewhere=true:hint +csharp_style_var_for_built_in_types=true:hint +csharp_style_var_when_type_is_apparent=true:hint +dotnet_style_predefined_type_for_locals_parameters_members=true:hint +dotnet_style_predefined_type_for_member_access=true:hint +dotnet_style_require_accessibility_modifiers=for_non_interface_members:hint + +# ReSharper properties +resharper_csharp_wrap_lines=false +resharper_space_within_single_line_array_initializer_braces=true + +# ReSharper inspection severities +resharper_arrange_this_qualifier_highlighting=none +resharper_empty_general_catch_clause_highlighting=none +resharper_inconsistent_naming_highlighting=none diff --git a/.gitignore b/.gitignore index 24bf8b200..d5ff53b4b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,10 @@ - -# Created by https://www.gitignore.io/api/visualstudio - -### VisualStudio ### ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## ## Get latest from /~https://github.com/github/gitignore/blob/master/VisualStudio.gitignore # User-specific files +*.rsuser *.suo *.user *.userosscache @@ -16,7 +13,8 @@ # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs -project.lock.json +# Mono auto generated files +mono_crash.* # Build results [Dd]ebug/ @@ -25,16 +23,21 @@ project.lock.json [Rr]eleases/ x64/ x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ -# Visual Studio 2015 cache/options directory +# Visual Studio 2015/2017 cache/options directory .vs/ # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ +# Visual Studio 2017 auto generated files +Generated\ Files/ + # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* @@ -42,26 +45,35 @@ bld/ # NUNIT *.VisualState.xml TestResult.xml +nunit-*.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c +# Benchmark Results +BenchmarkDotNet.Artifacts/ + # .NET Core project.lock.json project.fragment.lock.json artifacts/ -**/Properties/launchSettings.json +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio *_i.c *_p.c -*_i.h +*_h.h *.ilk *.meta *.obj +*.iobj *.pch *.pdb +*.ipdb *.pgc *.pgd *.rsp @@ -71,6 +83,7 @@ artifacts/ *.tlh *.tmp *.tmp_proj +*_wpftmp.csproj *.log *.vspscc *.vssscc @@ -99,6 +112,9 @@ ipch/ *.vspx *.sap +# Visual Studio Trace Files +*.e2e + # TFS 2012 Local Workspace $tf/ @@ -119,6 +135,10 @@ _TeamCity* # DotCover is a Code Coverage Tool *.dotCover +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + # Visual Studio code coverage results *.coverage *.coveragexml @@ -154,7 +174,7 @@ publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml -# TODO: Comment the next line if you want to checkin your web deploy settings +# Note: Comment the next line if you want to checkin your web deploy settings, # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj @@ -166,12 +186,14 @@ PublishScripts/ # NuGet Packages *.nupkg +# NuGet Symbol Packages +*.snupkg # The packages folder can be ignored because of Package Restore -**/packages/* +**/[Pp]ackages/* # except build/, which is used as an MSBuild target. -!**/packages/build/ +!**/[Pp]ackages/build/ # Uncomment if necessary however generally it will be regenerated when needed -#!**/packages/repositories.config +#!**/[Pp]ackages/repositories.config # NuGet v3's project.json files produces more ignorable files *.nuget.props *.nuget.targets @@ -189,12 +211,15 @@ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt +*.appx +*.appxbundle +*.appxupload # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache -!*.[Cc]ache/ +!?*.[Cc]ache/ # Others ClientBin/ @@ -207,6 +232,10 @@ ClientBin/ *.publishsettings orleans.codegen.cs +# Including strong name files can present a security risk +# (/~https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + # Since there are multiple workflows, uncomment next line to ignore bower_components # (/~https://github.com/github/gitignore/pull/1529#issuecomment-104372622) #bower_components/ @@ -221,6 +250,8 @@ _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak # SQL Server files *.mdf @@ -231,6 +262,10 @@ UpgradeLog*.htm *.rdl.data *.bim.layout *.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl # Microsoft Fakes FakesAssemblies/ @@ -242,9 +277,6 @@ FakesAssemblies/ .ntvs_analysis.dat node_modules/ -# Typescript v1 declaration files -typings/ - # Visual Studio 6 build log *.plg @@ -269,20 +301,19 @@ paket-files/ # FAKE - F# Make .fake/ -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ +# CodeRush personal settings +.cr/personal # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config +tools/** +!tools/packages.config + +# Tabs Studio +*.tss # Telerik's JustMock configuration file *.jmconfig @@ -293,8 +324,39 @@ __pycache__/ *.odx.cs *.xsd.cs -**/*BenchmarkDotNet.Artifacts/results +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ -# End of https://www.gitignore.io/api/visualstudio -DynamicData/project.lock.json +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# ReactiveUI +artifacts/ +src/ReactiveUI.Events*/Events_*.cs + +# macOS +.DS_Store + +src/*.Tests/**/ApiApprovalTests*.received.txt +.idea/ +# Fody Weavers (for tests) +src/Tools/ \ No newline at end of file diff --git a/NuGet.Config b/NuGet.Config deleted file mode 100644 index e2ce67cbb..000000000 --- a/NuGet.Config +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 22025e708..000000000 --- a/appveyor.yml +++ /dev/null @@ -1,126 +0,0 @@ -# configuration for master -- - branches: - only: - - master - - environment: - # Set the DOTNET_SKIP_FIRST_TIME_EXPERIENCE environment variable to stop wasting time caching packages - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - # Disable sending usage data to Microsoft - DOTNET_CLI_TELEMETRY_OPTOUT: true - - version: 6.11.0.{build} - image: Visual Studio 2017 - configuration: Release - install: - - cmd: - nuget: - disable_publish_on_pr: true - build_script: - - cmd: | - dotnet --info - msbuild /restore /p:Configuration=Release /p:version=%APPVEYOR_BUILD_VERSION% -verbosity:minimal src/DynamicData.sln - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\DynamicData\bin\Release\DynamicData.%APPVEYOR_BUILD_VERSION%.nupkg - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\DynamicData.ReactiveUI\bin\Release\DynamicData.ReactiveUI.%APPVEYOR_BUILD_VERSION%.nupkg - - test: off - test_script: - - cmd: | - cd %APPVEYOR_BUILD_FOLDER% - dotnet test src/DynamicData.Tests\DynamicData.Tests.csproj --configuration Release --no-build --no-restore - dotnet test src/DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj --configuration Release --no-build --no-restore - - deploy: - - provider: NuGet - api_key: - secure: CnOOhO/BwAogsD+dC9Q9BD38q6nxehHPS5LOHU977QtiJpqx3qE6vP+V+GHzpwac - skip_symbols: false - on: - # Only publish from the master branch - branch: master - appveyor_repo_tag: true - -# configuration for version 4 -- - version: 4.16.0.{build} - branches: - only: - - Version4 - image: Visual Studio 2017 - configuration: Release - platform: Any CPU - assembly_info: - patch: true - file: '**\AssemblyInfo.*' - assembly_version: '{version}' - assembly_file_version: '{version}' - assembly_informational_version: '{version}' - nuget: - account_feed: true - disable_publish_on_pr: true - before_build: - - cmd: | - appveyor DownloadFile https://dist.nuget.org/win-x86-commandline/latest/nuget.exe - nuget restore src/DynamicData.sln - build: - project: src/DynamicData.sln - publish_nuget: true - publish_nuget_symbols: true - include_nuget_references: true - verbosity: minimal - after_build: - - cmd: | - 7z a DynamicData.zip %APPVEYOR_BUILD_FOLDER%\DynamicData\bin\Release\*.* - appveyor PushArtifact DynamicData.zip - test: off - # test_script: - # - cmd: >- - # cd %APPVEYOR_BUILD_FOLDER% - # - # %APPVEYOR_BUILD_FOLDER%\packages\xunit.runner.console.2.2.0\tools\xunit.console %APPVEYOR_BUILD_FOLDER%\DynamicData.Tests\bin\Release\DynamicData.Tests.dll - deploy: - - provider: NuGet - api_key: - secure: CnOOhO/BwAogsD+dC9Q9BD38q6nxehHPS5LOHU977QtiJpqx3qE6vP+V+GHzpwac - skip_symbols: false - on: - # Only publish from the Version4 branch - branch: Version4 - appveyor_repo_tag: true - -# configuration for all other branches -- - branches: - except: - - master - - Version4 - - environment: - # Set the DOTNET_SKIP_FIRST_TIME_EXPERIENCE environment variable to stop wasting time caching packages - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - # Disable sending usage data to Microsoft - DOTNET_CLI_TELEMETRY_OPTOUT: true - - version: 99.99.99.{build}-branches - image: Visual Studio 2017 - configuration: Release - install: - - cmd: - nuget: - disable_publish_on_pr: true - - build_script: - - cmd: | - dotnet --info - msbuild /restore /p:Configuration=Release /p:version=%APPVEYOR_BUILD_VERSION% -verbosity:minimal src/DynamicData.sln - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\src\DynamicData\bin\Release\DynamicData.%APPVEYOR_BUILD_VERSION%.nupkg - appveyor PushArtifact %APPVEYOR_BUILD_FOLDER%\src\DynamicData.ReactiveUI\bin\Release\DynamicData.ReactiveUI.%APPVEYOR_BUILD_VERSION%.nupkg - - test: off - # Hangs indefinitely and I do not know why as the same command works locally - test_script: - - cmd: | - cd %APPVEYOR_BUILD_FOLDER% - dotnet test src\DynamicData.Tests\DynamicData.Tests.csproj --configuration Release --no-build --no-restore - dotnet test src\DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj --configuration Release --no-build --no-restore diff --git a/azure-pipelines.yml b/azure-pipelines.yml new file mode 100644 index 000000000..f7dfe749a --- /dev/null +++ b/azure-pipelines.yml @@ -0,0 +1,23 @@ +trigger: +- master +- rel/* +- preview/* + +pr: +- master +- rel/* +- preview/* + +resources: + repositories: + - repository: templates + type: github + name: ReactiveUI/ReactiveUI.Cake.Recipe + endpoint: reactiveui + +jobs: +- template: Azure/azure-pipelines.yml@templates # Template reference + parameters: + dotNetVersion: '3.0.100-preview6-012264' + runMacBuild: false + \ No newline at end of file diff --git a/build.cake b/build.cake new file mode 100644 index 000000000..13fc1546d --- /dev/null +++ b/build.cake @@ -0,0 +1,28 @@ +#load nuget:https://www.myget.org/F/reactiveui?package=ReactiveUI.Cake.Recipe&prerelease + +Environment.SetVariableNames(); + +// Whitelisted Packages +var packageWhitelist = new[] +{ + MakeAbsolute(File("./src/DynamicData/DynamicData.csproj")), + MakeAbsolute(File("./src/DynamicData.Profile/DynamicData.Profile.csproj")), + MakeAbsolute(File("./src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj")), +}; + +var packageTestWhitelist = new[] +{ + MakeAbsolute(File("./src/DynamicData.Tests/DynamicData.Tests.csproj")), +}; + +BuildParameters.SetParameters(context: Context, + buildSystem: BuildSystem, + title: "DynamicData", + whitelistPackages: packageWhitelist, + whitelistTestPackages: packageTestWhitelist, + artifactsDirectory: "./artifacts", + sourceDirectory: "./src"); + +ToolSettings.SetToolSettings(context: Context, usePrereleaseMsBuild: true); + +Build.Run(); diff --git a/build.cmd b/build.cmd new file mode 100644 index 000000000..e5fa880c2 --- /dev/null +++ b/build.cmd @@ -0,0 +1,2 @@ +@echo off +powershell -ExecutionPolicy Unrestricted ./build.ps1 %CAKE_ARGS% %* diff --git a/build.config b/build.config new file mode 100644 index 000000000..255374318 --- /dev/null +++ b/build.config @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +CAKE_VERSION=0.33.0 \ No newline at end of file diff --git a/build.ps1 b/build.ps1 new file mode 100644 index 000000000..5b0c98e5a --- /dev/null +++ b/build.ps1 @@ -0,0 +1,112 @@ +$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent + +[string] $CakeVersion = '' +foreach($line in Get-Content "$PSScriptRoot\build.config") +{ + if ($line -like 'CAKE_VERSION=*') { + $CakeVersion = $line.SubString(13) + } +} + + +if ([string]::IsNullOrEmpty($CakeVersion)) { + 'Failed to parse Cake Version' + exit 1 +} + +# Make sure tools folder exists +$ToolPath = Join-Path $PSScriptRoot "tools" +if (!(Test-Path $ToolPath)) { + Write-Verbose "Creating tools directory..." + New-Item -Path $ToolPath -Type Directory -Force | out-null +} + + +if ($PSVersionTable.PSEdition -ne 'Core') { + # Attempt to set highest encryption available for SecurityProtocol. + # PowerShell will not set this by default (until maybe .NET 4.6.x). This + # will typically produce a message for PowerShell v2 (just an info + # message though) + try { + # Set TLS 1.2 (3072), then TLS 1.1 (768), then TLS 1.0 (192), finally SSL 3.0 (48) + # Use integers because the enumeration values for TLS 1.2 and TLS 1.1 won't + # exist in .NET 4.0, even though they are addressable if .NET 4.5+ is + # installed (.NET 4.5 is an in-place upgrade). + [System.Net.ServicePointManager]::SecurityProtocol = 3072 -bor 768 -bor 192 -bor 48 + } catch { + Write-Output 'Unable to set PowerShell to use TLS 1.2 and TLS 1.1 due to old .NET Framework installed. If you see underlying connection closed or trust errors, you may need to upgrade to .NET Framework 4.5+ and PowerShell v3' + } +} + +########################################################################### +# INSTALL .NET CORE CLI +########################################################################### + +Function Remove-PathVariable([string]$VariableToRemove) +{ + $SplitChar = ';' + if ($IsMacOS -or $IsLinux) { + $SplitChar = ':' + } + + $path = [Environment]::GetEnvironmentVariable("PATH", "User") + if ($path -ne $null) + { + $newItems = $path.Split($SplitChar, [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } + [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join($SplitChar, $newItems), "User") + } + + $path = [Environment]::GetEnvironmentVariable("PATH", "Process") + if ($path -ne $null) + { + $newItems = $path.Split($SplitChar, [StringSplitOptions]::RemoveEmptyEntries) | Where-Object { "$($_)" -inotlike $VariableToRemove } + [Environment]::SetEnvironmentVariable("PATH", [System.String]::Join($SplitChar, $newItems), "Process") + } +} + +$env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +$env:DOTNET_CLI_TELEMETRY_OPTOUT=1 + +########################################################################### +# INSTALL CAKE +########################################################################### + +# Make sure Cake has been installed. +[string] $CakeExePath = '' +[string] $CakeInstalledVersion = Get-Command dotnet-cake -ErrorAction SilentlyContinue | % {&$_.Source --version} + +if ($CakeInstalledVersion -eq $CakeVersion) { + # Cake found locally + $CakeExePath = (Get-Command dotnet-cake).Source +} +else { + $CakePath = Join-Path $ToolPath ".store\cake.tool\$CakeVersion" + $CakeExePath = (Get-ChildItem -Path $ToolPath -Filter "dotnet-cake*" -File| ForEach-Object FullName | Select-Object -First 1) + + + if ((!(Test-Path -Path $CakePath -PathType Container)) -or (!(Test-Path $CakeExePath -PathType Leaf))) { + + if ((![string]::IsNullOrEmpty($CakeExePath)) -and (Test-Path $CakeExePath -PathType Leaf)) + { + & dotnet tool uninstall --tool-path $ToolPath Cake.Tool + } + + & dotnet tool install --tool-path $ToolPath --version $CakeVersion Cake.Tool + if ($LASTEXITCODE -ne 0) + { + 'Failed to install cake' + exit 1 + } + $CakeExePath = (Get-ChildItem -Path $ToolPath -Filter "dotnet-cake*" -File| ForEach-Object FullName | Select-Object -First 1) + } +} + +########################################################################### +# RUN BUILD SCRIPT +########################################################################### +& "$CakeExePath" ./build.cake --bootstrap +if ($LASTEXITCODE -eq 0) +{ + & "$CakeExePath" ./build.cake $args +} +exit $LASTEXITCODE \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 000000000..8c2183fdd --- /dev/null +++ b/build.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash +# Define varibles +SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) +source $SCRIPT_DIR/build.config +TOOLS_DIR=$SCRIPT_DIR/tools +CAKE_EXE=$TOOLS_DIR/dotnet-cake +CAKE_PATH=$TOOLS_DIR/.store/cake.tool/$CAKE_VERSION + +# Make sure the tools folder exist. +if [ ! -d "$TOOLS_DIR" ]; then + mkdir "$TOOLS_DIR" +fi + +########################################################################### +# INSTALL CAKE +########################################################################### +export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1 +export DOTNET_CLI_TELEMETRY_OPTOUT=1 +export DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=0 + +CAKE_INSTALLED_VERSION=$(dotnet-cake --version 2>&1) + +if [ "$CAKE_VERSION" != "$CAKE_INSTALLED_VERSION" ]; then + if [ ! -f "$CAKE_EXE" ] || [ ! -d "$CAKE_PATH" ]; then + if [ -f "$CAKE_EXE" ]; then + dotnet tool uninstall --tool-path $TOOLS_DIR Cake.Tool + fi + + echo "Installing Cake $CAKE_VERSION..." + dotnet tool install --tool-path $TOOLS_DIR --version $CAKE_VERSION Cake.Tool + if [ $? -ne 0 ]; then + echo "An error occured while installing Cake." + exit 1 + fi + fi + + # Make sure that Cake has been installed. + if [ ! -f "$CAKE_EXE" ]; then + echo "Could not find Cake.exe at '$CAKE_EXE'." + exit 1 + fi +else + CAKE_EXE="dotnet-cake" +fi + +########################################################################### +# RUN BUILD SCRIPT +########################################################################### + +# Start Cake +(exec "$CAKE_EXE" --bootstrap) && (exec "$CAKE_EXE" "$@") \ No newline at end of file diff --git a/cake.config b/cake.config new file mode 100644 index 000000000..c26865e62 --- /dev/null +++ b/cake.config @@ -0,0 +1,7 @@ +[Paths] +Tools=./tools +Addins=./tools/Addins +Modules=./tools/Modules + +[NuGet] +UseInProcessClient=true diff --git a/logo/dd-logo.png b/logo/dd-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..77f44f72e69d3ff74832a1f5be75b9b44df20230 GIT binary patch literal 22604 zcmd>mgO8KMiK#xWC;}XJFu=WpM1_F;=4%$AW2- z8N|kh&XJ}4R8|)Hff34m^i%xB-ACV_ZoYC+^v=&>(^TC&ZgaKJ(5<)Fon=Qzz`QCQ zom)D6_dF=phOH@qf&Z`n5s7@{7J8z!GfXp7vyvuR{oO#Oy0s|?`_=p*>D2SBrsexP%7=s{QH{^HN`@1VdrA(0VnNV5fH^C2@@$I_ zHLl~dH+YnPp6wcPNDn}bz@M!dgu?_6s47kGuH$jp9Q&Sb9YYb1N^xFld)!SQc)1Dd zK%yM7`0cMctIbPsam5$S-1kG8AYAYA^}rbya!#2SzIs2r>bu@KaoNa|Hc7iDIDEi}F|rmt>!B5L;j6J#Ri*Lg92f;e zgx=P=Z^y;=e*J|9rc>RE`NPEc>Pt$lXJcmT0E3I@#e{YDVHQdvgVj2?l}nNA@glAV z&~G+Og0N<ms~gcv9!s{NIO|`P`Jql zcRN_GYs3Rod&}bIG0W&kM$QOln5B=tXv92tH5-fv0uemBdhx`oN2aoG$2?oU>&^uS zMQ@*Y>FRx45U8BBZ}UY7ERx-&MxKFDZtsXPi9NgeyF-9QJ31&(@`ZX{FH7y$SJcF2 z-z5mWdQ?w`j&1M&x0JKA1T=tY8{R;3BOomGGO5gU+STzq>tj|R3{arpdesh3`dAyC*h16ZV4;3M-GW0++-37@?C)>$Q-M-p$liQa=tX~I2i=;o&isCW*h5>qN{-_AM zGV`&?YgsT(ew(v^tiK`jEejEDLM4kM7=W`vdtU(*a*0#W4t z)&C)^8v@yNKbP~;g{nG~i+y+n^oY|6320Rl`ZQ+`fk2+EI>*k4AwNriKw4PNe`-U< z>_0#t=Nl7e_KH8BXaoAbf04J2##x zzC~b}C8-Sf$?ROmc|?rA*ykaj;Y=&Ur`(x=v3s;^g)6hK`2qvfUbv2t>=QyvJoGF3 zaQZC3UJ3-#%--Xv>x(n}41we}DDDcUQ~3I@0W-}fDQTG5$LHYiFme|~SyCZJP@v~; zyDc65SdU6Rfa};x-D5gsS9XR|6^E^wD1#GHh{^#hlAvps0 zuI?;hpg8YB=|ctz)cHGgMkhoE1}iqoTK-j~UBCtzW4~-`9_;=Iv9>`tk5Pc=b9+lj zU+PWt#oYmF%t-)QB3w1JP5Gj1u#|sjI308m6hed}lt}1DGZ@ zV!k@n6(R+LAIT!@Y;LG`ip>gc}JFt5i}5W=+!3M!{nw(5hCKW?eUkh%4i@}D!@ z6H-jj30G)9YX0HXOJMAo*{@m5g>v?vK%fq`D;okT48|*7Y$U($iuVuL#)L=7VU(Tkr7Te`!$pK`Yoe4Ge{);;^IPz(-qf$ z^n+SB1X5ontzjn_a6&510uuQ;J3DK}49N@Su+w0Wp(7@?L*kb7#1~CUqP44eN~E{F zz)RS8rbNcXXbQPvbT@v01(K&fCiI9Uy4nc%_nl&w?7SicJ+LGiM9Z;zWky5_1p+7c zptqJTsJsY0aOcI#mjzm?s=Uq%6fB{{#C)bcS0b8JnVXdZ=z&~cct!=k&|hsE|6@kx z{|LtK*zSl6if0iJK;8+N$Ev~F-&4E5U}Q8sIl3^3jQ`gq(c_)SzaAA=iFt09Y&xwwzHzMFv?j%yoy_ALwwuw zAY-8}JsS2su{+hoAtCnHWhl|>j&yU6a}pEooA zC#QUHc@@Uf$zuJ61I&=gAut~!qgXfX!om;%A!x~v*?)YU{>G)lyRmxg)0G1lPxGJo z2Av3?L??Jx)J$+@w0#63QXp%i47;Hmp{!H$t$X?7#MIpvB*J!Zr#6P!u#N;k%=+6) zl5W;=Bu0BjDTy?35&e~9Xqzlx^ft-Xp^>k`)P1{zAfKf}@2F5HDJukJ`EvH;+)Y(9 z&>t$25t*6dhh{z9|H?=X}r29ZxX63}ur8gD-?s6t#UY-9U@aSNQCO zlZm_9i8M4xrd;in39XQ4=zZK;#~4 zx(X0|jN@y=e&DCq8gQTe_uCkhfd9uH|te29`t7?=^=k@8&oThgxy zLBq{%SvM%6C^SOYNfr_2(SuSSK+90(hM0&(e*nFm-yt2i5%cZbY*Hxb?RXsA>E`$H zOJ&82T?qyBb~MkH`Q@R1mZMqWNH@Y;1cio%mNEjMBF-*9pMa^8fsWcT>isUVPV4}) zu-3oc<+&o%Kr`NBYZZ1>o4rJR&8yc$gPl;pDhA{+yI4dzbO6<4oJ=S3)|H_AeOn}?e5w|eUkt^riLD93t7|_ z9fVSR0bs^W+U6a4@E^+tBgfy3{;uqYXiv#d$#C68m@8N6mS@Fz!WO(3OBX?JmjZ9QNTXMV+ZwJ33EQj17KVp?2&u!`3&gS| z<|rTVXka-xv9Ud_w7)~wpX#du?0I^m*>HmnbTEYeY=v2; z8rey2~mp#Opxz^ID;$_rMqV? zye`3USzh50@gO4Hb>wpHl+M3lcHdCQyu{nJHe)fOz4-&<;q>1?TXvNt>&dXEaHH_i#?uQR z=uxEb+ubbW&Y3KZElQOqQ0#+uA$d~VOd<|zsjG%#D+QDqtkJF%G!9b7_b~t$@Ev!F zPZUMnKMPT!5|C_l zK#f+^AA=$z;rI||c3bLf$IrL{_ozMv~DgoMMeI zfxD)xMb~GwzPZUr8k&_e<~O(?wQ%c1z`#ZE7NlK{`Z+y{+kNIy)BqIGhfQT#q?QMhf)|JEzFYJgz0i5Vt@vSeOiJ~q8hKSd00eLbhf$xj(tm%TXjjhkWrDRf^Q)zh>@$g429w-e&$kZeQ)ES{~S zilD-3{2*lrymX5g|9C}wqHr+P6i$$>9zJ zvLgRw&Np+8#hlf};x*hAws%D3c63|agP+7M#Ve2QEWIChZp9ua2SmF%3>CX34DL1)*Qi@W-v2k+`6U9xf-H+vM z|49_ws+S9QCScmMT#e7)qAj!J>h4v`h{ujXI>6orINv!Y*Rn}2l$ioe@`p|^%B}MG zjrYcVCxPQF7v|-I4=G-EB!|Dhrq-m9=spu`d;8NivM+N5-ve+~<8SdE4I&Hchekl4 z5bRY1)!2M6k8AZD>D!T) zSuPjh+TOL`6z$Ex+|E}pSE38s>>kAjAk2Mm-z$inI7suIaY*--5~80vKA@>$4> zU$$cNj9(=eqIoH`bMd1kYNy31*43WCGNDF>of@PkyHD8#Nv$1~f16q2yFRDxCHTZn zn9?Y1ZDCKC`dYX++0510kBlHNEvtTjva7shah{S9*I}b7Bew8tPP;RD+GXV`%6K&6 zsjGd)7Gbi^t1>wqJV1BD=D^OQKuMuFb>_mO(v;r|t#< zU;9zW=x7z6P|?o>R=V*c zOLJO98mG)SE$BvhoITcidTg4~&o+RdS$`VbY#^tUnVlNFslmF?M?U;^bZ9&N;_99~h9e+%N3ils>} zyYyPM;Z$5v45#i{*nZ>34Mk}vZK-R=( z5Qfq9OIx*G%*u&0GKIrf!|e_~wF(CNN-XzADT->gEj%B|fTO(65Sc+drYP zS}QdPl!{$b11%;QRxWza3r@cEEQ|gwuoDp$KGweUx7>f|99JKc|1Jpm`>>~mxElvK zJVAH~=@uRE>)_dNY1VWaAsMHAb_miI3biAWsdSv6jME}IIIS_bcw>4Z*MOfHIWu0Z z|5D0O%Ew0(dl~8i|204S3RxKQwI~S#=MRj*|MaL;SW(olF-@y3%Nu0-ObCXW`wcS_ z&^|L1$v?Y;7)v1e#Dwv_eWQ4LVgJP-KAS=gpknqq`o+E7sG84!{VyMmn@$Kt^&n+VN)o6B@v zO_!5(Bazr;kvD&d(~jOYOQh62V?-`S3B}aSeD=O@^lSWJHeRwxl1m2!pinKeH&nX& zkMgOgTB&z>_dOwO(cFZ7J{o4nM^Ys_cE2$S4TVc7u2n8;zoM&H_c)#^lwBOp`EP3y zn#0kw`9j4f=!48ZNaPzyJB0?UE)>sBsu~#h)l)BsuW08He;YrHK2}J=4Nz?pWE?f~ z*z4j?pewVb9l|yqVk?!y>r>7i_{*w*r}XMo39L{sSkmYx71Nd*E`zNkj`Cv*q3w!= z`pPd+DgGL#vrU|HzRmu>bYq3hf1nXTtzWCO6&)C8+RegGp6FDmyrd714c8q=H|tL% zgpuJz2+DIC|jjEZ6mHr$Sd9V?rV{LA1}|Z2MhXC{y0QtzMnNq zd%cLiHF;^5m=dKo_JV~HFb}N?Y+97npf#G`P#@dmC9y_B`f<6-90>kDF<_lK#d8K$1ma z8rTfZdEe#CZ`2!>;RM?+Iy0CPW!iKvP@nROmYN@qFsd!0?HENbe?z9l5X7NoPrBm2 z{JQII<*)});oAAAy#K^-NJ!O%2_4TRv4ho7(c7i{LjC?~^c#6dzi@UZgbdu#K<=FD z7H}dyT{SR?8o~nB`WIeYWE~burM|=hmS}2tEcC}z6VXN8AMCnO^(X? zm#SV}JzGC~IOl}5nlvwh*>A}ikE!U`oh6=+UkPQ6QEROxCcSP#Zu01bliVreVebOy4;TMtC(;AzJEa` zb5TmUrBXn_?P@H(MCrpi_;(=Y;Vfyl%sc_uw|G8v$8|>Epc8zfU69B!^0YLzs2lpp zZrg9MB+$-Mwu*HiOHA|HtIOip)PqYp#suD?PAD2oFWAwj#I3lH1#*(9(mdTm zf8^a#bMyMg;KPndW#j{iQa}bIfg1DuMNkg-dVZ-}f(|F-ozT$vUiQSv`K3Bi8t%rQ zo~0Exiz(3onIOcpoc=7~kGb+2qKXQ>{Ty|9SlAXVSJWAWU-M5y`ssAewr?&$ds&J~ z&_#3(KTwqkG0l(o_3Ibu;RLwHOiI+?65mlva($LFvuB_xDMCc~npSh|>%)PB9bldG zD753(wpZ@H=ksyT1WRD5Zb=gth%fmo>lZ$sg0N~uCxTkaI%Any^H&>042E;VHBHC0 zKk^){Zx1H%Ima)2F?$F7!jFJWh;yv*Tl{0l(rY&3_+_dhBvEku`viJq>O)}VY1_N+ zp$Yiz^J70}(uG(;!xWo0FMRq9c+C`5 z%zuE?fZDGb-qF3~W%Hn7dg};RlOPqEu}&R<pQab|MK%ON`dAqM`xkpHG6Si%6Y^Gj* z0?2RdB@zzQ^|ATw)`;syPNx{#3MZyotIWD^-Y3qw`n@BRAD*UW!6AY=hr#)~Q~}0c zXV?fxrCA-btIh||W`-lB%r5vuSqzNuM4X5H2S#tIRaq3&q0Ohe6iK39T@LSfN=1;v z>UzVGmMWO0E=p@T=Wphe2~ulzE?9X51KC(mIXPHeU*9)=FbyBmaV0Wsw{CU2-u|u4 zU}WDlK9}2k@vHp$(2)NGKGzJrCaoi>k1jHDtC`sr$(7&EI{6Iq9Zso9Q<&l7`i~_O zQ1g6@v=V+#l{BDrwt{PU)Fjr(AuZIyw6I*Wx}Pr@I0Eo2jZRqrCgCxRMN&_?A&4`zP#9F$_sH}Wc3n36&+_Yq$5Nzx|FZsmKW zKtJ8uO#hSqYLlwvhzHow{Akn&%^~L0`S|3iQWW${-Gy>;-Buz2sz~^P?Bn^5dQLzq zPNd)Fcd4U9I-2w_2Qr=*9{TaE?L@Dp*T9JhKh3Qfjz8~%8HM@f{cNK+zfTb0c3o)e zUt70}B84(99JOhvH(DH_D|ceY3%MIo3kk&n=dv|*`Wuy6IR5fWV?9Z;g{I)|(5>OPmA+UDAh2G`Q6C!S0T9A4>MQ=C!MZ0ZtN~ccsem{ z+S>Gn6z$7f@wPwD8eKfeq-q_?C?5}Z68tp_TQG?&vDzg#t@RU7iRmH6zXV>sm#XyWU8h!?m|fgMm&1>1 zaus=#d6B!0%SkD!{v@1q|59OK%NL%xvFL_4ne`yV2XZNmwirRnUL)+J>r*`m8wA@? z`cOa4zdUa&y)qBKCTU!nImNOY{DQl8lyWtdaoN>Glc-tGTMhJ_TX8BS=4&d#FP3T*>w+wm+q!$skTCT z|BUk=O*{9Y%#zwmx6t_{CVj2A9<;am5$?MBL|ocGLv&v@W3H6!*z)r&6o`iL4v89< zPWxj|FiqtgFQkwIF(@W}I&=Rq374p=2atMY)<4|(+dwPyx2t_E!tIZnmD_(j_!}A; z@Xd2enB6;(`^Q5I7zYyjmxG9Ti#)cqX#4;8g!nG)@&N)LK9v+3nH7bzNA6iUUKayJ zk`p0KA;9sn75)JlcW<0;z`?|-~)+a|#W_vsF4Hx6HtwW}iFX1)}!2?cw?5Vyu**qVG7n zqJ8CY4Kj5w^gqdxlXPDI!4*oKVf&N4R$%4gY8+yE31yadujv?cpb<$P!vj|osOg8J zpfPEYOThH5vu-tpnJk;w4|zvtJL zb4zK8#o|t+p-AojC@BQ>6oHi9a#}IjW%`SvEJq4qgcGOjwZ?6R^--_P+8okvj~jOR zi56H}txTP{NLUul`8nv?9|b)l^@~4CrO80*daVsxVgXuslRaPvVo}2r%C4@i#)_tD zh0X_xmHJdH37B_1vXkBa^U{zLHcP18aVsUob4SPBX#OpK2Ec%bfo_~^f2?VR3_f65 z?-w|>(B%D3-{A=;ps%ul?Zwrf4qrP1cJ9Xn`Q9{PC1S30W{2qi5~wi+FlWdBleppJ z!lZF(%5L4PRr85sn4I&6Ji4L>|I#O@^Gn@~!OKOtHOb}hmd}g=FmE;AqAOcFyWyG0 zV#C`kf+xeSA-^^!3=GQM|F=l&ZucPJr4Uxe!s(LJVjMh0yX8L(B+U@S4bfhw^k$Ac zBCYZj%+d?%WMTeOr3QTq+6_Rqp@q_R{j}d>2<<2pI8s9w=BM+TuOmt16*-SOH-NQ2A3 z)hv-T^8Zh=?Fq=0P%@hFoOtfS6OK`4A#K!yP$(91k~V>&t5HX>Iqp5f(*W|Ygw-D7 z+icH!&~|wKT2EJ!)PR32gOhiOpq;p{4p1PWRW$8E$0BE!17b=y`xevL5G zs#^$JaEk2)G`+f=x?Uh1v>wdnp8oN8Q zy^^d`q$ArRS+6s5q_xyuJ{?p1qir?LLUt81cJEf($5uP~a3L3;);A;o@PtOuwZuz8@q&>77b3rvNHukydx?#5KxLU&sNc2OaoQG$`Wx@)4y4YO(x%L2+rL}>Y`K9 z_O#r@KM9M+Tb^@WH^>F7)pV!ssD9}JI^1mG+QYpD%%-yb{sM|}u!zoGwmb7-_J^=1 zHmxv9*V;4$iza)0GO)`9=Fk%P^r08)3~G4XDRnmciFL{cr4tIz)2SZ(MNYy6xcaMm zWwT~nRm|f4Gmq;^E1VFJIQ5D2-@xXL5k@`V+#jZj+rbN!&u<;fi*=fP@ZiqO`s4k$ zLOFxyDO7$vWHW27g36D>kHZZ*?)@ibvDZ0~302D(`J(Dt_c~dIgVb+k4AE9jkmwg?>`7}x}MH9N=y zgo74WGQ;6#cTWo^?J{7an=e&-t?Yw; zhyUl=GbIBfw#dEkS8Ng=$?t)kC9+SBe1X`MC;NEG&co9do-f$V5=A~HCZHS1Ryl^) zkgk1{E)dVE0#vtwC2L|Z3?UxzO|V;j{%~AO06UVWb`0V5XQ*DznWc#IX3yR}$O-Ks zs_qtH8Z+7&q!6C7DuN;Z%FlZaPYUa#-4r%JmU7NAAI2&Ct^&n!?K+-W1zp99i2J2$ z<#h{&?pe736$R8-LF$E(gKcx*5ENehtLC|>TTlsRaoLHjXp%3ZuZYgbQyQZ6^S`r)KL zxa_$wO!%e^7yjb| z=@~Ro`ALa@Ug935Jb37ZHseRo*8JtWPbe5GoYpu(4oYkXz zXZ+*k(vU+LB|AtbliLv!Bbzt91o-SynWP_2*=f%HeUa%Pt+?!%E5FZO;_%E&lli8` z7`TCn{+_F=I9kPF@_fME-6v*_Zmi5DMMCeg_yuSH?96Ry4NOm|Nlh_{g2N<61!Bd^ zdsWW^=F1GE2R2%eO6et{d*zJ4zUNUWnv%TXyjs*3!TEtbt~N*NfA1bOFRBw&tUDv@Gpb{F1@iUOfd=43=h9Rvv774(jEJa~)2E zJziePp>e;WzHY+rdX!heh*nGn-Ekq7iDkn+e{E>!&kQg&3`nv~{F3J)kyCp`twq4W z>zuoWXDNH@GOQ%OSCGlcNf*z|9qCR%Lb7Dx*E1_ubetsZ>T0PUQ7aS?lX-VWW3%NI zsQ*kr&{oj>L_w9dF%tDnV7wcPzy+Q57>xIg=S2pq+0y%LZ~aW=qO$)MPDr>m6h)T& zM8$Q_Ob{5<+9<=a=EGd{)G^0?Fb?u8SSb`NQfk*4l21g#+F<2l`#fHu(yy zZOD1mnbK%70giO9aTqQt)_nVv3_%>=`A5ETHHA-6+JYHfn?Dq&->&Txu~h1Ips6({ zopgR|Z8m#qxNT)Kqj9Mn0zhhd2BuwNZ1q=5g4AC-C1BI(IawX76*6`QIKwF!(&@Lw zN`Xzc5d&=qRz$aVe0&87pj~wUnGetnsWTJ#r*m4ON@XzH6OR*h1aW@OTtvPdE|KlY zyA)jGr4Xepzu!M|Gn7~d=AK%ycDXZI+p`lB2igTL)_8$;Me$#}FmJlDPO_YEfZ{i>G*2h(bn9 z2Ec%kUpokDThuQ^0EruTnc~%&T||nA0A;Ki6z%kWCTifi%UBxT16q?RCm%FTO*yVY z0im)e(kxT9lTiG~sitF-M_}|F=$!aTVJxS=7U@BbSqNXhl*|i7U4WIe?4x7g|?Y zhf0#EakxUs6=lSJo@_WOZ2bO)wFBHHSSSMXnd0Win^rF{9c{l9kB=!IPlowTo4t|A z>Ia%TnCS8Y%`-0nG3>A3z2C2@94j2q{*1EOl>5|HHE|(#(Szd1SruQ>j^ND6l@Z@; ztDFRK6a+Ybc&@f-w-~#v6rl4E`BH>lH}gd4v7+uGyx8R&rtRFsq8gkq{Xi(QpZcAe z$!f*1#9_Rl_$hF1?0nz!d+7*p5uF@QlX*#puuj>ymp1*2!>q4;OdRwc3fHe{hwIbH z@#0sG!i!+ax*FhZ6Sg%^yamGZiaP+x-o@-x=2m>@aH$?@RTzftIa0@K$^0>48&<}p z5T&ZKz1z@JNra+HXsSLEMO1}$@K^V>z8M~Pb#^NH)^~&llP(CJQsswj__#N9@HuM4 zwANk@Y{hTa*npU4*J&wbO56-5ypJ-@zD>5z^jGRuQJ`&8KpiTHvfZl2M}I@&@SInV z!d+MNi%*stL>&q;7CXN*TLBkHy-sT47bU~7?gB>ATUBHxQkgDk)*kL0J5Nt&NLL38 zQu*;pNL*&kWKH}2qd)66E-C-QRadlH>!s&@=^m*jwM;6w!X@4oAerrKbpwb z@e~as0bu6~8NeizSkl8-P}g_@4yks=|1N8f7%LN% z)$bZVE1t0h)1s*U(&&u9+Q%RI=_CUFgaDuAsbFxxg2cys z%da&BDU_cpv@8mr%SaF=l-&b>@f8-JPJKCR7w0S(Mh56GyHK0tpfhDmapqb+V?Ky? zcp+X(s*>LRbLU#uq*|aXQ1;#;gl+g~_-CqSkZ;X}bmL-2CuFV%{# z4(ZV8c)3h22N%;3?~{eJtuV{lmVqoRz(}1{Zr#?JV$@`p?jf~FQGE@SOw+DAzpgk# zB=M$}_Q+-VCiu&Y_ffhMo zdiddGa3g*UYNwjn96;FtlO3LLClRMwZAiU(?WWxeQVxTUa}o{J@>9qzO3CFn>K6UY zJlJYuzKD;B(_?IGNwJH(LZWP1DoGv? zS2=MhVnu?J(4UVC$PHkOSBO`t-1MS#?`QduffwG_L|d+#eJ^DJ#>doaeQHhVpE@iReR$F^ zrUEdgOWfD1tDk6tb_+t1Owz43j;rd?sA-@Z5Y2K9PZ;}T$|u>L{<78AaLp=MW(uvY z?7B(snrHtH$sQqOo-Rrk9mlVn-4ndT_PzK8!8m1EHOl8=h6q@vlNSVV((#&ir$o=M zbn{Hqr8>*T&pk9#96?(HB_uBowrYju5yKbcvnt`4;^w9%B8UkU2`(G}BH;<7AGPQ@ zk})&b7}~sS(rC+noVs{NDLp6bJQ(kB{UO?8Xg-2sxIfBM_gcUEQ#p~tYpQ{=fjj$( zoN1f}a{IofcraIQs~~TGzp@$y$!I7caG2RWM95v%ZWNY(Po3=ik|rXs z!ZYPC?)XYN@)>9`nN3-yI!v#6d_W#^&+7AJW}8L1n|CGkp1yeMgKK|d=FDJ|DMZeN zDy)nF4-){_$PaGV*NgxQ7Ydrr0T}L8ZnoRBw|E*MVXNAUbrbC?zo(ja5SnLlB^y8F z@UT%GHh1q^k@l$l^4@1ZAPdQKp>A=PeBen&6=W7&raxv!^{Zhl3Jtg~%rxD%L*hnS zJ@Xo7{`J#~FfaVo(6@G??(A@w`_-YeAm2wVaWidy-UX`u1`Qg0RuHPo&x{&P8y*{n z_zWmmRQ1llmW^JX+gE9lD<%J{#aN@`^lao^NxXsg%l8QU9BUM|`hJ2Sq;YX^53~67 zoW4Cbx_~IV1dGTvab03h5XRRQAh6qbCpa8{N@PgeJbTzB2W=JU&Q`##kyk#lKactQ zXtojx-#AIYnJjJ7Z$YE@5jq`@nS;`UhJ~*%qm92tMwjCuq4yXGW$KEX)oG@rE>&HZ zh5p^Tr5@REB+j!fiDwo=BxM&yHd}0|bV#o`)c9ttyUvu^?L9yOF*g8gF@nX)?yEmS zp;T`+b1uI2MQQ$gNsV&LPa!nwW^%@whEJEv2M^amQs%DgL@h$1es-2qA8DHGRK1_* zN5PBxNYOb+{QI=D)nu(z@$KtEmc9N+AHEQBUzW zVVixgS$Tb9jE1FqMQa{^WA-G>M%@YbqX(Wyb$2Y|LL~5JaL+BbCE8D`#ZL^-|6NG0 zjyaruto6{#0uW;Lo~RXpi?Sd1J-`&W?>;@e6>LuGMNtd$fAj7eaznhtVzYn@kPA5W z&DxJONx!KuJ0tP3L(B=oh}2J*n<-%@8X7ni>S&@5nx>eFFv$ zc+b)de5?xX?R-naU-1@6H=JWLiv3SqhnDj(33NiHgJRkZyQ5V3lp0H$JT)o+Sb+I! zt4*3LY~zB3CrwT)*AjN(dB%3m4)t}%6Mo&Auf##FnDGxU;~t%nYAr%S;kR6PzM{>o zp1;Hnp!r@K5<&P($iH2Gp8_us?-y99KLmlDA!^NJ#3L?Oc5hjngQMrfqP1#ehZ!2& zOkeWc!$2J}5s!?wiWYrp?|fl28J$_9D;C!XDrXoQ8+)jsq0w*emlJ2-3b7XDQBp{# z=L=xT5|C)|nSNd(JNz`&NezsqO>pLNXTpVSE6Sg+;{ph!h)2AI8?!{`5cv3_c za4H7BCco8IDo&N^#N-LWM zz$oxw#bIZ8&xMCi&ModlM*)yCQYzHR-~)t07`6qx=;7t?C^c+3^F_7Xm?kpA@cE?4 z6Ri;GEJlFA$1G1iMpOOfvw8AQG;$Wey*7P3>=c?pJVOHf=;QMU7NoNof`+tme+YT8817uM> zS~6_^W9}zc#<>2as0)?1vwS40&iPmI|E!9waPWaX5kY}`9H2u^CB)c0dsiS-L&B0; ztltg3M27KCQLdKNOr;M5*|oX3K>&*z{2u-7bq~{!^A}YM)@Q~BiG%kM5D4FXrZDpE z4Obu!t{av;j4kC@9Z&;wVpmB8WO)?|kI!IYq_kyfaA1_I3;!m=R7!H3M{;Ysjoz1^ zF=;4GY#bdOF*t7Zcc15dB$Ljn_%oBCHk0U<5>Y`DpI?lgF7!SqF(8RTp-;em+v)d&Y`2uwj2G>#+|qloJjIt*bB%7S6b--=^N(A73)j>Y zaTfs+48!DYPZl|xUjuJd;S|ug$_}olV7vhwRu{So^4Ow!@ve7w`+HW(u7Lv3G!wig zs!zHnE{>XUrMA{Mg3 z2{k!@C9?09t|INpVZh@yV9f=d@pR6}DSV{*GO>GUss%1_>Aw&`B`j1qL(5v{oXT^3 z(FqO3@U2C?jsqZJO%?Zz12^4=MKue{fPcVe>sweL&EIFWc7q8p{drt3QqwrZN} z`;=5wD(cE+O&Sp&RPe7m)xqY`|E>{NNa2!e&oTzX`1BpBibyL~85Zw=4e>xM@ zy9lN3o(VR<1+{0Y`7UD&+`R)1o?6{_tfBFWh-zDJTO0VF7b4+kWu=UaL^S3wM5TtC zAhBJ!A#DpF#~-#UWB(BQ@2*56Xas4AGVPRm<6}iyM*WUhs7CH1t%Algvy?mcz%#me zQ0l{Vy#`Es`{v-Zd!TkKwPGKg1?VTNtXGoFV-kXn$-$lx?_EB7HmDbJi<5%_@Am#2 zBGQn!JZv?EqmMYI&s${bCh8QzC1t2LqkuUXQK^Uqs%Hz$d%RCf0TDu$d2p+V%EkdV z6n($Y00UU4>Fd-BW(KoY-o)uDy`bSZm^2Z#z;=MHn$_>X& zB&8K^c3E5doKs#EP08E7Lg?oRrp(CN@gZK8K)0h-i2bX&EDk0bo~6Ka9xIbB#06Y4 zl$>t6>xOwH*xqHoz(xbjl0uze-0}DxLcq&cZ!SbW5Pr+hG=qd&JKuqU2&$t*~5G`->9Xv^_vm_@iTsuo@DaC_{ z%h^fU!PARsG(|nyVqVboK0t!niPa>|V$d%Y$srW1u&pamo)ySPyYb@FU7Sohf+BpQh6N{sG;UqOHKgJ-rdXqOTfxOv%(|! zrVbC9oR87B@DfPlsGZv5=ZOg(8QDTyxXqb4UK`nroIeWTa~k8Nq?@)Ezg?*L=u)TA zFLE2UNd6pE(`vP2lQzeXP?t8sdt|;G-kYkqSmQwrOp3hq-U-a9=#H#IKkA=yK77#B znj?Cudz@4RjheV0pT<2@e5 zrpXuU+GsD1Q(3>kG@QKH7s>3a>_Y6**P<0yV zR{ka^iaiQr4JRG6W>%v^E4pN}q{g_@9O-%r?6BQX;>|@*3O_}yA>#&wqgnIpwwN;A zC~bhSj(*w;@5%gX{k~A^V`s_V%F@2=FOGjX%6VR~ZlwlrD((pPBAE}fH*Z}6E3?l* zo;;KQ-}R%tPH>v4`0Qrtq380A+d{$^kVMw_T#<`oi%Soswc8D|i`fE2SAU;uC4 zftonqa1?K2cM4L7llZ|5Bth!XAx z-?2AG|H}J3uPw;g25q0hBl^D7a5K*qc%MeLPsMCkcD;myPyXto_~G;VmkVM8Gx7%K zMQIj2KHZeW=U6NYEq-$rMJL-sUKNk#**x5~O9qJuoWoUcyWDueH5YZ(0!cf+N%o3J z8{c{TplFI9-)Kp_DA~K`=^v{pd&U>WoO}oDJMCtwut5>c-jm`af?`d1qVe(di0XKJ zg|JuA$5)cg@q^D}{vg}lvS6n*eC{5I$jTD$Ts1G&swmPY$V)?r*A2=HuV%#78j)M{ zj(O}Usd(_+!=Gb&D_+cDq`=AQ!HYUJG4cmv19wAKhC@h8G{y9x%fu$~KQP@}ewIk; z#MUo^YUzSvV%0t*rMuE;>+7c-UY4&MFE20m6lUzknRNU{enSKZ+5-wu+IlUulN@IB*`SU`@Lz}kMSM9zaCTkbqv?>Ij2_jOofDV`mEb@!Wg4~I((P#{k zGNhT8_#D|<+bb*S^+_<*@=HTFI0##1?px=b?>GH?VjnqF{GU!cJGA)9=tH7kfKd6Zkbj|aZI~)7){L}zhl|Y9-zrLeKBjWajvSqzxX_wtSQ=+gk(v3TFvc6AYRLq2kZ$CGfJ>xgZ0 z^HB)zzu0gGrj#3QtU_64{BIp<56F8|eL!cMt}IGx-FO~lSa>=UNR<^DE!#Y(IS&7+ zM>N&jwYY&C=Ww@d-kdB&C=5g8hEw@wZrC!huvaz@P=m`!GY zX0W8fW!tonlNBR=D3P?zhKN=ewVnfiQ8a!Vc%S8fu;RpW?q!?aOjhiACoI)rXfEjN zx?za`d$?uQ{4qA&NmkZ96_v%QV>qsSujfRA&_D?=h}Y9rtOI*KRsjJY8U1BPV&Mjj z58PA~yg`}8Y>g-$B~L-KNxSI*9V~-c!1!+E(uZ;>_M#9aSob^a~{a z{2{C2BE)jbL!|MFM5aupKT*DfuJ9^gp?TgEg2o<9R(@*eG_T6dOesOMy4A_xC)VJ6 z;JEoG0YmzjuQwEF+EGPTdF#5g;u?nbB(@~j-UE|j4Gt9ru3&TL|S{L zd9Zh{rA-0n6?Ec<(aOk}1*PHI=ReJe?UQ;Uhko-EeQp(WsuJzvy<-eYY?BiIxNMam59GRRk zxKm;ebXIN7qDVD3iL15wf#Dp0@v=AFWxi2Oyv4cOj<;c^EFDDS@vhVK0wsFXz&mRJ z#E@!uq$5gTV*28KiH@d#hdZP8y9)sKV}Ji6uy~hn*bu$>T`-Kp4_Zsj{+bWAn|os6~M+#x2*&HM<0_u`|?D|6x$^q*kP&-_Qihql{0ZH5j7+d9YTRxW8CJS}p-80GcspR@_fqJ@fE=U z%qWILI<~1b%)+PW7gn6^I$|^r-uCezi(^@J(sJN3V zx@-PqdIjirqZ`zVOAm}Oo)n8LiAm+$ z>=y{y#Gj3b&7TzB9pZBQ@DzpUOzWsVM;poMmPLPQc{XmlBsdwa*4%*MNxH z0Ma@i+=w!-@lL+r?&~j{b&?zTuH!ggy-zl}d-vLr;;ayH&&0zX!44+|qwK8LU0q#cBhv5CJ3qkTa66@CHoCIE zF*ZtekP_=_d(WlF5Vx5O=;$oMmRd=a_E1zwGl|B+_{b=V*2U*3W7gs&=wQF9_e&Yb z>#T(#+UKq&i4OeQ_lrVol5fOD2rre~QC}U8LW>W-%F{9KvisG$SnvN*oU5Xh4?FQl z>Yurs*j?f=nN8M~o)v650%eCMjn$lLRCv_BC=j^b0M+h`@ImHU5S$^Hjry;%snGQ; z0k@1`QD1&CP4K91@NT8n&eFW~cVFU6t$pZOh=R(Owr=P%cX{V8>ffasAG+nYjP3)a zgQIvXos7DS%WsdJT|3E_^Y;|mTo4G(k2T1ZPEF= z4wp5)FLcQlsc30wrDyyNkT75*jyuI*=WdsidM-bB!T>1MzjW>9M~SW*Q!AL9jw(!<)zlq=AC9{X2mAmRICHBgaNvY0-CDAt2-_DLC?0f zw#BL$fG&y-wYEIU5@xK&Yk2&rT@Vwf)u2axPxvjg%rBy@@M@WeTdp~(a#(l@+j&DNwGuy}@pny~4VZ zM6;{8(AcdehPzCyL-Kgg{fX{_Je{BV>S07vd+ZBBBGE_(aMqxiX#;s(@_fjv?%*X$ zzT~Dg{4D3=^xG>6S{rL?p!D~c9vTI`b@hq{KH2H1Dd~x;R}^Ny{+*p^^ATIDP+oBg z%PTP^9-;+|6aoG`HV3KpRM@oeV@X`%z7z5vBoEfSG6>k^BC4X)K^g3>oQ2aLp;E3w zu&}-6+?C{I;9VYZ1?3MaoS!y$Y&iavvN}Di(6%G6a94l;Ivw}<IEV9hNVPzRcF` z4L>Y2edUfH%*}1bPlyGeTUe1&A}^r3_>|ecT`OYvjz9hsU~ZMgV*Va@dSNMdvBl#S z$tr>fW@mKzB9nOVZ1FcTK;U75c0$Xis}}&K;-aGEK6qT-@Q>3z8dWx(*|mn&T%+fE zzu|=F674wZSAr=H*>frHPd$z@OE}l~a&#fB!D)FHPj$`Ga))RRbK%IE`cOb2HkaFB zQtZn}FmwyUqsQSIg!4Sf|F{aR34s#hY4=E*c<`GdJty=y6|ipN>q{n0am!j|$Jr;> zN@BTO-x=6@Gm*)~TTYh9+;o=VlR;A1v8H?q-zst*Y_$a_bTAIX1|w{pAvfZen(ttt z4PQ-i + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff --git a/logo/dd-shapes.svg b/logo/dd-shapes.svg new file mode 100644 index 000000000..ba061b5e2 --- /dev/null +++ b/logo/dd-shapes.svg @@ -0,0 +1,174 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f80437245..4e0ff96dc 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -2,43 +2,65 @@ - 7.2 - $(DefaultItemExcludes);*.DotSettings;*.ncrunchproject + true + $(NoWarn);1591;1701;1702;1705;VSX1000 + AnyCPU + $(MSBuildProjectName.Contains('Tests')) + $(MSBuildProjectName.Contains('Benchmarks')) embedded - + $(MSBuildThisFileDirectory)analyzers.ruleset - Roland Pheasant Roland Pheasant + Copyright (c) Roland Pheasant 2011-2019 MIT - /~https://github.com/RolandPheasant/DynamicData - /~https://github.com/RolandPheasant/DynamicData/blob/master/ReleaseNotes.md - Copyright Roland Pheasant 2011-2019 + http://dynamic-data.org + https://raw.githubusercontent.com/reactiveui/styleguide/master/logo_pharmacist/main.png DynamicData;Dynamic;Data;Rx;Reactive;Observable;Cache;Binding;ObservableCache;ObservableList;ObservableCollection;Collection;Linq - true - True - false - false + /~https://github.com/reactiveui/DynamicData/releases /~https://github.com/RolandPheasant/DynamicData git + + + false + $(EnableSourceLink) + + true + + true + + $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - true - true + + + Full - - false - false + + false + + + + + + + + + $(MSBuildThisFileDirectory) - + - - $(MSBuildThisFileDirectory) - + + + + + + + + diff --git a/src/DynamicData.Benchmarks/Cache/SourceCache.cs b/src/DynamicData.Benchmarks/Cache/SourceCache.cs index 5de42dfc3..69183af29 100644 --- a/src/DynamicData.Benchmarks/Cache/SourceCache.cs +++ b/src/DynamicData.Benchmarks/Cache/SourceCache.cs @@ -1,4 +1,8 @@ -using System.Linq; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq; using BenchmarkDotNet.Attributes; namespace DynamicData.Benchmarks.Cache @@ -13,7 +17,6 @@ public BenchmarkItem(int id) } } - [CoreJob] [MemoryDiagnoser] [MarkdownExporterAttribute.GitHub] diff --git a/src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj b/src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj index 10cdd6228..77ec9b6fb 100644 --- a/src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj +++ b/src/DynamicData.Benchmarks/DynamicData.Benchmarks.csproj @@ -5,7 +5,7 @@ netcoreapp2.0 AnyCPU false - ;1591;1701;1702;1705;CA1822 + ;1591;1701;1702;1705;CA1822;CA1001 diff --git a/src/DynamicData.Benchmarks/List/GroupAdd.cs b/src/DynamicData.Benchmarks/List/GroupAdd.cs index 167de3e23..7d6a31c7e 100644 --- a/src/DynamicData.Benchmarks/List/GroupAdd.cs +++ b/src/DynamicData.Benchmarks/List/GroupAdd.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using BenchmarkDotNet.Attributes; diff --git a/src/DynamicData.Benchmarks/List/GroupRemove.cs b/src/DynamicData.Benchmarks/List/GroupRemove.cs index 9b34827cf..a962934dd 100644 --- a/src/DynamicData.Benchmarks/List/GroupRemove.cs +++ b/src/DynamicData.Benchmarks/List/GroupRemove.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using BenchmarkDotNet.Attributes; @@ -17,7 +21,7 @@ public class GroupRemove [GlobalSetup] public void Setup() { - _sourceList = new SourceList(); + _sourceList = new SourceList(); _groupSubscription = _sourceList.Connect().GroupOn(i => i % 10).Subscribe(); } diff --git a/src/DynamicData.Benchmarks/List/SourceList.cs b/src/DynamicData.Benchmarks/List/SourceList.cs index 95e15adf6..e8834120f 100644 --- a/src/DynamicData.Benchmarks/List/SourceList.cs +++ b/src/DynamicData.Benchmarks/List/SourceList.cs @@ -1,4 +1,8 @@ -using System.Linq; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq; using BenchmarkDotNet.Attributes; namespace DynamicData.Benchmarks.List @@ -14,7 +18,6 @@ public class SourceList [Params(1, 100, 1_000, 10_000, 100_000)] public int N; - [GlobalSetup] public void Setup() => _sourceList = new SourceList(); @@ -28,7 +31,6 @@ public void SetupIteration() [GlobalCleanup] public void Teardown() => _sourceList = null; - [Benchmark] public void AddRange() => _sourceList.AddRange(_items); diff --git a/src/DynamicData.Benchmarks/Program.cs b/src/DynamicData.Benchmarks/Program.cs index 24888c295..89167b06c 100644 --- a/src/DynamicData.Benchmarks/Program.cs +++ b/src/DynamicData.Benchmarks/Program.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using BenchmarkDotNet.Running; namespace DynamicData.Benchmarks diff --git a/src/DynamicData.Profile/Allocations.cs b/src/DynamicData.Profile/Allocations.cs index bc0b2db10..0ebe7bf41 100644 --- a/src/DynamicData.Profile/Allocations.cs +++ b/src/DynamicData.Profile/Allocations.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; namespace DynamicData.Profile { diff --git a/src/DynamicData.Profile/AllocationsCount.cs b/src/DynamicData.Profile/AllocationsCount.cs index 604db548a..4e9f7a441 100644 --- a/src/DynamicData.Profile/AllocationsCount.cs +++ b/src/DynamicData.Profile/AllocationsCount.cs @@ -1,4 +1,8 @@ -namespace DynamicData.Profile +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace DynamicData.Profile { public class AllocationsCount { diff --git a/src/DynamicData.Profile/CacheAllocationChecks.cs b/src/DynamicData.Profile/CacheAllocationChecks.cs index 1650e9432..96f5dc0c7 100644 --- a/src/DynamicData.Profile/CacheAllocationChecks.cs +++ b/src/DynamicData.Profile/CacheAllocationChecks.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using Xunit; @@ -25,7 +29,7 @@ public void PopulateCache_NoObservers() public void PopulateCache_Observers() { var items = Enumerable.Range(1, 10_000).Select(j => new Person("P" + j, j)).ToList(); - + var cache = new SourceCache(p => p.Name); var subscribed = cache.Connect().Subscribe(); var result = Allocations.Run(() => @@ -43,7 +47,6 @@ public void Connect() var cache = new SourceCache(p => p.Name); cache.Connect().Subscribe();//do warm up - var added = Allocations.Run(() => cache.AddOrUpdate(items)); Console.WriteLine(added); diff --git a/src/DynamicData.Profile/CheapAndDirtyAllocationTest.cs b/src/DynamicData.Profile/CheapAndDirtyAllocationTest.cs index a5ee927a1..4f7fd9add 100644 --- a/src/DynamicData.Profile/CheapAndDirtyAllocationTest.cs +++ b/src/DynamicData.Profile/CheapAndDirtyAllocationTest.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; @@ -20,7 +24,6 @@ public void ChangeSetAllocations() ChangeSet changes = new ChangeSet(iList); - var startAllocs = GC.GetAllocatedBytesForCurrentThread(); // Assert @@ -58,13 +61,13 @@ public void ChangeSetAllocations2() { i++; } + var endAllocs = GC.GetAllocatedBytesForCurrentThread(); Assert.Equal(startAllocs, endAllocs); Assert.Equal(iList.Count, i); } - [Fact] public void NoAllocations() { @@ -88,8 +91,6 @@ public void NoAllocations() Assert.Equal(iList.Count, i); } - - [Fact] public void WithAllocations() { diff --git a/src/DynamicData.Profile/Person.cs b/src/DynamicData.Profile/Person.cs index 923641676..2e8ccc0b9 100644 --- a/src/DynamicData.Profile/Person.cs +++ b/src/DynamicData.Profile/Person.cs @@ -1,4 +1,8 @@ -namespace DynamicData.Profile +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace DynamicData.Profile { public class Person { diff --git a/src/DynamicData.ReactiveUI.Tests/Domain/Person.cs b/src/DynamicData.ReactiveUI.Tests/Domain/Person.cs deleted file mode 100644 index 49b8e62e4..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Domain/Person.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System; -using System.Collections.Generic; -using DynamicData.Binding; - -namespace DynamicData.ReactiveUI.Tests.Domain -{ - public class Person :AbstractNotifyPropertyChanged, IEquatable - { - private int _age; - - - public Person(string firstname, string lastname, int age, string gender = "F") - : this(firstname + " " + lastname, age, gender) - { - } - - public Person(string name, int age, string gender = "F") - { - Name = name; - _age = age; - Gender = gender; - - } - - public string Name { get; } - public string Gender { get; } - - public int Age - { - get => _age; - set => SetAndRaise(ref _age, value); - } - - - - - public override string ToString() - { - return $"{Name}. {Age}"; - } - - #region Equality Members - - - - public static bool operator ==(Person left, Person right) - { - return Equals(left, right); - } - - public static bool operator !=(Person left, Person right) - { - return !Equals(left, right); - } - - - public bool Equals(Person other) - { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return string.Equals(Gender, other.Gender) && _age == other._age; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((Person)obj); - } - - public override int GetHashCode() - { - unchecked - { - return ((Gender != null ? Gender.GetHashCode() : 0) * 397) ^ _age; - } - } - - private sealed class NameAgeEqualityComparer : IEqualityComparer - { - public bool Equals(Person x, Person y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; - return string.Equals(x.Name, y.Name) && x.Age == y.Age; - } - - public int GetHashCode(Person obj) - { - unchecked - { - return ((obj.Name != null ? obj.Name.GetHashCode() : 0) * 397) ^ obj.Age; - } - } - } - - private static readonly IEqualityComparer NameAgeComparerInstance = new NameAgeEqualityComparer(); - - - public static IEqualityComparer NameAgeComparer - { - get { return NameAgeComparerInstance; } - } - - - - #endregion - - - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs b/src/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs deleted file mode 100644 index c2d9f3acc..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Domain/RandomPersonGenerator.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; - -namespace DynamicData.ReactiveUI.Tests.Domain -{ - public class RandomPersonGenerator - { - readonly IEnumerable _boys = new List() - { - "Sergio","Daniel","Carolina","David","Reina","Saul","Bernard","Danny", - "Dimas","Yuri","Ivan","Laura","John","Bob","Charles","Rupert","William", - "Englebert","Aaron","Quasimodo","Henry","Edward","Zak", - "Kai", "Dominguez","Escobar","Martin","Crespo","Xavier","Lyons","Stephens","Aaron" - }; - - private readonly IEnumerable _girls = new List() - { - "Ruth","Katy","Patricia","Nikki","Zoe","Esmerelda","Fiona","Amber","Kirsty","Zaira", - "Claire","Isabel","Esmerelda","Nicola","Lucy","Louise","Elizabeth","Anne","Rebecca", - "Rhian","Beatrice" - }; - - private readonly IEnumerable _lastnames = new List() - { - "Johnson","Williams","Jones","Brown","David","Miller","Wilson","Anderson","Thomas", - "Jackson","White","Robinson","Williams","Jones","Windor","McQueen","X", "Black", - "Green","Chicken","Partrige","Broad","Flintoff","Root" - }; - - private readonly Random _random = new Random(); - public IEnumerable Take(int number = 10000) - { - - var girls = (from first in _girls - from second in _lastnames - from third in _lastnames - select new { First = first, Second = second, Third = third, Gender = "F" }); - - var boys = (from first in _boys - from second in _lastnames - from third in _lastnames - select new { First = first, Second = second, Third = third, Gender = "M" }); - - - var maxage = 100; - return girls.Union(boys).AsParallel().OrderBy(x => Guid.NewGuid()) - .Select - (x => - { - var lastname = x.Second == x.Third ? x.Second : string.Format("{0}-{1}", x.Second, x.Third); - var age = _random.Next(0, maxage); - return new Person(x.First, lastname, age, x.Gender); - } - ) - .Take(number).ToList(); - - } - - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj b/src/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj deleted file mode 100644 index a756c77a7..000000000 --- a/src/DynamicData.ReactiveUI.Tests/DynamicData.ReactiveUI.Tests.csproj +++ /dev/null @@ -1,25 +0,0 @@ - - - net462 - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs deleted file mode 100644 index fed0ce4d7..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/BindChangeSetFixture.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Linq; -using DynamicData.ReactiveUI.Tests.Domain; -using FluentAssertions; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class BindChangeSetFixture: IDisposable - { - private readonly ISourceCache _source; - private readonly IDisposable _binder; - private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); - private readonly ReactiveList _collection; - - public BindChangeSetFixture() - { - _collection = new ReactiveList(); - _source = new SourceCache(p => p.Name); - _binder = _source.Connect().Bind(_collection).Subscribe(); - } - - public void Dispose() - { - _binder.Dispose(); - _source.Dispose(); - } - - - [Fact] - public void AddToSourceAddsToDestination() - { - var person = new Person("Adult1", 50); - _source.AddOrUpdate(person); - - _collection.Count.Should().Be(1, "Should be 1 item in the collection"); - _collection.First().Should().Be(person, "Should be same person"); - } - - [Fact] - public void UpdateToSourceUpdatesTheDestination() - { - var person = new Person("Adult1", 50); - var personUpdated = new Person("Adult1", 51); - _source.AddOrUpdate(person); - _source.AddOrUpdate(personUpdated); - - _collection.Count.Should().Be(1, "Should be 1 item in the collection"); - _collection.First().Should().Be(personUpdated, "Should be updated person"); - } - - [Fact] - public void RemoveSourceRemovesFromTheDestination() - { - var person = new Person("Adult1", 50); - _source.AddOrUpdate(person); - _source.Remove(person); - - _collection.Count.Should().Be(0, "Should be 1 item in the collection"); - } - - [Fact] - public void BatchAdd() - { - var people = _generator.Take(100).ToList(); - _source.AddOrUpdate(people); - - _collection.Count.Should().Be(100, "Should be 100 items in the collection"); - _collection.ShouldAllBeEquivalentTo(_collection, "Collections should be equivalent"); - } - - [Fact] - public void BatchRemove() - { - var people = _generator.Take(100).ToList(); - _source.AddOrUpdate(people); - _source.Clear(); - _collection.Count.Should().Be(0, "Should be 100 items in the collection"); - } - - } -} diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs deleted file mode 100644 index d39cc1efd..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/BindFromObservableListFixture.cs +++ /dev/null @@ -1,86 +0,0 @@ -using System; -using System.Linq; -using DynamicData.ReactiveUI.Tests.Domain; -using FluentAssertions; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class BindFromObservableListFixture: IDisposable - { - private readonly ReactiveList _collection; - private readonly SourceList _source; - private readonly IDisposable _binder; - private static readonly RandomPersonGenerator Generator = new RandomPersonGenerator(); - - public BindFromObservableListFixture() - { - _collection = new ReactiveList(); - _source = new SourceList(); - _binder = _source.Connect().Bind(_collection).Subscribe(); - } - - public void Dispose() - { - _binder.Dispose(); - _source.Dispose(); - } - - - [Fact] - public void AddToSourceAddsToDestination() - { - var person = new Person("Adult1", 50); - _source.Add(person); - - _collection.Count.Should().Be(1, "Should be 1 item in the collection"); - _collection.First().Should().Be(person, "Should be same person"); - } - - [Fact] - public void UpdateToSourceUpdatesTheDestination() - { - var person = new Person("Adult1", 50); - var personUpdated = new Person("Adult1", 51); - _source.Add(person); - _source.Replace(person, personUpdated); - - _collection.Count.Should().Be(1, "Should be 1 item in the collection"); - _collection.First().Should().Be(personUpdated, "Should be updated person"); - } - - [Fact] - public void RemoveSourceRemovesFromTheDestination() - { - var person = new Person("Adult1", 50); - _source.Add(person); - _source.Remove(person); - - _collection.Count.Should().Be(0, "Should be 1 item in the collection"); - } - - [Fact] - public void AddRange() - { - var people = Generator.Take(100).ToList(); - _source.AddRange(people); - - _collection.Count.Should().Be(100, "Should be 100 items in the collection"); - _collection.ShouldAllBeEquivalentTo(_collection, "Collections should be equivalent"); - } - - [Fact] - public void Clear() - { - var people = Generator.Take(100).ToList(); - _source.AddRange(people); - _source.Clear(); - _collection.Count.Should().Be(0, "Should be 100 items in the collection"); - } - - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs deleted file mode 100644 index 3fe60d390..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/BindSortedChangeSetFixture.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Linq; -using DynamicData.Binding; -using DynamicData.ReactiveUI.Tests.Domain; -using FluentAssertions; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class BindSortedChangeSetFixture: IDisposable - { - private readonly ReactiveList _collection; - private readonly ISourceCache _source; - private readonly IDisposable _binder; - private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); - private readonly IComparer _comparer = SortExpressionComparer.Ascending(p => p.Name); - - public BindSortedChangeSetFixture() - { - _collection = new ReactiveList(); - _source = new SourceCache(p => p.Name); - _binder = _source.Connect() - .Sort(_comparer, resetThreshold: 25) - .Bind(_collection) - .Subscribe(); - - } - - public void Dispose() - { - _binder.Dispose(); - _source.Dispose(); - } - - - [Fact] - public void AddToSourceAddsToDestination() - { - var person = new Person("Adult1", 50); - _source.AddOrUpdate(person); - - _collection.Count.Should().Be(1, "Should be 1 item in the collection"); - _collection.First().Should().Be(person, "Should be same person"); - } - - [Fact] - public void UpdateToSourceUpdatesTheDestination() - { - var person = new Person("Adult1", 50); - var personUpdated = new Person("Adult1", 51); - _source.AddOrUpdate(person); - _source.AddOrUpdate(personUpdated); - - _collection.Count.Should().Be(1, "Should be 1 item in the collection"); - _collection.First().Should().Be(personUpdated, "Should be updated person"); - } - - [Fact] - public void RemoveSourceRemovesFromTheDestination() - { - var person = new Person("Adult1", 50); - _source.AddOrUpdate(person); - _source.Remove(person); - - _collection.Count.Should().Be(0, "Should be 1 item in the collection"); - } - - [Fact] - public void BatchAdd() - { - var people = _generator.Take(100).ToList(); - _source.AddOrUpdate(people); - - _collection.Count.Should().Be(100, "Should be 100 items in the collection"); - _collection.ShouldAllBeEquivalentTo(_collection, "Collections should be equivalent"); - } - - [Fact] - public void BatchRemove() - { - var people = _generator.Take(100).ToList(); - _source.AddOrUpdate(people); - _source.Clear(); - _collection.Count.Should().Be(0, "Should be 100 items in the collection"); - } - - [Fact] - public void CollectionIsInSortOrder() - { - _source.AddOrUpdate(_generator.Take(100)); - var sorted = _source.Items.OrderBy(p => p, _comparer).ToList(); - sorted.ShouldAllBeEquivalentTo(_collection.ToList()); - } - - [Fact] - public void LargeUpdateInvokesAReset() - { - //update once as intital load is always a reset - _source.AddOrUpdate(new Person("Me", 21)); - - bool invoked = false; - _collection.CollectionChanged += (sender, e) => - { - invoked = true; - e.Action.Should().Be(NotifyCollectionChangedAction.Reset); - }; - _source.AddOrUpdate(_generator.Take(100)); - - invoked.Should().BeTrue(); - } - - [Fact] - public void SmallChangeDoesNotInvokeReset() - { - //update once as intital load is always a reset - _source.AddOrUpdate(new Person("Me", 21)); - - bool invoked = false; - bool resetinvoked = false; - _collection.CollectionChanged += (sender, e) => - { - invoked = true; - if (e.Action == NotifyCollectionChangedAction.Reset) - resetinvoked = true; - }; - _source.AddOrUpdate(_generator.Take(24)); - - invoked.Should().BeTrue(); - resetinvoked.Should().BeFalse(); - } - - [Fact] - public void TreatMovesAsRemoveAdd() - { - var cache = new SourceCache(p => p.Name); - - var people = Enumerable.Range(0,10).Select(age => new Person("Person" + age, age)).ToList(); - var importantGuy = people.First(); - cache.AddOrUpdate(people); - - ISortedChangeSet latestSetWithoutMoves = null; - ISortedChangeSet latestSetWithMoves = null; - - var boundList1 = new ReactiveList(); - var boundList2 = new ReactiveList(); - - - using (cache.Connect() - .AutoRefresh(p => p.Age) - .Sort(SortExpressionComparer.Ascending(p => p.Age)) - .TreatMovesAsRemoveAdd() - .Bind(boundList1) - .Subscribe(set => latestSetWithoutMoves = set)) - - using (cache.Connect() - .AutoRefresh(p => p.Age) - .Sort(SortExpressionComparer.Ascending(p => p.Age)) - .Bind(boundList2) - .Subscribe(set => latestSetWithMoves = set)) - { - - importantGuy.Age = importantGuy.Age + 200; - - - latestSetWithoutMoves.Removes.Should().Be(1); - latestSetWithoutMoves.Adds.Should().Be(1); - latestSetWithoutMoves.Moves.Should().Be(0); - latestSetWithoutMoves.Updates.Should().Be(0); - - latestSetWithMoves.Moves.Should().Be(1); - latestSetWithMoves.Updates.Should().Be(0); - latestSetWithMoves.Removes.Should().Be(0); - latestSetWithMoves.Adds.Should().Be(0); - } - } - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs deleted file mode 100644 index 10fadba4b..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/ObservableCollectionToObservableChangeSetWithoutINdexFixture.cs +++ /dev/null @@ -1,99 +0,0 @@ -using System; -using DynamicData.ReactiveUI.Tests.Domain; -using DynamicData.Tests; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class ObservableCollectionToObservableChangeSetWithoutIndexFixture:IDisposable - { - private readonly ReactiveList _collection; - private readonly ChangeSetAggregator _results; - private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); - - public ObservableCollectionToObservableChangeSetWithoutIndexFixture() - { - _collection = new ReactiveList(); - _results = _collection.ToObservableChangeSet().AsAggregator(); - } - - public void Dispose() - { - _results.Dispose(); - } - - [Fact] - public void AddInvokesAnAddChange() - { - var person = new Person("Adult1", 50); - _collection.Add(person); - - //Assert.AreEqual(1, _results.Messages.Count, "Should be 1 updates"); - //Assert.AreEqual(1, _results.Data.Count, "Should be 1 item in the cache"); - //Assert.AreEqual(person, _results.Data.Items.First(), "Should be same person"); - } - - [Fact] - public void RemoveGetsRemovedFromDestination() - { - var person = new Person("Adult1", 50); - _collection.Add(person); - _collection.Remove(person); - - //Assert.AreEqual(2, _results.Messages.Count, "Should be 1 updates"); - //Assert.AreEqual(0, _results.Data.Count, "Should be nothing in the cache"); - //Assert.AreEqual(1, _results.Messages.First().Adds, "First message should be an add"); - //Assert.AreEqual(1, _results.Messages.Skip(1).First().Removes, "First message should be a remove"); - } - - [Fact] - public void DuplicatesAreAllowed() - { - //NB: the following is a replace bcause the hash code of user is calculated from the user name - _collection.Add(new Person("Adult1", 50)); - _collection.Add(new Person("Adult1", 51)); - - //Assert.AreEqual(2, _results.Messages.Count, "Should be 1 updates"); - //Assert.AreEqual(2, _results.Data.Count, "Should be 1 item in the cache"); - //Assert.AreEqual(1, _results.Messages.First().Adds, "First message should be an add"); - //Assert.AreEqual(1, _results.Messages.Skip(1).First().Adds, "First message should be an add"); - } - - [Fact] - public void Replace() - { - //NB: the following is a replace because the hash code of user is calculated from the user name - var person = new Person("Adult1", 50); - var replaced = new Person("Adult1", 50); - _collection.Add(person); - _collection.Replace(person, replaced); - - //Assert.AreEqual(2, _results.Messages.Count, "Should be 1 updates"); - //Assert.AreEqual(1, _results.Data.Count, "Should be 1 item in the cache"); - //Assert.AreEqual(1, _results.Messages.First().Adds, "First message should be an add"); - //Assert.AreEqual(1, _results.Messages.Skip(1).First().Replaced, "First message should be an update"); - } - - [Fact] - public void ResetFiresClearsAndAdds() - { - var people = _generator.Take(10); - _collection.AddRange(people); - //Assert.AreEqual(1, _results.Messages.Count, "Should be 1 updates"); - - _collection.Reset(); - //Assert.AreEqual(10, _results.Data.Count, "Should be 10 items in the cache"); - //Assert.AreEqual(2, _results.Messages.Count, "Should be 2 updates"); - - var update11 = _results.Messages[1]; - //Assert.AreEqual(10, update11.Removes, "Should be 10 removes"); - //Assert.AreEqual(10, update11.Adds, "Should be 10 adds"); - //Assert.AreEqual(10, _results.Data.Count, "Should be 10 items in the cache"); - } - - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs deleted file mode 100644 index bdea117c5..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/ToObservableChangeSetFixture.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System; -using System.Linq; -using DynamicData.Tests; -using ReactiveUI.Legacy; -using Xunit; -using FluentAssertions; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class ToObservableChangeSetFixture : IDisposable - { - private readonly ReactiveList _collection; - private readonly ChangeSetAggregator _results; - - public ToObservableChangeSetFixture() - { - _collection = new ReactiveList(); - _results = _collection.ToObservableChangeSet().AsAggregator(); - } - - public void Dispose() - { - _results.Dispose(); - } - - [Fact] - public void Move() - { - _collection.AddRange(Enumerable.Range(1, 10)); - - _results.Data.Items.ShouldAllBeEquivalentTo(_collection); - _collection.Move(5, 8); - _results.Data.Items.ShouldAllBeEquivalentTo(_collection); - - _collection.Move(7, 1); - _results.Data.Items.ShouldAllBeEquivalentTo(_collection); - } - - [Fact] - public void Add() - { - _collection.Add(1); - - _results.Messages.Count.Should().Be(1); - _results.Data.Count.Should().Be(1); - _results.Data.Items.First().Should().Be(1); - } - - [Fact] - public void Remove() - { - _collection.AddRange(Enumerable.Range(1, 10)); - - _collection.Remove(3); - - _results.Data.Count.Should().Be(9); - _results.Data.Items.Contains(3).Should().BeFalse(); - _results.Data.Items.ShouldAllBeEquivalentTo(_collection); - } - - [Fact] - public void Duplicates() - { - _collection.Add(1); - _collection.Add(1); - - _results.Data.Count.Should().Be(2); - } - - [Fact] - public void Replace() - { - _collection.AddRange(Enumerable.Range(1, 10)); - _collection[8] = 20; - - _results.Data.Items.ShouldBeEquivalentTo(new[] { 1, 2, 3, 4, 5, 6, 7, 8, 20, 10 }); - - } - - [Fact] - public void ResetFiresClearsAndAdds() - { - _collection.AddRange(Enumerable.Range(1, 10)); - - _collection.Reset(); - _results.Data.Items.ShouldAllBeEquivalentTo(_collection); - - var resetNotification = _results.Messages.Last(); - resetNotification.Removes.Should().Be(10); - resetNotification.Adds.Should().Be(10); - } - - - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs deleted file mode 100644 index db2c5be38..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyFixture.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reactive.Linq; -using DynamicData.ReactiveUI.Tests.Domain; -using DynamicData.Tests; -using FluentAssertions; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - public class TransformManyFixture - { - [Fact] - public void TransformMany() - { - var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 50) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - using (var source = new SourceList()) - using (var aggregator = source.Connect() - .TransformMany(p => p.Children) - .AsAggregator()) - { - source.AddRange(parents); - - aggregator.Data.Count.Should().Be(100); - - //add a child to an observable collection and check the new item is added - parents[0].Children.Add(new Person("NewlyAddded", 100)); - aggregator.Data.Count.Should().Be(101); - - ////remove first parent and check children have gone - source.RemoveAt(0); - aggregator.Data.Count.Should().Be(98); - - - //check items can be cleared and then added back in - var childrenInZero = parents[1].Children.ToArray(); - parents[1].Children.Clear(); - aggregator.Data.Count.Should().Be(96); - parents[1].Children.AddRange(childrenInZero); - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - var replacedChild = parents[1].Children[0]; - var replacement = new Person("Replacement", 100); - parents[1].Children[0] = replacement; - aggregator.Data.Count.Should().Be(98); - - aggregator.Data.Items.Contains(replacement).Should().BeTrue(); - aggregator.Data.Items.Contains(replacedChild).Should().BeFalse(); - } - } - - [Fact] - public void TransformManyWithKey() - { - var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 50) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - using (var source = new SourceCache(x => x.Id)) - using (var aggregator = source.Connect() - .TransformMany(p => p.Children, c => c.Name) - .AsAggregator()) - { - source.AddOrUpdate(parents); - - aggregator.Data.Count.Should().Be(100); - - //add a child to an observable collection and check the new item is added - parents[0].Children.Add(new Person("NewlyAddded", 100)); - aggregator.Data.Count.Should().Be(101); - - ////remove first parent and check children have gone - source.RemoveKey(1); - aggregator.Data.Count.Should().Be(98); - - - //check items can be cleared and then added back in - var childrenInZero = parents[1].Children.ToArray(); - parents[1].Children.Clear(); - aggregator.Data.Count.Should().Be(96); - parents[1].Children.AddRange(childrenInZero); - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - var replacedChild = parents[1].Children[0]; - parents[1].Children[0] = new Person("Replacement", 100); - aggregator.Data.Count.Should().Be(98); - - aggregator.Data.Lookup(replacedChild.Name).HasValue.Should().BeFalse(); - aggregator.Data.Lookup("Replacement").HasValue.Should().BeTrue(); - } - } - - [Fact] - [Trait("Performance", "Manual run only")] - public void Perf() - { - var children = Enumerable.Range(1, 10000).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 5000) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - - var sw = new Stopwatch(); - - using (var source = new SourceCache(x => x.Id)) - using (var sut = source.Connect() - .Do(_ => sw.Start()) - .TransformMany(p => p.Children, c => c.Name) - .Do(_ => sw.Stop()) - .Subscribe(c => Console.WriteLine($"Changes = {c.Count:N0}"))) - { - source.AddOrUpdate(parents); - Console.WriteLine($"{sw.ElapsedMilliseconds}"); - } - } - - - private class Parent - { - public int Id { get; } - public ReactiveList Children { get; } - - public Parent(int id, IEnumerable children) - { - Id = id; - Children = new ReactiveList(children); - } - } - } -} diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs deleted file mode 100644 index 0d77641cf..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionFixture.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using DynamicData.ReactiveUI.Tests.Domain; -using DynamicData.Tests; -using FluentAssertions; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class TransformManyObservableCollectionFixture - { - [Fact] - public void FlattenObservableCollection() - { - var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 50) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - using (var source = new SourceList()) - using (var aggregator = source.Connect().TransformMany(p => p.Children) - .AsAggregator()) - { - source.AddRange(parents); - - aggregator.Data.Count.Should().Be(100); - - //add a child to an observable collection and check the new item is added - parents[0].Children.Add(new Person("NewlyAddded", 100)); - aggregator.Data.Count.Should().Be(101); - - ////remove first parent and check children have gone - source.RemoveAt(0); - aggregator.Data.Count.Should().Be(98); - - - //check items can be cleared and then added back in - var childrenInZero = parents[1].Children.ToArray(); - parents[1].Children.Clear(); - aggregator.Data.Count.Should().Be(96); - parents[1].Children.AddRange(childrenInZero); - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - var replacedChild = parents[1].Children[0]; - var replacement = new Person("Replacement", 100); - parents[1].Children[0] = replacement; - aggregator.Data.Count.Should().Be(98); - - aggregator.Data.Items.Contains(replacement).Should().BeTrue(); - aggregator.Data.Items.Contains(replacedChild).Should().BeFalse(); - } - } - - [Fact] - public void FlattenReadOnlyObservableCollection() - { - var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 50) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - using (var source = new SourceList()) - using (var aggregator = source.Connect() - .TransformMany(p => p.ChildrenReadonly) - .AsAggregator()) - { - source.AddRange(parents); - - aggregator.Data.Count.Should().Be(100); - - //add a child to an observable collection and check the new item is added - parents[0].Children.Add(new Person("NewlyAddded", 100)); - aggregator.Data.Count.Should().Be(101); - - ////remove first parent and check children have gone - source.RemoveAt(0); - aggregator.Data.Count.Should().Be(98); - - - //check items can be cleared and then added back in - var childrenInZero = parents[1].Children.ToArray(); - parents[1].Children.Clear(); - aggregator.Data.Count.Should().Be(96); - parents[1].Children.AddRange(childrenInZero); - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - var replacedChild = parents[1].Children[0]; - var replacement = new Person("Replacement", 100); - parents[1].Children[0] = replacement; - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - aggregator.Data.Items.Contains(replacement).Should().BeTrue(); - aggregator.Data.Items.Contains(replacedChild).Should().BeFalse(); - } - } - - [Fact] - public void ObservableCollectionWithoutInitialData() - { - using (var parents = new SourceList()) - { - var collection = parents.Connect() - .TransformMany(d => d.Children) - .AsObservableList(); - - var parent = new Parent(); - parents.Add(parent); - - collection.Count.Should().Be(0); - - parent.Children.Add(new Person("child1", 1)); - collection.Count.Should().Be(1); - - parent.Children.Add(new Person("child2", 2)); - collection.Count.Should().Be(2); - } - } - - [Fact] - public void ReadOnlyObservableCollectionWithoutInitialData() - { - using (var parents = new SourceList()) - { - var collection = parents.Connect() - .TransformMany(d => d.ChildrenReadonly) - .AsObservableList(); - - var parent = new Parent(); - parents.Add(parent); - - collection.Count.Should().Be(0); - - parent.Children.Add(new Person("child1", 1)); - collection.Count.Should().Be(1); - - parent.Children.Add(new Person("child2", 2)); - collection.Count.Should().Be(2); - } - } - - private class Parent - { - public ReactiveList Children { get; } - public IReadOnlyReactiveList ChildrenReadonly { get; } - - public Parent(int id, IEnumerable children) - { - Children = new ReactiveList(children); - ChildrenReadonly = Children; - } - - public Parent() - { - Children = new ReactiveList(); - ChildrenReadonly = Children; - } - } - } -} diff --git a/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs b/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs deleted file mode 100644 index 6d17d9e33..000000000 --- a/src/DynamicData.ReactiveUI.Tests/Fixtures/TransformManyObservableCollectionWithKeyFixture.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reactive.Linq; -using DynamicData.ReactiveUI.Tests.Domain; -using DynamicData.Tests; -using FluentAssertions; -using ReactiveUI.Legacy; -using Xunit; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI.Tests.Fixtures -{ - - public class TransformManyObservableCollectionWithKeyFixture - { - [Fact] - public void FlattenObservableCollection() - { - var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 50) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - using (var source = new SourceCache(x => x.Id)) - using (var aggregator = source.Connect() - .TransformMany(p => p.Children, c => c.Name) - .AsAggregator()) - { - source.AddOrUpdate(parents); - - aggregator.Data.Count.Should().Be(100); - - //add a child to an observable collection and check the new item is added - parents[0].Children.Add(new Person("NewlyAddded", 100)); - aggregator.Data.Count.Should().Be(101); - - ////remove first parent and check children have gone - source.RemoveKey(1); - aggregator.Data.Count.Should().Be(98); - - - //check items can be cleared and then added back in - var childrenInZero = parents[1].Children.ToArray(); - parents[1].Children.Clear(); - aggregator.Data.Count.Should().Be(96); - parents[1].Children.AddRange(childrenInZero); - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - var replacedChild = parents[1].Children[0]; - parents[1].Children[0] = new Person("Replacement", 100); - aggregator.Data.Count.Should().Be(98); - - aggregator.Data.Lookup(replacedChild.Name).HasValue.Should().BeFalse(); - aggregator.Data.Lookup("Replacement").HasValue.Should().BeTrue(); - } - } - - [Fact] - public void FlattenReadOnlyObservableCollection() - { - var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 50) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - using (var source = new SourceCache(x => x.Id)) - using (var aggregator = source.Connect() - .TransformMany(p => p.ChildrenReadonly, c => c.Name) - .AsAggregator()) - { - source.AddOrUpdate(parents); - - aggregator.Data.Count.Should().Be(100); - - //add a child to an observable collection and check the new item is added - parents[0].Children.Add(new Person("NewlyAddded", 100)); - aggregator.Data.Count.Should().Be(101); - - ////remove first parent and check children have gone - source.RemoveKey(1); - aggregator.Data.Count.Should().Be(98); - - - //check items can be cleared and then added back in - var childrenInZero = parents[1].Children.ToArray(); - parents[1].Children.Clear(); - aggregator.Data.Count.Should().Be(96); - parents[1].Children.AddRange(childrenInZero); - aggregator.Data.Count.Should().Be(98); - - //replace produces an update - var replacedChild = parents[1].Children[0]; - parents[1].Children[0] = new Person("Replacement", 100); - aggregator.Data.Count.Should().Be(98); - - aggregator.Data.Lookup(replacedChild.Name).HasValue.Should().BeFalse(); - aggregator.Data.Lookup("Replacement").HasValue.Should().BeTrue(); - } - } - - [Fact] - [Trait("Performance", "Manual run only")] - public void Perf() - { - var children = Enumerable.Range(1, 10000).Select(i => new Person("Name" + i, i)).ToArray(); - - int childIndex = 0; - var parents = Enumerable.Range(1, 5000) - .Select(i => - { - var parent = new Parent(i, new[] - { - children[childIndex], - children[childIndex + 1] - }); - - childIndex = childIndex + 2; - return parent; - }).ToArray(); - - - - var sw = new Stopwatch(); - - using (var source = new SourceCache(x => x.Id)) - using (var sut = source.Connect() - .Do(_ => sw.Start()) - .TransformMany(p => p.Children, c => c.Name) - .Do(_ => sw.Stop()) - .Subscribe(c => Console.WriteLine($"Changes = {c.Count:N0}"))) - { - source.AddOrUpdate(parents); - Console.WriteLine($"{sw.ElapsedMilliseconds}"); - } - } - - [Fact] - public void ObservableCollectionWithoutInitialData() - { - using (var parents = new SourceCache(d => d.Id)) - { - var collection = parents.Connect() - .TransformMany(d => d.Children, p => p.Name) - .AsObservableCache(); - - var parent = new Parent(1); - parents.AddOrUpdate(parent); - - collection.Count.Should().Be(0); - - parent.Children.Add(new Person("child1", 1)); - collection.Count.Should().Be(1); - - parent.Children.Add(new Person("child2", 2)); - collection.Count.Should().Be(2); - } - } - - [Fact] - public void ReadOnlyObservableCollectionWithoutInitialData() - { - using (var parents = new SourceCache(d => d.Id)) - { - var collection = parents.Connect() - .TransformMany(d => d.ChildrenReadonly, p => p.Name) - .AsObservableCache(); - - var parent = new Parent(1); - parents.AddOrUpdate(parent); - - collection.Count.Should().Be(0); - - parent.Children.Add(new Person("child1", 1)); - collection.Count.Should().Be(1); - - parent.Children.Add(new Person("child2", 2)); - collection.Count.Should().Be(2); - } - } - - - private class Parent - { - public int Id { get; } - public ReactiveList Children { get; } - public IReadOnlyReactiveList ChildrenReadonly { get; } - - public Parent(int id, IEnumerable children) - { - Id = id; - Children = new ReactiveList(children); - ChildrenReadonly = Children; - } - - public Parent(int id) - { - Id = id; - Children = new ReactiveList(); - ChildrenReadonly = Children; - } - } - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj b/src/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj deleted file mode 100644 index 804eadfb0..000000000 --- a/src/DynamicData.ReactiveUI/DynamicData.ReactiveUI.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - netstandard2.0;net461;uap10.0.16299 - true - true - - - - - - - - - - - - Dynamic Data / Reactive UI inte gration - -Make reactive ui even more powerful by integrating with dynamic data and making use of it's numerious operators. -Dynamic data provides an observable cache and an observable list with at least 50 collection specific operators for each. - - - Integrate dynamic data observable collections with ReactiveUI - - - - diff --git a/src/DynamicData.ReactiveUI/DynamicDataEx.cs b/src/DynamicData.ReactiveUI/DynamicDataEx.cs deleted file mode 100644 index 2fe8d97c9..000000000 --- a/src/DynamicData.ReactiveUI/DynamicDataEx.cs +++ /dev/null @@ -1,315 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reactive.Disposables; -using System.Reactive.Linq; -using DynamicData.Annotations; -using DynamicData.Cache.Internal; -using DynamicData.List.Internal; -using ReactiveUI.Legacy; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI -{ - - /// - /// Integration methods between dynamic data and reactive list - /// - public static class DynamicDataEx - { - /// - /// Flattens a nested reactive list - /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The manyselector. - /// Used when an item has been replaced to determine whether child items are the same as previous children - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> TransformMany(this IObservable> source, - Func> manyselector, - IEqualityComparer equalityComparer = null) - { - return new TransformMany(source, manyselector, equalityComparer, t => Observable.Defer(() => - { - var subsequentChanges = manyselector(t).ToObservableChangeSet(); - - if (manyselector(t).Count > 0) - return subsequentChanges; - - return Observable.Return(ChangeSet.Empty) - .Concat(subsequentChanges); - })).Run(); - } - - - /// - /// Flattens a nested reactive list - /// - /// The type of the destination. - /// The type of the source. - /// The source. - /// The manyselector. - /// Used when an item has been replaced to determine whether child items are the same as previous children - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> TransformMany(this IObservable> source, - Func> manyselector, - IEqualityComparer equalityComparer = null) - { - return new TransformMany(source, manyselector, equalityComparer, t => Observable.Defer(() => - { - var subsequentChanges = manyselector(t).ToObservableChangeSet(); - - if (manyselector(t).Count > 0) - return subsequentChanges; - - return Observable.Return(ChangeSet.Empty) - .Concat(subsequentChanges); - })).Run(); - } - - /// - /// Flattens a nested reactive list, using the key selector to ensure only unique items are added - /// - /// The type of the destination. - /// The type of the destination key. - /// The type of the source. - /// The type of the source key. - /// The source. - /// The manyselector. - /// The key selector which must be unique across all - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> TransformMany( - this IObservable> source, - Func> manyselector, - Func keySelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (manyselector == null) throw new ArgumentNullException(nameof(manyselector)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return new TransformMany(source, - manyselector, - keySelector, - t => Observable.Defer(() => - { - var subsequentChanges = manyselector(t).ToObservableChangeSet(keySelector); - - if (manyselector(t).Count > 0) - return subsequentChanges; - - return Observable.Return(ChangeSet.Empty) - .Concat(subsequentChanges); - })).Run(); - } - - /// - /// Flattens a nested reactive list, using the key selector to ensure only unique items are added - /// - /// The type of the destination. - /// The type of the destination key. - /// The type of the source. - /// The type of the source key. - /// The source. - /// The manyselector. - /// The key selector which must be unique across all - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> TransformMany( - this IObservable> source, - Func> manyselector, - Func keySelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (manyselector == null) throw new ArgumentNullException(nameof(manyselector)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return new TransformMany(source, - manyselector, - keySelector, - t => Observable.Defer(() => - { - var subsequentChanges = manyselector(t).ToObservableChangeSet(keySelector); - - if (manyselector(t).Count > 0) - return subsequentChanges; - - return Observable.Return(ChangeSet.Empty) - .Concat(subsequentChanges); - })).Run(); - } - - /// - /// Binds the observable changeset to the target ReactiveList - /// - /// - /// The source. - /// The target collection. - /// The reset threshold. - /// - /// - /// source - /// or - /// targetCollection - /// - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> Bind([NotNull] this IObservable> source, - [NotNull] ReactiveList targetCollection, int resetThreshold = 25) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (targetCollection == null) throw new ArgumentNullException(nameof(targetCollection)); - - var adaptor = new ObservableListToReactiveListAdaptor(targetCollection, resetThreshold); - return source.Adapt(adaptor); - } - - - /// - /// Populate and maintain the specified reactive list from the source observable changeset - /// - /// The type of the object. - /// The type of the key. - /// The source. - /// The target. - /// The reset threshold before a reset event on the target list is invoked - /// - /// - /// destination - /// or - /// target - /// - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> Bind(this IObservable> source, ReactiveList target, - int resetThreshold = 25) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (target == null) throw new ArgumentNullException(nameof(target)); - - var adaptor = new ObservableCacheToReactiveListAdaptor(target, resetThreshold); - return source.Bind(adaptor); - - } - - /// - /// Binds the results using the specified changeset adaptor - /// - /// The type of the object. - /// The type of the key. - /// The source. - /// The updater. - /// - /// source - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> Bind(this IObservable> source, - IChangeSetAdaptor updater) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (updater == null) throw new ArgumentNullException(nameof(updater)); - - return Observable.Create> - (observer => - { - var locker = new object(); - var published = source.Synchronize(locker).Publish(); - - var adaptor = published.Subscribe(updates => - { - try - { - updater.Adapt(updates); - } - catch (Exception ex) - { - observer.OnError(ex); - } - }, - observer.OnError, observer.OnCompleted); - - var connected = published.Connect(); - var subscriber = published.SubscribeSafe(observer); - - return Disposable.Create(() => - { - adaptor.Dispose(); - subscriber.Dispose(); - connected.Dispose(); - }); - } - ); - } - - /// - /// Populate and maintain the specified reactive list from the source observable changeset - /// - /// The type of the object. - /// The type of the key. - /// The source. - /// The target. - /// The reset threshold before a reset event on the target list is invoked - /// - /// - /// destination - /// or - /// target - /// - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> Bind( - this IObservable> source, ReactiveList target, - int resetThreshold = 25) - { - if (target == null) throw new ArgumentNullException("target"); - - var adaptor = new SortedReactiveListAdaptor(target, resetThreshold); - return source.Bind(adaptor); - - } - - /// - /// Binds the results using the specified sorted changeset adaptor - /// - /// The type of the object. - /// The type of the key. - /// The source. - /// The updater. - /// - /// source - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static IObservable> Bind( - this IObservable> source, - ISortedChangeSetAdaptor updater) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (updater == null) throw new ArgumentNullException(nameof(updater)); - - return Observable.Create> - (observer => - { - var locker = new object(); - var published = source.Synchronize(locker).Publish(); - - var adaptor = published.Subscribe(updates => - { - try - { - updater.Adapt(updates); - } - catch (Exception ex) - { - observer.OnError(ex); - } - },observer.OnError, observer.OnCompleted); - - var connected = published.Connect(); - - var subscriber = published.SubscribeSafe(observer); - - return Disposable.Create(() => - { - adaptor.Dispose(); - subscriber.Dispose(); - connected.Dispose(); - }); - } - ); - } - } -} diff --git a/src/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs b/src/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs deleted file mode 100644 index d64d6cd96..000000000 --- a/src/DynamicData.ReactiveUI/ObservableCacheToReactiveListAdaptor.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Collections.Generic; -using ReactiveUI.Legacy; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI -{ - /// - /// Adaptor used to populate a from an observable changeset. - /// - /// The type of the object. - /// The type of the key. - internal class ObservableCacheToReactiveListAdaptor : IChangeSetAdaptor - { - private IDictionary _data; - private bool _loaded; - private readonly IReactiveList _target; - private readonly int _resetThreshold; - - /// - /// Initializes a new instance of the class. - /// - /// The target. - /// The reset threshold. - /// target - public ObservableCacheToReactiveListAdaptor(ReactiveList target, int resetThreshold = 50) - { - _target = target ?? throw new ArgumentNullException(nameof(target)); - _resetThreshold = resetThreshold; - } - - /// - /// Adapts the specified changeset - /// - /// The changes. - public void Adapt(IChangeSet changes) - { - Clone(changes); - - if (changes.Count - changes.Refreshes > _resetThreshold || !_loaded) - { - _loaded = true; - using (_target.SuppressChangeNotifications()) - { - _target.Clear(); - _target.AddRange(_data.Values); - } - } - else - { - DoUpdate(changes); - } - } - - private void Clone(IChangeSet changes) - { - //for efficiency resize dictionary to initial batch size - if (_data == null || _data.Count == 0) - _data = new Dictionary(changes.Count); - - foreach (var item in changes) - { - switch (item.Reason) - { - case ChangeReason.Update: - case ChangeReason.Add: - { - _data[item.Key] = item.Current; - } - break; - case ChangeReason.Remove: - _data.Remove(item.Key); - break; - } - } - } - - private void DoUpdate(IChangeSet changes) - { - foreach (var change in changes) - { - switch (change.Reason) - { - case ChangeReason.Add: - _target.Add(change.Current); - break; - case ChangeReason.Remove: - _target.Remove(change.Current); - break; - case ChangeReason.Update: - { - _target.Remove(change.Previous.Value); - _target.Add(change.Current); - } - break; - } - } - - } - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs b/src/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs deleted file mode 100644 index 47e0f410d..000000000 --- a/src/DynamicData.ReactiveUI/ObservableListToReactiveListAdaptor.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using ReactiveUI.Legacy; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI -{ - /// - /// Adaptor used to populate a from an observable changeset. - /// - /// The type of the object. - internal class ObservableListToReactiveListAdaptor : IChangeSetAdaptor - { - private bool _loaded; - private readonly ReactiveList _target; - private readonly int _resetThreshold; - - /// - /// Initializes a new instance of the class. - /// - /// The target. - /// The reset threshold. - /// target - public ObservableListToReactiveListAdaptor(ReactiveList target, int resetThreshold = 50) - { - _target = target ?? throw new ArgumentNullException(nameof(target)); - _resetThreshold = resetThreshold; - } - - - /// - public void Adapt(IChangeSet changes) - { - if (changes.Count - changes.Refreshes > _resetThreshold || !_loaded) - { - using (_target.SuppressChangeNotifications()) - { - _target.CloneReactiveList(changes); - _loaded = true; - } - } - else - { - _target.CloneReactiveList(changes); - } - } - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI/ReactiveListEx.cs b/src/DynamicData.ReactiveUI/ReactiveListEx.cs deleted file mode 100644 index d87199325..000000000 --- a/src/DynamicData.ReactiveUI/ReactiveListEx.cs +++ /dev/null @@ -1,210 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using DynamicData.Binding; -using ReactiveUI; -using ReactiveUI.Legacy; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI -{ - /// - ///Reactive List extensions - /// - [Obsolete("ReactiveList has been deprecated by the ReactiveUI team.")] - public static class ReactiveListEx - { - /// - /// Converts the Reactive List into an observable change set - /// - /// - /// The source. - /// - /// source - public static IObservable> ToObservableChangeSet(this IReadOnlyReactiveList source) - { - return source.ToObservableChangeSet, T>(); - } - - /// - /// Converts the Reactive List into an observable change set - /// - /// - /// The source. - /// - /// source - public static IObservable> ToObservableChangeSet(this ReactiveList source) - { - return source.ToObservableChangeSet, T>(); - } - - /// - /// Clones the ReactiveList from all changes - /// - /// The type of the object. - /// The type of the key. - /// The source. - /// The key selector. - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this IReadOnlyReactiveList source, Func keySelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return source.ToObservableChangeSet, TObject>().AddKey(keySelector); - } - - /// - /// Clones the ReactiveList from all changes - /// - /// The type of the object. - /// The type of the key. - /// The source. - /// The key selector. - /// - /// source - /// or - /// keySelector - public static IObservable> ToObservableChangeSet(this ReactiveList source, Func keySelector) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); - - return source.ToObservableChangeSet, TObject>().AddKey(keySelector); - } - - - internal static void CloneReactiveList(this ReactiveList source, IChangeSet changes) - { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (changes == null) throw new ArgumentNullException(nameof(changes)); - - changes.ForEach(item => - { - - switch (item.Reason) - { - case ListChangeReason.Add: - { - var change = item.Item; - var hasIndex = change.CurrentIndex >= 0; - if (hasIndex) - { - source.Insert(change.CurrentIndex, change.Current); - } - else - { - source.Add(change.Current); - } - break; - } - - case ListChangeReason.AddRange: - { - var startingIndex = item.Range.Index; - - if (RxApp.SupportsRangeNotifications) - { - if (startingIndex >= 0) - { - source.InsertRange(startingIndex,item.Range); - } - else - { - source.AddRange(item.Range); - } - } - else - { - if (startingIndex >= 0) - { - item.Range.Reverse().ForEach(t => source.Insert(startingIndex, t)); - } - else - { - item.Range.ForEach(source.Add); - } - } - - break; - } - - case ListChangeReason.Clear: - { - source.Clear(); - break; - } - - case ListChangeReason.Replace: - { - - var change = item.Item; - bool hasIndex = change.CurrentIndex >= 0; - if (hasIndex && change.CurrentIndex == change.PreviousIndex) - { - source[change.CurrentIndex] = change.Current; - } - else - { - source.RemoveAt(change.PreviousIndex); - source.Insert(change.CurrentIndex, change.Current); - } - } - break; - case ListChangeReason.Remove: - { - var change = item.Item; - bool hasIndex = change.CurrentIndex >= 0; - if (hasIndex) - { - source.RemoveAt(change.CurrentIndex); - } - else - { - source.Remove(change.Current); - } - break; - } - - case ListChangeReason.RemoveRange: - { - if (RxApp.SupportsRangeNotifications && item.Range.Index>=0) - { - source.RemoveRange(item.Range.Index, item.Range.Count); - } - else - { - source.RemoveMany(item.Range); - } - } - break; - - case ListChangeReason.Moved: - { - var change = item.Item; - bool hasIndex = change.CurrentIndex >= 0; - if (!hasIndex) - throw new UnspecifiedIndexException("Cannot move as an index was not specified"); - - source.Move(change.PreviousIndex, change.CurrentIndex); - break; - } - } - }); - - - } - - private static void ForEach(this IEnumerable source, Action action) - { - foreach (var item in source) - action(item); - - } - - } -} \ No newline at end of file diff --git a/src/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs b/src/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs deleted file mode 100644 index b349fdfdc..000000000 --- a/src/DynamicData.ReactiveUI/SortedReactiveListAdaptor.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Linq; -using ReactiveUI.Legacy; - -#pragma warning disable CS0618 // Using legacy code. - -namespace DynamicData.ReactiveUI -{ - /// - /// Adaptor used to populate a from an observable sorted changeset. - /// - /// The type of the object. - /// The type of the key. - internal class SortedReactiveListAdaptor : ISortedChangeSetAdaptor - { - private readonly ReactiveList _target; - private readonly int _resetThreshold; - - /// - /// Initializes a new instance of the class. - /// - /// The target. - /// The reset threshold. - /// target - public SortedReactiveListAdaptor(ReactiveList target, int resetThreshold = 50) - { - _target = target ?? throw new ArgumentNullException(nameof(target)); - _resetThreshold = resetThreshold; - } - - /// - /// Adapts the specified sorted changeset - /// - /// The changes. - public void Adapt(ISortedChangeSet changes) - { - - switch (changes.SortedItems.SortReason) - { - case SortReason.InitialLoad: - case SortReason.ComparerChanged: - case SortReason.Reset: - { - using (_target.SuppressChangeNotifications()) - { - _target.Clear(); - _target.AddRange(changes.SortedItems.Select(kv => kv.Value)); - } - } - break; - - case SortReason.DataChanged: - { - if (changes.Count > _resetThreshold) - { - using (_target.SuppressChangeNotifications()) - { - _target.Clear(); - _target.AddRange(changes.SortedItems.Select(kv => kv.Value)); - } - } - else - { - DoUpdate(changes); - } - } - break; - case SortReason.Reorder: - - //Updates will only be moves, so apply logic - DoUpdate(changes); - break; - - - default: - throw new ArgumentOutOfRangeException(); - } - } - - - private void DoUpdate(IChangeSet changes) - { - foreach (var change in changes) - { - switch (change.Reason) - { - case ChangeReason.Add: - _target.Insert(change.CurrentIndex, change.Current); - break; - case ChangeReason.Remove: - _target.RemoveAt(change.CurrentIndex); - break; - case ChangeReason.Moved: - _target.Move(change.PreviousIndex, change.CurrentIndex); - break; - case ChangeReason.Update: - { - _target.RemoveAt(change.PreviousIndex); - _target.Insert(change.CurrentIndex, change.Current); - } - break; - } - } - - } - } -} \ No newline at end of file diff --git a/src/DynamicData.Tests/AggregationTests/AggregationFixture.cs b/src/DynamicData.Tests/AggregationTests/AggregationFixture.cs index b9d1d20bd..e8e6dafea 100644 --- a/src/DynamicData.Tests/AggregationTests/AggregationFixture.cs +++ b/src/DynamicData.Tests/AggregationTests/AggregationFixture.cs @@ -6,10 +6,9 @@ using FluentAssertions; using Xunit; - namespace DynamicData.Tests.AggregationTests { - + public class AggregationFixture: IDisposable { private readonly SourceCache _source; @@ -29,9 +28,13 @@ public AggregationFixture() items.ForEach(x => { if (x.Type == AggregateType.Add) + { current = current + x.Item.Age; + } else + { current = current - x.Item.Age; + } }); return current; }); diff --git a/src/DynamicData.Tests/AggregationTests/AverageFixture.cs b/src/DynamicData.Tests/AggregationTests/AverageFixture.cs index a499b7417..46c5ef5a6 100644 --- a/src/DynamicData.Tests/AggregationTests/AverageFixture.cs +++ b/src/DynamicData.Tests/AggregationTests/AverageFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.AggregationTests { - + public class AverageFixture: IDisposable { private readonly SourceCache _source; diff --git a/src/DynamicData.Tests/AggregationTests/MaxFixture.cs b/src/DynamicData.Tests/AggregationTests/MaxFixture.cs index 19d18d8a3..95a6f8326 100644 --- a/src/DynamicData.Tests/AggregationTests/MaxFixture.cs +++ b/src/DynamicData.Tests/AggregationTests/MaxFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.AggregationTests { - + public class MaxFixture: IDisposable { private readonly SourceCache _source; @@ -82,6 +82,5 @@ public void InlineChangeReEvaluatesTotals() accumulator.Dispose(); } - } } diff --git a/src/DynamicData.Tests/AggregationTests/SumFixture.cs b/src/DynamicData.Tests/AggregationTests/SumFixture.cs index 228a10c38..ae822bccd 100644 --- a/src/DynamicData.Tests/AggregationTests/SumFixture.cs +++ b/src/DynamicData.Tests/AggregationTests/SumFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.AggregationTests { - + public class SumFixture: IDisposable { private readonly SourceCache _source; diff --git a/src/DynamicData.Tests/AutoRefreshFilter.cs b/src/DynamicData.Tests/AutoRefreshFilter.cs index 965dc8a93..dda051770 100644 --- a/src/DynamicData.Tests/AutoRefreshFilter.cs +++ b/src/DynamicData.Tests/AutoRefreshFilter.cs @@ -36,7 +36,6 @@ public void Test() } } - public class Item : INotifyPropertyChanged { public Guid Id { get; } @@ -53,7 +52,6 @@ public string Name } } - public Item(string name) { Id = Guid.NewGuid(); diff --git a/src/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs b/src/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs index 313dafa25..ce115473a 100644 --- a/src/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs +++ b/src/DynamicData.Tests/Binding/BindingLIstBindListFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.Binding { - + public class BindingLIstBindListFixture : IDisposable { private readonly BindingList _collection; diff --git a/src/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs b/src/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs index baeeec869..cab5cd161 100644 --- a/src/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs +++ b/src/DynamicData.Tests/Binding/BindingListBindCacheFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.Binding { - + public class BindingListCacheFixture : IDisposable { private readonly BindingList _collection = new BindingList(); diff --git a/src/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs b/src/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs index 1e4aa83cd..ed12ce837 100644 --- a/src/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs +++ b/src/DynamicData.Tests/Binding/BindingListBindCacheSortedFixture.cs @@ -11,7 +11,7 @@ namespace DynamicData.Tests.Binding { - + public class BindingListBindCacheSortedFixture : IDisposable { private readonly BindingList _collection; @@ -22,7 +22,7 @@ public class BindingListBindCacheSortedFixture : IDisposable public BindingListBindCacheSortedFixture() { - _collection = new BindingList(); + _collection = new BindingList(); _source = new SourceCache(p => p.Name); _binder = _source.Connect() .Sort(_comparer, resetThreshold: 25) @@ -124,7 +124,9 @@ public void SmallChangeDoesNotInvokeReset() { invoked = true; if (e.ListChangedType == ListChangedType.Reset) + { resetInvoked = true; + } }; _source.AddOrUpdate(_generator.Take(24)); @@ -147,7 +149,6 @@ public void TreatMovesAsRemoveAdd() var boundList1 = new ObservableCollectionExtended(); var boundList2 = new ObservableCollectionExtended(); - using (cache.Connect() .AutoRefresh(p => p.Age) .Sort(SortExpressionComparer.Ascending(p => p.Age)) @@ -164,7 +165,6 @@ public void TreatMovesAsRemoveAdd() importantGuy.Age = importantGuy.Age + 200; - latestSetWithoutMoves.Removes.Should().Be(1); latestSetWithoutMoves.Adds.Should().Be(1); latestSetWithoutMoves.Moves.Should().Be(0); diff --git a/src/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs b/src/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs index 53c91c311..0e324829b 100644 --- a/src/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs +++ b/src/DynamicData.Tests/Binding/DeeplyNestedNotifyPropertyChangedFixture.cs @@ -7,9 +7,8 @@ using Xunit; namespace DynamicData.Tests.Binding -{ +{ - public class DeeplyNestedNotifyPropertyChangedFixture { [Fact] @@ -64,7 +63,7 @@ public void NotifiesInitialValueAndNullChild() instance.Child.Age = 26; result.Should().Be(26); instance.Child = null; - + } [Fact] @@ -182,7 +181,6 @@ private void StressIt() .Do(_ => sw.Stop()) .Subscribe(); - items[1].Child.Age=-1; Console.WriteLine($"{sw.ElapsedMilliseconds}"); } @@ -209,16 +207,36 @@ public ClassB Child public bool Equals(ClassA other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(_name, other._name) && Equals(_classB, other._classB); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ClassA) obj); } @@ -256,7 +274,6 @@ public override string ToString() } } - public class ClassB : AbstractNotifyPropertyChanged, IEquatable { private int _age; @@ -271,16 +288,36 @@ public int Age public bool Equals(ClassB other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return _age == other._age; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ClassB) obj); } diff --git a/src/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs b/src/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs index ba818b2f7..532e24a18 100644 --- a/src/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs +++ b/src/DynamicData.Tests/Binding/NotifyPropertyChangedExFixture.cs @@ -95,6 +95,7 @@ public void SubscribeToProperyChangedOnASingleItem( bool notifyOnInitialValue) lastChange.Sender.Should().BeNull(); (-1).Should().Be(lastChange.Value); } + person.Age = 12; person.Should().Be(lastChange.Sender); 12.Should().Be(lastChange.Value); @@ -103,6 +104,5 @@ public void SubscribeToProperyChangedOnASingleItem( bool notifyOnInitialValue) 13.Should().Be(lastChange.Value); } - } } \ No newline at end of file diff --git a/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs index d09fb922f..791d6928c 100644 --- a/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs +++ b/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Binding { - + public class ObservableCollectionBindCacheFixture: IDisposable { private readonly ObservableCollectionExtended _collection = new ObservableCollectionExtended(); diff --git a/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs index 822292604..00083606b 100644 --- a/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs +++ b/src/DynamicData.Tests/Binding/ObservableCollectionBindCacheSortedFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.Binding { - + public class ObservableCollectionBindCacheSortedFixture: IDisposable { private readonly ObservableCollectionExtended _collection = new ObservableCollectionExtended(); @@ -122,7 +122,9 @@ public void SmallChangeDoesNotInvokeReset() { invoked = true; if (e.Action == NotifyCollectionChangedAction.Reset) + { resetInvoked = true; + } }; _source.AddOrUpdate(_generator.Take(24)); @@ -145,7 +147,6 @@ public void TreatMovesAsRemoveAdd() var boundList1 = new ObservableCollectionExtended(); var boundList2 = new ObservableCollectionExtended(); - using (cache.Connect() .AutoRefresh(p => p.Age) .Sort(SortExpressionComparer.Ascending(p => p.Age)) @@ -162,7 +163,6 @@ public void TreatMovesAsRemoveAdd() importantGuy.Age = importantGuy.Age + 200; - latestSetWithoutMoves.Removes.Should().Be(1); latestSetWithoutMoves.Adds.Should().Be(1); latestSetWithoutMoves.Moves.Should().Be(0); diff --git a/src/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs index bffb42b5c..c2b977653 100644 --- a/src/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs +++ b/src/DynamicData.Tests/Binding/ObservableCollectionBindListFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Binding { - + public class ObservableCollectionBindListFixture: IDisposable { private readonly ObservableCollectionExtended _collection = new ObservableCollectionExtended(); diff --git a/src/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs index 7b1b51029..45dff8f4e 100644 --- a/src/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs +++ b/src/DynamicData.Tests/Binding/ObservableCollectionExtendedToChangeSetFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Binding { - + public class ObservableCollectionExtendedToChangeSetFixture: IDisposable { private readonly ObservableCollectionExtended _collection; diff --git a/src/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs b/src/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs index 8db5d37a4..df9a909e7 100644 --- a/src/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs +++ b/src/DynamicData.Tests/Binding/ObservableCollectionToChangeSetFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Binding { - + public class ObservableCollectionToChangeSetFixture: IDisposable { private readonly TestObservableCollection _collection; diff --git a/src/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs b/src/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs index d9947eb1c..593174435 100644 --- a/src/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs +++ b/src/DynamicData.Tests/Binding/ReadOnlyObservableCollectionToChangeSetFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Binding { - + public class ReadOnlyObservableCollectionToChangeSetFixture: IDisposable { private readonly TestObservableCollection _collection; diff --git a/src/DynamicData.Tests/Cache/AndFixture.cs b/src/DynamicData.Tests/Cache/AndFixture.cs index d9f2b9100..a5b3257cf 100644 --- a/src/DynamicData.Tests/Cache/AndFixture.cs +++ b/src/DynamicData.Tests/Cache/AndFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class AndFixture : AndFixtureBase { protected override IObservable> CreateObservable() @@ -16,7 +16,6 @@ protected override IObservable> CreateObservable() } } - public class AndCollectionFixture : AndFixtureBase { protected override IObservable> CreateObservable() @@ -26,7 +25,6 @@ protected override IObservable> CreateObservable() } } - public abstract class AndFixtureBase: IDisposable { protected ISourceCache _source1; diff --git a/src/DynamicData.Tests/Cache/AutoRefreshFixture.cs b/src/DynamicData.Tests/Cache/AutoRefreshFixture.cs index cf35a5de2..7c4dda6a5 100644 --- a/src/DynamicData.Tests/Cache/AutoRefreshFixture.cs +++ b/src/DynamicData.Tests/Cache/AutoRefreshFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.Cache { - + public class AutoRefreshFixture { [Fact] diff --git a/src/DynamicData.Tests/Cache/BatchFixture.cs b/src/DynamicData.Tests/Cache/BatchFixture.cs index 6a697af8e..791640dac 100644 --- a/src/DynamicData.Tests/Cache/BatchFixture.cs +++ b/src/DynamicData.Tests/Cache/BatchFixture.cs @@ -6,14 +6,13 @@ namespace DynamicData.Tests.Cache { - + public class BatchFixture: IDisposable { private readonly ISourceCache _source; private readonly ChangeSetAggregator _results; private readonly TestScheduler _scheduler; - public BatchFixture() { _scheduler = new TestScheduler(); diff --git a/src/DynamicData.Tests/Cache/BatchIfFixture.cs b/src/DynamicData.Tests/Cache/BatchIfFixture.cs index eee966dc6..209f5da88 100644 --- a/src/DynamicData.Tests/Cache/BatchIfFixture.cs +++ b/src/DynamicData.Tests/Cache/BatchIfFixture.cs @@ -24,8 +24,6 @@ public BatchIfFixture() // _results = _source.Connect().BatchIf(new BehaviorSubject(true), scheduler: _scheduler).AsAggregator(); } - - public void Dispose() { _results.Dispose(); @@ -38,7 +36,6 @@ public void Dispose() [Fact] public void ChangesNotLostIfConsumerIsRunningOnDifferentThread() { - var producerScheduler = new TestScheduler(); var consumerScheduler = new TestScheduler(); diff --git a/src/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs b/src/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs index c3b4f72ad..5e2364ba2 100644 --- a/src/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs +++ b/src/DynamicData.Tests/Cache/BatchIfWithTimeOutFixture.cs @@ -82,7 +82,6 @@ public void Timeout() } } - public void Dispose() { _source.Dispose(); diff --git a/src/DynamicData.Tests/Cache/BufferInitialFixture.cs b/src/DynamicData.Tests/Cache/BufferInitialFixture.cs index 494315bcd..2285e80be 100644 --- a/src/DynamicData.Tests/Cache/BufferInitialFixture.cs +++ b/src/DynamicData.Tests/Cache/BufferInitialFixture.cs @@ -21,13 +21,15 @@ public void BufferInitial() using (var aggregator = cache.Connect().BufferInitial(TimeSpan.FromSeconds(1), scheduler).AsAggregator()) { foreach (var item in People) + { cache.AddOrUpdate(item); + } aggregator.Data.Count.Should().Be(0); aggregator.Messages.Count.Should().Be(0); scheduler.Start(); - + aggregator.Data.Count.Should().Be(10_000); aggregator.Messages.Count.Should().Be(1); diff --git a/src/DynamicData.Tests/Cache/ChangesReducerFixture.cs b/src/DynamicData.Tests/Cache/ChangesReducerFixture.cs index 0e2f2bd5d..f27715fc6 100644 --- a/src/DynamicData.Tests/Cache/ChangesReducerFixture.cs +++ b/src/DynamicData.Tests/Cache/ChangesReducerFixture.cs @@ -22,7 +22,6 @@ public class ChangesReducerFixture new Change(ChangeReason.Refresh, _testEntity.Key, _testEntity, _testIndex) }; - [Theory] [MemberData(nameof(ConstrainFirstValue), new object[] {ChangeReason.Refresh, new ChangeReason[] {}})] public void RefreshIsBeingOverridenByAnything(Change refresh, Change other) @@ -39,7 +38,6 @@ public void RemoveOverridesAnythingButAdd(Change remove, Change< result.Value.Should().Be(remove); } - [Theory] [MemberData(nameof(GetChanges))] public void NoneGetsOverridenByAnything(Change c) @@ -69,6 +67,7 @@ public void RemoveAndAddProduceUpdate() result.Value.Should().Be(expected); } + [Fact] public void AddAndUpdateProduceAdd() { @@ -111,7 +110,6 @@ public static IEnumerable ConstrainFirstValue(ChangeReason constraint, new object[] {constrainedValue, other}); } - // ReSharper disable once MemberCanBePrivate.Global public static IEnumerable GetChanges() { diff --git a/src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs b/src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs index 756f6f925..47a6d2071 100644 --- a/src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs +++ b/src/DynamicData.Tests/Cache/DeferUntilLoadedFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class DeferAnsdSkipFixture { [Fact] diff --git a/src/DynamicData.Tests/Cache/DisposeManyFixture.cs b/src/DynamicData.Tests/Cache/DisposeManyFixture.cs index 53559c853..bd31b2f70 100644 --- a/src/DynamicData.Tests/Cache/DisposeManyFixture.cs +++ b/src/DynamicData.Tests/Cache/DisposeManyFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Cache { - + public class DisposeManyFixture: IDisposable { private class DisposableObject : IDisposable diff --git a/src/DynamicData.Tests/Cache/DistinctFixture.cs b/src/DynamicData.Tests/Cache/DistinctFixture.cs index 5a392ec3b..f3e57e3af 100644 --- a/src/DynamicData.Tests/Cache/DistinctFixture.cs +++ b/src/DynamicData.Tests/Cache/DistinctFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class DistinctFixture: IDisposable { private readonly ISourceCache _source; @@ -89,7 +89,6 @@ public void BreakWithLoadsOfUpdates() updater.AddOrUpdate(new Person("Person1", 1)); updater.AddOrUpdate(new Person("Person2", 12)); - updater.AddOrUpdate(new Person("Person3", 13)); updater.AddOrUpdate(new Person("Person4", 14)); }); diff --git a/src/DynamicData.Tests/Cache/DynamicAndFixture.cs b/src/DynamicData.Tests/Cache/DynamicAndFixture.cs index 586761244..e1d517278 100644 --- a/src/DynamicData.Tests/Cache/DynamicAndFixture.cs +++ b/src/DynamicData.Tests/Cache/DynamicAndFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class DynamicAndFixture: IDisposable { private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); diff --git a/src/DynamicData.Tests/Cache/DynamicExceptFixture.cs b/src/DynamicData.Tests/Cache/DynamicExceptFixture.cs index 61fc0a925..b05f6c883 100644 --- a/src/DynamicData.Tests/Cache/DynamicExceptFixture.cs +++ b/src/DynamicData.Tests/Cache/DynamicExceptFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class DynamicExceptFixture: IDisposable { private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); diff --git a/src/DynamicData.Tests/Cache/DynamicOrFixture.cs b/src/DynamicData.Tests/Cache/DynamicOrFixture.cs index 97e9549b5..420ecda51 100644 --- a/src/DynamicData.Tests/Cache/DynamicOrFixture.cs +++ b/src/DynamicData.Tests/Cache/DynamicOrFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class DynamicOrFixture: IDisposable { private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); diff --git a/src/DynamicData.Tests/Cache/DynamicXorFixture.cs b/src/DynamicData.Tests/Cache/DynamicXorFixture.cs index e08b5e8c1..32bd9f24c 100644 --- a/src/DynamicData.Tests/Cache/DynamicXorFixture.cs +++ b/src/DynamicData.Tests/Cache/DynamicXorFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class DynamicXorFixture: IDisposable { private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); diff --git a/src/DynamicData.Tests/Cache/EditDiffFixture.cs b/src/DynamicData.Tests/Cache/EditDiffFixture.cs index e84103bcd..99c9c132f 100644 --- a/src/DynamicData.Tests/Cache/EditDiffFixture.cs +++ b/src/DynamicData.Tests/Cache/EditDiffFixture.cs @@ -7,13 +7,12 @@ namespace DynamicData.Tests.Cache { - + public class EditDiffFixture: IDisposable { private readonly SourceCache _cache; private readonly ChangeSetAggregator _result; - public EditDiffFixture() { _cache = new SourceCache(p => p.Name); @@ -27,8 +26,6 @@ public void Dispose() _result.Dispose(); } - - [Fact] public void New() { @@ -86,7 +83,6 @@ public void Removes() _cache.Items.ShouldAllBeEquivalentTo(newList); } - [Fact] public void VariousChanges() { @@ -162,7 +158,6 @@ public void Removes_WithEqualityComparer() _cache.Items.ShouldAllBeEquivalentTo(newList); } - [Fact] public void VariousChanges_WithEqualityComparer() { diff --git a/src/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs index 2e6979ca9..6b13dcf37 100644 --- a/src/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs +++ b/src/DynamicData.Tests/Cache/EnumerableObservableToObservableChangeSetFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.Cache { - + public class EnumerableObservableToObservableChangeSetFixture { [Fact] diff --git a/src/DynamicData.Tests/Cache/ExceptFixture.cs b/src/DynamicData.Tests/Cache/ExceptFixture.cs index 0d9d2691f..2e023d42f 100644 --- a/src/DynamicData.Tests/Cache/ExceptFixture.cs +++ b/src/DynamicData.Tests/Cache/ExceptFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class ExceptFixture : ExceptFixtureBase { protected override IObservable> CreateObservable() @@ -15,7 +15,6 @@ protected override IObservable> CreateObservable() } } - public class ExceptCollectionFixture : ExceptFixtureBase { protected override IObservable> CreateObservable() @@ -25,7 +24,6 @@ protected override IObservable> CreateObservable() } } - public abstract class ExceptFixtureBase: IDisposable { protected ISourceCache _targetSource; diff --git a/src/DynamicData.Tests/Cache/ExpireAfterFixture.cs b/src/DynamicData.Tests/Cache/ExpireAfterFixture.cs index 2780c5f76..0613667cd 100644 --- a/src/DynamicData.Tests/Cache/ExpireAfterFixture.cs +++ b/src/DynamicData.Tests/Cache/ExpireAfterFixture.cs @@ -7,14 +7,13 @@ namespace DynamicData.Tests.Cache { - + public class ExpireAfterFixture: IDisposable { private readonly ISourceCache _source; private readonly ChangeSetAggregator _results; private readonly TestScheduler _scheduler; - public ExpireAfterFixture() { _scheduler = new TestScheduler(); @@ -34,10 +33,15 @@ public void ComplexRemove() TimeSpan? RemoveFunc(Person t) { if (t.Age <= 40) + { return TimeSpan.FromSeconds(5); + } if (t.Age <= 80) + { return TimeSpan.FromSeconds(7); + } + return null; } diff --git a/src/DynamicData.Tests/Cache/FilterControllerFixture.cs b/src/DynamicData.Tests/Cache/FilterControllerFixture.cs index 7ce112eac..ad03255d3 100644 --- a/src/DynamicData.Tests/Cache/FilterControllerFixture.cs +++ b/src/DynamicData.Tests/Cache/FilterControllerFixture.cs @@ -9,14 +9,13 @@ namespace DynamicData.Tests.Cache { - + public class FilterControllerFixture: IDisposable { private readonly ISourceCache _source; private readonly ChangeSetAggregator _results; private readonly ISubject> _filter; - public FilterControllerFixture() { _source = new SourceCache(p => p.Key); @@ -109,6 +108,7 @@ public void ReevaluateFilter() { person.Age = person.Age + 10; } + _filter.OnNext(p => p.Age > 20); _results.Data.Count.Should().Be(90, "Should be 90 people in the cache"); @@ -119,6 +119,7 @@ public void ReevaluateFilter() { person.Age = person.Age - 10; } + _filter.OnNext(p => p.Age > 20); _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); @@ -126,7 +127,6 @@ public void ReevaluateFilter() _results.Messages[2].Removes.Should().Be(10, "Should be 10 removes in the third message"); } - #region Static filter tests /* Should be the same as standard lambda filter */ diff --git a/src/DynamicData.Tests/Cache/FilterFixture.cs b/src/DynamicData.Tests/Cache/FilterFixture.cs index 9ec484d30..0a466706e 100644 --- a/src/DynamicData.Tests/Cache/FilterFixture.cs +++ b/src/DynamicData.Tests/Cache/FilterFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class FilterFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs b/src/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs index cfbeed9cd..fa4144024 100644 --- a/src/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs +++ b/src/DynamicData.Tests/Cache/FilterOnPropertyFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class FilterOnPropertyFixture { [Fact] @@ -74,7 +74,6 @@ private class FilterPropertyStub : IDisposable public ISourceCache Source { get; } = new SourceCache(p => p.Name); public ChangeSetAggregator Results { get; } - public FilterPropertyStub() { Results = new ChangeSetAggregator diff --git a/src/DynamicData.Tests/Cache/ForEachChangeFixture.cs b/src/DynamicData.Tests/Cache/ForEachChangeFixture.cs index a8b9384f6..7b120c066 100644 --- a/src/DynamicData.Tests/Cache/ForEachChangeFixture.cs +++ b/src/DynamicData.Tests/Cache/ForEachChangeFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class ForEachChangeFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/FromAsyncFixture.cs b/src/DynamicData.Tests/Cache/FromAsyncFixture.cs index f13445aa5..773443ef3 100644 --- a/src/DynamicData.Tests/Cache/FromAsyncFixture.cs +++ b/src/DynamicData.Tests/Cache/FromAsyncFixture.cs @@ -10,7 +10,7 @@ namespace DynamicData.Tests.Cache { - + public class FromAsyncFixture { public TestScheduler Scheduler { get; } @@ -81,7 +81,6 @@ Task> Loader() //var subscribed = data.Connect() // - error.Should().NotBeNull(); } diff --git a/src/DynamicData.Tests/Cache/FullJoinFixture.cs b/src/DynamicData.Tests/Cache/FullJoinFixture.cs index 0bb18b4d6..7004353a2 100644 --- a/src/DynamicData.Tests/Cache/FullJoinFixture.cs +++ b/src/DynamicData.Tests/Cache/FullJoinFixture.cs @@ -19,14 +19,6 @@ private class Address public int PersonId { get; } } - private class PersonWithAddress - { - public PersonWithAddress(Person person, Address address) - { - } - } - - private readonly SourceCache _left; private readonly SourceCache _right; private readonly ChangeSetAggregator _result; @@ -60,7 +52,6 @@ public void AddLeftOnly() _result.Data.Items.All(dwm => dwm.Device != Optional.None).Should().BeTrue(); } - [Fact] public void AddRightOnly() { @@ -79,7 +70,6 @@ public void AddRightOnly() _result.Data.Items.All(dwm => dwm.Device == Optional.None).Should().BeTrue(); } - [Fact] public void AddLetThenRight() { @@ -127,14 +117,12 @@ public void RemoveVarious() 3.Should().Be(_result.Data.Count); 2.Should().Be(_result.Data.Items.Count(dwm => dwm.MetaData != Optional.None)); - _left.Remove("Device1"); _result.Data.Lookup("Device1").HasValue.Should().BeTrue(); _result.Data.Lookup("Device2").HasValue.Should().BeTrue(); _result.Data.Lookup("Device3").HasValue.Should().BeTrue(); } - [Fact] public void AddRightThenLeft() { @@ -145,7 +133,6 @@ public void AddRightThenLeft() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -168,7 +155,6 @@ public void UpdateRight() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -188,7 +174,6 @@ public void Dispose() _result.Dispose(); } - public class Device : IEquatable { public string Name { get; } @@ -202,16 +187,36 @@ public Device(string name) public bool Equals(Device other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Device)obj); } @@ -254,16 +259,36 @@ public DeviceMetaData(string name, bool isAutoConnect = false) public bool Equals(DeviceMetaData other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name) && IsAutoConnect == other.IsAutoConnect; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceMetaData)obj); } @@ -293,7 +318,6 @@ public override string ToString() } } - public class DeviceWithMetadata : IEquatable { public string Key { get; } @@ -311,16 +335,36 @@ public DeviceWithMetadata(string key, Optional device, Optional p.Name); @@ -158,7 +153,6 @@ private void AssertDataIsCorrectlyFormed(Person[] allPeople) }).ToArray(); - _result.Data.Count.Should().Be(all.Length); all.ForEach(parentAndChild => @@ -169,18 +163,22 @@ private void AssertDataIsCorrectlyFormed(Person[] allPeople) }); } - - private int CalculateParent(int index, int totalPeople) { if (index < 5) + { return 11; + } if (index == totalPeople - 1) + { return 1; + } if (index == totalPeople) + { return 1; + } return index + 1; } diff --git a/src/DynamicData.Tests/Cache/GroupControllerFixture.cs b/src/DynamicData.Tests/Cache/GroupControllerFixture.cs index 4bde77ae8..a3d0a475d 100644 --- a/src/DynamicData.Tests/Cache/GroupControllerFixture.cs +++ b/src/DynamicData.Tests/Cache/GroupControllerFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class GroupControllerFixture: IDisposable { private enum AgeBracket @@ -20,7 +20,11 @@ private enum AgeBracket private readonly Func _grouper = p => { - if (p.Age <= 19) return AgeBracket.Under20; + if (p.Age <= 19) + { + return AgeBracket.Under20; + } + return p.Age <= 60 ? AgeBracket.Adult : AgeBracket.Pensioner; }; @@ -28,22 +32,19 @@ private enum AgeBracket private readonly ISubject _refresher; private readonly IObservableCache, AgeBracket> _grouped; - - public GroupControllerFixture() { _source = new SourceCache(p => p.Name); _refresher =new Subject(); _grouped = _source.Connect().Group(_grouper, _refresher).AsObservableCache(); } - + public void Dispose() { _source?.Dispose(); _grouped?.Dispose(); } - [Fact] public void RegroupRecaluatesGroupings() { @@ -117,7 +118,10 @@ public void RegroupRecaluatesGroupings2() private bool IsContainedIn(string name, AgeBracket bracket) { var group = _grouped.Lookup(bracket); - if (!group.HasValue) return false; + if (!group.HasValue) + { + return false; + } return group.Value.Cache.Lookup(name).HasValue; } diff --git a/src/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs b/src/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs index 738f3512d..56a5b3f27 100644 --- a/src/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs +++ b/src/DynamicData.Tests/Cache/GroupControllerForFilteredItemsFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class GroupControllerForFilteredItemsFixture: IDisposable { private enum AgeBracket @@ -20,7 +20,11 @@ private enum AgeBracket private readonly Func _grouper = p => { - if (p.Age <= 19) return AgeBracket.Under20; + if (p.Age <= 19) + { + return AgeBracket.Under20; + } + return p.Age <= 60 ? AgeBracket.Adult : AgeBracket.Pensioner; }; @@ -35,15 +39,13 @@ public GroupControllerForFilteredItemsFixture() _grouped = _source.Connect(p => _grouper(p) != AgeBracket.Pensioner) .Group(_grouper, _refreshSubject).AsObservableCache(); } - + public void Dispose() { _source.Dispose(); _grouped.Dispose(); } - - [Fact] public void RegroupRecaluatesGroupings() { @@ -112,7 +114,10 @@ public void RegroupRecaluatesGroupings2() private bool IsContainedIn(string name, AgeBracket bracket) { var group = _grouped.Lookup(bracket); - if (!group.HasValue) return false; + if (!group.HasValue) + { + return false; + } return group.Value.Cache.Lookup(name).HasValue; } @@ -131,6 +136,5 @@ private bool IsNotContainedAnyWhere(string name) return person.Count == 0; } - } } diff --git a/src/DynamicData.Tests/Cache/GroupFixture.cs b/src/DynamicData.Tests/Cache/GroupFixture.cs index 96a9e9d04..664de4d02 100644 --- a/src/DynamicData.Tests/Cache/GroupFixture.cs +++ b/src/DynamicData.Tests/Cache/GroupFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class GroupFixture: IDisposable { public GroupFixture() @@ -109,6 +109,7 @@ public void FiresManyValueForBatchOfDifferentAdds() { update.Reason.Should().Be(ChangeReason.Add); } + called = true; }); _source.Edit(updater => @@ -161,6 +162,7 @@ public void FiresRemoveWhenEmptied() { update.Reason.Should().Be(ChangeReason.Remove); } + called = true; }); var person = new Person("Person1", 20); @@ -210,7 +212,6 @@ public void AddItemAfterUpdateItemProcessAdd() firstGroup.Entries.Count.Should().Be(2); - subscriber.Dispose(); } @@ -239,7 +240,6 @@ public void UpdateItemAfterAddItemProcessAdd() firstGroup.Entries.Count.Should().Be(2); - subscriber.Dispose(); } diff --git a/src/DynamicData.Tests/Cache/GroupImmutableFixture.cs b/src/DynamicData.Tests/Cache/GroupImmutableFixture.cs index 071100752..23225dc79 100644 --- a/src/DynamicData.Tests/Cache/GroupImmutableFixture.cs +++ b/src/DynamicData.Tests/Cache/GroupImmutableFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class GroupImmutableFixture: IDisposable { private readonly ISourceCache _source; @@ -25,8 +25,6 @@ public void Dispose() _results.Dispose(); } - - [Fact] public void Add() { @@ -159,7 +157,9 @@ public void Reevaluate() //do an inline update foreach (var person in initialPeople) + { person.Age = person.Age + 1; + } //signal operators to evaluate again _source.Refresh(); diff --git a/src/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs b/src/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs index c5b06af06..6161397a2 100644 --- a/src/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs +++ b/src/DynamicData.Tests/Cache/GroupOnPropertyFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class GroupOnPropertyFixture: IDisposable { private readonly SourceCache _source; @@ -88,7 +88,6 @@ public void CanHandleChangedItemsBatch() people.Take(25) .ForEach(p => p.Age = 200); - var changedCount = people.Select(p => p.Age).Distinct().Count(); _results.Data.Count.Should().Be(changedCount); diff --git a/src/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs b/src/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs index 3ddf5d0d0..ef8a0e8da 100644 --- a/src/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs +++ b/src/DynamicData.Tests/Cache/GroupOnPropertyWithImmutableStateFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class GroupOnPropertyWithImmutableStateFixture: IDisposable { private readonly SourceCache _source; @@ -88,7 +88,6 @@ public void CanHandleChangedItemsBatch() people.Take(25) .ForEach(p => p.Age = 200); - var changedCount = people.Select(p => p.Age).Distinct().Count(); _results.Data.Count.Should().Be(changedCount); diff --git a/src/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs b/src/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs index 686c2a98d..4780c0e92 100644 --- a/src/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs +++ b/src/DynamicData.Tests/Cache/IgnoreUpdateFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Cache { - + public class IgnoreUpdateFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/IncludeUpdateFixture.cs b/src/DynamicData.Tests/Cache/IncludeUpdateFixture.cs index 84a7ef7d0..9099eacae 100644 --- a/src/DynamicData.Tests/Cache/IncludeUpdateFixture.cs +++ b/src/DynamicData.Tests/Cache/IncludeUpdateFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Cache { - + public class IncludeUpdateFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/InnerJoinFixture.cs b/src/DynamicData.Tests/Cache/InnerJoinFixture.cs index 0484b05c2..109d1f93b 100644 --- a/src/DynamicData.Tests/Cache/InnerJoinFixture.cs +++ b/src/DynamicData.Tests/Cache/InnerJoinFixture.cs @@ -12,7 +12,6 @@ public class InnerJoinFixture: IDisposable private SourceCache _right; private ChangeSetAggregator _result; - public InnerJoinFixture() { _left = new SourceCache(device => device.Name); @@ -23,8 +22,6 @@ public InnerJoinFixture() .AsAggregator(); } - - public void Dispose() { _left?.Dispose(); @@ -48,7 +45,6 @@ public void AddLeftOnly() _result.Data.Lookup("Device3").HasValue.Should().BeFalse(); } - [Fact] public void AddRightOnly() { @@ -65,7 +61,6 @@ public void AddRightOnly() _result.Data.Lookup("Device3").HasValue.Should().BeFalse(); } - [Fact] public void AddLetThenRight() { @@ -113,7 +108,6 @@ public void RemoveVarious() 2.Should().Be(_result.Data.Count); - _left.Remove("Device1"); 1.Should().Be(_result.Data.Count); _result.Data.Lookup("Device1").HasValue.Should().BeFalse(); @@ -121,7 +115,6 @@ public void RemoveVarious() _result.Data.Lookup("Device3").HasValue.Should().BeFalse(); } - [Fact] public void AddRightThenLeft() { @@ -132,7 +125,6 @@ public void AddRightThenLeft() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -153,7 +145,6 @@ public void UpdateRight() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -164,7 +155,6 @@ public void UpdateRight() 3.Should().Be(_result.Data.Count); } - public class Device : IEquatable { public string Name { get; } @@ -178,16 +168,36 @@ public Device(string name) public bool Equals(Device other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Device)obj); } @@ -230,16 +240,36 @@ public DeviceMetaData(string name, bool isAutoConnect = false) public bool Equals(DeviceMetaData other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name) && IsAutoConnect == other.IsAutoConnect; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceMetaData)obj); } @@ -269,7 +299,6 @@ public override string ToString() } } - public class DeviceWithMetadata : IEquatable { public string Key { get; } @@ -287,16 +316,36 @@ public DeviceWithMetadata(string key, Device device, DeviceMetaData metaData) public bool Equals(DeviceWithMetadata other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Key, other.Key); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceWithMetadata)obj); } diff --git a/src/DynamicData.Tests/Cache/InnerJoinManyFixture.cs b/src/DynamicData.Tests/Cache/InnerJoinManyFixture.cs index 86105c0bc..2e1febdcc 100644 --- a/src/DynamicData.Tests/Cache/InnerJoinManyFixture.cs +++ b/src/DynamicData.Tests/Cache/InnerJoinManyFixture.cs @@ -21,14 +21,12 @@ public InnerJoinManyFixture() .AsAggregator(); } - public void Dispose() { _people.Dispose(); _result.Dispose(); } - [Fact] public void AddLeftOnly() { @@ -41,7 +39,6 @@ public void AddLeftOnly() _result.Data.Count.Should().Be(0); } - [Fact] public void AddPeopleWithParents() { @@ -123,7 +120,6 @@ public void AddChild() AssertDataIsCorrectlyFormed(updatedPeople); } - [Fact] public void RemoveChild() { @@ -145,19 +141,20 @@ public void RemoveChild() AssertDataIsCorrectlyFormed(updatedPeople, last.Name); } - private void AssertDataIsCorrectlyFormed(Person[] allPeople, params string[] missingParents) { var grouped = allPeople.GroupBy(p => p.ParentName ) .Where(p => p.Any() && !missingParents.Contains(p.Key)) .AsArray(); - _result.Data.Count.Should().Be(grouped.Length); grouped.ForEach(grouping => { - if (missingParents.Length > 0 && missingParents.Contains(grouping.Key)) return; + if (missingParents.Length > 0 && missingParents.Contains(grouping.Key)) + { + return; + } var result = _result.Data.Lookup(grouping.Key) .ValueOrThrow(() => new Exception("Missing result for " + grouping.Key)); @@ -167,18 +164,22 @@ private void AssertDataIsCorrectlyFormed(Person[] allPeople, params string[] mis }); } - - private int CalculateParent(int index, int totalPeople) { if (index < 5) + { return 10; + } if (index == totalPeople - 1) + { return 1; + } if (index == totalPeople) + { return 1; + } return index + 1; } diff --git a/src/DynamicData.Tests/Cache/LeftJoinFixture.cs b/src/DynamicData.Tests/Cache/LeftJoinFixture.cs index a7e782ebc..4cf8eaea0 100644 --- a/src/DynamicData.Tests/Cache/LeftJoinFixture.cs +++ b/src/DynamicData.Tests/Cache/LeftJoinFixture.cs @@ -22,7 +22,6 @@ public LeftJoinFixture() .AsAggregator(); } - public void Dispose() { _left.Dispose(); @@ -48,7 +47,6 @@ public void AddLeftOnly() _result.Data.Items.All(dwm => dwm.MetaData == Optional.None).Should().BeTrue(); } - [Fact] public void AddRightOnly() { @@ -62,7 +60,6 @@ public void AddRightOnly() 0.Should().Be(_result.Data.Count); } - [Fact] public void AddLetThenRight() { @@ -107,12 +104,10 @@ public void RemoveVarious() 3.Should().Be(_result.Data.Count); 2.Should().Be(_result.Data.Items.Count(dwm => dwm.MetaData != Optional.None)); - _left.Remove("Device1"); _result.Data.Lookup("Device1").HasValue.Should().BeFalse(); } - [Fact] public void AddRightThenLeft() { @@ -123,7 +118,6 @@ public void AddRightThenLeft() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -146,7 +140,6 @@ public void UpdateRight() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -159,9 +152,6 @@ public void UpdateRight() _result.Data.Items.All(dwm => dwm.MetaData != Optional.None).Should().BeTrue(); } - - - public class Device : IEquatable { public string Name { get; } @@ -175,16 +165,36 @@ public Device(string name) public bool Equals(Device other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Device)obj); } @@ -227,16 +237,36 @@ public DeviceMetaData(string name, bool isAutoConnect = false) public bool Equals(DeviceMetaData other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name) && IsAutoConnect == other.IsAutoConnect; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceMetaData)obj); } @@ -266,7 +296,6 @@ public override string ToString() } } - public class DeviceWithMetadata : IEquatable { public Device Device { get; } @@ -282,16 +311,36 @@ public DeviceWithMetadata(Device device, Optional metaData) public bool Equals(DeviceWithMetadata other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Equals(Device, other.Device) && MetaData.Equals(other.MetaData); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceWithMetadata)obj); } diff --git a/src/DynamicData.Tests/Cache/LeftJoinManyFixture.cs b/src/DynamicData.Tests/Cache/LeftJoinManyFixture.cs index 0a4b4580e..8595ef600 100644 --- a/src/DynamicData.Tests/Cache/LeftJoinManyFixture.cs +++ b/src/DynamicData.Tests/Cache/LeftJoinManyFixture.cs @@ -12,7 +12,6 @@ public class LeftJoinManyFixture: IDisposable private readonly SourceCache _people; private readonly ChangeSetAggregator _result; - public LeftJoinManyFixture() { _people = new SourceCache(p => p.Name); @@ -22,14 +21,12 @@ public LeftJoinManyFixture() .AsAggregator(); } - public void Dispose() { _people.Dispose(); _result.Dispose(); } - [Fact] public void AddLeftOnly() { @@ -45,7 +42,6 @@ public void AddLeftOnly() _result.Data.Items.ForEach(pac => { pac.Count.Should().Be(0); }); } - [Fact] public void AddPeopleWithParents() { @@ -127,7 +123,6 @@ public void AddChild() AssertDataIsCorrectlyFormed(updatedPeople); } - [Fact] public void RemoveChild() { @@ -149,7 +144,6 @@ public void RemoveChild() AssertDataIsCorrectlyFormed(updatedPeople, last.Name); } - private void AssertDataIsCorrectlyFormed(Person[] expected, params string[] missingParents) { _result.Data.Count.Should().Be(expected.Length); @@ -158,7 +152,10 @@ private void AssertDataIsCorrectlyFormed(Person[] expected, params string[] miss expected.GroupBy(p => p.ParentName) .ForEach(grouping => { - if (missingParents.Length > 0 && missingParents.Contains(grouping.Key)) return; + if (missingParents.Length > 0 && missingParents.Contains(grouping.Key)) + { + return; + } var result = _result.Data.Lookup(grouping.Key) .ValueOrThrow(() => new Exception("Missing result for " + grouping.Key)); @@ -171,13 +168,19 @@ private void AssertDataIsCorrectlyFormed(Person[] expected, params string[] miss private int CalculateParent(int index, int totalPeople) { if (index < 5) + { return 10; + } if (index == totalPeople - 1) + { return 1; + } if (index == totalPeople) + { return 1; + } return index + 1; } diff --git a/src/DynamicData.Tests/Cache/MergeManyFixture.cs b/src/DynamicData.Tests/Cache/MergeManyFixture.cs index 12f1473b9..1b97e2022 100644 --- a/src/DynamicData.Tests/Cache/MergeManyFixture.cs +++ b/src/DynamicData.Tests/Cache/MergeManyFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class MergeManyFixture: IDisposable { private class ObjectWithObservable diff --git a/src/DynamicData.Tests/Cache/MergeManyItemsFixture.cs b/src/DynamicData.Tests/Cache/MergeManyItemsFixture.cs index 4f4f7ff67..68b9c448a 100644 --- a/src/DynamicData.Tests/Cache/MergeManyItemsFixture.cs +++ b/src/DynamicData.Tests/Cache/MergeManyItemsFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class MergeManyItemsFixture: IDisposable { private class ObjectWithObservable diff --git a/src/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs b/src/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs index 5628df5ba..0798499ac 100644 --- a/src/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs +++ b/src/DynamicData.Tests/Cache/MergeManyWithKeyOverloadFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class MergeManyWithKeyOverloadFixture: IDisposable { private class ObjectWithObservable diff --git a/src/DynamicData.Tests/Cache/MonitorStatusFixture.cs b/src/DynamicData.Tests/Cache/MonitorStatusFixture.cs index b301d9ce4..95cfca7f5 100644 --- a/src/DynamicData.Tests/Cache/MonitorStatusFixture.cs +++ b/src/DynamicData.Tests/Cache/MonitorStatusFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class MonitorStatusFixture { [Fact] diff --git a/src/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs index 154160997..60dcc8787 100644 --- a/src/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs +++ b/src/DynamicData.Tests/Cache/ObservableChangeSetFixture.cs @@ -75,7 +75,6 @@ public void LoadsAndDisposeUsingActionAsync() isDisposed.Should().BeTrue(); } - [Fact] public void LoadsAndDisposeUsingDisposableAsync() { @@ -101,7 +100,7 @@ Task> Loader() { throw new Exception("Broken"); } - + var observable = ObservableChangeSet.Create(async cache => { var people = await Loader(); @@ -109,7 +108,6 @@ Task> Loader() return () => { }; }, p => p.Name); - using (var dervived = observable.AsObservableCache()) using (dervived.Connect().Subscribe(_ => { }, ex => error = ex )) { @@ -133,7 +131,6 @@ IEnumerable Loader() return () => { }; }, p => p.Name); - using (var dervived = observable.AsObservableCache()) using (dervived.Connect().Subscribe(_ => { }, ex => error = ex)) { @@ -141,8 +138,7 @@ IEnumerable Loader() } } - - private void SubscribeAndAssert(IObservable> observableChangeset, + private void SubscribeAndAssert(IObservable> observableChangeset, bool expectsError = false, Action> checkContentAction = null) { @@ -152,7 +148,7 @@ private void SubscribeAndAssert(IObservable complete = true).AsObservableCache()) using (cache.Connect().Subscribe(result => changes = result, ex => error = ex)) - { + { if (!expectsError) { error.Should().BeNull(); @@ -164,6 +160,7 @@ private void SubscribeAndAssert(IObservable> CreateObservable() @@ -16,7 +16,6 @@ protected override IObservable> CreateObservable() } } - public sealed class OrCollectionFixture : OrFixtureBase { protected override IObservable> CreateObservable() @@ -26,7 +25,6 @@ protected override IObservable> CreateObservable() } } - public abstract class OrFixtureBase : IDisposable { protected ISourceCache _source1; diff --git a/src/DynamicData.Tests/Cache/PageFixture.cs b/src/DynamicData.Tests/Cache/PageFixture.cs index 8508ba802..f2329b8fb 100644 --- a/src/DynamicData.Tests/Cache/PageFixture.cs +++ b/src/DynamicData.Tests/Cache/PageFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.Cache { - + public class PageFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs b/src/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs index 91fa77cf4..b4c9a5dfd 100644 --- a/src/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs +++ b/src/DynamicData.Tests/Cache/QueryWhenChangedFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Cache { - + public class QueryWhenChangedFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/RefCountFixture.cs b/src/DynamicData.Tests/Cache/RefCountFixture.cs index e475834b2..c80821ffc 100644 --- a/src/DynamicData.Tests/Cache/RefCountFixture.cs +++ b/src/DynamicData.Tests/Cache/RefCountFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class RefCountFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/RightJoinFixture.cs b/src/DynamicData.Tests/Cache/RightJoinFixture.cs index be5245372..6dc07ae80 100644 --- a/src/DynamicData.Tests/Cache/RightJoinFixture.cs +++ b/src/DynamicData.Tests/Cache/RightJoinFixture.cs @@ -35,7 +35,6 @@ public void AddLeftOnly() 0.Should().Be(_result.Data.Count); } - [Fact] public void AddRightOnly() { @@ -54,7 +53,6 @@ public void AddRightOnly() _result.Data.Items.All(dwm => dwm.Device == Optional.None).Should().BeTrue(); } - [Fact] public void AddLetThenRight() { @@ -99,12 +97,10 @@ public void RemoveVarious() 2.Should().Be(_result.Data.Count); 2.Should().Be(_result.Data.Items.Count(dwm => dwm.MetaData != Optional.None)); - _left.Remove("Device1"); _result.Data.Lookup("Device1").HasValue.Should().BeTrue(); } - [Fact] public void AddRightThenLeft() { @@ -115,7 +111,6 @@ public void AddRightThenLeft() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -138,7 +133,6 @@ public void UpdateRight() innerCache.AddOrUpdate(new DeviceMetaData("Device3")); }); - _left.Edit(innerCache => { innerCache.AddOrUpdate(new Device("Device1")); @@ -158,7 +152,6 @@ public void Dispose() _result.Dispose(); } - public class Device : IEquatable { public string Name { get; } @@ -172,16 +165,36 @@ public Device(string name) public bool Equals(Device other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Device)obj); } @@ -224,16 +237,36 @@ public DeviceMetaData(string name, bool isAutoConnect = false) public bool Equals(DeviceMetaData other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name) && IsAutoConnect == other.IsAutoConnect; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceMetaData)obj); } @@ -263,15 +296,12 @@ public override string ToString() } } - public class DeviceWithMetadata : IEquatable { public string Key { get; } public Optional Device { get; } public DeviceMetaData MetaData { get; } - - public DeviceWithMetadata(string key, Optional device, DeviceMetaData metaData) { Key = key; @@ -283,16 +313,36 @@ public DeviceWithMetadata(string key, Optional device, DeviceMetaData me public bool Equals(DeviceWithMetadata other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Key, other.Key); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((DeviceWithMetadata)obj); } diff --git a/src/DynamicData.Tests/Cache/RightJoinManyFixture.cs b/src/DynamicData.Tests/Cache/RightJoinManyFixture.cs index 452d74b0d..ec383a202 100644 --- a/src/DynamicData.Tests/Cache/RightJoinManyFixture.cs +++ b/src/DynamicData.Tests/Cache/RightJoinManyFixture.cs @@ -21,14 +21,12 @@ public RightJoinManyFixture() .AsAggregator(); } - public void Dispose() { _people.Dispose(); _result.Dispose(); } - [Fact] public void AddLeftOnly() { @@ -42,7 +40,6 @@ public void AddLeftOnly() _result.Data.Items.First().Parent.Should().BeNull(); } - [Fact] public void AddPeopleWithParents() { @@ -124,7 +121,6 @@ public void AddChild() AssertDataIsCorrectlyFormed(updatedPeople); } - [Fact] public void RemoveChild() { @@ -146,19 +142,20 @@ public void RemoveChild() AssertDataIsCorrectlyFormed(updatedPeople, last.Name); } - private void AssertDataIsCorrectlyFormed(Person[] allPeople, params string[] missingParents) { var grouped = allPeople.GroupBy(p => p.ParentName) .Where(p => p.Any() && !missingParents.Contains(p.Key)) .AsArray(); - _result.Data.Count.Should().Be(grouped.Length); grouped.ForEach(grouping => { - if (missingParents.Length > 0 && missingParents.Contains(grouping.Key)) return; + if (missingParents.Length > 0 && missingParents.Contains(grouping.Key)) + { + return; + } var result = _result.Data.Lookup(grouping.Key) .ValueOrThrow(() => new Exception("Missing result for " + grouping.Key)); @@ -168,18 +165,22 @@ private void AssertDataIsCorrectlyFormed(Person[] allPeople, params string[] mis }); } - - private int CalculateParent(int index, int totalPeople) { if (index < 5) + { return 11; + } if (index == totalPeople - 1) + { return 1; + } if (index == totalPeople) + { return 1; + } return index + 1; } diff --git a/src/DynamicData.Tests/Cache/SizeLimitFixture.cs b/src/DynamicData.Tests/Cache/SizeLimitFixture.cs index 3098d4b7a..cdf2b240a 100644 --- a/src/DynamicData.Tests/Cache/SizeLimitFixture.cs +++ b/src/DynamicData.Tests/Cache/SizeLimitFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class SizeLimitFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/SortFixture.cs b/src/DynamicData.Tests/Cache/SortFixture.cs index c2f190c35..cc902463f 100644 --- a/src/DynamicData.Tests/Cache/SortFixture.cs +++ b/src/DynamicData.Tests/Cache/SortFixture.cs @@ -22,7 +22,6 @@ public class SortFixtureWithReorder : IDisposable private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); private readonly IComparer _comparer; - public SortFixtureWithReorder() { _comparer = SortExpressionComparer.Ascending(p => p.Age).ThenByAscending(p => p.Name); @@ -40,7 +39,6 @@ public void Dispose() _results.Dispose(); } - [Fact] public void DoesNotThrow1() { @@ -63,6 +61,7 @@ public void DoesNotThrow2() disposable.Dispose(); } + public class Data { public Data(int id, string value) @@ -556,7 +555,6 @@ public class SortFixture: IDisposable private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); private readonly IComparer _comparer; - public SortFixture() { _comparer = SortExpressionComparer.Ascending(p => p.Age).ThenByAscending(p=>p.Name); @@ -564,7 +562,7 @@ public SortFixture() _source = new SourceCache(p => p.Key); _results = new SortedChangeSetAggregator ( - _source.Connect().Sort(_comparer) + _source.Connect().Sort(_comparer) ); } @@ -574,7 +572,6 @@ public void Dispose() _results.Dispose(); } - [Fact] public void DoesNotThrow1() { @@ -597,6 +594,7 @@ public void DoesNotThrow2() disposable.Dispose(); } + public class Data { public Data(int id, string value) diff --git a/src/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs b/src/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs index 316ec0a7d..0f3ee3349 100644 --- a/src/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs +++ b/src/DynamicData.Tests/Cache/SortObservableFixtureFixture.cs @@ -9,11 +9,11 @@ namespace DynamicData.Tests.Cache { - + public class SortObservableFixture: IDisposable { private readonly ISourceCache _cache; - private readonly SortedChangeSetAggregator _results; + private readonly SortedChangeSetAggregator _results; private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); private readonly BehaviorSubject> _comparerObservable; private readonly SortExpressionComparer _comparer; @@ -128,6 +128,7 @@ public void InlineChanges() { adaptor.Adapt(message, list); } + list.ShouldAllBeEquivalentTo(expected); } diff --git a/src/DynamicData.Tests/Cache/SubscribeManyFixture.cs b/src/DynamicData.Tests/Cache/SubscribeManyFixture.cs index 8d1f5ee11..35c56e1af 100644 --- a/src/DynamicData.Tests/Cache/SubscribeManyFixture.cs +++ b/src/DynamicData.Tests/Cache/SubscribeManyFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class SubscribeManyFixture: IDisposable { private class SubscribeableObject diff --git a/src/DynamicData.Tests/Cache/SwitchFixture.cs b/src/DynamicData.Tests/Cache/SwitchFixture.cs index 5efea04a9..6afb6484b 100644 --- a/src/DynamicData.Tests/Cache/SwitchFixture.cs +++ b/src/DynamicData.Tests/Cache/SwitchFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Cache { - + public class SwitchFixture: IDisposable { private readonly ISubject> _switchable; @@ -26,7 +26,7 @@ public void Dispose() _source.Dispose(); _results.Dispose(); } - + [Fact] public void PoulatesFirstSource() { @@ -48,7 +48,7 @@ public void ClearsForNewSource() _switchable.OnNext(newSource); _results.Data.Count.Should().Be(0); - + newSource.AddOrUpdate(inital); _results.Data.Count.Should().Be(100); diff --git a/src/DynamicData.Tests/Cache/TimeExpiryFixture.cs b/src/DynamicData.Tests/Cache/TimeExpiryFixture.cs index 160bb5321..36e00a159 100644 --- a/src/DynamicData.Tests/Cache/TimeExpiryFixture.cs +++ b/src/DynamicData.Tests/Cache/TimeExpiryFixture.cs @@ -36,10 +36,15 @@ public void AutoRemove() TimeSpan? RemoveFunc(Person t) { if (t.Age < 40) + { return TimeSpan.FromSeconds(4); + } if (t.Age < 80) + { return TimeSpan.FromSeconds(7); + } + return null; } diff --git a/src/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs b/src/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs index f0b0637db..2f90cfbd2 100644 --- a/src/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs +++ b/src/DynamicData.Tests/Cache/ToObservableChangeSetFixture.cs @@ -34,7 +34,7 @@ public ToObservableChangeSetFixture() .Clone(_target) .Subscribe(); } - + [Fact] public void ShouldLimitSizeOfBoundCollection() { diff --git a/src/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs b/src/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs index 90243430c..c49023bc1 100644 --- a/src/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs +++ b/src/DynamicData.Tests/Cache/ToObservableChangeSetFixtureWithCompletion.cs @@ -26,7 +26,6 @@ public ToObservableChangeSetFixtureWithCompletion() .Subscribe(x => { }, () => _hasCompleted = true); } - [Fact] public void ShouldReceiveUpdatesThenComplete() { diff --git a/src/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs b/src/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs index 367c12af2..7ad684376 100644 --- a/src/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs +++ b/src/DynamicData.Tests/Cache/ToSortedCollectionFixture.cs @@ -55,7 +55,7 @@ public void SortAscending() _sortedCollection.AddRange(persons); }) .Subscribe()); - + // Insert an item with a lower sort order _cache.AddOrUpdate(new Person("Name", 0)); diff --git a/src/DynamicData.Tests/Cache/TransformAsyncFixture.cs b/src/DynamicData.Tests/Cache/TransformAsyncFixture.cs index 4661aac17..7c31eb5cf 100644 --- a/src/DynamicData.Tests/Cache/TransformAsyncFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformAsyncFixture.cs @@ -4,7 +4,6 @@ using System.Threading.Tasks; using DynamicData.Tests.Domain; - namespace DynamicData.Tests.Cache { [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] @@ -168,7 +167,6 @@ public class TransformAsyncFixture // } //} - //[Fact] //public void HandleError() //{ @@ -186,7 +184,6 @@ public class TransformAsyncFixture // stub.Source.Connect() // .Subscribe(changes => { }, ex => error = ex); - // error.Should().NotBeNull(); // } @@ -212,6 +209,7 @@ public TransformStub() Source.Connect().TransformAsync(TransformFactory) ); } + public TransformStub(Func factory) { TransformFactory = (p) => diff --git a/src/DynamicData.Tests/Cache/TransformFixture.cs b/src/DynamicData.Tests/Cache/TransformFixture.cs index 0771a632a..04941d022 100644 --- a/src/DynamicData.Tests/Cache/TransformFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class TransformFixture { [Fact] diff --git a/src/DynamicData.Tests/Cache/TransformManyFixture.cs b/src/DynamicData.Tests/Cache/TransformManyFixture.cs index a0bc54215..2cee14abb 100644 --- a/src/DynamicData.Tests/Cache/TransformManyFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformManyFixture.cs @@ -4,10 +4,10 @@ using DynamicData.Tests.Utilities; using FluentAssertions; using Xunit; - + namespace DynamicData.Tests.Cache { - + public class TransformManyFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs b/src/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs index 34b74c843..5560d0232 100644 --- a/src/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformManyObservableCacheFixture.cs @@ -32,7 +32,6 @@ public void FlattenObservableCollection() return parent; }).ToArray(); - using (var source = new SourceCache(x => x.Id)) using (var aggregator = source.Connect() .TransformMany(p => p.Children, c => c.Name) @@ -50,7 +49,6 @@ public void FlattenObservableCollection() source.RemoveKey(1); aggregator.Data.Count.Should().Be(98); - //check items can be cleared and then added back in var childrenInZero = parents[1].Children.ToArray(); parents[1].Children.Clear(); @@ -87,7 +85,6 @@ public void FlattenReadOnlyObservableCollection() return parent; }).ToArray(); - using (var source = new SourceCache(x => x.Id)) using (var aggregator = source.Connect() .TransformMany(p => p.ChildrenReadonly, c => c.Name) @@ -105,7 +102,6 @@ public void FlattenReadOnlyObservableCollection() source.RemoveKey(1); aggregator.Data.Count.Should().Be(98); - //check items can be cleared and then added back in var childrenInZero = parents[1].Children.ToArray(); parents[1].Children.Clear(); @@ -143,8 +139,6 @@ public void Perf() return parent; }).ToArray(); - - var sw = new Stopwatch(); using (var source = new SourceCache(x => x.Id)) @@ -203,7 +197,6 @@ public void ReadOnlyObservableCollectionWithoutInitialData() } } - private class Parent { public int Id { get; } diff --git a/src/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs b/src/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs index d61cef579..267ffa9ce 100644 --- a/src/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformManyRefreshFixture.cs @@ -69,7 +69,7 @@ public void DirectRefresh() friends.Add(new PersonWithFriends("Friend2", 45)); _source.Refresh(person); - + _results.Data.Count.Should().Be(2, "Should be 2 in the cache"); _results.Data.Lookup("Friend1").HasValue.Should().BeTrue(); _results.Data.Lookup("Friend2").HasValue.Should().BeTrue(); diff --git a/src/DynamicData.Tests/Cache/TransformManySimpleFixture.cs b/src/DynamicData.Tests/Cache/TransformManySimpleFixture.cs index ef9100231..405dd5c33 100644 --- a/src/DynamicData.Tests/Cache/TransformManySimpleFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformManySimpleFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Cache { - + public class TransformManySimpleFixture: IDisposable { private readonly ISourceCache _source; @@ -42,7 +42,6 @@ public void Adds() _results.Data.Lookup("Child3").HasValue.Should().BeTrue(); } - [Fact] public void Remove() { @@ -91,7 +90,6 @@ public void UpdateWithLessChildren() _results.Data.Lookup("Child3").HasValue.Should().BeTrue(); } - [Fact] public void UpdateWithMultipleChanges() { diff --git a/src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs b/src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs index 9504de333..f1f288012 100644 --- a/src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformSafeAsyncFixture.cs @@ -173,7 +173,6 @@ public void ReTransformAll() // } //} - //[Fact] //public void HandleError() //{ @@ -194,7 +193,6 @@ public void ReTransformAll() // stub.Source.Connect() // .Subscribe(changes => { }, ex => error = ex); - // error.Should().BeNull(); // stub.HandledErrors.Count.Should().Be(50); @@ -209,7 +207,7 @@ private class TransformStub : IDisposable public Func< Person, Task> TransformFactory { get; } - public IList> HandledErrors { get; } = new List>(); + public IList> HandledErrors { get; } = new List>(); public TransformStub() { @@ -220,12 +218,11 @@ public TransformStub() }; Results = new ChangeSetAggregator - ( + ( Source.Connect().TransformSafeAsync(TransformFactory, ErrorHandler) ); } - public TransformStub(Func factory) { TransformFactory = (p) => diff --git a/src/DynamicData.Tests/Cache/TransformSafeFixture.cs b/src/DynamicData.Tests/Cache/TransformSafeFixture.cs index bbb4c77ed..3feced478 100644 --- a/src/DynamicData.Tests/Cache/TransformSafeFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformSafeFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class TransformSafeFixture: IDisposable { private ISourceCache _source; @@ -21,6 +21,7 @@ public class TransformSafeFixture: IDisposable { throw new Exception($"Cannot transform {p}"); } + string gender = p.Age % 2 == 0 ? "M" : "F"; return new PersonWithGender(p, gender); }; diff --git a/src/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs b/src/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs index 6f37a08b9..c43d4bfdd 100644 --- a/src/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformSafeParallelFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.Cache { - + public class TransformSafeParallelFixture: IDisposable { private readonly ISourceCache _source; @@ -21,6 +21,7 @@ public class TransformSafeParallelFixture: IDisposable { throw new Exception($"Cannot transform {p}"); } + string gender = p.Age % 2 == 0 ? "M" : "F"; return new PersonWithGender(p, gender); }; diff --git a/src/DynamicData.Tests/Cache/TransformTreeFixture.cs b/src/DynamicData.Tests/Cache/TransformTreeFixture.cs index 480eae8ed..f115ea4dd 100644 --- a/src/DynamicData.Tests/Cache/TransformTreeFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformTreeFixture.cs @@ -267,16 +267,36 @@ public EmployeeDto(int id) public bool Equals(EmployeeDto other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Id == other.Id; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((EmployeeDto)obj); } diff --git a/src/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs b/src/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs index a20d6626d..667f82c12 100644 --- a/src/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs +++ b/src/DynamicData.Tests/Cache/TransformTreeWithRefreshFixture.cs @@ -99,7 +99,7 @@ public void DoNotUpdateTreeWhenParentIdNotChanged() private IEnumerable CreateEmployees() { - yield return new EmployeeDto(1) + yield return new EmployeeDto(1) { BossId = 0, Name = "Employee1" @@ -176,16 +176,36 @@ public string Name public bool Equals(EmployeeDto other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Id == other.Id; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((EmployeeDto)obj); } diff --git a/src/DynamicData.Tests/Cache/TrueForAllFixture.cs b/src/DynamicData.Tests/Cache/TrueForAllFixture.cs index b709e1fe1..448ad7d84 100644 --- a/src/DynamicData.Tests/Cache/TrueForAllFixture.cs +++ b/src/DynamicData.Tests/Cache/TrueForAllFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class TrueForAllFixture: IDisposable { private readonly ISourceCache _source; @@ -72,7 +72,6 @@ public void MultipleValuesReturnTrue() item3.InvokeObservable(true); valuereturned.Value.Should().Be(true, "Value should be true"); - subscribed.Dispose(); } diff --git a/src/DynamicData.Tests/Cache/TrueForAnyFixture.cs b/src/DynamicData.Tests/Cache/TrueForAnyFixture.cs index 57957c902..130d74d0d 100644 --- a/src/DynamicData.Tests/Cache/TrueForAnyFixture.cs +++ b/src/DynamicData.Tests/Cache/TrueForAnyFixture.cs @@ -4,10 +4,9 @@ using FluentAssertions; using Xunit; - namespace DynamicData.Tests.Cache { - + public class TrueForAnyFixture: IDisposable { private readonly ISourceCache _source; diff --git a/src/DynamicData.Tests/Cache/WatchFixture.cs b/src/DynamicData.Tests/Cache/WatchFixture.cs index c76ce9ace..170e97362 100644 --- a/src/DynamicData.Tests/Cache/WatchFixture.cs +++ b/src/DynamicData.Tests/Cache/WatchFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Cache { - + public class WatchFixture: IDisposable { private class DisposableObject : IDisposable diff --git a/src/DynamicData.Tests/Cache/WatcherFixture.cs b/src/DynamicData.Tests/Cache/WatcherFixture.cs index a73e38266..bd186e0d8 100644 --- a/src/DynamicData.Tests/Cache/WatcherFixture.cs +++ b/src/DynamicData.Tests/Cache/WatcherFixture.cs @@ -15,7 +15,7 @@ namespace DynamicData.Tests.Cache { - + public class WatcherFixture: IDisposable { private readonly TestScheduler _scheduler = new TestScheduler(); @@ -137,6 +137,7 @@ public void WatchMany() { update.Reason.Should().Be(ChangeReason.Add, "Change reason should be add"); } + result.Clear(); _source.AddOrUpdate(new Person("Adult1", 51)); @@ -146,6 +147,7 @@ public void WatchMany() { update.Reason.Should().Be(ChangeReason.Update, "Change reason should be add"); } + result.Clear(); _source.Remove("Adult1"); @@ -155,6 +157,7 @@ public void WatchMany() { update.Reason.Should().Be(ChangeReason.Remove, "Change reason should be add"); } + result.Clear(); watch1.Dispose(); diff --git a/src/DynamicData.Tests/Cache/XorFixture.cs b/src/DynamicData.Tests/Cache/XorFixture.cs index 15e2815cb..e3185387a 100644 --- a/src/DynamicData.Tests/Cache/XorFixture.cs +++ b/src/DynamicData.Tests/Cache/XorFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Cache { - + public class XOrFixture : XOrFixtureBase { protected override IObservable> CreateObservable() @@ -15,7 +15,6 @@ protected override IObservable> CreateObservable() } } - public class XOrCollectionFixture : XOrFixtureBase { protected override IObservable> CreateObservable() @@ -25,7 +24,6 @@ protected override IObservable> CreateObservable() } } - public abstract class XOrFixtureBase: IDisposable { protected ISourceCache _source1; diff --git a/src/DynamicData.Tests/Domain/ParentAndChildren.cs b/src/DynamicData.Tests/Domain/ParentAndChildren.cs index 846bcf0da..dad96b868 100644 --- a/src/DynamicData.Tests/Domain/ParentAndChildren.cs +++ b/src/DynamicData.Tests/Domain/ParentAndChildren.cs @@ -28,16 +28,36 @@ public ParentAndChildren(string parentId, Optional parent, Person[] chil public bool Equals(ParentAndChildren other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(ParentId, other.ParentId); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ParentAndChildren) obj); } diff --git a/src/DynamicData.Tests/Domain/Person.cs b/src/DynamicData.Tests/Domain/Person.cs index 23ed5144a..8c5558dcf 100644 --- a/src/DynamicData.Tests/Domain/Person.cs +++ b/src/DynamicData.Tests/Domain/Person.cs @@ -35,16 +35,36 @@ public int Age public bool Equals(Person other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Person)obj); } @@ -67,10 +87,26 @@ private sealed class AgeEqualityComparer : IEqualityComparer { public bool Equals(Person x, Person y) { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + return x._age == y._age; } @@ -82,15 +118,30 @@ public int GetHashCode(Person obj) public static IEqualityComparer AgeComparer { get; } = new AgeEqualityComparer(); - private sealed class NameAgeGenderEqualityComparer : IEqualityComparer { public bool Equals(Person x, Person y) { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + return string.Equals(x.Name, y.Name) && x._age == y._age && string.Equals(x.Gender, y.Gender); } diff --git a/src/DynamicData.Tests/Domain/PersonEmployment.cs b/src/DynamicData.Tests/Domain/PersonEmployment.cs index 8eafa2fa0..b9a7447d4 100644 --- a/src/DynamicData.Tests/Domain/PersonEmployment.cs +++ b/src/DynamicData.Tests/Domain/PersonEmployment.cs @@ -24,7 +24,11 @@ public bool Equals(PersonEmpKey other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is PersonEmpKey && Equals((PersonEmpKey)obj); } @@ -63,9 +67,21 @@ protected bool Equals(PersonEmployment other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((PersonEmployment)obj); } diff --git a/src/DynamicData.Tests/Domain/PersonObs.cs b/src/DynamicData.Tests/Domain/PersonObs.cs index 2f3529269..f000905a0 100644 --- a/src/DynamicData.Tests/Domain/PersonObs.cs +++ b/src/DynamicData.Tests/Domain/PersonObs.cs @@ -38,16 +38,36 @@ public void SetAge(int age) public bool Equals(PersonObs other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return string.Equals(Name, other.Name); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((PersonObs)obj); } @@ -70,10 +90,26 @@ private sealed class AgeEqualityComparer : IEqualityComparer { public bool Equals(PersonObs x, PersonObs y) { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + return x._age.Value == y._age.Value; } @@ -85,15 +121,30 @@ public int GetHashCode(PersonObs obj) public static IEqualityComparer AgeComparer { get; } = new AgeEqualityComparer(); - private sealed class NameAgeGenderEqualityComparer : IEqualityComparer { public bool Equals(PersonObs x, PersonObs y) { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + return string.Equals(x.Name, y.Name) && x._age == y._age && string.Equals(x.Gender, y.Gender); } diff --git a/src/DynamicData.Tests/Domain/PersonWithChildren.cs b/src/DynamicData.Tests/Domain/PersonWithChildren.cs index 790ce775b..5a8728832 100644 --- a/src/DynamicData.Tests/Domain/PersonWithChildren.cs +++ b/src/DynamicData.Tests/Domain/PersonWithChildren.cs @@ -30,7 +30,6 @@ public PersonWithChildren(string name, int age, IEnumerable relations) public IEnumerable Relations { get; } - public override string ToString() { return $"{Name}. {Age}"; diff --git a/src/DynamicData.Tests/Domain/PersonWithGender.cs b/src/DynamicData.Tests/Domain/PersonWithGender.cs index 43e4fd2d5..2b4135c59 100644 --- a/src/DynamicData.Tests/Domain/PersonWithGender.cs +++ b/src/DynamicData.Tests/Domain/PersonWithGender.cs @@ -36,10 +36,12 @@ public bool Equals(PersonWithGender other) { return false; } + if (ReferenceEquals(this, other)) { return true; } + return Equals(other.Name, Name) && other.Age == Age && Equals(other.Gender, Gender); } @@ -49,14 +51,17 @@ public override bool Equals(object obj) { return false; } + if (ReferenceEquals(this, obj)) { return true; } + if (obj.GetType() != typeof(PersonWithGender)) { return false; } + return Equals((PersonWithGender)obj); } diff --git a/src/DynamicData.Tests/DynamicData.Tests.csproj b/src/DynamicData.Tests/DynamicData.Tests.csproj index 9c4064a82..1c6a15d62 100644 --- a/src/DynamicData.Tests/DynamicData.Tests.csproj +++ b/src/DynamicData.Tests/DynamicData.Tests.csproj @@ -1,7 +1,7 @@  netcoreapp2.2;net462 - $(NoWarn);CS0618 + $(NoWarn);CS0618;CA1801 diff --git a/src/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs b/src/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs index 3f2eac431..fc7b9efa8 100644 --- a/src/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs +++ b/src/DynamicData.Tests/Kernal/CacheUpdaterFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.Kernal { - + public class CacheUpdaterFixture { private readonly ChangeAwareCache _cache; @@ -19,8 +19,6 @@ public CacheUpdaterFixture() _updater = new CacheUpdater(_cache); } - - [Fact] public void Add() { diff --git a/src/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs b/src/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs index a2d11362e..34ff71b0d 100644 --- a/src/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs +++ b/src/DynamicData.Tests/Kernal/DistinctUpdateFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Kernal { - + public class DistinctUpdateFixture { [Fact] diff --git a/src/DynamicData.Tests/Kernal/EnumerableEx.cs b/src/DynamicData.Tests/Kernal/EnumerableEx.cs index 6e828d79c..6e4b961cd 100644 --- a/src/DynamicData.Tests/Kernal/EnumerableEx.cs +++ b/src/DynamicData.Tests/Kernal/EnumerableEx.cs @@ -8,8 +8,15 @@ public static class EnumerableEx public static IEnumerable PrevCurrentNextZip(this IEnumerable source, Func selector) { - if (source == null) throw new ArgumentNullException("source"); - if (selector == null) throw new ArgumentNullException("selector"); + if (source == null) + { + throw new ArgumentNullException("source"); + } + + if (selector == null) + { + throw new ArgumentNullException("selector"); + } var enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) @@ -32,8 +39,15 @@ public static IEnumerable PrevCurrentNextZip(this IEnumerab public static IEnumerable CurrentNextZip(this IEnumerable source, Func selector) { - if (source == null) throw new ArgumentNullException("source"); - if (selector == null) throw new ArgumentNullException("selector"); + if (source == null) + { + throw new ArgumentNullException("source"); + } + + if (selector == null) + { + throw new ArgumentNullException("selector"); + } var enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) @@ -54,8 +68,15 @@ public static IEnumerable CurrentNextZip(this IEnumerable PrevCurrentZip(this IEnumerable source, Func selector) { - if (source == null) throw new ArgumentNullException("source"); - if (selector == null) throw new ArgumentNullException("selector"); + if (source == null) + { + throw new ArgumentNullException("source"); + } + + if (selector == null) + { + throw new ArgumentNullException("selector"); + } var enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) diff --git a/src/DynamicData.Tests/Kernal/KeyValueFixture.cs b/src/DynamicData.Tests/Kernal/KeyValueFixture.cs index f3d98089b..4f72356ab 100644 --- a/src/DynamicData.Tests/Kernal/KeyValueFixture.cs +++ b/src/DynamicData.Tests/Kernal/KeyValueFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Kernal { - + public class KeyValueFixture { [Fact] diff --git a/src/DynamicData.Tests/Kernal/OptionFixture.cs b/src/DynamicData.Tests/Kernal/OptionFixture.cs index 11ffbdab7..349f145ca 100644 --- a/src/DynamicData.Tests/Kernal/OptionFixture.cs +++ b/src/DynamicData.Tests/Kernal/OptionFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.Kernal { - + public class OptionFixture { [Fact] diff --git a/src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs b/src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs index 7017690ed..8f1924576 100644 --- a/src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs +++ b/src/DynamicData.Tests/Kernal/SourceUpdaterFixture.cs @@ -49,7 +49,6 @@ public void BatchOfUniqueUpdates() _updater.AddOrUpdate(people); var updates = _cache.CaptureChanges(); - _cache.Items.ToArray().ShouldAllBeEquivalentTo(people); _cache.Count.Should().Be(100); updates.Adds.Should().Be(100); diff --git a/src/DynamicData.Tests/Kernal/UpdateFixture.cs b/src/DynamicData.Tests/Kernal/UpdateFixture.cs index 2b0413708..3a1311aa4 100644 --- a/src/DynamicData.Tests/Kernal/UpdateFixture.cs +++ b/src/DynamicData.Tests/Kernal/UpdateFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.Kernal { - + public class UpdateFixture { [Fact] diff --git a/src/DynamicData.Tests/List/AndFixture.cs b/src/DynamicData.Tests/List/AndFixture.cs index c5b309c37..79d684b7f 100644 --- a/src/DynamicData.Tests/List/AndFixture.cs +++ b/src/DynamicData.Tests/List/AndFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class AndFixture : AndFixtureBase { protected override IObservable> CreateObservable() @@ -15,7 +15,6 @@ protected override IObservable> CreateObservable() } } - public class AndCollectionFixture : AndFixtureBase { protected override IObservable> CreateObservable() @@ -25,14 +24,12 @@ protected override IObservable> CreateObservable() } } - public abstract class AndFixtureBase: IDisposable { protected ISourceList _source1; protected ISourceList _source2; private readonly ChangeSetAggregator _results; - protected AndFixtureBase() { _source1 = new SourceList(); diff --git a/src/DynamicData.Tests/List/AutoRefreshFixture.cs b/src/DynamicData.Tests/List/AutoRefreshFixture.cs index 98a440229..a78e97bde 100644 --- a/src/DynamicData.Tests/List/AutoRefreshFixture.cs +++ b/src/DynamicData.Tests/List/AutoRefreshFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.List { - + public class AutoRefreshFixture { [Fact] @@ -69,7 +69,7 @@ public void AutoRefreshBatched() results.Data.Count.Should().Be(100); results.Messages.Count.Should().Be(1); - + //update 50 records items.Skip(50) .ForEach(p => p.Age = p.Age + 1); @@ -130,12 +130,10 @@ public void AutoRefreshFilter() toRemove.Age = 101; results.Messages.Count.Should().Be(8); - - results.Messages.Last().First().Reason.Should().Be(ListChangeReason.Replace); } } - + [Fact] public void AutoRefreshTransform() { @@ -239,7 +237,7 @@ public void AutoRefreshGroup() .AutoRefresh(p => p.Age) .GroupOn(p=>p.Age % 10) .AsAggregator()) - { + { void CheckContent() { foreach (var grouping in items.GroupBy(p => p.Age % 10)) @@ -273,7 +271,6 @@ void CheckContent() results.Data.Count.Should().Be(10); CheckContent(); - var groupOf3 = results.Data.Items.ElementAt(2); IChangeSet changes = null; @@ -340,11 +337,10 @@ void CheckContent() items[2].Age = 13; CheckContent(); - results.Messages.Count.Should().Be(5); + results.Messages.Count.Should().Be(5); } } - [Fact] public void AutoRefreshDistinct() { @@ -437,9 +433,21 @@ protected bool Equals(SelectableItem other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((SelectableItem)obj); } diff --git a/src/DynamicData.Tests/List/BatchFixture.cs b/src/DynamicData.Tests/List/BatchFixture.cs index 1354be70f..ba51b6975 100644 --- a/src/DynamicData.Tests/List/BatchFixture.cs +++ b/src/DynamicData.Tests/List/BatchFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class BatchFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/BatchIfFixture.cs b/src/DynamicData.Tests/List/BatchIfFixture.cs index 4e34ac820..a5224d302 100644 --- a/src/DynamicData.Tests/List/BatchIfFixture.cs +++ b/src/DynamicData.Tests/List/BatchIfFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class BatchIfFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs b/src/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs index 7ba231681..9037464f5 100644 --- a/src/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs +++ b/src/DynamicData.Tests/List/BatchIfWithTimeOutFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class BatchIfWithTimeOutFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/BufferFixture.cs b/src/DynamicData.Tests/List/BufferFixture.cs index 0a72018b4..9c091c22b 100644 --- a/src/DynamicData.Tests/List/BufferFixture.cs +++ b/src/DynamicData.Tests/List/BufferFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class BufferFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/BufferInitialFixture.cs b/src/DynamicData.Tests/List/BufferInitialFixture.cs index 796730512..29e043df2 100644 --- a/src/DynamicData.Tests/List/BufferInitialFixture.cs +++ b/src/DynamicData.Tests/List/BufferInitialFixture.cs @@ -21,7 +21,9 @@ public void BufferInitial() using (var aggregator = cache.Connect().BufferInitial(TimeSpan.FromSeconds(1), scheduler).AsAggregator()) { foreach (var item in People) + { cache.Add(item); + } aggregator.Data.Count.Should().Be(0); aggregator.Messages.Count.Should().Be(0); diff --git a/src/DynamicData.Tests/List/CastFixture.cs b/src/DynamicData.Tests/List/CastFixture.cs index dc9475bdf..7dfcc9982 100644 --- a/src/DynamicData.Tests/List/CastFixture.cs +++ b/src/DynamicData.Tests/List/CastFixture.cs @@ -5,13 +5,12 @@ namespace DynamicData.Tests.List { - + public class CastFixture: IDisposable { private readonly ISourceList _source; private readonly ChangeSetAggregator _results; - public CastFixture() { _source = new SourceList(); diff --git a/src/DynamicData.Tests/List/ChangeAwareListFixture.cs b/src/DynamicData.Tests/List/ChangeAwareListFixture.cs index f97a2ed86..16ca26048 100644 --- a/src/DynamicData.Tests/List/ChangeAwareListFixture.cs +++ b/src/DynamicData.Tests/List/ChangeAwareListFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class ChangeAwareListFixture { private ChangeAwareList _list; @@ -206,7 +206,6 @@ public void RemoveMany() _list.Count.Should().Be(0); } - [Fact] public void RefreshAt() { @@ -222,7 +221,6 @@ public void RefreshAt() changes.First().Reason.Should().Be(ListChangeReason.Refresh); changes.First().Item.Current.Should().Be(1); - Assert.Throws(() => _list.RefreshAt(-1)); Assert.Throws(() => _list.RefreshAt(1000)); } @@ -247,8 +245,6 @@ public void Refresh() _list.Refresh(1000).Should().Be(false); } - - [Fact] public void ThrowWhenRemovingItemOutsideOfBoundaries() { diff --git a/src/DynamicData.Tests/List/CloneChangesFixture.cs b/src/DynamicData.Tests/List/CloneChangesFixture.cs index 682799ef9..c15a5a6ac 100644 --- a/src/DynamicData.Tests/List/CloneChangesFixture.cs +++ b/src/DynamicData.Tests/List/CloneChangesFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class CloneChangesFixture { private readonly ChangeAwareList _source; @@ -177,7 +177,9 @@ public void MovedItemInObservableCollectionIsMoved() clone.CollectionChanged += (s, e) => { if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move) + { itemMoved = true; + } }; clone.Clone(changes); diff --git a/src/DynamicData.Tests/List/CloneFixture.cs b/src/DynamicData.Tests/List/CloneFixture.cs index 64884028d..bf8c4ca12 100644 --- a/src/DynamicData.Tests/List/CloneFixture.cs +++ b/src/DynamicData.Tests/List/CloneFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.List { - + public class CloneFixture: IDisposable { private readonly ICollection _collection = new Collection(); @@ -17,7 +17,6 @@ public class CloneFixture: IDisposable private readonly IDisposable _cloner; private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); - public CloneFixture() { _collection = new Collection(); diff --git a/src/DynamicData.Tests/List/CreationFixtures.cs b/src/DynamicData.Tests/List/CreationFixtures.cs index 2793fdd99..3935a6789 100644 --- a/src/DynamicData.Tests/List/CreationFixtures.cs +++ b/src/DynamicData.Tests/List/CreationFixtures.cs @@ -33,7 +33,7 @@ private void SubscribeAndAssert(IObservable> observableChangese .Finally(()=> complete = true) .AsObservableList()) using (myList.Connect().Subscribe(result => changes = result, ex => error = ex)) - { + { if (!expectsError) { error.Should().BeNull(); @@ -43,6 +43,7 @@ private void SubscribeAndAssert(IObservable> observableChangese error.Should().NotBeNull(); } } + complete.Should().BeTrue(); } } diff --git a/src/DynamicData.Tests/List/DeferUntilLoadedFixture.cs b/src/DynamicData.Tests/List/DeferUntilLoadedFixture.cs index b466733f6..e7883e94a 100644 --- a/src/DynamicData.Tests/List/DeferUntilLoadedFixture.cs +++ b/src/DynamicData.Tests/List/DeferUntilLoadedFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class DeferAnsdSkipFixture { [Fact] diff --git a/src/DynamicData.Tests/List/DisposeManyFixture.cs b/src/DynamicData.Tests/List/DisposeManyFixture.cs index 70915f7fc..60fe9b2ba 100644 --- a/src/DynamicData.Tests/List/DisposeManyFixture.cs +++ b/src/DynamicData.Tests/List/DisposeManyFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class DisposeManyFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/DistinctValuesFixture.cs b/src/DynamicData.Tests/List/DistinctValuesFixture.cs index a8b94835a..c72175da8 100644 --- a/src/DynamicData.Tests/List/DistinctValuesFixture.cs +++ b/src/DynamicData.Tests/List/DistinctValuesFixture.cs @@ -6,13 +6,12 @@ namespace DynamicData.Tests.List { - + public class DistinctValuesFixture: IDisposable { private readonly ISourceList _source; private readonly ChangeSetAggregator _results; - public DistinctValuesFixture() { _source = new SourceList(); diff --git a/src/DynamicData.Tests/List/DynamicAndFixture.cs b/src/DynamicData.Tests/List/DynamicAndFixture.cs index f0ca8e557..b9a81b9ba 100644 --- a/src/DynamicData.Tests/List/DynamicAndFixture.cs +++ b/src/DynamicData.Tests/List/DynamicAndFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class DynamicAndFixture: IDisposable { private readonly ISourceList _source1; diff --git a/src/DynamicData.Tests/List/DynamicExceptFixture.cs b/src/DynamicData.Tests/List/DynamicExceptFixture.cs index fbfad2f55..35ea70ce4 100644 --- a/src/DynamicData.Tests/List/DynamicExceptFixture.cs +++ b/src/DynamicData.Tests/List/DynamicExceptFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class DynamicExceptFixture: IDisposable { private readonly ISourceList _source1; diff --git a/src/DynamicData.Tests/List/DynamicOrFixture.cs b/src/DynamicData.Tests/List/DynamicOrFixture.cs index 0040f89be..b4c4dd47b 100644 --- a/src/DynamicData.Tests/List/DynamicOrFixture.cs +++ b/src/DynamicData.Tests/List/DynamicOrFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class DynamicOrFixture: IDisposable { private readonly ISourceList _source1; @@ -15,7 +15,6 @@ public class DynamicOrFixture: IDisposable private readonly ChangeSetAggregator _results; - public DynamicOrFixture() { _source1 = new SourceList(); diff --git a/src/DynamicData.Tests/List/DynamicXOrFixture.cs b/src/DynamicData.Tests/List/DynamicXOrFixture.cs index 1bb12f4a4..00b8a2ac7 100644 --- a/src/DynamicData.Tests/List/DynamicXOrFixture.cs +++ b/src/DynamicData.Tests/List/DynamicXOrFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class DynamicXOrFixture: IDisposable { private readonly ISourceList _source1; @@ -15,7 +15,6 @@ public class DynamicXOrFixture: IDisposable private readonly ChangeSetAggregator _results; - public DynamicXOrFixture() { _source1 = new SourceList(); diff --git a/src/DynamicData.Tests/List/EditDiffFixture.cs b/src/DynamicData.Tests/List/EditDiffFixture.cs index 2226f2cbb..a355893b8 100644 --- a/src/DynamicData.Tests/List/EditDiffFixture.cs +++ b/src/DynamicData.Tests/List/EditDiffFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class EditDiffFixture: IDisposable { private readonly SourceList _cache; @@ -80,7 +80,6 @@ public void Removes() _cache.Items.ShouldAllBeEquivalentTo(newList); } - [Fact] public void VariousChanges() { diff --git a/src/DynamicData.Tests/List/ExceptFixture.cs b/src/DynamicData.Tests/List/ExceptFixture.cs index 718a243c6..6dc8c17c8 100644 --- a/src/DynamicData.Tests/List/ExceptFixture.cs +++ b/src/DynamicData.Tests/List/ExceptFixture.cs @@ -6,8 +6,8 @@ namespace DynamicData.Tests.List { - - public class ExceptFixture : ExceptFixtureBase + + public class ExceptFixture : ExceptFixtureBase { protected override IObservable> CreateObservable() { @@ -15,7 +15,6 @@ protected override IObservable> CreateObservable() } } - public class ExceptCollectionFixture : ExceptFixtureBase { protected override IObservable> CreateObservable() @@ -25,7 +24,6 @@ protected override IObservable> CreateObservable() } } - public abstract class ExceptFixtureBase :IDisposable { protected ISourceList Source1; diff --git a/src/DynamicData.Tests/List/ExpireAfterFixture.cs b/src/DynamicData.Tests/List/ExpireAfterFixture.cs index c8ad4b5ab..8523cfee4 100644 --- a/src/DynamicData.Tests/List/ExpireAfterFixture.cs +++ b/src/DynamicData.Tests/List/ExpireAfterFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class ExpireAfterFixture: IDisposable { private readonly ISourceList _source; @@ -34,10 +34,15 @@ public void ComplexRemove() TimeSpan? RemoveFunc(Person t) { if (t.Age <= 40) + { return TimeSpan.FromSeconds(5); + } if (t.Age <= 80) + { return TimeSpan.FromSeconds(7); + } + return null; } diff --git a/src/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs b/src/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs index d20c1a108..1abe2e9d6 100644 --- a/src/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs +++ b/src/DynamicData.Tests/List/FilterControllerFixtureWithClearAndReplace.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class FilterControllerFixtureWithClearAndReplace : IDisposable { private readonly ISourceList _source; @@ -50,7 +50,7 @@ public void VeryLargeDataSet() var result = source.Connect().Filter(filter, ListFilterPolicy.ClearAndReplace).AsObservableList(); source.AddRange(Enumerable.Range(1,250000)); - + filter.OnNext(i => true); filter.OnNext(i => false); } @@ -68,6 +68,7 @@ public void ReevaluateFilter() { person.Age = person.Age + 10; } + _filter.OnNext(p => p.Age > 20); _results.Data.Count.Should().Be(90); @@ -79,6 +80,7 @@ public void ReevaluateFilter() { person.Age = person.Age - 10; } + _filter.OnNext(p => p.Age > 20); _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); diff --git a/src/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs b/src/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs index 3f4d45e25..0201f660f 100644 --- a/src/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs +++ b/src/DynamicData.Tests/List/FilterControllerFixtureWithDiffSet.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class FilterControllerFixtureWithDiffSet : IDisposable { private readonly ISourceList _source; @@ -55,6 +55,7 @@ public void ReevaluateFilter() { person.Age = person.Age + 10; } + _filter.OnNext(p => p.Age > 20); _results.Data.Count.Should().Be(90, "Should be 90 people in the cache"); @@ -65,6 +66,7 @@ public void ReevaluateFilter() { person.Age = person.Age - 10; } + _filter.OnNext(p => p.Age > 20); _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); diff --git a/src/DynamicData.Tests/List/FilterFixture.cs b/src/DynamicData.Tests/List/FilterFixture.cs index cc2d89f22..ab0a741e7 100644 --- a/src/DynamicData.Tests/List/FilterFixture.cs +++ b/src/DynamicData.Tests/List/FilterFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class FilterFixture: IDisposable { private readonly ISourceList _source; @@ -35,7 +35,6 @@ public void AddMatched() _results.Data.Items.First().Should().Be(person, "Should be same person"); } - [Fact] public void ReplaceWithMatch() { diff --git a/src/DynamicData.Tests/List/FilterOnObservableFixture.cs b/src/DynamicData.Tests/List/FilterOnObservableFixture.cs index 368bd8088..d49425305 100644 --- a/src/DynamicData.Tests/List/FilterOnObservableFixture.cs +++ b/src/DynamicData.Tests/List/FilterOnObservableFixture.cs @@ -98,7 +98,6 @@ public void Clear() } } - [Fact] public void RemoveRange() { @@ -119,7 +118,6 @@ private class FilterPropertyStub : IDisposable public ISourceList Source { get; } = new SourceList(); public ChangeSetAggregator Results { get; } - public FilterPropertyStub() { Results = new ChangeSetAggregator(Source.Connect() diff --git a/src/DynamicData.Tests/List/FilterOnPropertyFixture.cs b/src/DynamicData.Tests/List/FilterOnPropertyFixture.cs index 30d841c4b..4dd6ee2b9 100644 --- a/src/DynamicData.Tests/List/FilterOnPropertyFixture.cs +++ b/src/DynamicData.Tests/List/FilterOnPropertyFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class FilterOnPropertyFixture { [Fact] @@ -81,7 +81,6 @@ public void Clear() } } - [Fact] public void RemoveRange() { @@ -100,7 +99,6 @@ private class FilterPropertyStub : IDisposable public ISourceList Source { get; } = new SourceList(); public ChangeSetAggregator Results { get; } - public FilterPropertyStub() { Results = new ChangeSetAggregator(Source.Connect().FilterOnProperty(p => p.Age, p => p.Age > 18)); diff --git a/src/DynamicData.Tests/List/FilterWithObservable.cs b/src/DynamicData.Tests/List/FilterWithObservable.cs index adc0ccc7b..023016ed3 100644 --- a/src/DynamicData.Tests/List/FilterWithObservable.cs +++ b/src/DynamicData.Tests/List/FilterWithObservable.cs @@ -10,7 +10,7 @@ namespace DynamicData.Tests.List { - + public class FilterWithObservable: IDisposable { private readonly ISourceList _source; @@ -58,6 +58,7 @@ public void ReevaluateFilter() { person.Age = person.Age + 10; } + _filter.OnNext(_filter.Value); _results.Data.Count.Should().Be(90, "Should be 90 people in the cache"); @@ -69,6 +70,7 @@ public void ReevaluateFilter() { person.Age = person.Age - 10; } + _filter.OnNext(_filter.Value); _results.Data.Count.Should().Be(80, "Should be 80 people in the cache"); diff --git a/src/DynamicData.Tests/List/ForEachChangeFixture.cs b/src/DynamicData.Tests/List/ForEachChangeFixture.cs index a6332e53d..ebbd69a62 100644 --- a/src/DynamicData.Tests/List/ForEachChangeFixture.cs +++ b/src/DynamicData.Tests/List/ForEachChangeFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class ForEachChangeFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/FromAsyncFixture.cs b/src/DynamicData.Tests/List/FromAsyncFixture.cs index b2026c3a2..f0a52f835 100644 --- a/src/DynamicData.Tests/List/FromAsyncFixture.cs +++ b/src/DynamicData.Tests/List/FromAsyncFixture.cs @@ -10,12 +10,11 @@ namespace DynamicData.Tests.List { - + public class FromAsyncFixture { private TestScheduler _scheduler; - public FromAsyncFixture() { _scheduler = new TestScheduler(); @@ -77,7 +76,6 @@ Task> Loader() var subscribed = data.Connect() .Subscribe(changes => { }, ex => error = ex); - error.Should().NotBeNull(); } diff --git a/src/DynamicData.Tests/List/GroupImmutableFixture.cs b/src/DynamicData.Tests/List/GroupImmutableFixture.cs index 55a9c30fc..deb94cad4 100644 --- a/src/DynamicData.Tests/List/GroupImmutableFixture.cs +++ b/src/DynamicData.Tests/List/GroupImmutableFixture.cs @@ -9,14 +9,13 @@ namespace DynamicData.Tests.List { - + public class GroupImmutableFixture: IDisposable { private readonly ISourceList _source; private readonly ChangeSetAggregator> _results; private readonly ISubject _regrouper; - public GroupImmutableFixture() { _source = new SourceList(); @@ -29,7 +28,7 @@ public void Dispose() _source.Dispose(); _results.Dispose(); } - + [Fact] public void Add() { @@ -160,7 +159,9 @@ public void Reevaluate() //do an inline update foreach (var person in initialPeople) + { person.Age = person.Age + 1; + } //signal operators to evaluate again _regrouper.OnNext(); diff --git a/src/DynamicData.Tests/List/GroupOnFixture.cs b/src/DynamicData.Tests/List/GroupOnFixture.cs index 248828216..2373ea139 100644 --- a/src/DynamicData.Tests/List/GroupOnFixture.cs +++ b/src/DynamicData.Tests/List/GroupOnFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class GroupOnFixture: IDisposable { private readonly ISourceList _source; @@ -18,7 +18,6 @@ public GroupOnFixture() _results = _source.Connect().GroupOn(p => p.Age).AsAggregator(); } - public void Dispose() { _source.Dispose(); diff --git a/src/DynamicData.Tests/List/GroupOnPropertyFixture.cs b/src/DynamicData.Tests/List/GroupOnPropertyFixture.cs index 88de5c4f9..183805e3d 100644 --- a/src/DynamicData.Tests/List/GroupOnPropertyFixture.cs +++ b/src/DynamicData.Tests/List/GroupOnPropertyFixture.cs @@ -7,13 +7,12 @@ namespace DynamicData.Tests.List { - + public class GroupOnPropertyFixture: IDisposable { private readonly ISourceList _source; private readonly ChangeSetAggregator> _results; - public GroupOnPropertyFixture() { _source = new SourceList(); @@ -68,7 +67,7 @@ public void CanHandleAddBatch() { var generator = new RandomPersonGenerator(); var people = generator.Take(1000).ToArray(); - + _source.AddRange(people); var expectedGroupCount = people.Select(p => p.Age).Distinct().Count(); @@ -89,7 +88,6 @@ public void CanHandleChangedItemsBatch() people.Take(25) .ForEach(p=>p.Age=200); - var changedCount = people.Select(p => p.Age).Distinct().Count(); _results.Data.Count.Should().Be(changedCount); diff --git a/src/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs b/src/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs index 36f34b100..343d5d6c7 100644 --- a/src/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs +++ b/src/DynamicData.Tests/List/GroupOnPropertyWithImmutableStateFixture.cs @@ -7,13 +7,12 @@ namespace DynamicData.Tests.List { - + public class GroupOnPropertyWithImmutableStateFixture: IDisposable { private readonly ISourceList _source; private readonly ChangeSetAggregator> _results; - public GroupOnPropertyWithImmutableStateFixture() { _source = new SourceList(); @@ -89,7 +88,6 @@ public void CanHandleChangedItemsBatch() people.Take(25) .ForEach(p => p.Age = 200); - var changedCount = people.Select(p => p.Age).Distinct().Count(); _results.Data.Count.Should().Be(changedCount); diff --git a/src/DynamicData.Tests/List/MergeManyFixture.cs b/src/DynamicData.Tests/List/MergeManyFixture.cs index 649a12efb..2ac3da534 100644 --- a/src/DynamicData.Tests/List/MergeManyFixture.cs +++ b/src/DynamicData.Tests/List/MergeManyFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class MergeManyFixture: IDisposable { private class ObjectWithObservable diff --git a/src/DynamicData.Tests/List/OrFixture.cs b/src/DynamicData.Tests/List/OrFixture.cs index dd3a0f81b..f241e7483 100644 --- a/src/DynamicData.Tests/List/OrFixture.cs +++ b/src/DynamicData.Tests/List/OrFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class OrFixture : OrFixtureBase { protected override IObservable> CreateObservable() @@ -15,7 +15,6 @@ protected override IObservable> CreateObservable() } } - public class OrCollectionFixture : OrFixtureBase { protected override IObservable> CreateObservable() @@ -34,7 +33,7 @@ public void RefreshPassesThrough() source1.Add(new Item("A")); SourceList source2 = new SourceList(); source2.Add(new Item("B")); - + var list = new List>> { source1.Connect().AutoRefresh(), source2.Connect().AutoRefresh() }; var results = list.Or().AsAggregator(); source1.Items.ElementAt(0).Name = "Test"; @@ -52,7 +51,6 @@ public abstract class OrFixtureBase: IDisposable protected ISourceList _source2; private readonly ChangeSetAggregator _results; - protected OrFixtureBase() { _source1 = new SourceList(); diff --git a/src/DynamicData.Tests/List/PageFixture.cs b/src/DynamicData.Tests/List/PageFixture.cs index 3d83a02f8..a1f5b3c30 100644 --- a/src/DynamicData.Tests/List/PageFixture.cs +++ b/src/DynamicData.Tests/List/PageFixture.cs @@ -9,7 +9,7 @@ namespace DynamicData.Tests.List { - + public class PageFixture: IDisposable { private readonly ISourceList _source; @@ -156,7 +156,7 @@ public void SimplePagging() pager.OnNext(new PageRequest(1, 2)); sut.Paged.Count.Should().Be(2); - pager.OnNext(new PageRequest(1, 4)); + pager.OnNext(new PageRequest(1, 4)); sut.Paged.Count.Should().Be(4); pager.OnNext(new PageRequest(2, 3)); @@ -187,5 +187,4 @@ public void Dispose() } } - } diff --git a/src/DynamicData.Tests/List/QueryWhenChangedFixture.cs b/src/DynamicData.Tests/List/QueryWhenChangedFixture.cs index 2e73cc651..1af18efc7 100644 --- a/src/DynamicData.Tests/List/QueryWhenChangedFixture.cs +++ b/src/DynamicData.Tests/List/QueryWhenChangedFixture.cs @@ -5,7 +5,7 @@ namespace DynamicData.Tests.List { - + public class QueryWhenChangedFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/RecursiveTransformManyFixture.cs b/src/DynamicData.Tests/List/RecursiveTransformManyFixture.cs index 902c775fe..20fadfe41 100644 --- a/src/DynamicData.Tests/List/RecursiveTransformManyFixture.cs +++ b/src/DynamicData.Tests/List/RecursiveTransformManyFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class RecursiveTransformManyFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/RefCountFixture.cs b/src/DynamicData.Tests/List/RefCountFixture.cs index 4dc89ac0b..104b12008 100644 --- a/src/DynamicData.Tests/List/RefCountFixture.cs +++ b/src/DynamicData.Tests/List/RefCountFixture.cs @@ -8,12 +8,11 @@ namespace DynamicData.Tests.List { - + public class RefCountFixture: IDisposable { private readonly ISourceList _source; - public RefCountFixture() { _source = new SourceList(); diff --git a/src/DynamicData.Tests/List/RemoveManyFixture.cs b/src/DynamicData.Tests/List/RemoveManyFixture.cs index d6d77c71e..aa4bdf201 100644 --- a/src/DynamicData.Tests/List/RemoveManyFixture.cs +++ b/src/DynamicData.Tests/List/RemoveManyFixture.cs @@ -6,12 +6,11 @@ namespace DynamicData.Tests.List { - + public class RemoveManyFixture { private readonly List _list; - public RemoveManyFixture() { _list = new List(); diff --git a/src/DynamicData.Tests/List/ReverseFixture.cs b/src/DynamicData.Tests/List/ReverseFixture.cs index f6951f0b2..9a76b6927 100644 --- a/src/DynamicData.Tests/List/ReverseFixture.cs +++ b/src/DynamicData.Tests/List/ReverseFixture.cs @@ -5,13 +5,12 @@ namespace DynamicData.Tests.List { - + public class ReverseFixture: IDisposable { private readonly ISourceList _source; private readonly ChangeSetAggregator _results; - public ReverseFixture() { _source = new SourceList(); diff --git a/src/DynamicData.Tests/List/SelectFixture.cs b/src/DynamicData.Tests/List/SelectFixture.cs index a037ceaab..859fdac9c 100644 --- a/src/DynamicData.Tests/List/SelectFixture.cs +++ b/src/DynamicData.Tests/List/SelectFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class SelectFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/SizeLimitFixture.cs b/src/DynamicData.Tests/List/SizeLimitFixture.cs index 83169af95..cfae9f31e 100644 --- a/src/DynamicData.Tests/List/SizeLimitFixture.cs +++ b/src/DynamicData.Tests/List/SizeLimitFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class SizeLimitFixture: IDisposable { private readonly ISourceList _source; @@ -16,7 +16,6 @@ public class SizeLimitFixture: IDisposable private readonly IDisposable _sizeLimiter; private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); - public SizeLimitFixture() { _scheduler = new TestScheduler(); diff --git a/src/DynamicData.Tests/List/SortMutableFixture.cs b/src/DynamicData.Tests/List/SortMutableFixture.cs index 9fc9edb63..41090f093 100644 --- a/src/DynamicData.Tests/List/SortMutableFixture.cs +++ b/src/DynamicData.Tests/List/SortMutableFixture.cs @@ -12,7 +12,7 @@ namespace DynamicData.Tests.List { - + public class SortMutableFixture: IDisposable { private readonly RandomPersonGenerator _generator = new RandomPersonGenerator(); @@ -21,7 +21,6 @@ public class SortMutableFixture: IDisposable private readonly ISubject _resort; private readonly ChangeSetAggregator _results; - private readonly IComparer _comparer = SortExpressionComparer .Ascending(p => p.Age) .ThenByAscending(p => p.Name); @@ -64,7 +63,6 @@ public void Insert() var shouldbeLast = new Person("__A", 10000); _source.Add(shouldbeLast); - _results.Data.Count.Should().Be(101); _results.Data.Items.Last().Should().Be(shouldbeLast); @@ -141,7 +139,6 @@ public void ResortOnInlineChanges() var people = _generator.Take(10).ToList(); _source.AddRange(people); - people[0].Age = -1; people[1].Age = -10; people[2].Age = -12; @@ -149,7 +146,6 @@ public void ResortOnInlineChanges() people[4].Age = -7; people[5].Age = -6; - var comparer = SortExpressionComparer.Descending(p => p.Age) .ThenByAscending(p => p.Name); @@ -162,7 +158,6 @@ public void ResortOnInlineChanges() actualResult.ShouldAllBeEquivalentTo(expectedResult); } - [Fact] public void RemoveManyOdds() { @@ -218,7 +213,6 @@ public void ChangeComparer() actualResult.ShouldAllBeEquivalentTo(expectedResult); } - [Fact] public void UpdateMoreThanThreshold() { diff --git a/src/DynamicData.Tests/List/SortPrimitiveFixture.cs b/src/DynamicData.Tests/List/SortPrimitiveFixture.cs index 7131bf50a..82e48aa19 100644 --- a/src/DynamicData.Tests/List/SortPrimitiveFixture.cs +++ b/src/DynamicData.Tests/List/SortPrimitiveFixture.cs @@ -45,9 +45,7 @@ public void RemoveRandomSorts() _source.Remove(i); } - } - } } \ No newline at end of file diff --git a/src/DynamicData.Tests/List/SourceListPreviewFixture.cs b/src/DynamicData.Tests/List/SourceListPreviewFixture.cs index e06d963f8..e373f1d63 100644 --- a/src/DynamicData.Tests/List/SourceListPreviewFixture.cs +++ b/src/DynamicData.Tests/List/SourceListPreviewFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List public class SourceListPreviewFixture : IDisposable { private readonly ISourceList _source; - + public SourceListPreviewFixture() { _source = new SourceList(); @@ -128,7 +128,6 @@ public void FormNewListFromChanges() { _source.Clear(); - _source.AddRange(Enumerable.Range(1,100)); // Collect preview messages about even numbers only diff --git a/src/DynamicData.Tests/List/SubscribeManyFixture.cs b/src/DynamicData.Tests/List/SubscribeManyFixture.cs index 355581bba..91a6b8754 100644 --- a/src/DynamicData.Tests/List/SubscribeManyFixture.cs +++ b/src/DynamicData.Tests/List/SubscribeManyFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class SubscribeManyFixture: IDisposable { private class SubscribeableObject diff --git a/src/DynamicData.Tests/List/SwitchFixture.cs b/src/DynamicData.Tests/List/SwitchFixture.cs index af48e25f5..eeaae0a2a 100644 --- a/src/DynamicData.Tests/List/SwitchFixture.cs +++ b/src/DynamicData.Tests/List/SwitchFixture.cs @@ -7,14 +7,13 @@ namespace DynamicData.Tests.List { - + public class SwitchFixture: IDisposable { private readonly ISubject> _switchable; private readonly ISourceList _source; private readonly ChangeSetAggregator _results; - public SwitchFixture() { _source = new SourceList(); diff --git a/src/DynamicData.Tests/List/TransformAsyncFixture.cs b/src/DynamicData.Tests/List/TransformAsyncFixture.cs index 310484cf2..ef447dfd1 100644 --- a/src/DynamicData.Tests/List/TransformAsyncFixture.cs +++ b/src/DynamicData.Tests/List/TransformAsyncFixture.cs @@ -2,10 +2,9 @@ using System.Threading.Tasks; using DynamicData.Tests.Domain; - namespace DynamicData.Tests.List { - + [Obsolete("Not obsolete - test commented out due to test run freezing on Appveyor")] public class TransformAsyncFixture: IDisposable { diff --git a/src/DynamicData.Tests/List/TransformFixture.cs b/src/DynamicData.Tests/List/TransformFixture.cs index 2f5dee286..c6572d7ef 100644 --- a/src/DynamicData.Tests/List/TransformFixture.cs +++ b/src/DynamicData.Tests/List/TransformFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class TransformFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/TransformManyFixture.cs b/src/DynamicData.Tests/List/TransformManyFixture.cs index e5eddce29..15950f29d 100644 --- a/src/DynamicData.Tests/List/TransformManyFixture.cs +++ b/src/DynamicData.Tests/List/TransformManyFixture.cs @@ -8,7 +8,7 @@ namespace DynamicData.Tests.List { - + public class TransformManyFixture: IDisposable { private readonly ISourceList _source; @@ -53,7 +53,6 @@ public void RemoveParent() var child3 = new PersonWithRelations("Child3", 8); var mother = new PersonWithRelations("Mother", 35, new[] {child1, child2, child3}); - _source.Add(mother); _source.Remove(mother); _results.Data.Count.Should().Be(0); @@ -95,7 +94,6 @@ public void AddRange() var child5 = new PersonWithRelations("Child5", 2); var anotherRelative1 = new PersonWithRelations("Another1", 2, new[] { child4, child5 }); - var child6 = new PersonWithRelations("Child6", 1); var child7 = new PersonWithRelations("Child7", 2); var anotherRelative2 = new PersonWithRelations("Another2", 2, new[] { child6, child7 }); @@ -129,7 +127,6 @@ public void RemoveRange() } - [Fact] public void Clear() { @@ -182,7 +179,6 @@ public void Remove() .TransformMany(tourProvider => tourProvider.Tours) .AsObservableList(); - var tour1_1 = new Tour("Tour 1.1"); var tour2_1 = new Tour("Tour 2.1"); var tour2_2 = new Tour("Tour 2.2"); @@ -206,8 +202,6 @@ public void Remove() allTours.Items.ShouldAllBeEquivalentTo(new[] { tour1_1, tour2_1, tour2_2, tour3_1 }); } - - public class TourProvider { public TourProvider(string name, IEnumerable tours) @@ -215,7 +209,9 @@ public TourProvider(string name, IEnumerable tours) Name = name; if (tours != null) + { Tours.AddRange(tours); + } } public string Name { get; } diff --git a/src/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs b/src/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs index ca673b3d8..2722e2e94 100644 --- a/src/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs +++ b/src/DynamicData.Tests/List/TransformManyObservableCollectionFixture.cs @@ -6,7 +6,7 @@ using Xunit; namespace DynamicData.Tests.List -{ +{ public class TransformManyObservableCollectionFixture { [Fact] @@ -28,7 +28,6 @@ public void FlattenObservableCollection() return parent; }).ToArray(); - using (var source = new SourceList()) using (var aggregator = source.Connect() .TransformMany(p => p.Children) @@ -46,7 +45,6 @@ public void FlattenObservableCollection() source.RemoveAt(0); aggregator.Data.Count.Should().Be(98); - //check items can be cleared and then added back in var childrenInZero = parents[1].Children.ToArray(); parents[1].Children.Clear(); @@ -84,7 +82,6 @@ public void FlattenReadOnlyObservableCollection() return parent; }).ToArray(); - using (var source = new SourceList()) using (var aggregator = source.Connect() .TransformMany(p => p.ChildrenReadonly) @@ -102,7 +99,6 @@ public void FlattenReadOnlyObservableCollection() source.RemoveAt(0); aggregator.Data.Count.Should().Be(98); - //check items can be cleared and then added back in var childrenInZero = parents[1].Children.ToArray(); parents[1].Children.Clear(); @@ -141,7 +137,6 @@ public void FlattenObservableList() return parent; }).ToArray(); - using (var source = new SourceList()) using (var aggregator = source.Connect() .TransformMany(p => p.ChildrenObservable) @@ -161,7 +156,6 @@ public void FlattenObservableList() source.RemoveAt(0); aggregator.Data.Count.Should().Be(98); - //check items can be cleared and then added back in var childrenInZero = parents[1].Children.Items.ToArray(); parents[1].Children.Clear(); @@ -252,7 +246,7 @@ private class Parent { public ObservableCollection Children { get; } public ReadOnlyObservableCollection ChildrenReadonly { get; } - + public Parent(int id, IEnumerable children) { Children = new ObservableCollection(children); diff --git a/src/DynamicData.Tests/List/VirtualisationFixture.cs b/src/DynamicData.Tests/List/VirtualisationFixture.cs index 8084b6599..ae8c037af 100644 --- a/src/DynamicData.Tests/List/VirtualisationFixture.cs +++ b/src/DynamicData.Tests/List/VirtualisationFixture.cs @@ -7,7 +7,7 @@ namespace DynamicData.Tests.List { - + public class VirtualisationFixture: IDisposable { private readonly ISourceList _source; diff --git a/src/DynamicData.Tests/List/XOrFixture.cs b/src/DynamicData.Tests/List/XOrFixture.cs index 9306a0966..9222f90a6 100644 --- a/src/DynamicData.Tests/List/XOrFixture.cs +++ b/src/DynamicData.Tests/List/XOrFixture.cs @@ -6,7 +6,7 @@ namespace DynamicData.Tests.List { - + public class XOrFixture : XOrFixtureBase { protected override IObservable> CreateObservable() @@ -15,7 +15,6 @@ protected override IObservable> CreateObservable() } } - public class XOrCollectionFixture : XOrFixtureBase { protected override IObservable> CreateObservable() @@ -25,7 +24,6 @@ protected override IObservable> CreateObservable() } } - public abstract class XOrFixtureBase: IDisposable { protected ISourceList _source1; diff --git a/src/DynamicData.Tests/Utilities/SelectManyExtensions.cs b/src/DynamicData.Tests/Utilities/SelectManyExtensions.cs index 51e216aec..fc780573b 100644 --- a/src/DynamicData.Tests/Utilities/SelectManyExtensions.cs +++ b/src/DynamicData.Tests/Utilities/SelectManyExtensions.cs @@ -11,8 +11,15 @@ public static class SelectManyExtensions { public static IEnumerable SelectManyRecursive(this IEnumerable source, Func> selector) { - if (source == null) throw new ArgumentNullException("source"); - if (selector == null) throw new ArgumentNullException("selector"); + if (source == null) + { + throw new ArgumentNullException("source"); + } + + if (selector == null) + { + throw new ArgumentNullException("selector"); + } T[] selectManyRecursive = source as T[] ?? source.ToArray(); return !selectManyRecursive.Any() diff --git a/src/DynamicData.sln b/src/DynamicData.sln index 08732c71e..33056ac49 100644 --- a/src/DynamicData.sln +++ b/src/DynamicData.sln @@ -1,12 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.3 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29025.244 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData", "DynamicData\DynamicData.csproj", "{FE903921-6C55-40E3-9A16-4127ECACC12C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".other", ".other", "{DFCC45F8-251C-4D78-AF9B-15C1EDF0E603}" ProjectSection(SolutionItems) = preProject + analyzers.ruleset = analyzers.ruleset appveyor.yml = appveyor.yml Directory.Build.props = Directory.Build.props Directory.build.targets = Directory.build.targets @@ -14,14 +15,11 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".other", ".other", "{DFCC45 NuGet.Config = NuGet.Config README.md = README.md ReleaseNotes.md = ReleaseNotes.md + stylecop.json = stylecop.json EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Tests", "DynamicData.Tests\DynamicData.Tests.csproj", "{1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.ReactiveUI", "DynamicData.ReactiveUI\DynamicData.ReactiveUI.csproj", "{EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.ReactiveUI.Tests", "DynamicData.ReactiveUI.Tests\DynamicData.ReactiveUI.Tests.csproj", "{9F0897D7-B92A-4162-AEA5-C143614C5904}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Profile", "DynamicData.Profile\DynamicData.Profile.csproj", "{7A1B3B5B-50A0-491F-BC2F-513299DEE82D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DynamicData.Benchmarks", "DynamicData.Benchmarks\DynamicData.Benchmarks.csproj", "{42566F48-05FC-483E-8B2F-D0EA4F28E870}" @@ -66,38 +64,6 @@ Global {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x64.Build.0 = Release|Any CPU {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x86.ActiveCfg = Release|Any CPU {1D53F0EE-F579-4398-82E5-ECAB2AA70BF1}.Release|x86.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x64.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x64.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x86.ActiveCfg = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Debug|x86.Build.0 = Debug|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Any CPU.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x64.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x64.Build.0 = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x86.ActiveCfg = Release|Any CPU - {EF8A5EB3-8C27-4D96-B7A5-D7CB92F50315}.Release|x86.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x64.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x64.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x86.ActiveCfg = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Debug|x86.Build.0 = Debug|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Any CPU.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|Mixed Platforms.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x64.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x64.Build.0 = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x86.ActiveCfg = Release|Any CPU - {9F0897D7-B92A-4162-AEA5-C143614C5904}.Release|x86.Build.0 = Release|Any CPU {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Any CPU.Build.0 = Debug|Any CPU {7A1B3B5B-50A0-491F-BC2F-513299DEE82D}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU diff --git a/src/DynamicData/Aggregation/AggregateEnumerator.cs b/src/DynamicData/Aggregation/AggregateEnumerator.cs index a6c6d3a89..64c4abe9b 100644 --- a/src/DynamicData/Aggregation/AggregateEnumerator.cs +++ b/src/DynamicData/Aggregation/AggregateEnumerator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections; using System.Collections.Generic; @@ -23,7 +27,10 @@ public IEnumerator> GetEnumerator() break; case ListChangeReason.AddRange: foreach (var item in change.Range) + { yield return new AggregateItem(AggregateType.Add, item); + } + break; case ListChangeReason.Replace: yield return new AggregateItem(AggregateType.Remove, change.Item.Previous.Value); @@ -35,7 +42,10 @@ public IEnumerator> GetEnumerator() case ListChangeReason.RemoveRange: case ListChangeReason.Clear: foreach (var item in change.Range) + { yield return new AggregateItem(AggregateType.Remove, item); + } + break; } } diff --git a/src/DynamicData/Aggregation/AggregateItem.cs b/src/DynamicData/Aggregation/AggregateItem.cs index 016a85d41..713608197 100644 --- a/src/DynamicData/Aggregation/AggregateItem.cs +++ b/src/DynamicData/Aggregation/AggregateItem.cs @@ -1,11 +1,17 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; +using System.Collections.Generic; + namespace DynamicData.Aggregation { /// /// An object representing added and removed items in a continuous aggregation stream /// /// The type of the object. - public readonly struct AggregateItem + public readonly struct AggregateItem : IEquatable> { /// /// Initializes a new instance of the struct. @@ -27,5 +33,34 @@ public AggregateItem(AggregateType type, TObject item) /// Gets the item. /// public TObject Item { get; } + + public override bool Equals(object obj) + { + return obj is AggregateItem item && Equals(item); + } + + public bool Equals(AggregateItem other) + { + return Type == other.Type && + EqualityComparer.Default.Equals(Item, other.Item); + } + + public override int GetHashCode() + { + var hashCode = -1719135621; + hashCode = hashCode * -1521134295 + Type.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Item); + return hashCode; + } + + public static bool operator ==(AggregateItem left, AggregateItem right) + { + return left.Equals(right); + } + + public static bool operator !=(AggregateItem left, AggregateItem right) + { + return !(left == right); + } } } diff --git a/src/DynamicData/Aggregation/AggregateType.cs b/src/DynamicData/Aggregation/AggregateType.cs index dd92e231e..c652028e6 100644 --- a/src/DynamicData/Aggregation/AggregateType.cs +++ b/src/DynamicData/Aggregation/AggregateType.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData.Aggregation { /// diff --git a/src/DynamicData/Aggregation/AggregationEx.cs b/src/DynamicData/Aggregation/AggregationEx.cs index eb4778c62..a7b7f22d5 100644 --- a/src/DynamicData/Aggregation/AggregationEx.cs +++ b/src/DynamicData/Aggregation/AggregationEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; using System.Reactive; @@ -21,7 +25,11 @@ public static class AggregationEx /// public static IObservable> ForAggregation([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Select(changeset => (IAggregateChangeSet)new AggregateEnumerator(changeset)); } @@ -34,7 +42,11 @@ public static IObservable> ForAggregation public static IObservable> ForAggregation([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Select(changeset => (IAggregateChangeSet)new AggregateEnumerator(changeset)); } @@ -99,10 +111,25 @@ internal static IObservable Accumlate([NotNull] this [NotNull] Func addAction, [NotNull] Func removeAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (accessor == null) throw new ArgumentNullException(nameof(accessor)); - if (addAction == null) throw new ArgumentNullException(nameof(addAction)); - if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (accessor == null) + { + throw new ArgumentNullException(nameof(accessor)); + } + + if (addAction == null) + { + throw new ArgumentNullException(nameof(addAction)); + } + + if (removeAction == null) + { + throw new ArgumentNullException(nameof(removeAction)); + } return source.Scan(seed, (state, changes) => { diff --git a/src/DynamicData/Aggregation/Avg.cs b/src/DynamicData/Aggregation/Avg.cs index 10dfdcb2a..c856674b9 100644 --- a/src/DynamicData/Aggregation/Avg.cs +++ b/src/DynamicData/Aggregation/Avg.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData.Aggregation { internal readonly struct Avg diff --git a/src/DynamicData/Aggregation/AvgEx.cs b/src/DynamicData/Aggregation/AvgEx.cs index 662586682..e279ee446 100644 --- a/src/DynamicData/Aggregation/AvgEx.cs +++ b/src/DynamicData/Aggregation/AvgEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; using System.Reactive.Linq; @@ -5,9 +9,9 @@ namespace DynamicData.Aggregation { - /// + /// /// Average extensions - /// + /// public static class AvgEx { #region From IChangeSet @@ -517,11 +521,30 @@ private static IObservable AvgCalc(this IObse [NotNull] Func, TValue, Avg> removeAction, [NotNull] Func, TResult> resultAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); - if (addAction == null) throw new ArgumentNullException(nameof(addAction)); - if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); - if (resultAction == null) throw new ArgumentNullException(nameof(resultAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + + if (addAction == null) + { + throw new ArgumentNullException(nameof(addAction)); + } + + if (removeAction == null) + { + throw new ArgumentNullException(nameof(removeAction)); + } + + if (resultAction == null) + { + throw new ArgumentNullException(nameof(resultAction)); + } return source.Scan(default(Avg), (state, changes) => { diff --git a/src/DynamicData/Aggregation/CountEx.cs b/src/DynamicData/Aggregation/CountEx.cs index e4f40480f..83c4dd855 100644 --- a/src/DynamicData/Aggregation/CountEx.cs +++ b/src/DynamicData/Aggregation/CountEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; namespace DynamicData.Aggregation { diff --git a/src/DynamicData/Aggregation/IAggregateChangeSet.cs b/src/DynamicData/Aggregation/IAggregateChangeSet.cs index 6c17b21f7..be41d0737 100644 --- a/src/DynamicData/Aggregation/IAggregateChangeSet.cs +++ b/src/DynamicData/Aggregation/IAggregateChangeSet.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; namespace DynamicData.Aggregation diff --git a/src/DynamicData/Aggregation/MaxEx.cs b/src/DynamicData/Aggregation/MaxEx.cs index c367b7cd7..a2a073e97 100644 --- a/src/DynamicData/Aggregation/MaxEx.cs +++ b/src/DynamicData/Aggregation/MaxEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; @@ -92,8 +96,16 @@ private static IObservable Calculate([NotNull] this I TResult emptyValue = default(TResult)) where TResult : struct, IComparable { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Scan(default(TResult?), (state, latest) => { var current = state; @@ -111,13 +123,19 @@ private static IObservable Calculate([NotNull] this I { int isMatched = maxOrMin == MaxOrMin.Max ? 1 : -1; if (value.CompareTo(current.Value) == isMatched) + { current = value; + } } else { //check whether the max / min has been removed. If so we need to look //up the latest from the underlying collection - if (value.CompareTo(current.Value) != 0) continue; + if (value.CompareTo(current.Value) != 0) + { + continue; + } + requiresReset = true; break; } @@ -137,6 +155,7 @@ private static IObservable Calculate([NotNull] this I : collecton.Min(valueSelector); } } + return current; }) .Select(t => t ?? emptyValue) @@ -147,7 +166,11 @@ private static IObservable Calculate([NotNull] this I private static IObservable> ToChangesAndCollection([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Publish(shared => { var changes = shared.ForAggregation(); @@ -158,7 +181,11 @@ private static IObservable> ToChangesAndCollection private static IObservable> ToChangesAndCollection([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Publish(shared => { var changes = shared.ForAggregation(); diff --git a/src/DynamicData/Aggregation/StdDev.cs b/src/DynamicData/Aggregation/StdDev.cs index 173739c06..0eca634ec 100644 --- a/src/DynamicData/Aggregation/StdDev.cs +++ b/src/DynamicData/Aggregation/StdDev.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. namespace DynamicData.Aggregation { diff --git a/src/DynamicData/Aggregation/StdDevEx.cs b/src/DynamicData/Aggregation/StdDevEx.cs index f3a77a033..8aa8fedc8 100644 --- a/src/DynamicData/Aggregation/StdDevEx.cs +++ b/src/DynamicData/Aggregation/StdDevEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using System.Reactive.Linq; using DynamicData.Annotations; @@ -279,11 +283,30 @@ private static IObservable StdDevCalc(this IO [NotNull] Func, TValue, StdDev> removeAction, [NotNull] Func, TResult> resultAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); - if (addAction == null) throw new ArgumentNullException(nameof(addAction)); - if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); - if (resultAction == null) throw new ArgumentNullException(nameof(resultAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + + if (addAction == null) + { + throw new ArgumentNullException(nameof(addAction)); + } + + if (removeAction == null) + { + throw new ArgumentNullException(nameof(removeAction)); + } + + if (resultAction == null) + { + throw new ArgumentNullException(nameof(resultAction)); + } return source.Scan(default(StdDev), (state, changes) => { diff --git a/src/DynamicData/Aggregation/SumEx.cs b/src/DynamicData/Aggregation/SumEx.cs index e863f466a..b21dbcb50 100644 --- a/src/DynamicData/Aggregation/SumEx.cs +++ b/src/DynamicData/Aggregation/SumEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using DynamicData.Annotations; using DynamicData.Kernel; @@ -289,8 +293,16 @@ public static IObservable Sum([NotNull] this IObservable public static IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0, valueSelector, (current, value) => current + value, @@ -322,8 +334,16 @@ public static IObservable Sum([NotNull] this IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0, valueSelector, (current, value) => current + value, @@ -339,8 +359,16 @@ public static IObservable Sum([NotNull] this IObservable public static IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0L, t => valueSelector(t).ValueOr(0), (current, value) => current + value, @@ -357,8 +385,16 @@ public static IObservable Sum([NotNull] this IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0, valueSelector, (current, value) => current + value, @@ -374,8 +410,16 @@ public static IObservable Sum([NotNull] this IObservable public static IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0D, t => valueSelector(t).ValueOr(0), (current, value) => current + value, @@ -392,8 +436,16 @@ public static IObservable Sum([NotNull] this IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0, valueSelector, (current, value) => current + value, @@ -409,8 +461,16 @@ public static IObservable Sum([NotNull] this IObservable public static IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0M, t => valueSelector(t).ValueOr(0), (current, value) => current + value, @@ -427,8 +487,16 @@ public static IObservable Sum([NotNull] this IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0, valueSelector, (current, value) => current + value, @@ -444,8 +512,16 @@ public static IObservable Sum([NotNull] this IObservable public static IObservable Sum([NotNull] this IObservable> source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.Accumlate(0F, t => valueSelector(t).ValueOr(0), (current, value) => current + value, diff --git a/src/DynamicData/Alias/ObservableCacheAlias.cs b/src/DynamicData/Alias/ObservableCacheAlias.cs index 3a8179e4b..973e53a6f 100644 --- a/src/DynamicData/Alias/ObservableCacheAlias.cs +++ b/src/DynamicData/Alias/ObservableCacheAlias.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Reactive; using DynamicData.Annotations; @@ -23,7 +27,11 @@ public static class ObservableCacheAlias /// public static IObservable> Where(this IObservable> source, Func filter) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Filter(filter); } @@ -38,8 +46,16 @@ public static IObservable> Where(this I public static IObservable> Where([NotNull] this IObservable> source, [NotNull] IObservable> predicateChanged) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicateChanged == null) throw new ArgumentNullException(nameof(predicateChanged)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicateChanged == null) + { + throw new ArgumentNullException(nameof(predicateChanged)); + } + return source.Filter(predicateChanged); } @@ -56,12 +72,19 @@ public static IObservable> Where([NotNu public static IObservable> Where([NotNull] this IObservable> source, [NotNull] IObservable reapplyFilter) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (reapplyFilter == null) throw new ArgumentNullException(nameof(reapplyFilter)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (reapplyFilter == null) + { + throw new ArgumentNullException(nameof(reapplyFilter)); + } + return source.Filter(reapplyFilter); } - /// /// Creates a filtered stream which can be dynamically filtered /// @@ -75,18 +98,28 @@ public static IObservable> Where([NotNu [NotNull] IObservable> predicateChanged, [NotNull] IObservable reapplyFilter) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicateChanged == null) throw new ArgumentNullException(nameof(predicateChanged)); - if (reapplyFilter == null) throw new ArgumentNullException(nameof(reapplyFilter)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicateChanged == null) + { + throw new ArgumentNullException(nameof(predicateChanged)); + } + + if (reapplyFilter == null) + { + throw new ArgumentNullException(nameof(reapplyFilter)); + } + return source.Filter(predicateChanged, reapplyFilter); } - #endregion #region Transform -> Select - /// /// Projects each update item to a new form using the specified transform function /// @@ -106,9 +139,21 @@ public static IObservable> Select transformFactory, IObservable forceTransform) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (forceTransform == null) throw new ArgumentNullException(nameof(forceTransform)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (forceTransform == null) + { + throw new ArgumentNullException(nameof(forceTransform)); + } + return source.Transform(transformFactory, forceTransform); } @@ -175,8 +220,15 @@ public static IObservable> Select transformFactory, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.Transform(transformFactory, forceTransform); } @@ -193,8 +245,16 @@ public static IObservable, TKey>> SelectTree pivotOn) where TObject : class { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pivotOn == null) throw new ArgumentNullException(nameof(pivotOn)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (pivotOn == null) + { + throw new ArgumentNullException(nameof(pivotOn)); + } + return source.TransformToTree(pivotOn); } @@ -202,7 +262,6 @@ public static IObservable, TKey>> SelectTree SelectMany - /// /// Equivalent to a select many transform. To work, the key must individually identify each child. /// @@ -221,8 +280,6 @@ public static IObservable> SelectMany< return source.TransformMany(manyselector, keySelector); } - - #endregion #region Transform safe -> SelectSafe @@ -251,10 +308,25 @@ public static IObservable> SelectSafe> errorHandler, IObservable forceTransform) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); - if (forceTransform == null) throw new ArgumentNullException(nameof(forceTransform)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } + + if (forceTransform == null) + { + throw new ArgumentNullException(nameof(forceTransform)); + } return source.TransformSafe(transformFactory, errorHandler, forceTransform); } @@ -283,9 +355,20 @@ public static IObservable> SelectSafe> errorHandler, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } return source.TransformSafe(transformFactory, errorHandler, forceTransform); } @@ -314,9 +397,20 @@ public static IObservable> SelectSafe> errorHandler, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } return source.TransformSafe(transformFactory, errorHandler, forceTransform); } diff --git a/src/DynamicData/Alias/ObservableListAlias.cs b/src/DynamicData/Alias/ObservableListAlias.cs index d5d6e7bc2..a839dc1c0 100644 --- a/src/DynamicData/Alias/ObservableListAlias.cs +++ b/src/DynamicData/Alias/ObservableListAlias.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using DynamicData.Annotations; @@ -11,7 +15,6 @@ public static class ObservableListAlias { #region Filter -> Where - /// /// Filters the source using the specified valueSelector /// @@ -22,12 +25,19 @@ public static class ObservableListAlias /// source public static IObservable> Where(this IObservable> source, Func predicate) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + return source.Filter(predicate); } - /// /// Filters source using the specified filter observable predicate. /// @@ -40,18 +50,23 @@ public static IObservable> Where(this IObservable /// filterController public static IObservable> Where([NotNull] this IObservable> source, [NotNull] IObservable> predicate) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } return source.Filter(predicate); } - #endregion #region Transform -> Select - /// /// Projects each update item to a new form using the specified transform function /// @@ -67,8 +82,15 @@ public static IObservable> Where([NotNull] this IObservable public static IObservable> Select(this IObservable> source, Func transformFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.Transform(transformFactory); } @@ -89,8 +111,16 @@ public static IObservable> Select public static IObservable> SelectMany([NotNull] this IObservable> source, [NotNull] Func> manyselector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (manyselector == null) throw new ArgumentNullException(nameof(manyselector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (manyselector == null) + { + throw new ArgumentNullException(nameof(manyselector)); + } + return source.TransformMany(manyselector); } diff --git a/src/DynamicData/Attributes.cs b/src/DynamicData/Attributes.cs index 993ec2b2a..5d2d3ef98 100644 --- a/src/DynamicData/Attributes.cs +++ b/src/DynamicData/Attributes.cs @@ -1,4 +1,8 @@ -using System.Runtime.CompilerServices; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("DynamicData.Tests")] [assembly: InternalsVisibleTo("DynamicData.ReactiveUI")] diff --git a/src/DynamicData/Binding/AbstractNotifyPropertyChanged.cs b/src/DynamicData/Binding/AbstractNotifyPropertyChanged.cs index e9f348787..66c188ee4 100644 --- a/src/DynamicData/Binding/AbstractNotifyPropertyChanged.cs +++ b/src/DynamicData/Binding/AbstractNotifyPropertyChanged.cs @@ -1,6 +1,11 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Disposables; using System.Runtime.CompilerServices; using DynamicData.Annotations; @@ -51,7 +56,11 @@ protected virtual void SetAndRaise(ref T backingField, T newValue, [CallerMem protected virtual void SetAndRaise(ref T backingField, T newValue, IEqualityComparer comparer, [CallerMemberName] string propertyName = null) { comparer = comparer ?? EqualityComparer.Default; - if (comparer.Equals(backingField, newValue)) return; + if (comparer.Equals(backingField, newValue)) + { + return; + } + backingField = newValue; OnPropertyChanged(propertyName); } @@ -62,6 +71,7 @@ protected virtual void SetAndRaise(ref T backingField, T newValue, IEqualityC /// /// [Obsolete("This never worked properly in the first place")] + [SuppressMessage("Design", "CA1822: Make static", Justification = "Backwards compatibility")] public IDisposable SuspendNotifications(bool invokePropertyChangeEventWhenDisposed = true) { diff --git a/src/DynamicData/Binding/BindingListAdaptor.cs b/src/DynamicData/Binding/BindingListAdaptor.cs index 7782e3937..16a9883a4 100644 --- a/src/DynamicData/Binding/BindingListAdaptor.cs +++ b/src/DynamicData/Binding/BindingListAdaptor.cs @@ -1,4 +1,8 @@ -#if SUPPORTS_BINDINGLIST +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +#if SUPPORTS_BINDINGLIST using System; using System.ComponentModel; @@ -26,6 +30,11 @@ public BindingListAdaptor([NotNull] BindingList list, int refreshThreshold = /// public void Adapt(IChangeSet changes) { + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } + if (changes.TotalChanges - changes.Refreshes > _refreshThreshold || !_loaded) { using (new BindingListEventsSuspender(_list)) diff --git a/src/DynamicData/Binding/BindingListEventsSuspender.cs b/src/DynamicData/Binding/BindingListEventsSuspender.cs index 940590312..e32509a6d 100644 --- a/src/DynamicData/Binding/BindingListEventsSuspender.cs +++ b/src/DynamicData/Binding/BindingListEventsSuspender.cs @@ -1,4 +1,8 @@ -#if SUPPORTS_BINDINGLIST +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +#if SUPPORTS_BINDINGLIST using System; using System.ComponentModel; diff --git a/src/DynamicData/Binding/ExpressionBuilder.cs b/src/DynamicData/Binding/ExpressionBuilder.cs index 95b8bb4b9..0d744e32e 100644 --- a/src/DynamicData/Binding/ExpressionBuilder.cs +++ b/src/DynamicData/Binding/ExpressionBuilder.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; @@ -21,10 +25,13 @@ IEnumerable GetNames() { yield return typeof(TObject).FullName; foreach (var member in members.Reverse()) + { yield return member.Member.Name; + } } + return string.Join(".",GetNames()); - } + } public static IEnumerable GetMembers(this Expression> source) { @@ -50,6 +57,7 @@ internal static IEnumerable GetMemberChain { yield return memberExpression; } + memberExpression = memberExpression.Expression as MemberExpression; } } @@ -59,7 +67,7 @@ internal static Func CreateValueAccessor(this MemberExpression s //create an expression which accepts the parent and returns the child var property = source.GetProperty(); var method = property.GetMethod; - + //convert the parameter i.e. the declaring class to an object var parameter = Expression.Parameter(typeof(object)); var converted = Expression.Convert(parameter, source.Expression.Type); @@ -80,8 +88,15 @@ internal static Func> CreatePropertyChangedFactory(thi return t => { - if (t == null) return Observable.Never; - if (!inpc) return Observable.Return(Unit.Default); + if (t == null) + { + return Observable.Never; + } + + if (!inpc) + { + return Observable.Return(Unit.Default); + } return Observable.FromEventPattern ( @@ -93,12 +108,13 @@ internal static Func> CreatePropertyChangedFactory(thi }; } - internal static PropertyInfo GetProperty(this Expression> expression) { var property = expression.GetMember() as PropertyInfo; if (property == null) + { throw new ArgumentException("Not a property expression"); + } return property; } @@ -107,7 +123,9 @@ internal static PropertyInfo GetProperty(this MemberExpression expression) { var property = expression.Member as PropertyInfo; if (property == null) + { throw new ArgumentException("Not a property expression"); + } return property; } @@ -115,7 +133,9 @@ internal static PropertyInfo GetProperty(this MemberExpression expression) internal static MemberInfo GetMember(this Expression> expression) { if (expression == null) + { throw new ArgumentException("Not a property expression"); + } return GetMemberInfo(expression); } @@ -123,7 +143,9 @@ internal static MemberInfo GetMember(this Expression diff --git a/src/DynamicData/Binding/INotifyCollectionChangedSuspender.cs b/src/DynamicData/Binding/INotifyCollectionChangedSuspender.cs index 5f6e9749a..d52bc887f 100644 --- a/src/DynamicData/Binding/INotifyCollectionChangedSuspender.cs +++ b/src/DynamicData/Binding/INotifyCollectionChangedSuspender.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; namespace DynamicData.Binding diff --git a/src/DynamicData/Binding/IObservableCollection.cs b/src/DynamicData/Binding/IObservableCollection.cs index e75565166..651eca7b5 100644 --- a/src/DynamicData/Binding/IObservableCollection.cs +++ b/src/DynamicData/Binding/IObservableCollection.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; using System.Collections.Specialized; using System.ComponentModel; @@ -13,7 +17,6 @@ public interface IObservableCollection : INotifyCollectionChanged, IList, INotifyCollectionChangedSuspender { - /// /// Moves the item at the specified index to a new location in the collection. diff --git a/src/DynamicData/Binding/IObservableCollectionAdaptor.cs b/src/DynamicData/Binding/IObservableCollectionAdaptor.cs index 3b79c782b..1f2e3ae7b 100644 --- a/src/DynamicData/Binding/IObservableCollectionAdaptor.cs +++ b/src/DynamicData/Binding/IObservableCollectionAdaptor.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. namespace DynamicData.Binding { diff --git a/src/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs b/src/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs index 2c8fec8d2..3488842ba 100644 --- a/src/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs +++ b/src/DynamicData/Binding/ISortedObservableCollectionAdaptor.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. namespace DynamicData.Binding { diff --git a/src/DynamicData/Binding/NotifyPropertyChangedEx.cs b/src/DynamicData/Binding/NotifyPropertyChangedEx.cs index 562e0a3e4..62d9d1d36 100644 --- a/src/DynamicData/Binding/NotifyPropertyChangedEx.cs +++ b/src/DynamicData/Binding/NotifyPropertyChangedEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.ComponentModel; using System.Linq; @@ -23,7 +27,11 @@ public static class NotifyPropertyChangedEx public static IObservable WhenAnyPropertyChanged([NotNull] this TObject source, params string[] propertiesToMonitor) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return Observable.FromEventPattern ( handler => source.PropertyChanged += handler, @@ -46,14 +54,21 @@ public static IObservable WhenAnyPropertyChanged([NotNull] thi /// For an object like Parent.Child.Sibling, sibling is an object so if Child == null, the value null and obtainable and is returned as null. /// /// propertyAccessor - public static IObservable> WhenPropertyChanged([NotNull] this TObject source, - Expression> propertyAccessor, - bool notifyOnInitialValue = true, + public static IObservable> WhenPropertyChanged([NotNull] this TObject source, + Expression> propertyAccessor, + bool notifyOnInitialValue = true, Func fallbackValue = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertyAccessor == null) + { + throw new ArgumentNullException(nameof(propertyAccessor)); + } var cache = ObservablePropertyFactoryCache.Instance.GetFactory(propertyAccessor); return cache.Create(source, notifyOnInitialValue) @@ -72,8 +87,15 @@ public static IObservable> WhenPropertyChanged public static IObservable WhenValueChanged([NotNull] this TObject source, Expression> propertyAccessor, bool notifyOnInitialValue = true, Func fallbackValue = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertyAccessor == null) + { + throw new ArgumentNullException(nameof(propertyAccessor)); + } return source.WhenChanged(propertyAccessor, notifyOnInitialValue, fallbackValue); } @@ -91,9 +113,20 @@ public static IObservable WhenChanged([No Func p1Fallback = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (p1 == null) throw new ArgumentNullException(nameof(p1)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (p1 == null) + { + throw new ArgumentNullException(nameof(p1)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return source.WhenChanged(p1, true, p1Fallback).Select(v => resultSelector(source, v)); } @@ -105,18 +138,33 @@ public static IObservable WhenChanged([No /// For example when observing Parent.Child.Age, if Child == null the value is unobtainable as Age is a struct and cannot be set to Null. /// For an object like Parent.Child.Sibling, sibling is an object so if Child == null, the value null and obtainable and is returned as null. /// - public static IObservable WhenChanged([NotNull] this TObject source, + public static IObservable WhenChanged([NotNull] this TObject source, Expression> p1, - Expression> p2, + Expression> p2, Func resultSelector, Func p1Fallback = null, Func p2Fallback = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (p1 == null) throw new ArgumentNullException(nameof(p1)); - if (p2 == null) throw new ArgumentNullException(nameof(p2)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (p1 == null) + { + throw new ArgumentNullException(nameof(p1)); + } + + if (p2 == null) + { + throw new ArgumentNullException(nameof(p2)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return Observable.CombineLatest ( @@ -143,11 +191,30 @@ public static IObservable WhenChanged p3Fallback = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (p1 == null) throw new ArgumentNullException(nameof(p1)); - if (p2 == null) throw new ArgumentNullException(nameof(p2)); - if (p3 == null) throw new ArgumentNullException(nameof(p3)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (p1 == null) + { + throw new ArgumentNullException(nameof(p1)); + } + + if (p2 == null) + { + throw new ArgumentNullException(nameof(p2)); + } + + if (p3 == null) + { + throw new ArgumentNullException(nameof(p3)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return Observable.CombineLatest ( @@ -177,12 +244,35 @@ public static IObservable WhenChanged p4Fallback = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (p1 == null) throw new ArgumentNullException(nameof(p1)); - if (p2 == null) throw new ArgumentNullException(nameof(p2)); - if (p3 == null) throw new ArgumentNullException(nameof(p3)); - if (p4 == null) throw new ArgumentNullException(nameof(p4)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (p1 == null) + { + throw new ArgumentNullException(nameof(p1)); + } + + if (p2 == null) + { + throw new ArgumentNullException(nameof(p2)); + } + + if (p3 == null) + { + throw new ArgumentNullException(nameof(p3)); + } + + if (p4 == null) + { + throw new ArgumentNullException(nameof(p4)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return Observable.CombineLatest ( @@ -215,13 +305,40 @@ public static IObservable WhenChanged p5Fallback = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (p1 == null) throw new ArgumentNullException(nameof(p1)); - if (p2 == null) throw new ArgumentNullException(nameof(p2)); - if (p3 == null) throw new ArgumentNullException(nameof(p3)); - if (p4 == null) throw new ArgumentNullException(nameof(p4)); - if (p5 == null) throw new ArgumentNullException(nameof(p5)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (p1 == null) + { + throw new ArgumentNullException(nameof(p1)); + } + + if (p2 == null) + { + throw new ArgumentNullException(nameof(p2)); + } + + if (p3 == null) + { + throw new ArgumentNullException(nameof(p3)); + } + + if (p4 == null) + { + throw new ArgumentNullException(nameof(p4)); + } + + if (p5 == null) + { + throw new ArgumentNullException(nameof(p5)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return Observable.CombineLatest ( @@ -257,14 +374,45 @@ public static IObservable WhenChanged p6Fallback = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (p1 == null) throw new ArgumentNullException(nameof(p1)); - if (p2 == null) throw new ArgumentNullException(nameof(p2)); - if (p3 == null) throw new ArgumentNullException(nameof(p3)); - if (p4 == null) throw new ArgumentNullException(nameof(p4)); - if (p5 == null) throw new ArgumentNullException(nameof(p5)); - if (p6 == null) throw new ArgumentNullException(nameof(p6)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (p1 == null) + { + throw new ArgumentNullException(nameof(p1)); + } + + if (p2 == null) + { + throw new ArgumentNullException(nameof(p2)); + } + + if (p3 == null) + { + throw new ArgumentNullException(nameof(p3)); + } + + if (p4 == null) + { + throw new ArgumentNullException(nameof(p4)); + } + + if (p5 == null) + { + throw new ArgumentNullException(nameof(p5)); + } + + if (p6 == null) + { + throw new ArgumentNullException(nameof(p6)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return Observable.CombineLatest ( @@ -278,7 +426,6 @@ public static IObservable WhenChanged WhenChanged(this TObject source, Expression> expression, bool notifyInitial = true, Func fallbackValue = null) where TObject : INotifyPropertyChanged { @@ -288,7 +435,9 @@ internal static IObservable WhenChanged(this TObj .Select(pv => { if (pv.UnobtainableValue && fallbackValue != null) + { return fallbackValue(); + } return pv.Value; }); diff --git a/src/DynamicData/Binding/ObservableCollectionAdaptor.cs b/src/DynamicData/Binding/ObservableCollectionAdaptor.cs index 425647d56..026afc6bf 100644 --- a/src/DynamicData/Binding/ObservableCollectionAdaptor.cs +++ b/src/DynamicData/Binding/ObservableCollectionAdaptor.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using DynamicData.Annotations; using DynamicData.Cache.Internal; @@ -32,6 +36,11 @@ public ObservableCollectionAdaptor([NotNull] IObservableCollection collection /// The changes. public void Adapt(IChangeSet changes) { + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } + if (changes.TotalChanges - changes.Refreshes > _refreshThreshold || !_loaded) { using (_collection.SuspendNotifications()) @@ -75,6 +84,16 @@ public ObservableCollectionAdaptor(int refreshThreshold = 25) /// The collection. public void Adapt(IChangeSet changes, IObservableCollection collection) { + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } + + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + _cache.Clone(changes); if (changes.Count - changes.Refreshes > _refreshThreshold || !_loaded) diff --git a/src/DynamicData/Binding/ObservableCollectionEx.cs b/src/DynamicData/Binding/ObservableCollectionEx.cs index 58ad7e276..ff767f892 100644 --- a/src/DynamicData/Binding/ObservableCollectionEx.cs +++ b/src/DynamicData/Binding/ObservableCollectionEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -22,12 +26,14 @@ public static class ObservableCollectionEx /// source public static IObservable> ToObservableChangeSet(this ObservableCollection source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return ToObservableChangeSet,T>(source); } - /// /// Convert an observable collection into an observable change set /// @@ -41,8 +47,15 @@ public static IObservable> ToObservableChangeSet(this Observabl /// keySelector public static IObservable> ToObservableChangeSet(this ObservableCollection source, Func keySelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return ToObservableChangeSet, TObject>(source).AddKey(keySelector); } @@ -56,7 +69,10 @@ public static IObservable> ToObservableChangeSetsource public static IObservable> ToObservableChangeSet(this ReadOnlyObservableCollection source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return ToObservableChangeSet, T>(source); } @@ -74,8 +90,15 @@ public static IObservable> ToObservableChangeSet(this ReadOnlyO /// keySelector public static IObservable> ToObservableChangeSet(this ReadOnlyObservableCollection source, Func keySelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return ToObservableChangeSet, TObject>(source).AddKey(keySelector); } @@ -91,14 +114,19 @@ public static IObservable> ToObservableChangeSet> ToObservableChangeSet(this TCollection source) where TCollection : INotifyCollectionChanged, IEnumerable { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return Observable.Create>(observer => { var data = new ChangeAwareList(source); if (data.Count > 0) + { observer.OnNext(data.CaptureChanges()); + } return source.ObserveCollectionChanges() .Scan(data, (list, args) => @@ -117,8 +145,10 @@ public static IObservable> ToObservableChangeSet(t { list.InsertRange(changes.NewItems.Cast(), changes.NewStartingIndex); } + break; } + case NotifyCollectionChangedAction.Remove: { if (changes.OldItems.Count == 1) @@ -129,13 +159,16 @@ public static IObservable> ToObservableChangeSet(t { list.RemoveRange(changes.OldStartingIndex, changes.OldItems.Count); } + break; } + case NotifyCollectionChangedAction.Replace: { list[changes.NewStartingIndex] = (T) changes.NewItems[0]; break; } + case NotifyCollectionChangedAction.Reset: { list.Clear(); @@ -149,6 +182,7 @@ public static IObservable> ToObservableChangeSet(t break; } } + return list; }) .Select(list => list.CaptureChanges()) diff --git a/src/DynamicData/Binding/ObservableCollectionExtended.cs b/src/DynamicData/Binding/ObservableCollectionExtended.cs index 73c21bca6..916d00216 100644 --- a/src/DynamicData/Binding/ObservableCollectionExtended.cs +++ b/src/DynamicData/Binding/ObservableCollectionExtended.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -78,7 +82,9 @@ public IDisposable SuspendCount() _suspendCount = false; if (Count != count) + { OnPropertyChanged(new PropertyChangedEventArgs("Count")); + } }); } @@ -88,8 +94,15 @@ public IDisposable SuspendCount() /// The instance containing the event data. protected override void OnPropertyChanged(PropertyChangedEventArgs e) { + if (e == null) + { + throw new ArgumentNullException(nameof(e)); + } + if (_suspendCount && e.PropertyName == "Count") + { return; + } base.OnPropertyChanged(e); } @@ -100,7 +113,11 @@ protected override void OnPropertyChanged(PropertyChangedEventArgs e) /// The instance containing the event data. protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { - if (_suspendNotifications) return; + if (_suspendNotifications) + { + return; + } + base.OnCollectionChanged(e); } @@ -112,11 +129,18 @@ protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) /// The items. public void Load(IEnumerable items) { + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + CheckReentrancy(); Clear(); foreach (var item in items) + { Add(item); + } } #region Implementation of IExtendedList @@ -128,8 +152,15 @@ public void Load(IEnumerable items) /// is null. public void AddRange(IEnumerable collection) { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + foreach (var item in collection) + { Add(item); + } } /// @@ -141,8 +172,15 @@ public void AddRange(IEnumerable collection) /// is less than 0.-or- is greater than . public void InsertRange(IEnumerable collection, int index) { + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + foreach (var item in collection) + { InsertItem(index++, item); + } } /// @@ -152,7 +190,9 @@ public void InsertRange(IEnumerable collection, int index) public void RemoveRange(int index, int count) { for (var i = 0; i < count; i++) + { RemoveAt(index); + } } #endregion } diff --git a/src/DynamicData/Binding/ObservablePropertyFactory.cs b/src/DynamicData/Binding/ObservablePropertyFactory.cs index 70531695f..cdb76416d 100644 --- a/src/DynamicData/Binding/ObservablePropertyFactory.cs +++ b/src/DynamicData/Binding/ObservablePropertyFactory.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.ComponentModel; @@ -16,7 +20,7 @@ internal static class Observable public static readonly IObservable Default = Observable.Return(default(T)); } - internal class ObservablePropertyFactory + internal class ObservablePropertyFactory where TObject: INotifyPropertyChanged { private readonly Func>> _factory; @@ -33,6 +37,7 @@ public ObservablePropertyFactory(Func valueAccessor, Observa valueHasChanged = Observable.Defer(() => Observable.Return(Unit.Default)) .Concat(valueHasChanged); } + return valueHasChanged.Select(_ => GetPropertyValue(t,chain, valueAccessor)); }; } @@ -56,7 +61,9 @@ public ObservablePropertyFactory(Expression> expression .Select(x => Factory()); if (!notifyInitial) + { return propertyChanged; + } var initial = Observable.Defer(() => Observable.Return(Factory())); return initial.Concat(propertyChanged); @@ -78,7 +85,10 @@ private static IEnumerable> GetNotifiers(TObject source, Obser value = metadata.Accessor(value); yield return obs; - if (value == null) yield break; + if (value == null) + { + yield break; + } } } @@ -89,8 +99,12 @@ private static PropertyValue GetPropertyValue(TObject source foreach (var metadata in chain.Reverse()) { value = metadata.Accessor(value); - if (value == null) return new PropertyValue(source); + if (value == null) + { + return new PropertyValue(source); + } } + return new PropertyValue(source, valueAccessor(source)); } diff --git a/src/DynamicData/Binding/ObservablePropertyFactoryCache.cs b/src/DynamicData/Binding/ObservablePropertyFactoryCache.cs index 9728c43c2..31dc242ca 100644 --- a/src/DynamicData/Binding/ObservablePropertyFactoryCache.cs +++ b/src/DynamicData/Binding/ObservablePropertyFactoryCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Concurrent; using System.ComponentModel; @@ -21,7 +25,6 @@ public ObservablePropertyFactory GetFactory { ObservablePropertyFactory factory; @@ -37,10 +40,10 @@ public ObservablePropertyFactory GetFactory(accessor, chain); } + return factory; }); - return (ObservablePropertyFactory)result; } } diff --git a/src/DynamicData/Binding/ObservablePropertyPart.cs b/src/DynamicData/Binding/ObservablePropertyPart.cs index 506f3b787..912d2b992 100644 --- a/src/DynamicData/Binding/ObservablePropertyPart.cs +++ b/src/DynamicData/Binding/ObservablePropertyPart.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Diagnostics; using System.Linq.Expressions; diff --git a/src/DynamicData/Binding/PropertyValue.cs b/src/DynamicData/Binding/PropertyValue.cs index 9dc305899..447849500 100644 --- a/src/DynamicData/Binding/PropertyValue.cs +++ b/src/DynamicData/Binding/PropertyValue.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; @@ -37,7 +41,7 @@ internal PropertyValue(TObject sender) /// Latest observed value /// public TValue Value { get; } - + /// /// Flag to indicated that the value was unobtainable when observing a deeply nested struct /// @@ -48,16 +52,32 @@ internal PropertyValue(TObject sender) /// public bool Equals(PropertyValue other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Sender, other.Sender) && EqualityComparer.Default.Equals(Value, other.Value); } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is PropertyValue && Equals((PropertyValue)obj); } diff --git a/src/DynamicData/Binding/SortDirection.cs b/src/DynamicData/Binding/SortDirection.cs index a992e7111..c6c0fa4f8 100644 --- a/src/DynamicData/Binding/SortDirection.cs +++ b/src/DynamicData/Binding/SortDirection.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData.Binding { /// diff --git a/src/DynamicData/Binding/SortExpression.cs b/src/DynamicData/Binding/SortExpression.cs index 1a894749e..91eb4a918 100644 --- a/src/DynamicData/Binding/SortExpression.cs +++ b/src/DynamicData/Binding/SortExpression.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; namespace DynamicData.Binding { diff --git a/src/DynamicData/Binding/SortExpressionComparer.cs b/src/DynamicData/Binding/SortExpressionComparer.cs index fb36dee47..1b5b82ce3 100644 --- a/src/DynamicData/Binding/SortExpressionComparer.cs +++ b/src/DynamicData/Binding/SortExpressionComparer.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; namespace DynamicData.Binding @@ -19,22 +23,48 @@ public int Compare(T x, T y) { foreach (var item in this) { - if (x == null && y == null) continue; - if (x == null) return -1; - if (y == null) return 1; + if (x == null && y == null) + { + continue; + } + + if (x == null) + { + return -1; + } + + if (y == null) + { + return 1; + } var xValue = item.Expression(x); var yValue = item.Expression(y); - if (xValue == null && yValue == null) continue; - if (xValue == null) return -1; - if (yValue == null) return 1; + if (xValue == null && yValue == null) + { + continue; + } + + if (xValue == null) + { + return -1; + } + + if (yValue == null) + { + return 1; + } int result = xValue.CompareTo(yValue); - if (result == 0) continue; + if (result == 0) + { + continue; + } return (item.Direction == SortDirection.Ascending) ? result : -result; } + return 0; } diff --git a/src/DynamicData/Binding/SortedBindingListAdaptor.cs b/src/DynamicData/Binding/SortedBindingListAdaptor.cs index cf476eb71..accf2f318 100644 --- a/src/DynamicData/Binding/SortedBindingListAdaptor.cs +++ b/src/DynamicData/Binding/SortedBindingListAdaptor.cs @@ -1,4 +1,8 @@ -#if SUPPORTS_BINDINGLIST +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +#if SUPPORTS_BINDINGLIST using System; using System.ComponentModel; @@ -23,10 +27,14 @@ public SortedBindingListAdaptor([NotNull] BindingList list, int refresh _refreshThreshold = refreshThreshold; } - /// public void Adapt(ISortedChangeSet changes) { + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } + switch (changes.SortedItems.SortReason) { case SortReason.InitialLoad: @@ -61,11 +69,10 @@ public void Adapt(ISortedChangeSet changes) break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(changes)); } } - private void DoUpdate(ISortedChangeSet changes) { foreach (var change in changes) diff --git a/src/DynamicData/Binding/SortedObservableCollectionAdaptor.cs b/src/DynamicData/Binding/SortedObservableCollectionAdaptor.cs index 570b1bfcc..01c54c416 100644 --- a/src/DynamicData/Binding/SortedObservableCollectionAdaptor.cs +++ b/src/DynamicData/Binding/SortedObservableCollectionAdaptor.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; @@ -29,6 +33,16 @@ public SortedObservableCollectionAdaptor(int refreshThreshold = 25) /// The collection. public void Adapt(ISortedChangeSet changes, IObservableCollection collection) { + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } + + if (collection == null) + { + throw new ArgumentNullException(nameof(collection)); + } + switch (changes.SortedItems.SortReason) { case SortReason.InitialLoad: @@ -38,6 +52,7 @@ public void Adapt(ISortedChangeSet changes, IObservableCollection { collection.Load(changes.SortedItems.Select(kv => kv.Value)); } + break; case SortReason.DataChanged: @@ -55,6 +70,7 @@ public void Adapt(ISortedChangeSet changes, IObservableCollection DoUpdate(changes, collection); } } + break; case SortReason.Reorder: @@ -63,10 +79,11 @@ public void Adapt(ISortedChangeSet changes, IObservableCollection { DoUpdate(changes, collection); } + break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(changes)); } } diff --git a/src/DynamicData/Cache/Change.cs b/src/DynamicData/Cache/Change.cs index 6e17d8930..0069d7dce 100644 --- a/src/DynamicData/Cache/Change.cs +++ b/src/DynamicData/Cache/Change.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -72,10 +76,14 @@ public Change(TKey key, TObject current, int currentIndex, int previousIndex) : this() { if (currentIndex < 0) + { throw new ArgumentException("CurrentIndex must be greater than or equal to zero"); + } if (previousIndex < 0) + { throw new ArgumentException("PreviousIndex must be greater than or equal to zero"); + } Current = current; Previous = Optional.None(); @@ -110,10 +118,14 @@ public Change(ChangeReason reason, TKey key, TObject current, Optional PreviousIndex = previousIndex; if (reason == ChangeReason.Add && previous.HasValue) + { throw new ArgumentException("For ChangeReason.Add, a previous value cannot be specified"); + } if (reason == ChangeReason.Update && !previous.HasValue) + { throw new ArgumentException("For ChangeReason.Change, must supply previous value"); + } } #region Equality @@ -137,18 +149,22 @@ public Change(ChangeReason reason, TKey key, TObject current, Optional /// public bool Equals(Change other) { - return EqualityComparer.Default.Equals(Key, other.Key) - && Reason == other.Reason - && EqualityComparer.Default.Equals(Current, other.Current) - && CurrentIndex == other.CurrentIndex - && Previous.Equals(other.Previous) + return EqualityComparer.Default.Equals(Key, other.Key) + && Reason == other.Reason + && EqualityComparer.Default.Equals(Current, other.Current) + && CurrentIndex == other.CurrentIndex + && Previous.Equals(other.Previous) && PreviousIndex == other.PreviousIndex; } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is Change && Equals((Change) obj); } diff --git a/src/DynamicData/Cache/ChangeAwareCache.cs b/src/DynamicData/Cache/ChangeAwareCache.cs index db1c81842..15c69838f 100644 --- a/src/DynamicData/Cache/ChangeAwareCache.cs +++ b/src/DynamicData/Cache/ChangeAwareCache.cs @@ -1,9 +1,12 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using DynamicData.Kernel; - // ReSharper disable once CheckNamespace namespace DynamicData { @@ -79,27 +82,42 @@ public void AddOrUpdate(TObject item, TKey key) /// The keys. public void Remove(IEnumerable keys) { - if (_data == null) return; + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + + if (_data == null) + { + return; + } if (keys is IList list) { EnsureInitialised(list.Count); var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { Remove(item); + } } else { EnsureInitialised(); foreach (var key in keys) + { Remove(key); + } } } /// public void Remove(TKey key) { - if (_data == null) return; + if (_data == null) + { + return; + } if (_data.TryGetValue(key, out var existingItem)) { @@ -109,25 +127,32 @@ public void Remove(TKey key) } } - - /// /// Raises an evaluate change for the specified keys /// public void Refresh(IEnumerable keys) { + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + if (keys is IList list) { EnsureInitialised(list.Count); var enumerable = EnumerableIList.Create(list); foreach (var key in enumerable) + { Refresh(key); + } } else { EnsureInitialised(); foreach (var key in keys) + { Refresh(key); + } } } @@ -140,7 +165,6 @@ public void Refresh() _changes.AddRange(_data.Select(t => new Change(ChangeReason.Refresh, t.Key, t.Value))); } - /// /// Raises an evaluate change for the specified key /// @@ -154,11 +178,13 @@ public void Refresh(TKey key) } } - /// public void Clear() { - if (_data == null) return; + if (_data == null) + { + return; + } EnsureInitialised(_data.Count); @@ -170,7 +196,10 @@ public void Clear() /// public void Clone(IChangeSet changes) { - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } EnsureInitialised(changes.Count); @@ -196,10 +225,14 @@ public void Clone(IChangeSet changes) private void EnsureInitialised(int capacity = -1) { if (_changes == null) + { _changes = capacity > 0 ? new ChangeSet(capacity) : new ChangeSet(); + } if (_data == null) + { _data = capacity > 0 ? new Dictionary(capacity) : new Dictionary(); + } } /// @@ -208,7 +241,9 @@ private void EnsureInitialised(int capacity = -1) public ChangeSet CaptureChanges() { if (_changes == null || _changes.Count==0) + { return ChangeSet.Empty; + } var copy = _changes; _changes = null; diff --git a/src/DynamicData/Cache/ChangeSet.cs b/src/DynamicData/Cache/ChangeSet.cs index b8f370cdf..705ff2406 100644 --- a/src/DynamicData/Cache/ChangeSet.cs +++ b/src/DynamicData/Cache/ChangeSet.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; using System.Linq; // ReSharper disable once CheckNamespace @@ -18,7 +22,6 @@ internal static class CacheChangeSetEx public static ChangeSet ToConcreteType(this IChangeSet changeset) => (ChangeSet)changeset; } - /// /// A collection of changes /// @@ -35,7 +38,7 @@ public ChangeSet() } /// - public ChangeSet(IEnumerable> collection) + public ChangeSet(IEnumerable> collection) : base(collection) { } @@ -44,7 +47,7 @@ public ChangeSet(IEnumerable> collection) public ChangeSet(int capacity) : base(capacity) { } - + /// public int Adds => this.Count(c => c.Reason == ChangeReason.Add); @@ -60,7 +63,6 @@ public ChangeSet(int capacity) : base(capacity) /// public int Moves => this.Count(c => c.Reason == ChangeReason.Moved); - /// public override string ToString() { diff --git a/src/DynamicData/Cache/DistinctChangeSet.cs b/src/DynamicData/Cache/DistinctChangeSet.cs index 1ea286260..02e7ff3f0 100644 --- a/src/DynamicData/Cache/DistinctChangeSet.cs +++ b/src/DynamicData/Cache/DistinctChangeSet.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/Cache/GroupChangeSet.cs b/src/DynamicData/Cache/GroupChangeSet.cs index 802d9e797..71819e716 100644 --- a/src/DynamicData/Cache/GroupChangeSet.cs +++ b/src/DynamicData/Cache/GroupChangeSet.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/Cache/ICache.cs b/src/DynamicData/Cache/ICache.cs index 9aabc56b5..d52b12da9 100644 --- a/src/DynamicData/Cache/ICache.cs +++ b/src/DynamicData/Cache/ICache.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. using System.Collections.Generic; @@ -19,7 +22,7 @@ public interface ICache : IQuery /// /// The changes. void Clone(IChangeSet changes); - + /// /// Adds or updates the item using the specified key /// @@ -27,7 +30,6 @@ public interface ICache : IQuery /// The key. void AddOrUpdate(TObject item, TKey key); - /// /// Removes the item matching the specified key. /// diff --git a/src/DynamicData/Cache/ICacheUpdater.cs b/src/DynamicData/Cache/ICacheUpdater.cs index c87088e5d..a82414f6f 100644 --- a/src/DynamicData/Cache/ICacheUpdater.cs +++ b/src/DynamicData/Cache/ICacheUpdater.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; // ReSharper disable once CheckNamespace @@ -105,7 +109,6 @@ public interface ICacheUpdater : IQuery [Obsolete("Use Clone()")] void Update(IChangeSet changes); - /// /// Clones the change set to the cache /// diff --git a/src/DynamicData/Cache/IChangeSet.cs b/src/DynamicData/Cache/IChangeSet.cs index cb9cd0529..f208d0647 100644 --- a/src/DynamicData/Cache/IChangeSet.cs +++ b/src/DynamicData/Cache/IChangeSet.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/Cache/IGrouping.cs b/src/DynamicData/Cache/IGrouping.cs index 55139982a..2948db89d 100644 --- a/src/DynamicData/Cache/IGrouping.cs +++ b/src/DynamicData/Cache/IGrouping.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; using DynamicData.Kernel; // ReSharper disable once CheckNamespace diff --git a/src/DynamicData/Cache/IIntermediateCache.cs b/src/DynamicData/Cache/IIntermediateCache.cs index 37b1be9e2..2ffdc1af9 100644 --- a/src/DynamicData/Cache/IIntermediateCache.cs +++ b/src/DynamicData/Cache/IIntermediateCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/Cache/IKeyValueCollection.cs b/src/DynamicData/Cache/IKeyValueCollection.cs index a12ec45d1..058c98790 100644 --- a/src/DynamicData/Cache/IKeyValueCollection.cs +++ b/src/DynamicData/Cache/IKeyValueCollection.cs @@ -1,9 +1,13 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData { /// - /// + /// A key collection which contains sorting information. /// /// The type of the object. /// The type of the key. diff --git a/src/DynamicData/Cache/IObservableCache.cs b/src/DynamicData/Cache/IObservableCache.cs index e8567b124..e3b74e680 100644 --- a/src/DynamicData/Cache/IObservableCache.cs +++ b/src/DynamicData/Cache/IObservableCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; diff --git a/src/DynamicData/Cache/IPagedChangeSet.cs b/src/DynamicData/Cache/IPagedChangeSet.cs index d413fdf15..f9d798785 100644 --- a/src/DynamicData/Cache/IPagedChangeSet.cs +++ b/src/DynamicData/Cache/IPagedChangeSet.cs @@ -1,4 +1,8 @@ -using DynamicData.Operators; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using DynamicData.Operators; // ReSharper disable once CheckNamespace namespace DynamicData { diff --git a/src/DynamicData/Cache/IQuery.cs b/src/DynamicData/Cache/IQuery.cs index 73d1c72a1..1641f1bef 100644 --- a/src/DynamicData/Cache/IQuery.cs +++ b/src/DynamicData/Cache/IQuery.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; using DynamicData.Kernel; // ReSharper disable once CheckNamespace diff --git a/src/DynamicData/Cache/ISourceCache.cs b/src/DynamicData/Cache/ISourceCache.cs index 2eac00ad1..b0fb04d20 100644 --- a/src/DynamicData/Cache/ISourceCache.cs +++ b/src/DynamicData/Cache/ISourceCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/Cache/ISourceUpdater.cs b/src/DynamicData/Cache/ISourceUpdater.cs index 88cfabc17..a99bf608e 100644 --- a/src/DynamicData/Cache/ISourceUpdater.cs +++ b/src/DynamicData/Cache/ISourceUpdater.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; // ReSharper disable once CheckNamespace @@ -68,14 +72,12 @@ public interface ISourceUpdater : ICacheUpdater [Obsolete(Constants.EvaluateIsDead)] void Evaluate(TObject item); - /// ///Removes the specified items /// /// The items. void Remove(IEnumerable items); - /// /// Removes the specified item. /// diff --git a/src/DynamicData/Cache/IndexedItem.cs b/src/DynamicData/Cache/IndexedItem.cs index ae7342a10..10ed91241 100644 --- a/src/DynamicData/Cache/IndexedItem.cs +++ b/src/DynamicData/Cache/IndexedItem.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData @@ -43,7 +47,6 @@ public IndexedItem(TObject value, TKey key, int index) #region Equality - private bool Equals(IndexedItem other) { return EqualityComparer.Default.Equals(Key, other.Key) && @@ -53,9 +56,21 @@ private bool Equals(IndexedItem other) /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((IndexedItem)obj); } diff --git a/src/DynamicData/Cache/IntermediateCache.cs b/src/DynamicData/Cache/IntermediateCache.cs index e670acd91..113185919 100644 --- a/src/DynamicData/Cache/IntermediateCache.cs +++ b/src/DynamicData/Cache/IntermediateCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -21,7 +25,11 @@ public sealed class IntermediateCache : IIntermediateCachesource public IntermediateCache(IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + _innnerCache = new ObservableCache(source); } diff --git a/src/DynamicData/Cache/Internal/AbstractFilter.cs b/src/DynamicData/Cache/Internal/AbstractFilter.cs index cd0991c67..1d3f44ec9 100644 --- a/src/DynamicData/Cache/Internal/AbstractFilter.cs +++ b/src/DynamicData/Cache/Internal/AbstractFilter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -36,12 +40,16 @@ Optional> Factory(KeyValuePair kv) if (matches) { if (!exisiting.HasValue) + { return new Change(ChangeReason.Add, kv.Key, kv.Value); + } } else { if (exisiting.HasValue) + { return new Change(ChangeReason.Remove, kv.Key, kv.Value, exisiting); + } } return Optional.None>(); @@ -50,7 +58,6 @@ Optional> Factory(KeyValuePair kv) var result = Refresh(items, Factory); _cache.Clone(new ChangeSet(result)); - return _cache.CaptureChanges(); } @@ -80,8 +87,11 @@ private IChangeSet ProcessResult(IEnumerable so case ChangeReason.Add: { if (matches) + { _cache.AddOrUpdate(u.Current, u.Key); + } } + break; case ChangeReason.Update: { @@ -94,6 +104,7 @@ private IChangeSet ProcessResult(IEnumerable so _cache.Remove(u.Key); } } + break; case ChangeReason.Remove: _cache.Remove(u.Key); @@ -115,16 +126,19 @@ private IChangeSet ProcessResult(IEnumerable so else { if (exisiting.HasValue) + { _cache.Remove(u.Key); + } } } + break; } } + return _cache.CaptureChanges(); } - protected struct UpdateWithFilter { /// diff --git a/src/DynamicData/Cache/Internal/AnonymousObservableCache.cs b/src/DynamicData/Cache/Internal/AnonymousObservableCache.cs index b5f4e98a1..27da495fa 100644 --- a/src/DynamicData/Cache/Internal/AnonymousObservableCache.cs +++ b/src/DynamicData/Cache/Internal/AnonymousObservableCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -10,7 +14,11 @@ internal sealed class AnonymousObservableCache : IObservableCache public AnonymousObservableCache(IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + _cache = new ObservableCache(source); } diff --git a/src/DynamicData/Cache/Internal/AnonymousQuery.cs b/src/DynamicData/Cache/Internal/AnonymousQuery.cs index c8d1be888..1a61db194 100644 --- a/src/DynamicData/Cache/Internal/AnonymousQuery.cs +++ b/src/DynamicData/Cache/Internal/AnonymousQuery.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; using DynamicData.Kernel; diff --git a/src/DynamicData/Cache/Internal/AutoRefresh.cs b/src/DynamicData/Cache/Internal/AutoRefresh.cs index 9e56f10f2..7a8b5f3cc 100644 --- a/src/DynamicData/Cache/Internal/AutoRefresh.cs +++ b/src/DynamicData/Cache/Internal/AutoRefresh.cs @@ -1,10 +1,13 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Disposables; using System.Reactive.Linq; - namespace DynamicData.Cache.Internal { internal class AutoRefresh @@ -51,7 +54,6 @@ public IObservable> Run() .Select(items => new ChangeSet(items)); } - //publish refreshes and underlying changes var locker = new object(); var publisher = shared.Synchronize(locker) diff --git a/src/DynamicData/Cache/Internal/BatchIf.cs b/src/DynamicData/Cache/Internal/BatchIf.cs index b2bfb0aae..7c7a5a96b 100644 --- a/src/DynamicData/Cache/Internal/BatchIf.cs +++ b/src/DynamicData/Cache/Internal/BatchIf.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive; using System.Reactive.Concurrency; @@ -46,13 +50,17 @@ public IObservable> Run() void ResumeAction() { - if (batchedChanges.Count == 0) return; + if (batchedChanges.Count == 0) + { + return; + } var resultingBatch = new ChangeSet(batchedChanges.Select(cs=>cs.Count).Sum()); foreach (var cs in batchedChanges) { resultingBatch.AddRange(cs); } + observer.OnNext(resultingBatch); batchedChanges.Clear(); } @@ -67,12 +75,16 @@ IDisposable IntervalFunction() paused = false; ResumeAction(); if (_intervalTimer!=null) + { paused = true; + } }); } if (_intervalTimer != null) + { intervalTimerDisposer.Disposable = IntervalFunction(); + } var pausedHandler = _pauseIfTrueSelector .Synchronize(locker) @@ -82,12 +94,17 @@ IDisposable IntervalFunction() if (!p) { //pause window has closed, so reset timer - if (_timeOut.HasValue) timeoutDisposer.Disposable = Disposable.Empty; + if (_timeOut.HasValue) + { + timeoutDisposer.Disposable = Disposable.Empty; + } + ResumeAction(); } else { if (_timeOut.HasValue) + { timeoutDisposer.Disposable = Observable.Timer(_timeOut.Value, _scheduler) .Synchronize(locker) .Subscribe(_ => @@ -95,6 +112,7 @@ IDisposable IntervalFunction() paused = false; ResumeAction(); }); + } } }); @@ -107,7 +125,9 @@ IDisposable IntervalFunction() //publish if not paused if (!paused) + { ResumeAction(); + } }); return new CompositeDisposable(publisher, pausedHandler, timeoutDisposer, intervalTimerDisposer); diff --git a/src/DynamicData/Cache/Internal/Cache.cs b/src/DynamicData/Cache/Internal/Cache.cs index 67d03fded..dbcb1c6b6 100644 --- a/src/DynamicData/Cache/Internal/Cache.cs +++ b/src/DynamicData/Cache/Internal/Cache.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -27,14 +31,17 @@ public Cache(Dictionary data) public Cache Clone() { - return _data== null - ? new Cache() + return _data== null + ? new Cache() : new Cache(new Dictionary(_data)); } public void Clone(IChangeSet changes) { - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } foreach (var item in changes) { @@ -45,6 +52,7 @@ public void Clone(IChangeSet changes) { _data[item.Key] = item.Current; } + break; case ChangeReason.Remove: _data.Remove(item.Key); @@ -69,19 +77,25 @@ public void Remove(IEnumerable keys) { var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { Remove(item); + } } else { foreach (var key in keys) - Remove(key); + { + Remove(key); + } } } public void Remove(TKey key) { if (_data.ContainsKey(key)) + { _data.Remove(key); + } } public void Clear() diff --git a/src/DynamicData/Cache/Internal/CacheEx.cs b/src/DynamicData/Cache/Internal/CacheEx.cs index 30d059c1c..2caaaaee0 100644 --- a/src/DynamicData/Cache/Internal/CacheEx.cs +++ b/src/DynamicData/Cache/Internal/CacheEx.cs @@ -1,8 +1,11 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; - namespace DynamicData.Cache.Internal { internal static class CacheEx @@ -14,7 +17,6 @@ public static IChangeSet GetInitialUpdates(this Ch return new ChangeSet(filtered.Select(i => new Change(ChangeReason.Add, i.Key, i.Value))); } - public static void Clone(this IDictionary souce, IChangeSet changes) { var enumerable = changes.ToConcreteType(); diff --git a/src/DynamicData/Cache/Internal/CacheUpdater.cs b/src/DynamicData/Cache/Internal/CacheUpdater.cs index c7a3c16a2..16fe23dae 100644 --- a/src/DynamicData/Cache/Internal/CacheUpdater.cs +++ b/src/DynamicData/Cache/Internal/CacheUpdater.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -15,10 +19,14 @@ public CacheUpdater(ICache cache, Func keySelector _cache = cache ?? throw new ArgumentNullException(nameof(cache)); _keySelector = keySelector; } - + public CacheUpdater(Dictionary data, Func keySelector = null) { - if (data == null) throw new ArgumentNullException(nameof(data)); + if (data == null) + { + throw new ArgumentNullException(nameof(data)); + } + _cache = new Cache(data); _keySelector = keySelector; } @@ -37,35 +45,51 @@ public Optional Lookup(TKey key) public void Load(IEnumerable items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + Clear(); AddOrUpdate(items); } public void AddOrUpdate(IEnumerable items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); - + } + if (items is IList list) { //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { _cache.AddOrUpdate(item, _keySelector(item)); + } } else { foreach (var item in items) + { _cache.AddOrUpdate(item, _keySelector(item)); + } } } public void AddOrUpdate(TObject item) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } var key = _keySelector(item); _cache.AddOrUpdate(item, key); @@ -74,33 +98,42 @@ public void AddOrUpdate(TObject item) public void AddOrUpdate(TObject item, IEqualityComparer comparer) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } var key = _keySelector(item); var oldItem = _cache.Lookup(key); if (oldItem.HasValue) { if (comparer.Equals(oldItem.Value, item)) + { return; + } + _cache.AddOrUpdate(item, key); return; } + _cache.AddOrUpdate(item, key); } public TKey GetKey(TObject item) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } return _keySelector(item); } - public IEnumerable> GetKeyValues(IEnumerable items) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } return items.Select(t => new KeyValuePair(_keySelector(t), t)); } @@ -112,12 +145,16 @@ public void AddOrUpdate(IEnumerable> itemsPairs) //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { _cache.AddOrUpdate(item.Value, item.Key); + } } else { foreach (var item in itemsPairs) + { _cache.AddOrUpdate(item.Value, item.Key); + } } } @@ -138,43 +175,60 @@ public void Refresh() public void Refresh(IEnumerable items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } if (items is IList list) { //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { Refresh(item); + } } else { foreach (var item in items) + { Refresh(item); + } } } public void Refresh(IEnumerable keys) { - if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + if (keys is IList list) { //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { Refresh(item); + } } else { foreach (var key in keys) + { Refresh(key); + } } } public void Refresh(TObject item) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } var key = _keySelector(item); _cache.Refresh(key); @@ -208,49 +262,70 @@ public void Evaluate(TKey key) public void Remove(IEnumerable items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } if (items is IList list) { //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { Remove(item); + } } else { foreach (var item in items) + { Remove(item); + } } } public void Remove(IEnumerable keys) { - if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + if (keys is IList list) { //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var key in enumerable) + { Remove(key); + } } else { foreach (var key in keys) + { Remove(key); + } } } public void RemoveKeys(IEnumerable keys) { - if (keys == null) throw new ArgumentNullException(nameof(keys)); + if (keys == null) + { + throw new ArgumentNullException(nameof(keys)); + } + _cache.Remove(keys); } public void Remove(TObject item) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } var key = _keySelector(item); _cache.Remove(key); @@ -259,7 +334,9 @@ public void Remove(TObject item) public Optional Lookup(TObject item) { if (_keySelector == null) + { throw new KeySelectorException("A key selector must be specified"); + } TKey key = _keySelector(item); return Lookup(key); @@ -277,19 +354,26 @@ public void RemoveKey(TKey key) public void Remove(IEnumerable> items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } if (items is IList list) { //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var key in enumerable) + { Remove(key); + } } else { foreach (var key in items) + { Remove(key); + } } } @@ -310,7 +394,6 @@ public void Update(IChangeSet changes) _cache.Clone(changes); } - public void Clone(IChangeSet changes) { _cache.Clone(changes); diff --git a/src/DynamicData/Cache/Internal/Cast.cs b/src/DynamicData/Cache/Internal/Cast.cs index 9de147194..aa491527f 100644 --- a/src/DynamicData/Cache/Internal/Cast.cs +++ b/src/DynamicData/Cache/Internal/Cast.cs @@ -1,9 +1,12 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using System.Reactive.Linq; using DynamicData.Kernel; - namespace DynamicData.Cache.Internal { internal class Cast diff --git a/src/DynamicData/Cache/Internal/ChangesReducer.cs b/src/DynamicData/Cache/Internal/ChangesReducer.cs index 4e54c3e44..570145f1e 100644 --- a/src/DynamicData/Cache/Internal/ChangesReducer.cs +++ b/src/DynamicData/Cache/Internal/ChangesReducer.cs @@ -1,4 +1,8 @@ -using System.Diagnostics.Contracts; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Diagnostics.Contracts; using DynamicData.Kernel; namespace DynamicData.Cache.Internal @@ -8,7 +12,11 @@ internal static class ChangesReducer [Pure] public static Optional> Reduce(Optional> previous, Change next) { - if (!previous.HasValue) return next; + if (!previous.HasValue) + { + return next; + } + var previousValue = previous.Value; switch (previousValue.Reason) diff --git a/src/DynamicData/Cache/Internal/CombineOperator.cs b/src/DynamicData/Cache/Internal/CombineOperator.cs index 10b27bac5..6919c1243 100644 --- a/src/DynamicData/Cache/Internal/CombineOperator.cs +++ b/src/DynamicData/Cache/Internal/CombineOperator.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData.Cache.Internal { /// diff --git a/src/DynamicData/Cache/Internal/Combiner.cs b/src/DynamicData/Cache/Internal/Combiner.cs index cce1e871e..30ea83e8c 100644 --- a/src/DynamicData/Cache/Internal/Combiner.cs +++ b/src/DynamicData/Cache/Internal/Combiner.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -57,7 +61,9 @@ private void Update(Cache cache, IChangeSet update } if (notifications.Count != 0) + { _updatedCallback(notifications); + } } private IChangeSet UpdateCombined(IChangeSet updates) @@ -83,7 +89,9 @@ private IChangeSet UpdateCombined(IChangeSet updat if (contained) { if (!ReferenceEquals(update.Current, cached.Value)) + { _combinedCache.AddOrUpdate(update.Current, key); + } } else { @@ -93,9 +101,12 @@ private IChangeSet UpdateCombined(IChangeSet updat else { if (contained) + { _combinedCache.Remove(key); + } } } + break; case ChangeReason.Remove: @@ -122,18 +133,23 @@ private IChangeSet UpdateCombined(IChangeSet updat else { if (contained) + { _combinedCache.Remove(key); + } } } + break; case ChangeReason.Refresh: { _combinedCache.Refresh(key); } + break; } } + return _combinedCache.CaptureChanges(); } @@ -145,22 +161,26 @@ private bool MatchesConstraint(TKey key) { return _sourceCaches.All(s => s.Lookup(key).HasValue); } + case CombineOperator.Or: { return _sourceCaches.Any(s => s.Lookup(key).HasValue); } + case CombineOperator.Xor: { return _sourceCaches.Count(s => s.Lookup(key).HasValue) == 1; } + case CombineOperator.Except: { bool first = _sourceCaches.Take(1).Any(s => s.Lookup(key).HasValue); bool others = _sourceCaches.Skip(1).Any(s => s.Lookup(key).HasValue); return first && !others; } + default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(key)); } } } diff --git a/src/DynamicData/Cache/Internal/DeferUntilLoaded.cs b/src/DynamicData/Cache/Internal/DeferUntilLoaded.cs index 20c4f3082..230d8c658 100644 --- a/src/DynamicData/Cache/Internal/DeferUntilLoaded.cs +++ b/src/DynamicData/Cache/Internal/DeferUntilLoaded.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Annotations; @@ -11,7 +15,11 @@ internal class DeferUntilLoaded public DeferUntilLoaded([NotNull] IObservableCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + _result = source.CountChanged.Where(count => count != 0) .Take(1) .Select(_ => new ChangeSet()) diff --git a/src/DynamicData/Cache/Internal/DictionaryExtensions.cs b/src/DynamicData/Cache/Internal/DictionaryExtensions.cs index d0994c6e9..54e0cb665 100644 --- a/src/DynamicData/Cache/Internal/DictionaryExtensions.cs +++ b/src/DynamicData/Cache/Internal/DictionaryExtensions.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; using System.Linq; namespace DynamicData.Cache.Internal @@ -8,7 +12,9 @@ internal static class DictionaryExtensions internal static IEnumerable GetOrEmpty(this IDictionary> dict, TDictKey key) { if (dict.ContainsKey(key)) + { return dict[key]; + } return Enumerable.Empty(); } diff --git a/src/DynamicData/Cache/Internal/DisposeMany.cs b/src/DynamicData/Cache/Internal/DisposeMany.cs index 78f5e5a25..ba26f968e 100644 --- a/src/DynamicData/Cache/Internal/DisposeMany.cs +++ b/src/DynamicData/Cache/Internal/DisposeMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; diff --git a/src/DynamicData/Cache/Internal/DistinctCalculator.cs b/src/DynamicData/Cache/Internal/DistinctCalculator.cs index 8cff75fdb..fe612f77a 100644 --- a/src/DynamicData/Cache/Internal/DistinctCalculator.cs +++ b/src/DynamicData/Cache/Internal/DistinctCalculator.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Reactive.Linq; using DynamicData.Kernel; @@ -38,12 +42,18 @@ void AddAction(TValue value) => _valueCounters.Lookup(value) void RemoveAction(TValue value) { var counter = _valueCounters.Lookup(value); - if (!counter.HasValue) return; + if (!counter.HasValue) + { + return; + } //decrement counter var newCount = counter.Value - 1; _valueCounters[value] = newCount; - if (newCount != 0) return; + if (newCount != 0) + { + return; + } //if there are none, then remove and notify _valueCounters.Remove(value); @@ -63,18 +73,23 @@ void RemoveAction(TValue value) _itemCache[key] = value; break; } + case ChangeReason.Refresh: case ChangeReason.Update: { var value = _valueSelector(change.Current); var previous = _itemCache[key]; - if (value.Equals(previous)) continue; + if (value.Equals(previous)) + { + continue; + } RemoveAction(previous); AddAction(value); _itemCache[key] = value; break; } + case ChangeReason.Remove: { var previous = _itemCache[key]; @@ -84,6 +99,7 @@ void RemoveAction(TValue value) } } } + return result; } } diff --git a/src/DynamicData/Cache/Internal/DynamicCombiner.cs b/src/DynamicData/Cache/Internal/DynamicCombiner.cs index c9f5548cb..fb064640a 100644 --- a/src/DynamicData/Cache/Internal/DynamicCombiner.cs +++ b/src/DynamicData/Cache/Internal/DynamicCombiner.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -12,9 +16,6 @@ internal sealed class DynamicCombiner { private readonly IObservableList>> _source; - - - private readonly CombineOperator _type; public DynamicCombiner([NotNull] IObservableList>> source, CombineOperator type) @@ -23,8 +24,6 @@ public DynamicCombiner([NotNull] IObservableList> Run() { return Observable.Create>(observer => @@ -34,7 +33,6 @@ public IObservable> Run() //this is the resulting cache which produces all notifications var resultCache = new ChangeAwareCache(); - //Transform to a merge container. //This populates a RefTracker when the original source is subscribed to var sourceLists = _source.Connect() @@ -55,7 +53,9 @@ public IObservable> Run() var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) + { observer.OnNext(notifications); + } }); //when an list is removed, need to @@ -73,7 +73,9 @@ public IObservable> Run() var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) + { observer.OnNext(notifications); + } }) .Subscribe(); @@ -85,11 +87,15 @@ public IObservable> Run() ProcessChanges(resultCache, sourceLists.Items.AsArray(), mc.Current.Cache.KeyValues); if (_type == CombineOperator.And || _type == CombineOperator.Except) + { ProcessChanges(resultCache, sourceLists.Items.AsArray(), resultCache.KeyValues.ToArray()); + } var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) + { observer.OnNext(notifications); + } }) .Subscribe(); @@ -100,7 +106,9 @@ public IObservable> Run() private void UpdateResultList(ChangeAwareCache target, MergeContainer[] sourceLists, IChangeSet changes) { foreach (var change in changes.ToConcreteType()) + { ProcessItem(target, sourceLists, change.Current, change.Key); + } } private void ProcessChanges(ChangeAwareCache target, MergeContainer[] sourceLists, IEnumerable> items) @@ -112,12 +120,16 @@ private void ProcessChanges(ChangeAwareCache target, MergeContain //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { ProcessItem(target, sourceLists, item.Value, item.Key); + } } else { foreach (var item in items) + { ProcessItem(target, sourceLists, item.Value, item.Key); + } } } @@ -140,14 +152,18 @@ private void ProcessItem(ChangeAwareCache target, MergeContainer[ else { if (cached.HasValue) + { target.Remove(key); + } } } private bool MatchesConstraint(MergeContainer[] sources, TKey key) { if (sources.Length == 0) + { return false; + } switch (_type) { @@ -155,22 +171,26 @@ private bool MatchesConstraint(MergeContainer[] sources, TKey key) { return sources.All(s => s.Cache.Lookup(key).HasValue); } + case CombineOperator.Or: { return sources.Any(s => s.Cache.Lookup(key).HasValue); } + case CombineOperator.Xor: { return sources.Count(s => s.Cache.Lookup(key).HasValue) == 1; } + case CombineOperator.Except: { bool first = sources.Take(1).Any(s => s.Cache.Lookup(key).HasValue); bool others = sources.Skip(1).Any(s => s.Cache.Lookup(key).HasValue); return first && !others; } + default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(key)); } } diff --git a/src/DynamicData/Cache/Internal/DynamicFilter.cs b/src/DynamicData/Cache/Internal/DynamicFilter.cs index 63c257067..f2e4bf584 100644 --- a/src/DynamicData/Cache/Internal/DynamicFilter.cs +++ b/src/DynamicData/Cache/Internal/DynamicFilter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive; using System.Reactive.Disposables; @@ -46,7 +50,7 @@ public IObservable> Run() .Select(changes => { //maintain all data [required to re-apply filter] - allData.Clone(changes); + allData.Clone(changes); //maintain filtered data filteredData.FilterChanges(changes, predicate); diff --git a/src/DynamicData/Cache/Internal/EditDiff.cs b/src/DynamicData/Cache/Internal/EditDiff.cs index 524d82ccd..7cae4f6b6 100644 --- a/src/DynamicData/Cache/Internal/EditDiff.cs +++ b/src/DynamicData/Cache/Internal/EditDiff.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using DynamicData.Annotations; diff --git a/src/DynamicData/Cache/Internal/ExpirableItem.cs b/src/DynamicData/Cache/Internal/ExpirableItem.cs index d0fb3713a..b28b7c1d8 100644 --- a/src/DynamicData/Cache/Internal/ExpirableItem.cs +++ b/src/DynamicData/Cache/Internal/ExpirableItem.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; namespace DynamicData.Cache.Internal @@ -32,7 +36,11 @@ public bool Equals(ExpirableItem other) /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is ExpirableItem && Equals((ExpirableItem) obj); } diff --git a/src/DynamicData/Cache/Internal/FilterEx.cs b/src/DynamicData/Cache/Internal/FilterEx.cs index 2cf18893a..6fa3aae32 100644 --- a/src/DynamicData/Cache/Internal/FilterEx.cs +++ b/src/DynamicData/Cache/Internal/FilterEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; namespace DynamicData.Cache.Internal @@ -5,12 +9,14 @@ namespace DynamicData.Cache.Internal internal static class FilterEx { public static IChangeSet RefreshFilteredFrom( - this ChangeAwareCache filtered, + this ChangeAwareCache filtered, Cache allData, Func predicate) { if (allData.Count == 0) + { return ChangeSet.Empty; + } foreach (var kvp in allData.KeyValues) { @@ -20,17 +26,22 @@ public static IChangeSet RefreshFilteredFrom( if (matches) { if (!exisiting.HasValue) + { filtered.Add(kvp.Value, kvp.Key); + } } else { if (exisiting.HasValue) + { filtered.Remove(kvp.Key); + } } } + return filtered.CaptureChanges(); } - + public static void FilterChanges(this ChangeAwareCache cache, IChangeSet changes, Func predicate) @@ -46,17 +57,25 @@ public static void FilterChanges(this ChangeAwareCache(this ChangeAwareCache { public IList> Calculate(IKeyValueCollection currentItems, - IKeyValueCollection previousItems, + IKeyValueCollection previousItems, IChangeSet sourceUpdates) { if (currentItems.SortReason == SortReason.ComparerChanged || currentItems.SortReason== SortReason.InitialLoad) @@ -24,7 +28,7 @@ public IList> Calculate(IKeyValueCollection var removes = previousItems.Except(currentItems, keyComparer).ToList(); var adds = currentItems.Except(previousItems, keyComparer).ToList(); - var inbothKeys = new HashSet(previousItems.Intersect(currentItems, keyComparer).Select(x => x.Key)); + var inbothKeys = new HashSet(previousItems.Intersect(currentItems, keyComparer).Select(x => x.Key)); var result = new List>(); foreach (var remove in removes) @@ -81,10 +85,15 @@ public IList> Calculate(IKeyValueCollection var previousindex = previousList.IndexOf(current); int desiredIndex = currentItems.IndexOf(current); - if (previousindex == desiredIndex) continue; - + if (previousindex == desiredIndex) + { + continue; + } + if (desiredIndex < 0) + { throw new SortException("Cannot determine current index"); + } previousList.RemoveAt(previousindex); previousList.Insert(desiredIndex, current); @@ -109,7 +118,10 @@ public IList> Calculate(IKeyValueCollection var current = new KeyValuePair(u.Key, u.Current); var old = previousList.IndexOf(current); - if (old == -1) continue; + if (old == -1) + { + continue; + } int newposition = GetInsertPositionLinear(previousList, current, currentItems.Comparer); @@ -127,10 +139,11 @@ public IList> Calculate(IKeyValueCollection previousList.Insert(newposition, current); result.Add(new Change(u.Key, u.Current, newposition, old)); } + return result; } - private int GetInsertPositionLinear(IList> list, KeyValuePair item, + private static int GetInsertPositionLinear(IList> list, KeyValuePair item, IComparer> comparer) { for (var i = 0; i < list.Count; i++) @@ -140,6 +153,7 @@ private int GetInsertPositionLinear(IList> list, Key return i; } } + return list.Count; } } diff --git a/src/DynamicData/Cache/Internal/FinallySafe.cs b/src/DynamicData/Cache/Internal/FinallySafe.cs index c79796023..5d0f8f566 100644 --- a/src/DynamicData/Cache/Internal/FinallySafe.cs +++ b/src/DynamicData/Cache/Internal/FinallySafe.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; diff --git a/src/DynamicData/Cache/Internal/FullJoin.cs b/src/DynamicData/Cache/Internal/FullJoin.cs index cef61de7b..2054434e7 100644 --- a/src/DynamicData/Cache/Internal/FullJoin.cs +++ b/src/DynamicData/Cache/Internal/FullJoin.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -64,6 +68,7 @@ public IObservable> Run() //update with no left value innerCache.AddOrUpdate(_resultSelector(change.Key, Optional.None, right), change.Key); } + break; case ChangeReason.Refresh: //propagate upstream @@ -91,6 +96,7 @@ public IObservable> Run() { innerCache.AddOrUpdate(_resultSelector(change.Key, left, right), change.Key); } + break; case ChangeReason.Remove: { @@ -105,6 +111,7 @@ public IObservable> Run() innerCache.AddOrUpdate(_resultSelector(change.Key, left, Optional.None), change.Key); } } + break; case ChangeReason.Refresh: //propagate upstream @@ -115,7 +122,6 @@ public IObservable> Run() }); }); - return new CompositeDisposable( joinedCache.Connect().NotEmpty().SubscribeSafe(observer), leftCache, diff --git a/src/DynamicData/Cache/Internal/FullJoinMany.cs b/src/DynamicData/Cache/Internal/FullJoinMany.cs index b481be541..5f0d997eb 100644 --- a/src/DynamicData/Cache/Internal/FullJoinMany.cs +++ b/src/DynamicData/Cache/Internal/FullJoinMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using DynamicData.Annotations; using DynamicData.Kernel; diff --git a/src/DynamicData/Cache/Internal/GroupImmutable.cs b/src/DynamicData/Cache/Internal/GroupImmutable.cs index 38b21f641..a28023eb3 100644 --- a/src/DynamicData/Cache/Internal/GroupImmutable.cs +++ b/src/DynamicData/Cache/Internal/GroupImmutable.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive; @@ -83,7 +87,9 @@ private IImmutableGroupChangeSet HandleUpdates(IEnumer var cacheToModify = groupCache.Cache; if (!initialStateOfGroups.ContainsKey(group.Key)) + { initialStateOfGroups[group.Key] = GetGroupState(groupCache); + } //1. Iterate through group changes and maintain the current group foreach (var current in group) @@ -96,6 +102,7 @@ private IImmutableGroupChangeSet HandleUpdates(IEnumer _itemCache[current.Key] = current; break; } + case ChangeReason.Update: { cacheToModify.AddOrUpdate(current.Item, current.Key); @@ -105,11 +112,14 @@ private IImmutableGroupChangeSet HandleUpdates(IEnumer .ValueOrThrow(() => CreateMissingKeyException(ChangeReason.Update, current.Key)); if (!previous.GroupKey.Equals(current.GroupKey)) - RemoveFromOldGroup(initialStateOfGroups, previous.GroupKey, current.Key); - - _itemCache[current.Key] = current; + { + RemoveFromOldGroup(initialStateOfGroups, previous.GroupKey, current.Key); + } + + _itemCache[current.Key] = current; break; } + case ChangeReason.Remove: { var existing = cacheToModify.Lookup(current.Key); @@ -126,10 +136,12 @@ private IImmutableGroupChangeSet HandleUpdates(IEnumer RemoveFromOldGroup(initialStateOfGroups, previousGroupKey, current.Key); } + _itemCache.Remove(current.Key); break; } + case ChangeReason.Refresh: { //check whether the previous item was in a different group. If so remove from old group @@ -137,7 +149,11 @@ private IImmutableGroupChangeSet HandleUpdates(IEnumer previous.IfHasValue(p => { - if (p.GroupKey.Equals(current.GroupKey)) return; + if (p.GroupKey.Equals(current.GroupKey)) + { + return; + } + RemoveFromOldGroup(initialStateOfGroups, p.GroupKey, current.Key); //add to new group because the group value has changed cacheToModify.AddOrUpdate(current.Item, current.Key); @@ -158,7 +174,7 @@ private IImmutableGroupChangeSet HandleUpdates(IEnumer return CreateChangeSet(initialStateOfGroups); } - private Exception CreateMissingKeyException(ChangeReason reason, TKey key) + private static Exception CreateMissingKeyException(ChangeReason reason, TKey key) { var message = $"{key} is missing from previous group on {reason}." + $"{Environment.NewLine}Object type {typeof(TObject)}, Key type {typeof(TKey)}, Group key type {typeof(TGroupKey)}"; @@ -171,7 +187,9 @@ private void RemoveFromOldGroup(IDictionary { if (!groupState.ContainsKey(g.Key)) + { groupState[g.Key] = GetGroupState(g.Key, g.Cache); + } g.Cache.Remove(currentKey); }); @@ -204,15 +222,16 @@ private IImmutableGroupChangeSet CreateChangeSet(IDict } } } + return new ImmutableGroupChangeSet(result); } - private IGrouping GetGroupState(GroupCache grouping) + private static IGrouping GetGroupState(GroupCache grouping) { return new ImmutableGroup(grouping.Key, grouping.Cache); } - private IGrouping GetGroupState(TGroupKey key, ICache cache) + private static IGrouping GetGroupState(TGroupKey key, ICache cache) { return new ImmutableGroup(key, cache); } @@ -233,7 +252,9 @@ private Tuple GetCache(TGroupKey key) { var cache = _allGroupings.Lookup(key); if (cache.HasValue) + { return Tuple.Create(cache.Value, false); + } var newcache = new GroupCache(key); _allGroupings[key] = newcache; @@ -264,7 +285,11 @@ public bool Equals(ChangeWithGroup other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is ChangeWithGroup && Equals((ChangeWithGroup)obj); } diff --git a/src/DynamicData/Cache/Internal/GroupOn.cs b/src/DynamicData/Cache/Internal/GroupOn.cs index ab04539e4..7f8fbca38 100644 --- a/src/DynamicData/Cache/Internal/GroupOn.cs +++ b/src/DynamicData/Cache/Internal/GroupOn.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive; @@ -13,8 +17,7 @@ internal sealed class GroupOn private readonly IObservable> _source; private readonly Func _groupSelectorKey; private readonly IObservable _regrouper; - - + public GroupOn(IObservable> source, Func groupSelectorKey, IObservable regrouper) { _source = source ?? throw new ArgumentNullException(nameof(source)); @@ -95,7 +98,9 @@ private GroupChangeSet HandleUpdates(IEnumerable, TGroupKey>(ChangeReason.Add, group.Key, groupCache)); + } groupCache.Update(groupUpdater => { @@ -109,6 +114,7 @@ private GroupChangeSet HandleUpdates(IEnumerable HandleUpdates(IEnumerable { g.Update(u => u.Remove(current.Key)); - if (g.Count != 0) return; + if (g.Count != 0) + { + return; + } + _groupCache.Remove(g.Key); result.Add(new Change, TGroupKey>(ChangeReason.Remove, g.Key, g)); }); _itemCache[current.Key] = current; } + break; } + case ChangeReason.Remove: { var previousInSameGroup = groupUpdater.Lookup(current.Key); @@ -145,12 +157,16 @@ private GroupChangeSet HandleUpdates(IEnumerable new MissingKeyException($"{current.Key} is missing from previous value on remove. Object type {typeof(TObject).FullName}, Key type {typeof(TKey).FullName}, Group key type {typeof(TGroupKey).FullName}")) .GroupKey; - + _groupCache.Lookup(previousGroupKey) .IfHasValue(g => { g.Update(u => u.Remove(current.Key)); - if (g.Count != 0) return; + if (g.Count != 0) + { + return; + } + _groupCache.Remove(g.Key); result.Add(new Change, TGroupKey>(ChangeReason.Remove, g.Key, g)); }); @@ -161,6 +177,7 @@ private GroupChangeSet HandleUpdates(IEnumerable HandleUpdates(IEnumerable { - + if (EqualityComparer.Default.Equals(p.GroupKey,current.GroupKey)) { //propagate evaluates up the chain - if (!isRegrouping) groupUpdater.Refresh(current.Key); + if (!isRegrouping) + { + groupUpdater.Refresh(current.Key); + } + return; } @@ -180,7 +201,11 @@ private GroupChangeSet HandleUpdates(IEnumerable { g.Update(u => u.Remove(current.Key)); - if (g.Count != 0) return; + if (g.Count != 0) + { + return; + } + _groupCache.Remove(g.Key); result.Add(new Change, TGroupKey>(ChangeReason.Remove, g.Key, g)); }); @@ -207,16 +232,16 @@ private GroupChangeSet HandleUpdates(IEnumerable(result); } - private Tuple, bool> GetCache(TGroupKey key) { var cache = _groupCache.Lookup(key); if (cache.HasValue) + { return Tuple.Create(cache.Value, false); + } var newcache = new ManagedGroup(key); _groupCache[key] = newcache; @@ -250,7 +275,11 @@ public bool Equals(ChangeWithGroup other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is ChangeWithGroup @group && Equals(@group); } diff --git a/src/DynamicData/Cache/Internal/GroupOnProperty.cs b/src/DynamicData/Cache/Internal/GroupOnProperty.cs index cfadf578a..8407f0ed6 100644 --- a/src/DynamicData/Cache/Internal/GroupOnProperty.cs +++ b/src/DynamicData/Cache/Internal/GroupOnProperty.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.ComponentModel; using System.Linq.Expressions; using System.Reactive.Concurrency; @@ -31,10 +35,12 @@ public IObservable> Run() { // Monitor explicit property changes var regrouper = shared.WhenValueChanged(_propertySelector, false).ToUnit(); - + //add a throttle if specified if (_throttle != null) + { regrouper = regrouper.Throttle(_throttle.Value, _scheduler ?? Scheduler.Default); + } // Use property changes as a trigger to re-evaluate Grouping return shared.Group(_groupSelector, regrouper); diff --git a/src/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs b/src/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs index 4402bc918..79fe7155b 100644 --- a/src/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs +++ b/src/DynamicData/Cache/Internal/GroupOnPropertyWithImmutableState.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.ComponentModel; using System.Linq.Expressions; using System.Reactive.Concurrency; @@ -34,7 +38,9 @@ public IObservable> Run() //add a throttle if specified if (_throttle != null) + { regrouper = regrouper.Throttle(_throttle.Value, _scheduler ?? Scheduler.Default); + } // Use property changes as a trigger to re-evaluate Grouping return shared.GroupWithImmutableState(_groupSelector, regrouper); diff --git a/src/DynamicData/Cache/Internal/IFilter.cs b/src/DynamicData/Cache/Internal/IFilter.cs index 1bbf84597..9c587e652 100644 --- a/src/DynamicData/Cache/Internal/IFilter.cs +++ b/src/DynamicData/Cache/Internal/IFilter.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; namespace DynamicData.Cache.Internal diff --git a/src/DynamicData/Cache/Internal/IKeySelector.cs b/src/DynamicData/Cache/Internal/IKeySelector.cs index f1a1ac602..c35154b3f 100644 --- a/src/DynamicData/Cache/Internal/IKeySelector.cs +++ b/src/DynamicData/Cache/Internal/IKeySelector.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. namespace DynamicData.Cache.Internal { diff --git a/src/DynamicData/Cache/Internal/ImmutableGroup.cs b/src/DynamicData/Cache/Internal/ImmutableGroup.cs index 2f9025018..40958fcda 100644 --- a/src/DynamicData/Cache/Internal/ImmutableGroup.cs +++ b/src/DynamicData/Cache/Internal/ImmutableGroup.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -31,15 +35,31 @@ public Optional Lookup(TKey key) public bool Equals(ImmutableGroup other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Key, other.Key); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is ImmutableGroup && Equals((ImmutableGroup) obj); } diff --git a/src/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs b/src/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs index c35fa8f40..8df0b0709 100644 --- a/src/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs +++ b/src/DynamicData/Cache/Internal/ImmutableGroupChangeSet.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; namespace DynamicData.Cache.Internal { diff --git a/src/DynamicData/Cache/Internal/IndexAndNode.cs b/src/DynamicData/Cache/Internal/IndexAndNode.cs index e1a5ba910..2b32f0edf 100644 --- a/src/DynamicData/Cache/Internal/IndexAndNode.cs +++ b/src/DynamicData/Cache/Internal/IndexAndNode.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; namespace DynamicData.Cache.Internal { diff --git a/src/DynamicData/Cache/Internal/IndexCalculator.cs b/src/DynamicData/Cache/Internal/IndexCalculator.cs index 412eb9d4e..758ba18eb 100644 --- a/src/DynamicData/Cache/Internal/IndexCalculator.cs +++ b/src/DynamicData/Cache/Internal/IndexCalculator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; using System.Linq; @@ -81,6 +85,7 @@ public IChangeSet Reorder() { continue; } + var old = _list.IndexOf(current); _list.RemoveAt(old); _list.Insert(index, current); @@ -114,6 +119,7 @@ public IChangeSet Calculate(IChangeSet changes) result.Add(new Change(ChangeReason.Add, u.Key, u.Current, position)); } + break; case ChangeReason.Update: @@ -129,6 +135,7 @@ public IChangeSet Calculate(IChangeSet changes) u.Key, u.Current, u.Previous, newposition, old)); } + break; case ChangeReason.Remove: @@ -137,6 +144,7 @@ public IChangeSet Calculate(IChangeSet changes) _list.RemoveAt(position); result.Add(new Change(ChangeReason.Remove, u.Key, u.Current, position)); } + break; case ChangeReason.Refresh: @@ -144,6 +152,7 @@ public IChangeSet Calculate(IChangeSet changes) refreshes.Add(u); result.Add(u); } + break; } } @@ -165,15 +174,22 @@ public IChangeSet Calculate(IChangeSet changes) { var current = new KeyValuePair(u.Key, u.Current); var old = _list.IndexOf(current); - if (old == -1) continue; + if (old == -1) + { + continue; + } int newposition = GetInsertPositionLinear(_list, current); if (old < newposition) + { newposition--; + } if (old == newposition) + { continue; + } _list.RemoveAt(old); _list.Insert(newposition, current); @@ -197,15 +213,20 @@ private int GetCurrentPosition(KeyValuePair item) index = _list.BinarySearch(item, _comparer); if (index < 0) + { throw new SortException("Current position cannot be found. Ensure the comparer includes a unique value, or do not specify ComparesImmutableValuesOnly"); + } } else { index = _list.IndexOf(item); if (index < 0) + { throw new SortException("Current position cannot be found. The item is not in the collection"); + } } + return index; } @@ -214,8 +235,11 @@ private int GetInsertPositionLinear(IList> list, Key for (var i = 0; i < list.Count; i++) { if (_comparer.Compare(item, list[i]) < 0) + { return i; + } } + return _list.Count; } @@ -227,7 +251,10 @@ private int GetInsertPositionBinary(KeyValuePair item) { var indx = index; index = _list.BinarySearch(indx - 1, _list.Count - indx, item, _comparer); - if (index > 0) return indx; + if (index > 0) + { + return indx; + } } int insertIndex = ~index; diff --git a/src/DynamicData/Cache/Internal/InnerJoin.cs b/src/DynamicData/Cache/Internal/InnerJoin.cs index a62513582..34f32409a 100644 --- a/src/DynamicData/Cache/Internal/InnerJoin.cs +++ b/src/DynamicData/Cache/Internal/InnerJoin.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -22,7 +26,6 @@ public InnerJoin(IObservable> left, _resultSelector = resultSelector ?? throw new ArgumentNullException(nameof(resultSelector)); } - public IObservable> Run() { return Observable.Create>(observer => @@ -99,11 +102,13 @@ public IObservable> Run() innerCache.Remove(change.Key); } } + break; case ChangeReason.Remove: { innerCache.Remove(change.Key); } + break; case ChangeReason.Refresh: //propagate upstream diff --git a/src/DynamicData/Cache/Internal/InnerJoinMany.cs b/src/DynamicData/Cache/Internal/InnerJoinMany.cs index 24e259375..1e9abe091 100644 --- a/src/DynamicData/Cache/Internal/InnerJoinMany.cs +++ b/src/DynamicData/Cache/Internal/InnerJoinMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; namespace DynamicData.Cache.Internal @@ -19,7 +23,7 @@ public InnerJoinMany(IObservable> left, _rightKeySelector = rightKeySelector ?? throw new ArgumentNullException(nameof(rightKeySelector)); _resultSelector = resultSelector ?? throw new ArgumentNullException(nameof(resultSelector)); } - + public IObservable> Run() { var rightGrouped = _right.GroupWithImmutableState(_rightKeySelector); diff --git a/src/DynamicData/Cache/Internal/KeyComparer.cs b/src/DynamicData/Cache/Internal/KeyComparer.cs index 09bcc44e6..2338abb24 100644 --- a/src/DynamicData/Cache/Internal/KeyComparer.cs +++ b/src/DynamicData/Cache/Internal/KeyComparer.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; namespace DynamicData.Cache.Internal { diff --git a/src/DynamicData/Cache/Internal/KeySelector.cs b/src/DynamicData/Cache/Internal/KeySelector.cs index aba7d59b1..6adcd9848 100644 --- a/src/DynamicData/Cache/Internal/KeySelector.cs +++ b/src/DynamicData/Cache/Internal/KeySelector.cs @@ -1,4 +1,9 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; +using System.Diagnostics.CodeAnalysis; namespace DynamicData.Cache.Internal { @@ -11,6 +16,7 @@ public KeySelector(Func keySelector) _keySelector = keySelector ?? throw new ArgumentNullException(nameof(keySelector)); } + [SuppressMessage("Design", "CA1822: Member can be static", Justification = "Backwards compatibilty")] public Type Type => typeof(TObject); public TKey GetKey(TObject item) diff --git a/src/DynamicData/Cache/Internal/KeySelectorException.cs b/src/DynamicData/Cache/Internal/KeySelectorException.cs index d6dc367a7..44e0a2777 100644 --- a/src/DynamicData/Cache/Internal/KeySelectorException.cs +++ b/src/DynamicData/Cache/Internal/KeySelectorException.cs @@ -1,10 +1,15 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; namespace DynamicData.Cache.Internal { /// - /// + /// An exception that happens when there is a problem with the key selector. /// + [Serializable] public class KeySelectorException : Exception { /// @@ -31,5 +36,10 @@ public KeySelectorException(string message, Exception innerException) : base(message, innerException) { } + + protected KeySelectorException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } } } diff --git a/src/DynamicData/Cache/Internal/KeyValueCollection.cs b/src/DynamicData/Cache/Internal/KeyValueCollection.cs index 80a887be7..e482e0399 100644 --- a/src/DynamicData/Cache/Internal/KeyValueCollection.cs +++ b/src/DynamicData/Cache/Internal/KeyValueCollection.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections; using System.Collections.Generic; diff --git a/src/DynamicData/Cache/Internal/KeyValueComparer.cs b/src/DynamicData/Cache/Internal/KeyValueComparer.cs index 26e3c04d5..d180d0a8d 100644 --- a/src/DynamicData/Cache/Internal/KeyValueComparer.cs +++ b/src/DynamicData/Cache/Internal/KeyValueComparer.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; namespace DynamicData.Cache.Internal { @@ -18,7 +22,9 @@ public int Compare(KeyValuePair x, KeyValuePair y) int result = _comparer.Compare(x.Value, y.Value); if (result != 0) + { return result; + } } return x.Key.GetHashCode().CompareTo(y.Key.GetHashCode()); diff --git a/src/DynamicData/Cache/Internal/LeftJoin.cs b/src/DynamicData/Cache/Internal/LeftJoin.cs index c526db6bc..99cb9d7aa 100644 --- a/src/DynamicData/Cache/Internal/LeftJoin.cs +++ b/src/DynamicData/Cache/Internal/LeftJoin.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; using DynamicData.Kernel; @@ -23,7 +27,7 @@ public LeftJoin(IObservable> left, _rightKeySelector = rightKeySelector ?? throw new ArgumentNullException(nameof(rightKeySelector)); _resultSelector = resultSelector ?? throw new ArgumentNullException(nameof(resultSelector)); } - + public IObservable> Run() { return Observable.Create>(observer => @@ -93,6 +97,7 @@ public IObservable> Run() innerCache.Remove(change.Key); } } + break; case ChangeReason.Remove: { @@ -109,6 +114,7 @@ public IObservable> Run() innerCache.Remove(change.Key); } } + break; case ChangeReason.Refresh: //propagate upstream @@ -119,7 +125,6 @@ public IObservable> Run() }); }); - return new CompositeDisposable( joinedCache.Connect().NotEmpty().SubscribeSafe(observer), leftCache, diff --git a/src/DynamicData/Cache/Internal/LeftJoinMany.cs b/src/DynamicData/Cache/Internal/LeftJoinMany.cs index d9758d7d8..555e8ef15 100644 --- a/src/DynamicData/Cache/Internal/LeftJoinMany.cs +++ b/src/DynamicData/Cache/Internal/LeftJoinMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using DynamicData.Kernel; diff --git a/src/DynamicData/Cache/Internal/LockFreeObservableCache.cs b/src/DynamicData/Cache/Internal/LockFreeObservableCache.cs index 91615a2a5..6566ff1f1 100644 --- a/src/DynamicData/Cache/Internal/LockFreeObservableCache.cs +++ b/src/DynamicData/Cache/Internal/LockFreeObservableCache.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; @@ -23,6 +27,7 @@ public class LockFreeObservableCache : IObservableCache> _changesPreview = new Subject>(); private readonly ISubject _countChanged = new Subject(); private readonly IDisposable _cleanUp; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -61,7 +66,6 @@ public LockFreeObservableCache() }); } - /// /// Returns a observable of cache changes preceded with the initial cache state /// @@ -98,7 +102,9 @@ public IObservable> Watch(TKey key) { var initial = _innerCache.Lookup(key); if (initial.HasValue) + { observer.OnNext(new Change(ChangeReason.Add, key, initial.Value)); + } return _changes.Subscribe(changes => { @@ -117,6 +123,11 @@ public IObservable> Watch(TKey key) /// The edit action. public void Edit(Action> editAction) { + if (editAction == null) + { + throw new ArgumentNullException(nameof(editAction)); + } + editAction(_updater); _changes.OnNext(_innerCache.CaptureChanges()); } @@ -126,7 +137,6 @@ public void Edit(Action> editAction) /// public IObservable CountChanged => _countChanged.StartWith(_innerCache.Count).DistinctUntilChanged(); - /// /// Lookup a single item using the specified key. /// @@ -160,9 +170,28 @@ public Optional Lookup(TKey key) /// public int Count => _innerCache.Count; - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// - public void Dispose() => _cleanUp.Dispose(); + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _cleanUp?.Dispose(); + } + } } } \ No newline at end of file diff --git a/src/DynamicData/Cache/Internal/ManagedGroup.cs b/src/DynamicData/Cache/Internal/ManagedGroup.cs index c6117f920..dfb8e1b73 100644 --- a/src/DynamicData/Cache/Internal/ManagedGroup.cs +++ b/src/DynamicData/Cache/Internal/ManagedGroup.cs @@ -1,9 +1,13 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; namespace DynamicData.Cache.Internal { - internal sealed class ManagedGroup : IGroup + internal sealed class ManagedGroup : IGroup, IDisposable { private readonly IntermediateCache _cache = new IntermediateCache(); @@ -44,8 +48,16 @@ private bool Equals(ManagedGroup other) /// The to compare with the current . public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is ManagedGroup && Equals((ManagedGroup)obj); } diff --git a/src/DynamicData/Cache/Internal/MergeMany.cs b/src/DynamicData/Cache/Internal/MergeMany.cs index aaffff1dd..4bfc5685c 100644 --- a/src/DynamicData/Cache/Internal/MergeMany.cs +++ b/src/DynamicData/Cache/Internal/MergeMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; @@ -16,7 +20,10 @@ public MergeMany(IObservable> source, Func> source, Func> observableSelector) { - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } _source = source ?? throw new ArgumentNullException(nameof(source)); _observableSelector = (t, key) => observableSelector(t); diff --git a/src/DynamicData/Cache/Internal/MergeManyItems.cs b/src/DynamicData/Cache/Internal/MergeManyItems.cs index 761b3aa0d..e6aef9b54 100644 --- a/src/DynamicData/Cache/Internal/MergeManyItems.cs +++ b/src/DynamicData/Cache/Internal/MergeManyItems.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Kernel; @@ -17,7 +21,10 @@ public MergeManyItems(IObservable> source, Func> source, Func> observableSelector) { - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } _source = source ?? throw new ArgumentNullException(nameof(source)); _observableSelector = (t, key) => observableSelector(t); diff --git a/src/DynamicData/Cache/Internal/ObservableWithValue.cs b/src/DynamicData/Cache/Internal/ObservableWithValue.cs index 9405e477b..9ab541331 100644 --- a/src/DynamicData/Cache/Internal/ObservableWithValue.cs +++ b/src/DynamicData/Cache/Internal/ObservableWithValue.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Kernel; diff --git a/src/DynamicData/Cache/Internal/Page.cs b/src/DynamicData/Cache/Internal/Page.cs index c291eb6d5..4fd8fa6b8 100644 --- a/src/DynamicData/Cache/Internal/Page.cs +++ b/src/DynamicData/Cache/Internal/Page.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; using System.Reactive.Linq; @@ -51,7 +55,9 @@ public IPagedChangeSet Paginate(IPageRequest parameters) } if (parameters.Size == _request.Size && parameters.Page == _request.Page) + { return null; + } _request = parameters; @@ -67,8 +73,15 @@ public IPagedChangeSet Update(ISortedChangeSet upd private IPagedChangeSet Paginate(ISortedChangeSet updates = null) { - if (_isLoaded == false) return null; - if (_request == null) return null; + if (_isLoaded == false) + { + return null; + } + + if (_request == null) + { + return null; + } var previous = _current; @@ -88,6 +101,7 @@ private IPagedChangeSet Paginate(ISortedChangeSet { return null; } + var response = new PageResponse(_request.Size, _all.Count, page, pages); return new PagedChangeSet(_current, notifications, response); @@ -107,6 +121,7 @@ private int CalculatePages() { return pages; } + return pages + 1; } } diff --git a/src/DynamicData/Cache/Internal/QueryWhenChanged.cs b/src/DynamicData/Cache/Internal/QueryWhenChanged.cs index 15bb8c02c..9d69a35e7 100644 --- a/src/DynamicData/Cache/Internal/QueryWhenChanged.cs +++ b/src/DynamicData/Cache/Internal/QueryWhenChanged.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Annotations; @@ -23,7 +27,10 @@ public IObservable> Run() .Scan((Cache)null, (cache, changes) => { if (cache == null) + { cache = new Cache(changes.Count); + } + cache.Clone(changes); return cache; }).Select(list => new AnonymousQuery(list)); @@ -46,7 +53,7 @@ public IObservable> Run() list.Clone(changes); return list; }).Select(list => new AnonymousQuery(list)); - + return sourceChanged.Merge(inlineChange); }); } diff --git a/src/DynamicData/Cache/Internal/ReaderWriter.cs b/src/DynamicData/Cache/Internal/ReaderWriter.cs index acf6b7ce2..f7b72a782 100644 --- a/src/DynamicData/Cache/Internal/ReaderWriter.cs +++ b/src/DynamicData/Cache/Internal/ReaderWriter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -18,21 +22,30 @@ internal sealed class ReaderWriter public ChangeSet Write(IChangeSet changes, Action> previewHandler, bool collectChanges) { - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } return DoUpdate(updater => updater.Clone(changes), previewHandler, collectChanges); } public ChangeSet Write(Action> updateAction, Action> previewHandler, bool collectChanges) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } return DoUpdate(updateAction, previewHandler, collectChanges); } public ChangeSet Write(Action> updateAction, Action> previewHandler, bool collectChanges) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } return DoUpdate(updateAction, previewHandler, collectChanges); } @@ -86,6 +99,7 @@ internal void WriteNested(Action> updateAction) { throw new InvalidOperationException("WriteNested can only be used if another write is already in progress."); } + updateAction(_activeUpdater); } } @@ -101,7 +115,9 @@ public ChangeSet GetInitialUpdates( Func filter = var dictionary = _data; if (dictionary.Count == 0) + { return ChangeSet.Empty; + } var changes = filter == null ? new ChangeSet(dictionary.Count) @@ -110,7 +126,9 @@ public ChangeSet GetInitialUpdates( Func filter = foreach (var kvp in dictionary) { if (filter == null || filter(kvp.Value)) + { changes.Add(new Change(ChangeReason.Add, kvp.Key, kvp.Value)); + } } return changes; @@ -165,7 +183,9 @@ public TObject[] Items public Optional Lookup(TKey key) { lock (_locker) + { return _data.Lookup(key); + } } public int Count @@ -173,7 +193,9 @@ public int Count get { lock (_locker) + { return _data.Count; + } } } diff --git a/src/DynamicData/Cache/Internal/RefCount.cs b/src/DynamicData/Cache/Internal/RefCount.cs index 04c239859..0a5dab364 100644 --- a/src/DynamicData/Cache/Internal/RefCount.cs +++ b/src/DynamicData/Cache/Internal/RefCount.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; using DynamicData.Annotations; @@ -22,8 +26,12 @@ public IObservable> Run() return Observable.Create>(observer => { lock (_locker) + { if (++_refCount == 1) + { _cache = _source.AsObservableCache(); + } + } var subscriber = _cache.Connect().SubscribeSafe(observer); @@ -32,11 +40,14 @@ public IObservable> Run() subscriber.Dispose(); IDisposable cacheToDispose = null; lock (_locker) + { if (--_refCount == 0) { cacheToDispose = _cache; _cache = null; } + } + cacheToDispose?.Dispose(); }); }); diff --git a/src/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs b/src/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs index 2807449f2..2fb734fce 100644 --- a/src/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs +++ b/src/DynamicData/Cache/Internal/RemoveKeyEnumerator.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections; using System.Collections.Generic; using DynamicData.Annotations; diff --git a/src/DynamicData/Cache/Internal/RightJoin.cs b/src/DynamicData/Cache/Internal/RightJoin.cs index b4faaa0ba..08d72f590 100644 --- a/src/DynamicData/Cache/Internal/RightJoin.cs +++ b/src/DynamicData/Cache/Internal/RightJoin.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -91,6 +95,7 @@ public IObservable> Run() innerCache.Remove(change.Key); } } + break; case ChangeReason.Remove: { @@ -105,6 +110,7 @@ public IObservable> Run() innerCache.Remove(change.Key); } } + break; case ChangeReason.Refresh: //propagate upstream @@ -115,7 +121,6 @@ public IObservable> Run() }); }); - return new CompositeDisposable( joinedCache.Connect().NotEmpty().SubscribeSafe(observer), leftCache, diff --git a/src/DynamicData/Cache/Internal/RightJoinMany.cs b/src/DynamicData/Cache/Internal/RightJoinMany.cs index ed6f6d8c5..df821978f 100644 --- a/src/DynamicData/Cache/Internal/RightJoinMany.cs +++ b/src/DynamicData/Cache/Internal/RightJoinMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using DynamicData.Annotations; using DynamicData.Kernel; diff --git a/src/DynamicData/Cache/Internal/SizeExpirer.cs b/src/DynamicData/Cache/Internal/SizeExpirer.cs index fcbf7d75d..63b8fef29 100644 --- a/src/DynamicData/Cache/Internal/SizeExpirer.cs +++ b/src/DynamicData/Cache/Internal/SizeExpirer.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; using System.Reactive.Disposables; @@ -13,7 +17,10 @@ internal class SizeExpirer public SizeExpirer(IObservable> source, int size) { - if (size <= 0) throw new ArgumentException("Size limit must be greater than zero"); + if (size <= 0) + { + throw new ArgumentException("Size limit must be greater than zero"); + } _source = source ?? throw new ArgumentNullException(nameof(source)); _size = size; diff --git a/src/DynamicData/Cache/Internal/SizeLimiter.cs b/src/DynamicData/Cache/Internal/SizeLimiter.cs index 34f8e9946..9f89a18fe 100644 --- a/src/DynamicData/Cache/Internal/SizeLimiter.cs +++ b/src/DynamicData/Cache/Internal/SizeLimiter.cs @@ -1,4 +1,8 @@ -using System.Linq; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Linq; using DynamicData.Kernel; namespace DynamicData.Cache.Internal diff --git a/src/DynamicData/Cache/Internal/Sort.cs b/src/DynamicData/Cache/Internal/Sort.cs index 74c592644..f44ef72fc 100644 --- a/src/DynamicData/Cache/Internal/Sort.cs +++ b/src/DynamicData/Cache/Internal/Sort.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Diagnostics; @@ -25,7 +29,9 @@ public Sort(IObservable> source, int resetThreshold = -1) { if (comparer == null && comparerChangedObservable == null) + { throw new ArgumentException("Must specify comparer or comparerChangedObservable"); + } _source = source ?? throw new ArgumentNullException(nameof(source)); _comparer = comparer; @@ -133,7 +139,9 @@ private ISortedChangeSet DoSort(SortReason sortReason, IChangeSet changes = _cache.CaptureChanges(); _haveReceivedData = true; if (_comparer == null) + { return null; + } } //if the comparer is not set, return nothing @@ -162,17 +170,20 @@ private ISortedChangeSet DoSort(SortReason sortReason, IChangeSet _calculator = new IndexCalculator(_comparer, _optimisations); changeSet = _calculator.Load(_cache); } + break; case SortReason.Reset: { _calculator.Reset(_cache); changeSet = changes; } + break; case SortReason.DataChanged: { changeSet = _calculator.Calculate(changes); } + break; case SortReason.ComparerChanged: @@ -189,12 +200,14 @@ private ISortedChangeSet DoSort(SortReason sortReason, IChangeSet changeSet = _calculator.Reorder(); } } + break; case SortReason.Reorder: { changeSet = _calculator.Reorder(); } + break; default: throw new ArgumentOutOfRangeException(nameof(sortReason)); @@ -207,7 +220,10 @@ private ISortedChangeSet DoSort(SortReason sortReason, IChangeSet return null; } - if (sortReason == SortReason.Reorder && changeSet.Count == 0) return null; + if (sortReason == SortReason.Reorder && changeSet.Count == 0) + { + return null; + } _sorted = new KeyValueCollection(_calculator.List.ToList(), _comparer, sortReason, _optimisations); return new SortedChangeSet(_sorted, changeSet); diff --git a/src/DynamicData/Cache/Internal/SpecifiedGrouper.cs b/src/DynamicData/Cache/Internal/SpecifiedGrouper.cs index e0e1e2933..743a3973f 100644 --- a/src/DynamicData/Cache/Internal/SpecifiedGrouper.cs +++ b/src/DynamicData/Cache/Internal/SpecifiedGrouper.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; using System.Reactive.Disposables; @@ -47,6 +51,7 @@ public IObservable> Run() var group = (ManagedGroup)child.Value; result.Update(updater => updater.Clone(group.GetInitialUpdates())); } + return result; }) .DisposeMany() diff --git a/src/DynamicData/Cache/Internal/StaticFilter.cs b/src/DynamicData/Cache/Internal/StaticFilter.cs index cc555a506..40069b9f9 100644 --- a/src/DynamicData/Cache/Internal/StaticFilter.cs +++ b/src/DynamicData/Cache/Internal/StaticFilter.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Linq; namespace DynamicData.Cache.Internal @@ -19,7 +23,10 @@ public IObservable> Run() return _source.Scan((ChangeAwareCache)null, (cache, changes) => { if (cache == null) + { cache = new ChangeAwareCache(changes.Count); + } + cache.FilterChanges(changes, _filter); return cache; }) diff --git a/src/DynamicData/Cache/Internal/StatusMonitor.cs b/src/DynamicData/Cache/Internal/StatusMonitor.cs index 12f7e394b..ef0aaa239 100644 --- a/src/DynamicData/Cache/Internal/StatusMonitor.cs +++ b/src/DynamicData/Cache/Internal/StatusMonitor.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -31,14 +35,22 @@ void Error(Exception ex) void Completion() { - if (status == ConnectionStatus.Errored) return; + if (status == ConnectionStatus.Errored) + { + return; + } + status = ConnectionStatus.Completed; statusSubject.OnNext(status); } void Updated() { - if (status != ConnectionStatus.Pending) return; + if (status != ConnectionStatus.Pending) + { + return; + } + status = ConnectionStatus.Loaded; statusSubject.OnNext(status); } diff --git a/src/DynamicData/Cache/Internal/SubscribeMany.cs b/src/DynamicData/Cache/Internal/SubscribeMany.cs index 18af43f95..bebfc8acc 100644 --- a/src/DynamicData/Cache/Internal/SubscribeMany.cs +++ b/src/DynamicData/Cache/Internal/SubscribeMany.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -11,7 +15,10 @@ internal class SubscribeMany public SubscribeMany(IObservable> source, Func subscriptionFactory) { - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); + if (subscriptionFactory == null) + { + throw new ArgumentNullException(nameof(subscriptionFactory)); + } _source = source ?? throw new ArgumentNullException(nameof(source)); _subscriptionFactory = (t, key) => subscriptionFactory(t); @@ -37,7 +44,7 @@ public IObservable> Run() .Subscribe(); return new CompositeDisposable( - subscriptions, + subscriptions, published.SubscribeSafe(observer), published.Connect()); }); diff --git a/src/DynamicData/Cache/Internal/Switch.cs b/src/DynamicData/Cache/Internal/Switch.cs index b32373aa8..99a23d31b 100644 --- a/src/DynamicData/Cache/Internal/Switch.cs +++ b/src/DynamicData/Cache/Internal/Switch.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -25,13 +29,15 @@ public IObservable> Run() .Do(_ => { lock (locker) + { destination.Clear(); + } })) .Synchronize(locker) .PopulateInto(destination); - return new CompositeDisposable(destination, - populator, + return new CompositeDisposable(destination, + populator, destination.Connect().SubscribeSafe(observer)); }); } diff --git a/src/DynamicData/Cache/Internal/TimeExpirer.cs b/src/DynamicData/Cache/Internal/TimeExpirer.cs index 63daeccab..4ba553d61 100644 --- a/src/DynamicData/Cache/Internal/TimeExpirer.cs +++ b/src/DynamicData/Cache/Internal/TimeExpirer.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -77,6 +81,7 @@ void RemovalAction() }) .Subscribe(); } + return Disposable.Create(() => { removalSubscripion.Dispose(); diff --git a/src/DynamicData/Cache/Internal/ToObservableChangeSet.cs b/src/DynamicData/Cache/Internal/ToObservableChangeSet.cs index 09b1cfd02..ab27bdfec 100644 --- a/src/DynamicData/Cache/Internal/ToObservableChangeSet.cs +++ b/src/DynamicData/Cache/Internal/ToObservableChangeSet.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Concurrency; @@ -40,7 +44,6 @@ public ToObservableChangeSet([NotNull] IObservable> source, _scheduler = scheduler ?? Scheduler.Default; } - public IObservable> Run() { return Observable.Create>(observer => @@ -57,13 +60,18 @@ public IObservable> Run() //zero allocation enumerator var enumerable = EnumerableIList.Create(list); foreach (var item in enumerable) + { state.AddOrUpdate(item, _keySelector(item)); + } } else { foreach (var item in latest) + { state.AddOrUpdate(item, _keySelector(item)); + } } + return state; }) .Select(state => state.CaptureChanges()) @@ -91,6 +99,7 @@ public IObservable> Run() .Take(toRemove) .ForEach(ei => cache.Remove(ei.Key)); } + return state; }) .Select(state => state.CaptureChanges()) diff --git a/src/DynamicData/Cache/Internal/Transform.cs b/src/DynamicData/Cache/Internal/Transform.cs index c22802a0b..b28dc3a7c 100644 --- a/src/DynamicData/Cache/Internal/Transform.cs +++ b/src/DynamicData/Cache/Internal/Transform.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Kernel; @@ -27,7 +31,9 @@ public IObservable> Run() return _source.Scan((ChangeAwareCache)null, (cache, changes) => { if (cache == null) + { cache = new ChangeAwareCache(changes.Count); + } var concreteType = changes.ToConcreteType(); foreach (var change in concreteType) @@ -56,6 +62,7 @@ public IObservable> Run() cache.AddOrUpdate(transformed, change.Key); } } + break; case ChangeReason.Remove: cache.Remove(change.Key); @@ -79,6 +86,7 @@ public IObservable> Run() break; } } + return cache; }) .Select(cache => cache.CaptureChanges()) diff --git a/src/DynamicData/Cache/Internal/TransformAsync.cs b/src/DynamicData/Cache/Internal/TransformAsync.cs index 493f693a8..b0b8d1049 100644 --- a/src/DynamicData/Cache/Internal/TransformAsync.cs +++ b/src/DynamicData/Cache/Internal/TransformAsync.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using System.Reactive.Linq; using System.Threading.Tasks; @@ -22,7 +26,7 @@ public TransformAsync(IObservable> source, Func> Run() { return Observable.Create>(observer => @@ -40,6 +44,7 @@ public IObservable> Run() transformer = transformer.Synchronize(locker).Merge(forced); } + return transformer.SubscribeSafe(observer); }); } @@ -51,13 +56,13 @@ private async Task> DoTransform(ChangeAwareCache< .Select(kvp => new Change(ChangeReason.Update, kvp.Key, kvp.Value.Source, kvp.Value.Source)) .ToArray(); - var transformed = await toTransform.SelectParallel(Transform, _maximumConcurrency); + var transformed = await toTransform.SelectParallel(Transform, _maximumConcurrency).ConfigureAwait(false); return ProcessUpdates(cache, transformed.ToArray()); } private async Task> DoTransform(ChangeAwareCache cache, IChangeSet changes ) { - var transformed = await changes.SelectParallel(Transform, _maximumConcurrency); + var transformed = await changes.SelectParallel(Transform, _maximumConcurrency).ConfigureAwait(false); return ProcessUpdates(cache, transformed.ToArray()); } @@ -67,16 +72,20 @@ private async Task Transform(Change change) { if (change.Reason == ChangeReason.Add || change.Reason == ChangeReason.Update) { - var destination = await _transformFactory(change.Current, change.Previous, change.Key); + var destination = await _transformFactory(change.Current, change.Previous, change.Key).ConfigureAwait(false); return new TransformResult(change, new TransformedItemContainer(change.Current, destination)); } + return new TransformResult(change); } catch (Exception ex) { //only handle errors if a handler has been specified if (_exceptionCallback != null) + { return new TransformResult(change, ex); + } + throw; } } @@ -86,7 +95,9 @@ private IChangeSet ProcessUpdates(ChangeAwareCache !t.Success).ToArray(); if (errors.Any()) + { errors.ForEach(t => _exceptionCallback(new Error(t.Error, t.Change.Current, t.Change.Key))); + } foreach (var result in transformedItems.Where(t => t.Success)) { @@ -135,7 +146,6 @@ public TransformResult(Change change, TransformedItemContainer co Key = change.Key; } - public TransformResult(Change change) { Change = change; diff --git a/src/DynamicData/Cache/Internal/TransformMany.cs b/src/DynamicData/Cache/Internal/TransformMany.cs index 9c8e45e19..5d7223d98 100644 --- a/src/DynamicData/Cache/Internal/TransformMany.cs +++ b/src/DynamicData/Cache/Internal/TransformMany.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -25,7 +29,9 @@ public TransformMany(IObservable> source, var subsequentChanges = manySelector(t).ToObservableChangeSet(keySelector); if (manySelector(t).Count > 0) + { return subsequentChanges; + } return Observable.Return(ChangeSet.Empty) .Concat(subsequentChanges); @@ -42,7 +48,9 @@ public TransformMany(IObservable> source, var subsequentChanges = manySelector(t).ToObservableChangeSet(keySelector); if (manySelector(t).Count > 0) + { return subsequentChanges; + } return Observable.Return(ChangeSet.Empty) .Concat(subsequentChanges); @@ -60,10 +68,10 @@ public TransformMany(IObservable> source, _keySelector = keySelector; _childChanges = childChanges; } - + public IObservable> Run() { - return _childChanges == null + return _childChanges == null ? Create() : CreateWithChangeset(); } @@ -96,9 +104,11 @@ private IObservable> CreateWithChanges return new ManyContainer(() => { lock (locker) + { return collection .Select(m => new DestinationContainer(m, _keySelector(m))) .ToArray(); + } }, changes); }).Publish(); @@ -141,8 +151,11 @@ public IEnumerator> GetEnumerator() case ChangeReason.Refresh: { foreach (var destination in change.Current.Destination) + { yield return new Change(change.Reason, destination.Key, destination.Item); + } } + break; case ChangeReason.Update: { @@ -154,10 +167,14 @@ public IEnumerator> GetEnumerator() var updates = currentItems.Intersect(previousItems, DestinationContainer.KeyComparer); foreach (var destination in removes) + { yield return new Change(ChangeReason.Remove, destination.Key, destination.Item); + } foreach (var destination in adds) + { yield return new Change(ChangeReason.Add, destination.Key, destination.Item); + } foreach (var destination in updates) { @@ -166,9 +183,12 @@ public IEnumerator> GetEnumerator() //Do not update is items are the same reference if (!ReferenceEquals(current.Item, previous.Item)) + { yield return new Change(ChangeReason.Update, destination.Key, current.Item, previous.Item); + } } } + break; } } @@ -210,10 +230,26 @@ private sealed class KeyEqualityComparer : IEqualityComparer.Default.Equals(x.Key, y.Key); } diff --git a/src/DynamicData/Cache/Internal/TransformWithForcedTransform.cs b/src/DynamicData/Cache/Internal/TransformWithForcedTransform.cs index f3d04703d..799d76e03 100644 --- a/src/DynamicData/Cache/Internal/TransformWithForcedTransform.cs +++ b/src/DynamicData/Cache/Internal/TransformWithForcedTransform.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -51,12 +55,14 @@ public IObservable> Run() } - private IEnumerable> CaptureChanges(Cache cache, Func shouldTransform) + private static IEnumerable> CaptureChanges(Cache cache, Func shouldTransform) { foreach (var kvp in cache.KeyValues) { if (shouldTransform(kvp.Value, kvp.Key)) + { yield return new Change(ChangeReason.Refresh, kvp.Key,kvp.Value); + } } } } diff --git a/src/DynamicData/Cache/Internal/TreeBuilder.cs b/src/DynamicData/Cache/Internal/TreeBuilder.cs index 86dcffd84..3205d73db 100644 --- a/src/DynamicData/Cache/Internal/TreeBuilder.cs +++ b/src/DynamicData/Cache/Internal/TreeBuilder.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Linq; using System.Reactive; @@ -31,7 +35,7 @@ public IObservable, TKey>> Run() { var locker = new object(); var refilterObservable = new BehaviorSubject(Unit.Default); - + var allData = _source.Synchronize(locker).AsObservableCache(); //for each object we need a node which provides @@ -45,7 +49,7 @@ public IObservable, TKey>> Run() .Synchronize(locker) .Group(x => _pivotOn(x.Item)) .AsObservableCache(); - + void UpdateChildren(Node parentNode) { var lookup = groupedByPivot.Lookup(parentNode.Key); @@ -102,6 +106,7 @@ void UpdateChildren(Node parentNode) break; } + case ChangeReason.Remove: { //remove children and null out parent @@ -150,6 +155,7 @@ void UpdateChildren(Node parentNode) break; } + case ChangeReason.Update: { //copy children to the new node amd set parent @@ -174,6 +180,7 @@ void UpdateChildren(Node parentNode) break; } + case ChangeReason.Remove: { node.Parent = null; @@ -185,6 +192,7 @@ void UpdateChildren(Node parentNode) break; } + case ChangeReason.Refresh: { var previousParent = change.Current.Parent; @@ -194,6 +202,7 @@ void UpdateChildren(Node parentNode) change.Current.Parent = p; updater.AddOrUpdate(change.Current); } + break; } } diff --git a/src/DynamicData/Cache/Internal/TrueFor.cs b/src/DynamicData/Cache/Internal/TrueFor.cs index 083f89f23..b8a06c804 100644 --- a/src/DynamicData/Cache/Internal/TrueFor.cs +++ b/src/DynamicData/Cache/Internal/TrueFor.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; diff --git a/src/DynamicData/Cache/Internal/Virtualiser.cs b/src/DynamicData/Cache/Internal/Virtualiser.cs index 956ed5c02..6e4e02a84 100644 --- a/src/DynamicData/Cache/Internal/Virtualiser.cs +++ b/src/DynamicData/Cache/Internal/Virtualiser.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Linq; using System.Reactive.Linq; @@ -50,8 +54,11 @@ public IVirtualChangeSet Virtualise(IVirtualRequest parameters) { return null; } + if (parameters.Size == _parameters.Size && parameters.StartIndex == _parameters.StartIndex) + { return null; + } _parameters = parameters; return Virtualise(); @@ -66,7 +73,10 @@ public IVirtualChangeSet Update(ISortedChangeSet u private IVirtualChangeSet Virtualise(ISortedChangeSet updates = null) { - if (_isLoaded == false) return null; + if (_isLoaded == false) + { + return null; + } var previous = _current; var virtualised = _all.Skip(_parameters.StartIndex) diff --git a/src/DynamicData/Cache/MissingKeyException.cs b/src/DynamicData/Cache/MissingKeyException.cs index 14841c774..119c9e6a3 100644 --- a/src/DynamicData/Cache/MissingKeyException.cs +++ b/src/DynamicData/Cache/MissingKeyException.cs @@ -1,10 +1,16 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; + // ReSharper disable once CheckNamespace namespace DynamicData { /// /// Thrown when a key is expected in a cache but not found /// + [Serializable] public class MissingKeyException : Exception { /// @@ -15,5 +21,27 @@ public MissingKeyException(string message) : base(message) { } + + /// + /// Initializes a new instance of the class. + /// + public MissingKeyException() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The message that describes the error. + /// A inner exception with further information. + public MissingKeyException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected MissingKeyException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } } } diff --git a/src/DynamicData/Cache/Node.cs b/src/DynamicData/Cache/Node.cs index 6f49b980a..32b8c39e4 100644 --- a/src/DynamicData/Cache/Node.cs +++ b/src/DynamicData/Cache/Node.cs @@ -1,5 +1,10 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reactive.Disposables; using DynamicData.Annotations; using DynamicData.Kernel; @@ -16,6 +21,7 @@ public class Node : IDisposable, IEquatable> { private readonly ISourceCache, TKey> _children = new SourceCache, TKey>(n => n.Key); private readonly IDisposable _cleanUp; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -83,7 +89,10 @@ public int Depth do { if (!parent.HasValue) + { break; + } + i++; parent = parent.Value.Parent; } while (true); @@ -98,15 +107,22 @@ internal void Update(Action, TKey>> updateAct #region Equality - /// Determines whether the specified object is equal to the current object. /// true if the specified object is equal to the current object; otherwise, false. /// The object to compare with the current object. /// 2 public bool Equals(Node other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Key, other.Key); } @@ -116,9 +132,21 @@ public bool Equals(Node other) /// 2 public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Node)obj); } @@ -160,12 +188,27 @@ public override string ToString() return $"{Item}{count}"; } - /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// 2 public void Dispose() { - _cleanUp.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _cleanUp?.Dispose(); + } } } } diff --git a/src/DynamicData/Cache/ObservableCache.cs b/src/DynamicData/Cache/ObservableCache.cs index 305635a15..8c6411bd5 100644 --- a/src/DynamicData/Cache/ObservableCache.cs +++ b/src/DynamicData/Cache/ObservableCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -71,11 +75,15 @@ public ObservableCache(Func keySelector = null) internal void UpdateFromIntermediate(Action> updateAction) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } + lock (_writeLock) { ChangeSet changes = null; - + _editLevel++; if (_editLevel == 1) { @@ -86,6 +94,7 @@ internal void UpdateFromIntermediate(Action> update { _readerWriter.WriteNested(updateAction); } + _editLevel--; if (_editLevel == 0) @@ -97,7 +106,11 @@ internal void UpdateFromIntermediate(Action> update internal void UpdateFromSource(Action> updateAction) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } + lock (_writeLock) { ChangeSet changes = null; @@ -112,6 +125,7 @@ internal void UpdateFromSource(Action> updateActio { _readerWriter.WriteNested(updateAction); } + _editLevel--; if (_editLevel == 0) @@ -126,7 +140,9 @@ private void InvokePreview(ChangeSet changes) lock (_locker) { if (changes.Count != 0) + { _changesPreview.OnNext(changes); + } } } @@ -135,10 +151,14 @@ private void InvokeNext(ChangeSet changes) lock (_locker) { if (changes.Count != 0) + { _changes.OnNext(changes); + } if (_countChanged.IsValueCreated) + { _countChanged.Value.OnNext(_readerWriter.Count); + } } } @@ -154,7 +174,9 @@ public IObservable> Watch(TKey key) { var initial = _readerWriter.Lookup(key); if (initial.HasValue) + { observer.OnNext(new Change(ChangeReason.Add, key, initial.Value)); + } return _changes.Finally(observer.OnCompleted).Subscribe(changes => { @@ -162,7 +184,9 @@ public IObservable> Watch(TKey key) { var match = EqualityComparer.Default.Equals(change.Key, key); if (match) + { observer.OnNext(change); + } } }); } diff --git a/src/DynamicData/Cache/ObservableCacheEx.cs b/src/DynamicData/Cache/ObservableCacheEx.cs index d4b6a0ff6..cb2d0d2d6 100644 --- a/src/DynamicData/Cache/ObservableCacheEx.cs +++ b/src/DynamicData/Cache/ObservableCacheEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -37,8 +41,15 @@ public static class ObservableCacheEx [Obsolete("This can cause unhandled exception issues so do not use")] public static IObservable FinallySafe(this IObservable source, Action finallyAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (finallyAction == null) throw new ArgumentNullException(nameof(finallyAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (finallyAction == null) + { + throw new ArgumentNullException(nameof(finallyAction)); + } return new FinallySafe(source, finallyAction).Run(); } @@ -52,7 +63,11 @@ public static IObservable FinallySafe(this IObservable source, Action f /// public static IObservable> RefCount(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new RefCount(source).Run(); } @@ -78,7 +93,11 @@ public static IObservable MonitorStatus(this IObservable /// source public static IObservable> NotEmpty(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Where(changes => changes.Count != 0); } @@ -92,7 +111,10 @@ public static IObservable> NotEmpty(thi /// source internal static IObservable> NotEmpty_Experiment(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return source.Publish(shared => { @@ -112,7 +134,11 @@ internal static IObservable> NotEmpty_Experiment> Flatten( this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.SelectMany(changes => changes); } @@ -130,12 +156,19 @@ public static IObservable> ForEachChange> source, [NotNull] Action> action) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (action == null) throw new ArgumentNullException(nameof(action)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + return source.Do(changes => changes.ForEach(action)); } - /// /// Ignores updates when the update is the same reference /// @@ -165,7 +198,9 @@ public static IObservable> IgnoreUpdateWhen { if (u.Reason != ChangeReason.Update) + { return true; + } return !ignoreFunction(u.Current, u.Previous.Value); }); @@ -185,8 +220,15 @@ public static IObservable> IgnoreUpdateWhen> IncludeUpdateWhen(this IObservable> source, Func includeFunction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (includeFunction == null) throw new ArgumentNullException(nameof(includeFunction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (includeFunction == null) + { + throw new ArgumentNullException(nameof(includeFunction)); + } return source.Select(changes => { @@ -212,8 +254,15 @@ public static IObservable> MergeManyItems> source, Func> observableSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } return new MergeManyItems(source, observableSelector).Run(); } @@ -235,8 +284,15 @@ public static IObservable> MergeManyItems> source, Func> observableSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } return new MergeManyItems(source, observableSelector).Run(); } @@ -256,8 +312,15 @@ public static IObservable> MergeManyItems public static IObservable MergeMany(this IObservable> source, Func> observableSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } return new MergeMany(source, observableSelector).Run(); } @@ -277,8 +340,15 @@ public static IObservable MergeMany(t /// observableSelector public static IObservable MergeMany(this IObservable> source, Func> observableSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } return new MergeMany(source, observableSelector).Run(); } @@ -300,8 +370,15 @@ public static IObservable WhenValueChanged([NotNu bool notifyOnInitialValue = true) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertyAccessor == null) + { + throw new ArgumentNullException(nameof(propertyAccessor)); + } return source.MergeMany(t => t.WhenChanged(propertyAccessor, notifyOnInitialValue)); } @@ -319,12 +396,19 @@ public static IObservable WhenValueChanged([NotNu /// /// public static IObservable> WhenPropertyChanged([NotNull] this IObservable> source, - [NotNull] Expression> propertyAccessor, + [NotNull] Expression> propertyAccessor, bool notifyOnInitialValue = true) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertyAccessor == null) + { + throw new ArgumentNullException(nameof(propertyAccessor)); + } return source.MergeMany(t => t.WhenPropertyChanged(propertyAccessor, notifyOnInitialValue)); } @@ -342,7 +426,10 @@ public static IObservable> WhenPropertyChanged WhenAnyPropertyChanged([NotNull] this IObservable> source, params string[] propertiesToMonitor) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return source.MergeMany(t => t.WhenAnyPropertyChanged(propertiesToMonitor)); } @@ -364,8 +451,15 @@ public static IObservable WhenAnyPropertyChanged([NotNul public static IObservable> SubscribeMany(this IObservable> source, Func subscriptionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (subscriptionFactory == null) + { + throw new ArgumentNullException(nameof(subscriptionFactory)); + } return new SubscribeMany(source, subscriptionFactory).Run(); } @@ -387,13 +481,19 @@ public static IObservable> SubscribeMany> SubscribeMany(this IObservable> source, Func subscriptionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (subscriptionFactory == null) + { + throw new ArgumentNullException(nameof(subscriptionFactory)); + } return new SubscribeMany(source, subscriptionFactory).Run(); } - /// /// Callback for each item as and when it is being added to the stream /// @@ -404,8 +504,15 @@ public static IObservable> SubscribeMany public static IObservable> OnItemAdded(this IObservable> source, [NotNull] Action addAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (addAction == null) throw new ArgumentNullException(nameof(addAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (addAction == null) + { + throw new ArgumentNullException(nameof(addAction)); + } return source.Do(changes => changes.Where(c => c.Reason == ChangeReason.Add) .ForEach(c => addAction(c.Current))); @@ -426,8 +533,15 @@ public static IObservable> OnItemAdded( /// public static IObservable> OnItemRemoved(this IObservable> source, Action removeAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (removeAction == null) + { + throw new ArgumentNullException(nameof(removeAction)); + } return source.Do(changes => changes.Where(c => c.Reason == ChangeReason.Remove) .ForEach(c => removeAction(c.Current))); @@ -445,8 +559,15 @@ public static IObservable> OnItemRemoved public static IObservable> OnItemUpdated(this IObservable> source, Action updateAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } return source.Do(changes => changes.Where(c => c.Reason == ChangeReason.Update) .ForEach(c => updateAction(c.Current, c.Previous.Value))); @@ -467,7 +588,11 @@ public static IObservable> OnItemUpdatedsource public static IObservable> DisposeMany(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new DisposeMany(source, t => { var d = t as IDisposable; @@ -489,8 +614,16 @@ public static IObservable> DisposeMany( public static IObservable> WhereReasonsAre( this IObservable> source, params ChangeReason[] reasons) { - if (reasons == null) throw new ArgumentNullException(nameof(reasons)); - if (!reasons.Any()) throw new ArgumentException("Must select at least one reason"); + if (reasons == null) + { + throw new ArgumentNullException(nameof(reasons)); + } + + if (!reasons.Any()) + { + throw new ArgumentException("Must select at least one reason"); + } + var hashed = new HashSet(reasons); return source.Select(updates => @@ -511,8 +644,15 @@ public static IObservable> WhereReasonsAreMust select at least on reason public static IObservable> WhereReasonsAreNot(this IObservable> source, params ChangeReason[] reasons) { - if (reasons == null) throw new ArgumentNullException(nameof(reasons)); - if (!reasons.Any()) throw new ArgumentException("Must select at least one reason"); + if (reasons == null) + { + throw new ArgumentNullException(nameof(reasons)); + } + + if (!reasons.Any()) + { + throw new ArgumentException("Must select at least one reason"); + } var hashed = new HashSet(reasons); @@ -540,12 +680,17 @@ public static IObservable> AutoRefresh( IScheduler scheduler = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return source.AutoRefreshOnObservable((t, v) => { if (propertyChangeThrottle == null) + { return t.WhenAnyPropertyChanged(); + } return t.WhenAnyPropertyChanged() .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); @@ -568,12 +713,17 @@ public static IObservable> AutoRefresh { if (propertyChangeThrottle == null) + { return t.WhenPropertyChanged(propertyAccessor, false); + } return t.WhenPropertyChanged(propertyAccessor, false) .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); @@ -609,8 +759,16 @@ public static IObservable> AutoRefreshOnObservable(source, reevaluator, changeSetBuffer, scheduler).Run(); } @@ -707,7 +865,11 @@ public static IObservable> StartWithEmpty(this IObserv /// public static IObservable> RemoveKey([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Select(changes => { var enumerator = new RemoveKeyEnumerator(changes); @@ -727,8 +889,15 @@ public static IObservable> RemoveKey([NotNull /// source public static IObservable> ChangeKey(this IObservable> source, Func keySelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return source.Select(updates => { @@ -749,8 +918,15 @@ public static IObservable> ChangeKeysource public static IObservable> ChangeKey(this IObservable> source, Func keySelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return source.Select(updates => { @@ -774,8 +950,16 @@ public static IObservable> ChangeKey> Convert(this IObservable> source, Func conversionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (conversionFactory == null) + { + throw new ArgumentNullException(nameof(conversionFactory)); + } + return source.Select(changes => { var transformed = changes.Select(change => new Change(change.Reason, @@ -788,7 +972,6 @@ public static IObservable> Convert /// Cast the object to the specified type. /// Alas, I had to add the converter due to type inference issues @@ -803,11 +986,14 @@ public static IObservable> Convert> Cast(this IObservable> source, Func converter) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new Cast(source, converter).Run(); } - #endregion #region Delayed Stream @@ -846,7 +1032,10 @@ public static IObservable> Batch(this I TimeSpan timeSpan, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return source.Buffer(timeSpan, scheduler ?? Scheduler.Default).FlattenBufferResult(); } @@ -860,7 +1049,11 @@ public static IObservable> Batch(this I /// public static IObservable> FlattenBufferResult([NotNull] this IObservable>> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Where(x => x.Count != 0) .Select(updates => new ChangeSet(updates.SelectMany(u => u))); } @@ -942,9 +1135,16 @@ public static IObservable> BatchIf(this TimeSpan? timeOut = null, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pauseIfTrueSelector == null) throw new ArgumentNullException(nameof(pauseIfTrueSelector)); - + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (pauseIfTrueSelector == null) + { + throw new ArgumentNullException(nameof(pauseIfTrueSelector)); + } + return new BatchIf(source, pauseIfTrueSelector, timeOut, intialPauseState,scheduler: scheduler).Run(); } @@ -980,7 +1180,11 @@ public static IObservable> BatchIf(this /// source public static IObservable> SkipInitial(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.DeferUntilLoaded().Skip(1); } @@ -993,7 +1197,11 @@ public static IObservable> SkipInitial( /// public static IObservable> DeferUntilLoaded(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new DeferUntilLoaded(source).Run(); } @@ -1006,7 +1214,11 @@ public static IObservable> DeferUntilLoaded public static IObservable> DeferUntilLoaded(this IObservableCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new DeferUntilLoaded(source).Run(); } @@ -1112,9 +1324,20 @@ public static IObservable TrueForAny(this IObservab Func> observableSelector, Func equalityCondition) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); - if (equalityCondition == null) throw new ArgumentNullException(nameof(equalityCondition)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } + + if (equalityCondition == null) + { + throw new ArgumentNullException(nameof(equalityCondition)); + } return source.TrueFor(observableSelector, items => items.Any(o => o.LatestValue.HasValue && equalityCondition(o.LatestValue.Value))); @@ -1148,8 +1371,15 @@ private static IObservable TrueFor(this IObservable public static IObservable QueryWhenChanged(this IObservable> source, Func, TDestination> resultSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return source.QueryWhenChanged().Select(resultSelector); } @@ -1164,7 +1394,11 @@ public static IObservable QueryWhenChangedsource public static IObservable> QueryWhenChanged(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new QueryWhenChanged(source).Run(); } @@ -1181,8 +1415,15 @@ public static IObservable> QueryWhenChanged public static IObservable> QueryWhenChanged([NotNull] this IObservable> source, [NotNull] Func> itemChangedTrigger) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (itemChangedTrigger == null) throw new ArgumentNullException(nameof(itemChangedTrigger)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (itemChangedTrigger == null) + { + throw new ArgumentNullException(nameof(itemChangedTrigger)); + } return new QueryWhenChanged(source, itemChangedTrigger).Run(); } @@ -1198,7 +1439,7 @@ public static IObservable> ToCollection new ReadOnlyCollectionLight(query.Items)); } - + /// /// Converts the changeset into a fully formed sorted collection. Each change in the source results in a new sorted collection /// @@ -1209,11 +1450,11 @@ public static IObservable> ToCollectionThe sort function /// The sort order. Defaults to ascending /// - public static IObservable> ToSortedCollection(this IObservable> source, + public static IObservable> ToSortedCollection(this IObservable> source, Func sort, SortDirection sortOrder = SortDirection.Ascending) { - return source.QueryWhenChanged(query => sortOrder == SortDirection.Ascending - ? new ReadOnlyCollectionLight(query.Items.OrderBy(sort)) + return source.QueryWhenChanged(query => sortOrder == SortDirection.Ascending + ? new ReadOnlyCollectionLight(query.Items.OrderBy(sort)) : new ReadOnlyCollectionLight(query.Items.OrderByDescending(sort))); } @@ -1251,7 +1492,11 @@ public static IObservable> ToSortedCollectionsource public static IObservable WatchValue(this IObservableCache source, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Watch(key).Select(u => u.Current); } @@ -1266,7 +1511,11 @@ public static IObservable WatchValue(this IObservableCac /// source public static IObservable WatchValue(this IObservable> source, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Watch(key).Select(u => u.Current); } @@ -1280,7 +1529,11 @@ public static IObservable WatchValue(this IObservable public static IObservable> Watch(this IObservable> source, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.SelectMany(updates => updates).Where(update => update.Key.Equals(key)); } @@ -1288,7 +1541,6 @@ public static IObservable> Watch(this IObse #region Clone - /// /// Clones the changes into the specified collection /// @@ -1299,8 +1551,16 @@ public static IObservable> Watch(this IObse /// public static IObservable> Clone(this IObservable> source, [NotNull] ICollection target) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (target == null) throw new ArgumentNullException(nameof(target)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (target == null) + { + throw new ArgumentNullException(nameof(target)); + } + return source.Do(changes => { foreach (var item in changes) @@ -1311,12 +1571,14 @@ public static IObservable> Clone(this I { target.Add(item.Current); } + break; case ChangeReason.Update: { target.Remove(item.Previous.Value); target.Add(item.Current); } + break; case ChangeReason.Remove: target.Remove(item.Current); @@ -1368,8 +1630,15 @@ public static IObservable> ExpireAfter( public static IObservable> ExpireAfter(this IObservable> source, Func timeSelector, IScheduler scheduler) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (timeSelector == null) throw new ArgumentNullException(nameof(timeSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (timeSelector == null) + { + throw new ArgumentNullException(nameof(timeSelector)); + } return source.ExpireAfter(timeSelector, null, scheduler); } @@ -1414,8 +1683,16 @@ public static IObservable> ExpireAfter( public static IObservable> ExpireAfter(this IObservable> source, Func timeSelector, TimeSpan? pollingInterval, IScheduler scheduler) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (timeSelector == null) throw new ArgumentNullException(nameof(timeSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (timeSelector == null) + { + throw new ArgumentNullException(nameof(timeSelector)); + } + return new TimeExpirer(source, timeSelector, pollingInterval, scheduler).ExpireAfter(); } @@ -1457,8 +1734,15 @@ internal static IObservable>> ForExpiry< public static IObservable> LimitSizeTo( this IObservable> source, int size) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (size <= 0) throw new ArgumentException("Size limit must be greater than zero"); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (size <= 0) + { + throw new ArgumentException("Size limit must be greater than zero"); + } return new SizeExpirer(source, size).Run(); } @@ -1467,7 +1751,6 @@ public static IObservable> LimitSizeTo( #region Paged - /// /// Returns the page as specified by the pageRequests observable /// @@ -1479,8 +1762,16 @@ public static IObservable> LimitSizeTo( public static IObservable> Page([NotNull] this IObservable> source, [NotNull] IObservable pageRequests) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pageRequests == null) throw new ArgumentNullException(nameof(pageRequests)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (pageRequests == null) + { + throw new ArgumentNullException(nameof(pageRequests)); + } + return new Page(source, pageRequests).Run(); } @@ -1498,7 +1789,10 @@ public static IObservable> Page([N /// public static IObservable> Filter(this IObservable> source, Func filter) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return new StaticFilter(source, filter).Run(); } @@ -1514,8 +1808,16 @@ public static IObservable> Filter(this public static IObservable> Filter([NotNull] this IObservable> source, [NotNull] IObservable> predicateChanged) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicateChanged == null) throw new ArgumentNullException(nameof(predicateChanged)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicateChanged == null) + { + throw new ArgumentNullException(nameof(predicateChanged)); + } + return new DynamicFilter(source, predicateChanged).Run(); } @@ -1530,13 +1832,20 @@ public static IObservable> Filter([NotN public static IObservable> Filter([NotNull] this IObservable> source, [NotNull] IObservable reapplyFilter) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (reapplyFilter == null) throw new ArgumentNullException(nameof(reapplyFilter)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (reapplyFilter == null) + { + throw new ArgumentNullException(nameof(reapplyFilter)); + } + var empty = Observable.Empty>(); return new DynamicFilter(source, empty, reapplyFilter).Run(); } - /// /// Creates a filtered stream which can be dynamically filtered /// @@ -1550,13 +1859,24 @@ public static IObservable> Filter([NotN [NotNull] IObservable> predicateChanged, [NotNull] IObservable reapplyFilter) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicateChanged == null) throw new ArgumentNullException(nameof(predicateChanged)); - if (reapplyFilter == null) throw new ArgumentNullException(nameof(reapplyFilter)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicateChanged == null) + { + throw new ArgumentNullException(nameof(predicateChanged)); + } + + if (reapplyFilter == null) + { + throw new ArgumentNullException(nameof(reapplyFilter)); + } + return new DynamicFilter(source, predicateChanged, reapplyFilter).Run(); } - /// /// Filters source on the specified property using the specified predicate. /// The filter will automatically reapply when a property changes. @@ -1581,9 +1901,20 @@ public static IObservable> FilterOnProperty(source, propertySelector, predicate, propertyChangedThrottle, scheduler).Run(); } @@ -1647,8 +1978,16 @@ public static IObservable> Sort(t SortOptimisations sortOptimisations = SortOptimisations.None, int resetThreshold = DefaultSortResetThreshold) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (comparer == null) + { + throw new ArgumentNullException(nameof(comparer)); + } + return new Sort(source, comparer, sortOptimisations, resetThreshold: resetThreshold).Run(); } @@ -1667,8 +2006,15 @@ public static IObservable> Sort(t SortOptimisations sortOptimisations = SortOptimisations.None, int resetThreshold = DefaultSortResetThreshold) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparerObservable == null) throw new ArgumentNullException(nameof(comparerObservable)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (comparerObservable == null) + { + throw new ArgumentNullException(nameof(comparerObservable)); + } return new Sort(source, null, sortOptimisations, comparerObservable, resetThreshold: resetThreshold).Run(); } @@ -1690,8 +2036,15 @@ public static IObservable> Sort(t SortOptimisations sortOptimisations = SortOptimisations.None, int resetThreshold = DefaultSortResetThreshold) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparerObservable == null) throw new ArgumentNullException(nameof(comparerObservable)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (comparerObservable == null) + { + throw new ArgumentNullException(nameof(comparerObservable)); + } return new Sort(source, null, sortOptimisations, comparerObservable, resorter, resetThreshold).Run(); } @@ -1713,8 +2066,15 @@ public static IObservable> Sort(t SortOptimisations sortOptimisations = SortOptimisations.None, int resetThreshold = DefaultSortResetThreshold) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (resorter == null) throw new ArgumentNullException(nameof(resorter)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (resorter == null) + { + throw new ArgumentNullException(nameof(resorter)); + } return new Sort(source, comparer, sortOptimisations, null, resorter, resetThreshold).Run(); } @@ -1729,9 +2089,12 @@ public static IObservable> Sort(t public static IObservable> TreatMovesAsRemoveAdd( this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } - IEnumerable> ReplaceMoves(IChangeSet items) + IEnumerable> ReplaceMoves(IChangeSet items) { foreach (var change in items) { @@ -1758,7 +2121,6 @@ IEnumerable> ReplaceMoves(IChangeSet items) return source.Select(changes => new SortedChangeSet(changes.SortedItems, ReplaceMoves(changes))); } - #endregion #region And, or, except @@ -1779,8 +2141,15 @@ IEnumerable> ReplaceMoves(IChangeSet items) public static IObservable> And(this IObservable> source, params IObservable>[] others) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (others == null || others.Length == 0) throw new ArgumentNullException(nameof(others)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (others == null || others.Length == 0) + { + throw new ArgumentNullException(nameof(others)); + } return source.Combine(CombineOperator.And, others); } @@ -1799,7 +2168,10 @@ public static IObservable> And(this IOb /// public static IObservable> And(this ICollection>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.And); } @@ -1814,7 +2186,10 @@ public static IObservable> And(this ICo /// public static IObservable> And(this IObservableList>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.And); } @@ -1829,7 +2204,10 @@ public static IObservable> And(this IOb /// public static IObservable> And(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.And); } @@ -1844,7 +2222,10 @@ public static IObservable> And(this IOb /// public static IObservable> And(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.And); } @@ -1865,8 +2246,15 @@ public static IObservable> And(this IOb public static IObservable> Or(this IObservable> source, params IObservable>[] others) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (others == null || others.Length == 0) throw new ArgumentNullException(nameof(others)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (others == null || others.Length == 0) + { + throw new ArgumentNullException(nameof(others)); + } return source.Combine(CombineOperator.Or, others); } @@ -1885,7 +2273,10 @@ public static IObservable> Or(this IObs /// public static IObservable> Or(this ICollection>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Or); } @@ -1900,7 +2291,10 @@ public static IObservable> Or(this ICol /// public static IObservable> Or(this IObservableList>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Or); } @@ -1930,7 +2324,6 @@ public static IObservable> Or(this IObs // return sources.Combine(CombineOperator.Or); //} - /// /// Dynamically apply a logical Or operator between the items in the outer observable list. /// Items which are in any of the sources are included in the result @@ -1941,7 +2334,10 @@ public static IObservable> Or(this IObs /// public static IObservable> Or(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Or); } @@ -1956,7 +2352,10 @@ public static IObservable> Or(this IObs /// public static IObservable> Or(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Or); } @@ -1978,8 +2377,15 @@ public static IObservable> Or(this IObs public static IObservable> Xor(this IObservable> source, params IObservable>[] others) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (others == null || others.Length == 0) throw new ArgumentNullException(nameof(others)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (others == null || others.Length == 0) + { + throw new ArgumentNullException(nameof(others)); + } return source.Combine(CombineOperator.Xor, others); } @@ -1999,7 +2405,10 @@ public static IObservable> Xor(this IOb /// public static IObservable> Xor(this ICollection>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Xor); } @@ -2014,7 +2423,10 @@ public static IObservable> Xor(this ICo /// public static IObservable> Xor(this IObservableList>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Xor); } @@ -2029,7 +2441,10 @@ public static IObservable> Xor(this IOb /// public static IObservable> Xor(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Xor); } @@ -2044,7 +2459,10 @@ public static IObservable> Xor(this IOb /// public static IObservable> Xor(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Xor); } @@ -2066,8 +2484,15 @@ public static IObservable> Xor(this IOb public static IObservable> Except(this IObservable> source, params IObservable>[] others) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (others == null || others.Length == 0) throw new ArgumentNullException(nameof(others)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (others == null || others.Length == 0) + { + throw new ArgumentNullException(nameof(others)); + } return source.Combine(CombineOperator.Except, others); } @@ -2087,7 +2512,10 @@ public static IObservable> Except(this /// public static IObservable> Except(this ICollection>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Except); } @@ -2102,7 +2530,10 @@ public static IObservable> Except(this /// public static IObservable> Except(this IObservableList>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Except); } @@ -2117,7 +2548,10 @@ public static IObservable> Except(this /// public static IObservable> Except(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Except); } @@ -2132,14 +2566,21 @@ public static IObservable> Except(this /// public static IObservable> Except(this IObservableList> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return sources.Combine(CombineOperator.Except); } private static IObservable> Combine([NotNull] this IObservableList> source, CombineOperator type) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return Observable.Create>(observer => { var connections = source.Connect().Transform(x => x.Connect()).AsObservableList(); @@ -2150,7 +2591,11 @@ private static IObservable> Combine([No private static IObservable> Combine([NotNull] this IObservableList> source, CombineOperator type) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return Observable.Create>(observer => { var connections = source.Connect().Transform(x => x.Connect()).AsObservableList(); @@ -2161,13 +2606,20 @@ private static IObservable> Combine([No private static IObservable> Combine([NotNull] this IObservableList>> source, CombineOperator type) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new DynamicCombiner(source, type).Run(); } private static IObservable> Combine(this ICollection>> sources, CombineOperator type) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return Observable.Create> ( @@ -2205,7 +2657,10 @@ private static IObservable> Combine(thi CombineOperator type, params IObservable>[] combinetarget) { - if (combinetarget == null) throw new ArgumentNullException(nameof(combinetarget)); + if (combinetarget == null) + { + throw new ArgumentNullException(nameof(combinetarget)); + } return Observable.Create> ( @@ -2254,7 +2709,11 @@ void UpdateAction(IChangeSet updates) public static IObservable> StartWithItem(this IObservable> source, TObject item) where TObject : IKey { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.StartWithItem(item, item.Key); } @@ -2270,7 +2729,10 @@ public static IObservable> StartWithItem> StartWithItem(this IObservable> source, TObject item, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } var change = new Change(ChangeReason.Add, key, item); return source.StartWith(new ChangeSet{change}); @@ -2299,8 +2761,15 @@ public static IObservable> Transform transformFactory, bool transformOnRefresh) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.Transform((current, previous, key) => transformFactory(current), transformOnRefresh); } @@ -2324,8 +2793,16 @@ public static IObservable> Transform transformFactory, bool transformOnRefresh) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + return source.Transform((current, previous, key) => transformFactory(current, key), transformOnRefresh); } @@ -2348,8 +2825,15 @@ public static IObservable> Transform, TKey, TDestination> transformFactory, bool transformOnRefresh) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return new Transform(source, transformFactory, transformOnRefresh: transformOnRefresh).Run(); } @@ -2371,8 +2855,16 @@ public static IObservable> Transform public static IObservable> Transform(this IObservable> source, Func transformFactory, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + return source.Transform((current, previous, key) => transformFactory(current), forceTransform.ForForced()); } @@ -2393,8 +2885,16 @@ public static IObservable> Transform public static IObservable> Transform(this IObservable> source, Func transformFactory, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + return source.Transform((current, previous, key) => transformFactory(current, key), forceTransform); } @@ -2415,11 +2915,20 @@ public static IObservable> Transform public static IObservable> Transform(this IObservable> source, Func, TKey, TDestination> transformFactory, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } if (forceTransform!=null) + { return new TransformWithForcedTransform(source, transformFactory, forceTransform).Run(); + } return new Transform(source, transformFactory).Run(); } @@ -2444,7 +2953,6 @@ public static IObservable> Transform transformFactory(cur), forceTransform.ForForced()); } - /// /// Projects each update item to a new form using the specified transform function /// @@ -2462,9 +2970,20 @@ public static IObservable> Transform public static IObservable> Transform(this IObservable> source, Func transformFactory, IObservable forceTransform) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (forceTransform == null) throw new ArgumentNullException(nameof(forceTransform)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (forceTransform == null) + { + throw new ArgumentNullException(nameof(forceTransform)); + } return source.Transform((cur, prev, key) => transformFactory(cur, key), forceTransform.ForForced()); } @@ -2486,9 +3005,20 @@ public static IObservable> Transform public static IObservable> Transform(this IObservable> source, Func, TKey, TDestination> transformFactory, IObservable forceTransform) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (forceTransform == null) throw new ArgumentNullException(nameof(forceTransform)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (forceTransform == null) + { + throw new ArgumentNullException(nameof(forceTransform)); + } return source.Transform(transformFactory, forceTransform.ForForced()); } @@ -2536,8 +3066,15 @@ public static IObservable> TransformAsync> forceTransform = null, int maximumConcurrency = 1) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.TransformAsync((current, previous, key) => transformFactory(current), maximumConcurrency, forceTransform); } @@ -2563,13 +3100,19 @@ public static IObservable> TransformAsync> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.TransformAsync((current, previous, key) => transformFactory(current, key), maximumConcurrency, forceTransform); } - /// /// Projects each update item to a new form using the specified transform function /// @@ -2591,8 +3134,15 @@ public static IObservable> TransformAsync> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return new TransformAsync(source, transformFactory, null, maximumConcurrency, forceTransform).Run(); } @@ -2601,7 +3151,6 @@ public static IObservable> TransformAsync /// Equivalent to a select many transform. To work, the key must individually identify each child. /// @@ -2655,9 +3204,6 @@ public static IObservable> TransformMa return new TransformMany(source, manyselector, keySelector).Run(); } - - - #endregion #region Transform safe @@ -2681,9 +3227,21 @@ public static IObservable> TransformMa /// transformFactory public static IObservable> TransformSafe(this IObservable> source, Func transformFactory, Action> errorHandler, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } + return source.TransformSafe((current, previous, key) => transformFactory(current), errorHandler, forceTransform.ForForced()); } @@ -2706,9 +3264,21 @@ public static IObservable> TransformSafe public static IObservable> TransformSafe(this IObservable> source, Func transformFactory, Action> errorHandler, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } + return source.TransformSafe((current, previous, key) => transformFactory(current, key), errorHandler, forceTransform); } @@ -2734,13 +3304,25 @@ public static IObservable> TransformSafe> errorHandler, IObservable> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } if (forceTransform != null) + { return new TransformWithForcedTransform(source, transformFactory, forceTransform, errorHandler).Run(); + } return new Transform(source, transformFactory, errorHandler).Run(); } @@ -2767,7 +3349,6 @@ public static IObservable> TransformSafe transformFactory(cur), errorHandler, forceTransform.ForForced()); } - /// /// Projects each update item to a new form using the specified transform function, /// providing an error handling action to safely handle transform errors without killing the stream. @@ -2788,9 +3369,20 @@ public static IObservable> TransformSafe> TransformSafe(this IObservable> source, Func transformFactory, Action> errorHandler, IObservable forceTransform) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (forceTransform == null) throw new ArgumentNullException(nameof(forceTransform)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (forceTransform == null) + { + throw new ArgumentNullException(nameof(forceTransform)); + } return source.TransformSafe((cur, prev, key) => transformFactory(cur, key), errorHandler, forceTransform.ForForced()); } @@ -2817,20 +3409,28 @@ public static IObservable> TransformSafe> errorHandler, IObservable forceTransform) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (forceTransform == null) throw new ArgumentNullException(nameof(forceTransform)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (forceTransform == null) + { + throw new ArgumentNullException(nameof(forceTransform)); + } return source.TransformSafe(transformFactory, errorHandler, forceTransform.ForForced()); } - #endregion #region Transform safe async - - /// /// Projects each update item to a new form using the specified transform function /// @@ -2854,9 +3454,20 @@ public static IObservable> TransformSafeAsync> forceTransform = null, int maximumConcurrency = 1) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } return source.TransformSafeAsync((current, previous, key) => transformFactory(current), errorHandler, maximumConcurrency, forceTransform); } @@ -2884,14 +3495,24 @@ public static IObservable> TransformSafeAsync> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } return source.TransformSafeAsync((current, previous, key) => transformFactory(current, key), errorHandler, maximumConcurrency, forceTransform); } - /// /// Projects each update item to a new form using the specified transform function /// @@ -2915,14 +3536,23 @@ public static IObservable> TransformSafeAsync> forceTransform = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } - return new TransformAsync(source, transformFactory, errorHandler, maximumConcurrency, forceTransform).Run(); - } + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } + return new TransformAsync(source, transformFactory, errorHandler, maximumConcurrency, forceTransform).Run(); + } /// /// Transforms the object to a fully recursive tree, create a hiearchy based on the pivot function @@ -2938,12 +3568,18 @@ public static IObservable, TKey>> TransformToTree IObservable, bool>> predicateChanged = null) where TObject : class { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pivotOn == null) throw new ArgumentNullException(nameof(pivotOn)); - return new TreeBuilder(source, pivotOn, predicateChanged).Run(); - } + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + if (pivotOn == null) + { + throw new ArgumentNullException(nameof(pivotOn)); + } + return new TreeBuilder(source, pivotOn, predicateChanged).Run(); + } #endregion @@ -2964,8 +3600,15 @@ public static IObservable, TKey>> TransformToTree /// source public static IObservable> DistinctValues(this IObservable> source, Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } return Observable.Create>(observer => { @@ -2998,9 +3641,20 @@ public static IObservable> Group groupSelector, IObservable> resultGroupSource) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelector == null) throw new ArgumentNullException(nameof(groupSelector)); - if (resultGroupSource == null) throw new ArgumentNullException(nameof(resultGroupSource)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (groupSelector == null) + { + throw new ArgumentNullException(nameof(groupSelector)); + } + + if (resultGroupSource == null) + { + throw new ArgumentNullException(nameof(resultGroupSource)); + } return new SpecifiedGrouper(source, groupSelector, resultGroupSource).Run(); } @@ -3017,13 +3671,19 @@ public static IObservable> Group> Group(this IObservable> source, Func groupSelectorKey) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelectorKey == null) throw new ArgumentNullException(nameof(groupSelectorKey)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (groupSelectorKey == null) + { + throw new ArgumentNullException(nameof(groupSelectorKey)); + } return new GroupOn(source, groupSelectorKey, null).Run(); } - /// /// Groups the source on the value returned by group selector factory. /// @@ -3045,14 +3705,23 @@ public static IObservable> Group groupSelectorKey, IObservable regrouper) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelectorKey == null) throw new ArgumentNullException(nameof(groupSelectorKey)); - if (regrouper == null) throw new ArgumentNullException(nameof(regrouper)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } - return new GroupOn(source, groupSelectorKey, regrouper).Run(); - } + if (groupSelectorKey == null) + { + throw new ArgumentNullException(nameof(groupSelectorKey)); + } + if (regrouper == null) + { + throw new ArgumentNullException(nameof(regrouper)); + } + return new GroupOn(source, groupSelectorKey, regrouper).Run(); + } /// /// Groups the source on the value returned by group selector factory. Each update produces immuatable grouping. @@ -3075,8 +3744,15 @@ public static IObservable> Gr Func groupSelectorKey, IObservable regrouper = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelectorKey == null) throw new ArgumentNullException(nameof(groupSelectorKey)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (groupSelectorKey == null) + { + throw new ArgumentNullException(nameof(groupSelectorKey)); + } return new GroupOnImmutable(source, groupSelectorKey, regrouper).Run(); } @@ -3102,8 +3778,15 @@ public static IObservable> GroupOnProp IScheduler scheduler = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertySelector == null) + { + throw new ArgumentNullException(nameof(propertySelector)); + } return new GroupOnProperty(source, propertySelector, propertyChangedThrottle, scheduler).Run(); } @@ -3129,8 +3812,15 @@ public static IObservable> Gr IScheduler scheduler = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertySelector == null) throw new ArgumentNullException(nameof(propertySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertySelector == null) + { + throw new ArgumentNullException(nameof(propertySelector)); + } return new GroupOnPropertyWithImmutableState(source, propertySelector, propertyChangedThrottle, scheduler).Run(); } @@ -3152,8 +3842,16 @@ public static IObservable> Gr public static IObservable> Top( this IObservable> source, int size) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size), "Size should be greater than zero"); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (size <= 0) + { + throw new ArgumentOutOfRangeException(nameof(size), "Size should be greater than zero"); + } + return new Virtualise(source, Observable.Return(new VirtualRequest(0, size))).Run(); } @@ -3173,14 +3871,24 @@ public static IObservable> Top( IComparer comparer, int size) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); - if (size <= 0) throw new ArgumentOutOfRangeException(nameof(size), "Size should be greater than zero"); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (comparer == null) + { + throw new ArgumentNullException(nameof(comparer)); + } + + if (size <= 0) + { + throw new ArgumentOutOfRangeException(nameof(size), "Size should be greater than zero"); + } return source.Sort(comparer).Top(size); } - /// /// Virtualises the underlying data from the specified source. /// @@ -3193,8 +3901,16 @@ public static IObservable> Top( public static IObservable> Virtualise(this IObservable> source, IObservable virtualRequests) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (virtualRequests == null) throw new ArgumentNullException(nameof(virtualRequests)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (virtualRequests == null) + { + throw new ArgumentNullException(nameof(virtualRequests)); + } + return new Virtualise(source, virtualRequests).Run(); } @@ -3214,8 +3930,16 @@ public static IObservable> Virtualise> Bind(this IObservable> source, IObservableCollection destination) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + var updater = new ObservableCollectionAdaptor(); return source.Bind(destination, updater); } @@ -3234,9 +3958,20 @@ public static IObservable> Bind(this IO IObservableCollection destination, IObservableCollectionAdaptor updater) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - if (updater == null) throw new ArgumentNullException(nameof(updater)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (updater == null) + { + throw new ArgumentNullException(nameof(updater)); + } return Observable.Create>(observer => { @@ -3263,8 +3998,16 @@ public static IObservable> Bind(this IO public static IObservable> Bind(this IObservable> source, IObservableCollection destination) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + var updater = new SortedObservableCollectionAdaptor(); return source.Bind(destination, updater); } @@ -3284,9 +4027,20 @@ public static IObservable> Bind( IObservableCollection destination, ISortedObservableCollectionAdaptor updater) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - if (updater == null) throw new ArgumentNullException(nameof(updater)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } + + if (updater == null) + { + throw new ArgumentNullException(nameof(updater)); + } return Observable.Create>(observer => { @@ -3317,7 +4071,10 @@ public static IObservable> Bind(this IO int resetThreshold = 25, ISortedObservableCollectionAdaptor adaptor = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } var target = new ObservableCollectionExtended(); var result = new ReadOnlyObservableCollection(target); @@ -3342,7 +4099,10 @@ public static IObservable> Bind(this IO int resetThreshold = 25, IObservableCollectionAdaptor adaptor = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } var target = new ObservableCollectionExtended(); var result = new ReadOnlyObservableCollection(target); @@ -3351,7 +4111,6 @@ public static IObservable> Bind(this IO return source.Bind(target, updater); } - #if SUPPORTS_BINDINGLIST /// @@ -3370,8 +4129,15 @@ public static IObservable> Bind(this IO public static IObservable> Bind([NotNull] this IObservable> source, [NotNull] BindingList bindingList, int resetThreshold = 25) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (bindingList == null) throw new ArgumentNullException(nameof(bindingList)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (bindingList == null) + { + throw new ArgumentNullException(nameof(bindingList)); + } return source.Adapt(new BindingListAdaptor(bindingList, resetThreshold)); } @@ -3392,8 +4158,15 @@ public static IObservable> Bind([NotNul public static IObservable> Bind([NotNull] this IObservable> source, [NotNull] BindingList bindingList, int resetThreshold = 25) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (bindingList == null) throw new ArgumentNullException(nameof(bindingList)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (bindingList == null) + { + throw new ArgumentNullException(nameof(bindingList)); + } return source.Adapt(new SortedBindingListAdaptor(bindingList, resetThreshold)); } @@ -3419,8 +4192,15 @@ public static IObservable> Bind([NotNul /// public static IObservable> Adapt(this IObservable> source, IChangeSetAdaptor adaptor) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (adaptor == null) throw new ArgumentNullException(nameof(adaptor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (adaptor == null) + { + throw new ArgumentNullException(nameof(adaptor)); + } return source.Do(adaptor.Adapt); } @@ -3440,8 +4220,15 @@ public static IObservable> Adapt(this I /// public static IObservable> Adapt(this IObservable> source, ISortedChangeSetAdaptor adaptor) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (adaptor == null) throw new ArgumentNullException(nameof(adaptor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (adaptor == null) + { + throw new ArgumentNullException(nameof(adaptor)); + } return source.Do(adaptor.Adapt); } @@ -3469,10 +4256,26 @@ public static IObservable> InnerJoin rightKeySelector, [NotNull] Func resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.InnerJoin(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3496,10 +4299,26 @@ public static IObservable> InnerJoin rightKeySelector, [NotNull] Func resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new InnerJoin(left, right, rightKeySelector, resultSelector).Run(); } @@ -3523,10 +4342,26 @@ public static IObservable> InnerJoinMany rightKeySelector, [NotNull] Func, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.InnerJoinMany(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3550,14 +4385,29 @@ public static IObservable> InnerJoinMany rightKeySelector, [NotNull] Func, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new InnerJoinMany(left, right, rightKeySelector, resultSelector).Run(); } - /// /// Joins the left and right observable data sources, taking any left or right values and matching them, provided that the left or the right has a value. /// This is the equivalent of SQL full join. @@ -3578,10 +4428,26 @@ public static IObservable> FullJoin rightKeySelector, [NotNull] Func, Optional, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.FullJoin(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3605,14 +4471,29 @@ public static IObservable> FullJoin rightKeySelector, [NotNull] Func, Optional, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new FullJoin(left, right, rightKeySelector, resultSelector).Run(); } - /// /// Groups the right data source and joins the resulting group to the left data source, matching these using the specified key selector. Results are included when the left or the right has a value. /// This is the equivalent of SQL full join. @@ -3633,10 +4514,26 @@ public static IObservable> FullJoinMany rightKeySelector, [NotNull] Func, IGrouping, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.FullJoinMany(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3660,10 +4557,26 @@ public static IObservable> FullJoinMany rightKeySelector, [NotNull] Func, IGrouping, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new FullJoinMany(left, right, rightKeySelector, resultSelector).Run(); } @@ -3686,10 +4599,26 @@ public static IObservable> LeftJoin rightKeySelector, [NotNull] Func, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.LeftJoin(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3712,10 +4641,26 @@ public static IObservable> LeftJoin rightKeySelector, [NotNull] Func, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new LeftJoin(left, right, rightKeySelector, resultSelector).Run(); } @@ -3739,10 +4684,26 @@ public static IObservable> LeftJoinMany rightKeySelector, [NotNull] Func, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.LeftJoinMany(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3766,10 +4727,26 @@ public static IObservable> LeftJoinMany rightKeySelector, [NotNull] Func, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new LeftJoinMany(left, right, rightKeySelector, resultSelector).Run(); } @@ -3792,10 +4769,26 @@ public static IObservable> RightJoin rightKeySelector, [NotNull] Func, TRight, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.RightJoin(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3818,10 +4811,26 @@ public static IObservable> RightJoin rightKeySelector, [NotNull] Func, TRight, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new RightJoin(left, right, rightKeySelector, resultSelector).Run(); } @@ -3846,10 +4855,26 @@ public static IObservable> RightJoinMany rightKeySelector, [NotNull] Func, IGrouping, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return left.RightJoinMany(right, rightKeySelector, (leftKey, leftValue, rightValue) => resultSelector(leftValue, rightValue)); } @@ -3873,14 +4898,29 @@ public static IObservable> RightJoinMany rightKeySelector, [NotNull] Func, IGrouping, TDestination> resultSelector) { - if (left == null) throw new ArgumentNullException(nameof(left)); - if (right == null) throw new ArgumentNullException(nameof(right)); - if (rightKeySelector == null) throw new ArgumentNullException(nameof(rightKeySelector)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (left == null) + { + throw new ArgumentNullException(nameof(left)); + } + + if (right == null) + { + throw new ArgumentNullException(nameof(right)); + } + + if (rightKeySelector == null) + { + throw new ArgumentNullException(nameof(rightKeySelector)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } + return new RightJoinMany(left, right, rightKeySelector, resultSelector).Run(); } - #endregion #region Populate into an observable cache @@ -3900,8 +4940,15 @@ public static IObservable> RightJoinMany public static IDisposable PopulateInto(this IObservable> source, ISourceCache detination) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (detination == null) throw new ArgumentNullException(nameof(detination)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (detination == null) + { + throw new ArgumentNullException(nameof(detination)); + } return source.Subscribe(changes => detination.Edit(updater => updater.Clone(changes))); } @@ -3919,8 +4966,15 @@ public static IDisposable PopulateInto(this IObservable public static IDisposable PopulateInto(this IObservable> source, IIntermediateCache detination) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (detination == null) throw new ArgumentNullException(nameof(detination)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (detination == null) + { + throw new ArgumentNullException(nameof(detination)); + } return source.Subscribe(changes => detination.Edit(updater => updater.Clone(changes))); } @@ -3940,7 +4994,11 @@ public static IDisposable PopulateInto(this IObservable public static IDisposable PopulateFrom(this ISourceCache source, IObservable> observable) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return observable.Subscribe(source.AddOrUpdate); } @@ -3959,7 +5017,11 @@ public static IDisposable PopulateFrom(this ISourceCache public static IDisposable PopulateFrom(this ISourceCache source, IObservable observable) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return observable.Subscribe(source.AddOrUpdate); } @@ -3977,7 +5039,11 @@ public static IDisposable PopulateFrom(this ISourceCachesource public static IObservableCache AsObservableCache(this IObservableCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new AnonymousObservableCache(source); } @@ -3992,14 +5058,19 @@ public static IObservableCache AsObservableCache(t /// source public static IObservableCache AsObservableCache(this IObservable> source, bool applyLocking = true) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + if (applyLocking) + { return new AnonymousObservableCache(source); + } return new LockFreeObservableCache(source); } - #endregion #region Populate changetset from observables @@ -4025,13 +5096,19 @@ public static IObservable> ToObservableChangeSet(source, keySelector, expireAfter, limitSizeTo, scheduler).Run(); } - /// /// Converts the observable to an observable changeset /// @@ -4051,8 +5128,15 @@ public static IObservable> ToObservableChangeSet(source, keySelector, expireAfter, limitSizeTo, scheduler).Run(); } @@ -4075,8 +5159,15 @@ public static IObservable> ToObservableChangeSetSize limit must be greater than zero public static IObservable>> LimitSizeTo(this ISourceCache source, int sizeLimit, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (sizeLimit <= 0) throw new ArgumentException("Size limit must be greater than zero"); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (sizeLimit <= 0) + { + throw new ArgumentException("Size limit must be greater than zero", nameof(sizeLimit)); + } return Observable.Create>>(observer => { @@ -4152,8 +5243,15 @@ public static IObservable>> ExpireAfter< public static IObservable>> ExpireAfter(this ISourceCache source, Func timeSelector, TimeSpan? pollingInterval, IScheduler scheduler) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (timeSelector == null) throw new ArgumentNullException(nameof(timeSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (timeSelector == null) + { + throw new ArgumentNullException(nameof(timeSelector)); + } return Observable.Create>>(observer => { @@ -4167,7 +5265,11 @@ public static IObservable>> ExpireAfter< { //remove from cache and notify which items have been auto removed var keyValuePairs = toRemove as KeyValuePair[] ?? toRemove.AsArray(); - if (keyValuePairs.Length == 0) return; + if (keyValuePairs.Length == 0) + { + return; + } + source.Remove(keyValuePairs.Select(kv => kv.Key)); observer.OnNext(keyValuePairs); } @@ -4197,9 +5299,21 @@ public static void EditDiff([NotNull] this ISourceCache alltems, [NotNull] IEqualityComparer equalityComparer) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (alltems == null) throw new ArgumentNullException(nameof(alltems)); - if (equalityComparer == null) throw new ArgumentNullException(nameof(equalityComparer)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (alltems == null) + { + throw new ArgumentNullException(nameof(alltems)); + } + + if (equalityComparer == null) + { + throw new ArgumentNullException(nameof(equalityComparer)); + } + source.EditDiff(alltems, equalityComparer.Equals); } @@ -4217,15 +5331,25 @@ public static void EditDiff([NotNull] this ISourceCache alltems, [NotNull] Func areItemsEqual) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (alltems == null) throw new ArgumentNullException(nameof(alltems)); - if (areItemsEqual == null) throw new ArgumentNullException(nameof(areItemsEqual)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (alltems == null) + { + throw new ArgumentNullException(nameof(alltems)); + } + + if (areItemsEqual == null) + { + throw new ArgumentNullException(nameof(areItemsEqual)); + } + var editDiff = new EditDiff(source, areItemsEqual); editDiff.Edit(alltems); } - - /// /// Adds or updates the cache with the specified item. /// @@ -4236,7 +5360,11 @@ public static void EditDiff([NotNull] this ISourceCachesource public static void AddOrUpdate(this ISourceCache source, TObject item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.AddOrUpdate(item)); } @@ -4251,7 +5379,11 @@ public static void AddOrUpdate(this ISourceCache s /// source public static void AddOrUpdate(this ISourceCache source, TObject item, IEqualityComparer equalityComparer) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.AddOrUpdate(item, equalityComparer)); } @@ -4267,7 +5399,11 @@ public static void AddOrUpdate(this ISourceCache s /// source public static void AddOrUpdate(this ISourceCache source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.AddOrUpdate(items)); } @@ -4283,7 +5419,11 @@ public static void AddOrUpdate(this ISourceCache s /// source public static void Remove(this ISourceCache source, TObject item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Remove(item)); } @@ -4298,7 +5438,11 @@ public static void Remove(this ISourceCache source /// source public static void Remove(this ISourceCache source, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Remove(key)); } @@ -4313,7 +5457,11 @@ public static void Remove(this ISourceCache source /// source public static void RemoveKey(this ISourceCache source, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.RemoveKey(key)); } @@ -4329,7 +5477,11 @@ public static void RemoveKey(this ISourceCache sou /// source public static void Remove(this ISourceCache source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Remove(items)); } @@ -4345,7 +5497,11 @@ public static void Remove(this ISourceCache source /// source public static void Remove(this ISourceCache source, IEnumerable keys) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Remove(keys)); } @@ -4361,7 +5517,11 @@ public static void Remove(this ISourceCache source /// source public static void RemoveKeys(this ISourceCache source, IEnumerable keys) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.RemoveKeys(keys)); } @@ -4374,7 +5534,11 @@ public static void RemoveKeys(this ISourceCache so /// source public static void Clear(this ISourceCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Clear()); } @@ -4388,7 +5552,11 @@ public static void Clear(this ISourceCache source) /// source public static void Refresh(this ISourceCache source, TObject item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Refresh(item)); } @@ -4402,7 +5570,11 @@ public static void Refresh(this ISourceCache sourc /// source public static void Refresh(this ISourceCache source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Refresh(items)); } @@ -4415,11 +5587,14 @@ public static void Refresh(this ISourceCache sourc /// source public static void Refresh(this ISourceCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Refresh()); } - /// /// Signal observers to re-evaluate the specified item. /// @@ -4431,7 +5606,11 @@ public static void Refresh(this ISourceCache sourc [Obsolete(Constants.EvaluateIsDead)] public static void Evaluate(this ISourceCache source, TObject item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Refresh(item)); } @@ -4446,11 +5625,14 @@ public static void Evaluate(this ISourceCache sour [Obsolete(Constants.EvaluateIsDead)] public static void Evaluate(this ISourceCache source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Refresh(items)); } - /// /// Removes the specified key from the cache. /// If the item is not contained in the cache then the operation does nothing. @@ -4463,8 +5645,16 @@ public static void Evaluate(this ISourceCache sour /// source public static void AddOrUpdate(this IIntermediateCache source, TObject item, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (item == null) throw new ArgumentNullException(nameof(item)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + source.Edit(updater => updater.AddOrUpdate(item, key)); } @@ -4478,7 +5668,11 @@ public static void AddOrUpdate(this IIntermediateCache(this ISourceCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Refresh()); } @@ -4493,7 +5687,11 @@ public static void Evaluate(this ISourceCache sour /// source public static void Remove(this IIntermediateCache source, TKey key) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Remove(key)); } @@ -4509,7 +5707,11 @@ public static void Remove(this IIntermediateCache /// source public static void Remove(this IIntermediateCache source, IEnumerable keys) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Remove(keys)); } @@ -4522,11 +5724,14 @@ public static void Remove(this IIntermediateCache /// source public static void Clear(this IIntermediateCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Clear()); } - /// /// Clears all data /// @@ -4536,11 +5741,14 @@ public static void Clear(this IIntermediateCache s /// source public static void Clear(this LockFreeObservableCache source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(updater => updater.Clear()); } - /// /// Populates a source into the specified cache. /// @@ -4556,8 +5764,15 @@ public static void Clear(this LockFreeObservableCache public static IDisposable PopulateInto(this IObservable> source, LockFreeObservableCache detination) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (detination == null) throw new ArgumentNullException(nameof(detination)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (detination == null) + { + throw new ArgumentNullException(nameof(detination)); + } return source.Subscribe(changes => detination.Edit(updater => updater.Clone(changes))); } @@ -4582,9 +5797,14 @@ public static IDisposable PopulateInto(this IObservable is null. public static IObservable> Switch(this IObservable> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + return sources.Select(cache => cache.Connect()).Switch(); } + /// /// Transforms an observable sequence of observable changes sets into an observable sequence /// producing values only from the most recent observable sequence. @@ -4602,7 +5822,11 @@ public static IObservable> Switch(this public static IObservable> Switch(this IObservable>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + return new Switch(sources).Run(); } diff --git a/src/DynamicData/Cache/PageRequest.cs b/src/DynamicData/Cache/PageRequest.cs index 144334799..68d469094 100644 --- a/src/DynamicData/Cache/PageRequest.cs +++ b/src/DynamicData/Cache/PageRequest.cs @@ -1,5 +1,11 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + // ReSharper disable once CheckNamespace namespace DynamicData { @@ -25,8 +31,16 @@ public sealed class PageRequest : IPageRequest, IEquatable /// The size. public PageRequest(int page, int size) { - if (page < 0) throw new ArgumentException("Page must be positive"); - if (size < 0) throw new ArgumentException("Size must be positive"); + if (page < 0) + { + throw new ArgumentException("Page must be positive"); + } + + if (size < 0) + { + throw new ArgumentException("Size must be positive"); + } + Page = page; Size = size; } @@ -60,7 +74,10 @@ public bool Equals(IPageRequest other) public override bool Equals(object obj) { if (!(obj is IPageRequest)) + { return false; + } + return Equals((IPageRequest)obj); } @@ -81,10 +98,26 @@ private sealed class PageSizeEqualityComparer : IEqualityComparer { public bool Equals(IPageRequest x, IPageRequest y) { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - if (x.GetType() != y.GetType()) return false; + if (ReferenceEquals(x, y)) + { + return true; + } + + if (ReferenceEquals(x, null)) + { + return false; + } + + if (ReferenceEquals(y, null)) + { + return false; + } + + if (x.GetType() != y.GetType()) + { + return false; + } + return x.Page == y.Page && x.Size == y.Size; } @@ -105,6 +138,7 @@ public int GetHashCode(IPageRequest obj) /// /// The default comparer. /// + [SuppressMessage("Design", "CA1822: Member can be static", Justification = "Backwards compatibilty")] public IEqualityComparer DefaultComparer => _pageSizeComparerInstance; #endregion diff --git a/src/DynamicData/Cache/PageResponse.cs b/src/DynamicData/Cache/PageResponse.cs index 7cf772c2c..85b832676 100644 --- a/src/DynamicData/Cache/PageResponse.cs +++ b/src/DynamicData/Cache/PageResponse.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Operators; @@ -45,7 +49,11 @@ public bool Equals(IPageResponse other) /// The to compare with the current . public override bool Equals(object obj) { - if (!(obj is IPageResponse)) return false; + if (!(obj is IPageResponse)) + { + return false; + } + return Equals(obj as IPageResponse); } @@ -75,10 +83,26 @@ private sealed class PageResponseEqualityComparer : IEqualityComparer : ChangeSet, { public new static readonly IPagedChangeSet Empty = new PagedChangeSet(); - public IKeyValueCollection SortedItems { get; } public IPageResponse Response { get; } - public PagedChangeSet(IKeyValueCollection sortedItems, IEnumerable> updates, IPageResponse response) : base(updates) { @@ -41,9 +43,20 @@ public bool Equals(PagedChangeSet other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } return Equals((PagedChangeSet)obj); } diff --git a/src/DynamicData/Cache/SortOptimisations.cs b/src/DynamicData/Cache/SortOptimisations.cs index 0dabf912c..46f1ce8fd 100644 --- a/src/DynamicData/Cache/SortOptimisations.cs +++ b/src/DynamicData/Cache/SortOptimisations.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; // ReSharper disable once CheckNamespace namespace DynamicData { @@ -13,7 +17,6 @@ public enum SortOptimisations /// None = 0, - /// /// Specify this option if the comparer used for sorting compares immutable fields only. /// In which case index changes can be calculated using BinarySearch rather than the expensive IndexOf @@ -26,11 +29,10 @@ public enum SortOptimisations /// IgnoreEvaluates = 2, - /// - /// The insert at end then sort entire set. This can be the best algorthm for large datasets with many changes + /// The insert at end then sort entire set. This can be the best algorithm for large datasets with many changes /// - [Obsolete] + [Obsolete("This is no longer being used. Use one of the other options instead.")] InsertAtEndThenSort = 3 } } diff --git a/src/DynamicData/Cache/SortedChangeSet.cs b/src/DynamicData/Cache/SortedChangeSet.cs index 1532d8af1..06c531fb8 100644 --- a/src/DynamicData/Cache/SortedChangeSet.cs +++ b/src/DynamicData/Cache/SortedChangeSet.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; using System.Linq; using DynamicData.Cache.Internal; @@ -8,9 +12,8 @@ namespace DynamicData internal class SortedChangeSet : ChangeSet, ISortedChangeSet { public new static readonly ISortedChangeSet Empty = new SortedChangeSet(); - - public IKeyValueCollection SortedItems { get; } + public IKeyValueCollection SortedItems { get; } public SortedChangeSet(IKeyValueCollection sortedItems, IEnumerable> updates) : base(updates) @@ -23,7 +26,6 @@ private SortedChangeSet() SortedItems = new KeyValueCollection(); } - #region Equality Members public bool Equals(SortedChangeSet other) @@ -33,9 +35,20 @@ public bool Equals(SortedChangeSet other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } return Equals((SortedChangeSet)obj); } diff --git a/src/DynamicData/Cache/SourceCache.cs b/src/DynamicData/Cache/SourceCache.cs index 09a82167d..961855bbf 100644 --- a/src/DynamicData/Cache/SourceCache.cs +++ b/src/DynamicData/Cache/SourceCache.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -13,6 +17,7 @@ namespace DynamicData public class SourceCache : ISourceCache { private readonly ObservableCache _innerCache; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -21,7 +26,11 @@ public class SourceCache : ISourceCache /// keySelector public SourceCache(Func keySelector) { - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } + _innerCache = new ObservableCache(keySelector); } @@ -57,7 +66,26 @@ public SourceCache(Func keySelector) public Optional Lookup(TKey key) => _innerCache.Lookup(key); /// - public void Dispose() => _innerCache.Dispose(); + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _innerCache.Dispose(); + } + } #endregion } diff --git a/src/DynamicData/Cache/SourceCacheEx.cs b/src/DynamicData/Cache/SourceCacheEx.cs index b6f4ae193..059e52280 100644 --- a/src/DynamicData/Cache/SourceCacheEx.cs +++ b/src/DynamicData/Cache/SourceCacheEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; // ReSharper disable once CheckNamespace namespace DynamicData @@ -20,6 +24,11 @@ public static class SourceCacheEx /// public static IObservable> Cast(this IObservableCache source, Func converter) { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Connect().Cast(converter); } } diff --git a/src/DynamicData/Cache/Tests/ChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/ChangeSetAggregator.cs index 39982fd5e..75114fa8e 100644 --- a/src/DynamicData/Cache/Tests/ChangeSetAggregator.cs +++ b/src/DynamicData/Cache/Tests/ChangeSetAggregator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -15,6 +19,7 @@ namespace DynamicData.Tests public class ChangeSetAggregator : IDisposable { private readonly IDisposable _disposer; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -76,7 +81,23 @@ public ChangeSetAggregator(IObservable> source) /// public void Dispose() { - _disposer.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _disposer?.Dispose(); + } } } } diff --git a/src/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs index 6d6ab5f2c..d8635864f 100644 --- a/src/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs +++ b/src/DynamicData/Cache/Tests/DistinctChangeSetAggregator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -16,6 +20,7 @@ public class DistinctChangeSetAggregator : IDisposable private readonly IDisposable _disposer; private ChangeSummary _summary = ChangeSummary.Empty; private Exception _error; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -64,11 +69,28 @@ public DistinctChangeSetAggregator(IObservable> sourc public Exception Error => _error; /// - /// Releases unmanaged and - optionally - managed resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - _disposer.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _disposer?.Dispose(); + } + } + } } diff --git a/src/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs index 1fa1978d3..65cd4b527 100644 --- a/src/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs +++ b/src/DynamicData/Cache/Tests/PagedChangeSetAggregator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -17,6 +21,7 @@ public class PagedChangeSetAggregator : IDisposable private readonly IDisposable _disposer; private Exception _error; private ChangeSummary _summary = ChangeSummary.Empty; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -72,11 +77,27 @@ public PagedChangeSetAggregator(IObservable> sour public Exception Error => _error; /// - /// Releases unmanaged and - optionally - managed resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - _disposer.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _disposer?.Dispose(); + } } } } diff --git a/src/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs index 18f3e41bc..a796d82b5 100644 --- a/src/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs +++ b/src/DynamicData/Cache/Tests/SortedChangeSetAggregator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -15,6 +19,7 @@ namespace DynamicData.Tests public class SortedChangeSetAggregator : IDisposable { private readonly IDisposable _disposer; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -66,11 +71,27 @@ public SortedChangeSetAggregator(IObservable> so public Exception Error { get; private set; } /// - /// Releases unmanaged and - optionally - managed resources. + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - _disposer.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _disposer?.Dispose(); + } } } } diff --git a/src/DynamicData/Cache/Tests/TestEx.cs b/src/DynamicData/Cache/Tests/TestEx.cs index cef3b0e42..5441bb611 100644 --- a/src/DynamicData/Cache/Tests/TestEx.cs +++ b/src/DynamicData/Cache/Tests/TestEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; // ReSharper disable once CheckNamespace namespace DynamicData.Tests @@ -29,7 +33,11 @@ public static ChangeSetAggregator AsAggregator(thi /// source public static DistinctChangeSetAggregator AsAggregator(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new DistinctChangeSetAggregator(source); } @@ -43,7 +51,11 @@ public static DistinctChangeSetAggregator AsAggregator(this IObs /// source public static SortedChangeSetAggregator AsAggregator(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new SortedChangeSetAggregator(source); } @@ -57,7 +69,11 @@ public static SortedChangeSetAggregator AsAggregatorsource public static VirtualChangeSetAggregator AsAggregator(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new VirtualChangeSetAggregator(source); } @@ -70,7 +86,11 @@ public static VirtualChangeSetAggregator AsAggregator public static PagedChangeSetAggregator AsAggregator(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new PagedChangeSetAggregator(source); } } diff --git a/src/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs b/src/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs index 8d4838450..73d86b0f1 100644 --- a/src/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs +++ b/src/DynamicData/Cache/Tests/VirtualChangeSetAggregator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -15,6 +19,7 @@ namespace DynamicData.Tests public class VirtualChangeSetAggregator : IDisposable { private readonly IDisposable _disposer; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -76,7 +81,23 @@ public VirtualChangeSetAggregator(IObservable> /// public void Dispose() { - _disposer.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _disposer?.Dispose(); + } } } } diff --git a/src/DynamicData/Cache/VirtualChangeSet.cs b/src/DynamicData/Cache/VirtualChangeSet.cs index 3c71026f7..b44465990 100644 --- a/src/DynamicData/Cache/VirtualChangeSet.cs +++ b/src/DynamicData/Cache/VirtualChangeSet.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using DynamicData.Cache.Internal; @@ -9,7 +13,6 @@ internal sealed class VirtualChangeSet : ChangeSet { public new static readonly IVirtualChangeSet Empty = new VirtualChangeSet(); - public IKeyValueCollection SortedItems { get; } public IVirtualResponse Response { get; } @@ -26,22 +29,41 @@ private VirtualChangeSet() Response = new VirtualResponse(0,0,0); } - #region Equality public bool Equals(VirtualChangeSet other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Response.Equals(other.Response) && Equals(SortedItems, other.SortedItems); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((VirtualChangeSet)obj); } diff --git a/src/DynamicData/Cache/VirtualRequest.cs b/src/DynamicData/Cache/VirtualRequest.cs index 2d8ebd28e..40759031a 100644 --- a/src/DynamicData/Cache/VirtualRequest.cs +++ b/src/DynamicData/Cache/VirtualRequest.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData @@ -47,10 +51,26 @@ private sealed class StartIndexSizeEqualityComparer : IEqualityComparer public bool Equals(ChangeStatistics other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Adds == other.Adds && Updates == other.Updates && Removes == other.Removes && Refreshes == other.Refreshes && Moves == other.Moves && Count == other.Count && Index == other.Index && LastUpdated.Equals(other.LastUpdated); } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ChangeStatistics)obj); } @@ -129,7 +153,6 @@ public override int GetHashCode() } } - #pragma warning disable 1591 public static bool operator ==(ChangeStatistics left, ChangeStatistics right) @@ -138,7 +161,6 @@ public override int GetHashCode() return Equals(left, right); } - public static bool operator !=(ChangeStatistics left, ChangeStatistics right) { return !Equals(left, right); @@ -152,6 +174,5 @@ public override string ToString() return $"CurrentIndex: {Index}, Adds: {Adds}, Updates: {Updates}, Removes: {Removes}, Refreshes: {Refreshes}, Count: {Count}, Timestamp: {LastUpdated}"; } - } } diff --git a/src/DynamicData/Diagnostics/ChangeSummary.cs b/src/DynamicData/Diagnostics/ChangeSummary.cs index 8ee85793e..8ca1a950d 100644 --- a/src/DynamicData/Diagnostics/ChangeSummary.cs +++ b/src/DynamicData/Diagnostics/ChangeSummary.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData.Diagnostics { /// @@ -59,9 +62,21 @@ private bool Equals(ChangeSummary other) /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ChangeSummary)obj); } diff --git a/src/DynamicData/Diagnostics/DiagnosticOperators.cs b/src/DynamicData/Diagnostics/DiagnosticOperators.cs index 3efe0ebc5..c0d749caf 100644 --- a/src/DynamicData/Diagnostics/DiagnosticOperators.cs +++ b/src/DynamicData/Diagnostics/DiagnosticOperators.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Linq; namespace DynamicData.Diagnostics @@ -18,7 +22,11 @@ public static class DiagnosticOperators /// source public static IObservable CollectUpdateStats(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Scan(ChangeSummary.Empty, (seed, next) => { int index = seed.Overall.Index + 1; @@ -44,7 +52,11 @@ public static IObservable CollectUpdateStats(this /// source public static IObservable CollectUpdateStats(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Scan(ChangeSummary.Empty, (seed, next) => { int index = seed.Overall.Index + 1; diff --git a/src/DynamicData/DynamicData.csproj b/src/DynamicData/DynamicData.csproj index 04cdf1709..2bad2de89 100644 --- a/src/DynamicData/DynamicData.csproj +++ b/src/DynamicData/DynamicData.csproj @@ -1,6 +1,6 @@  - netstandard2.0;net46;uap10.0;uap10.0.16299 + netstandard2.0;net461;uap10.0.16299 true true diff --git a/src/DynamicData/EnumerableEx.cs b/src/DynamicData/EnumerableEx.cs index 4f90b0c12..864cdb3f7 100644 --- a/src/DynamicData/EnumerableEx.cs +++ b/src/DynamicData/EnumerableEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; @@ -31,8 +35,15 @@ public static IObservable> AsObservableChangeSet keySelector, bool completable = false) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return Observable.Create>(obs => { @@ -43,13 +54,13 @@ public static IObservable> AsObservableChangeSet @@ -63,7 +74,10 @@ public static IObservable> AsObservableChangeSet> AsObservableChangeSet(this IEnumerable source, bool completable = false) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return Observable.Create>(obs => { @@ -74,6 +88,7 @@ public static IObservable> AsObservableChangeSet(th { obs.OnCompleted(); } + return Disposable.Empty; }); } diff --git a/src/DynamicData/Experimental/ExperimentalEx.cs b/src/DynamicData/Experimental/ExperimentalEx.cs index a13ec1669..69b0a5eff 100644 --- a/src/DynamicData/Experimental/ExperimentalEx.cs +++ b/src/DynamicData/Experimental/ExperimentalEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Concurrency; namespace DynamicData.Experimental @@ -19,7 +23,11 @@ public static class ExperimentalEx /// source public static IWatcher AsWatcher(this IObservable> source, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new Watcher(source, scheduler ?? Scheduler.Default); } } diff --git a/src/DynamicData/Experimental/ISubjectWithRefCount.cs b/src/DynamicData/Experimental/ISubjectWithRefCount.cs index e1351c808..2c6ee5965 100644 --- a/src/DynamicData/Experimental/ISubjectWithRefCount.cs +++ b/src/DynamicData/Experimental/ISubjectWithRefCount.cs @@ -1,4 +1,8 @@ -using System.Reactive.Subjects; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Reactive.Subjects; namespace DynamicData.Experimental { diff --git a/src/DynamicData/Experimental/IWatcher.cs b/src/DynamicData/Experimental/IWatcher.cs index 474f71b51..7585fec6c 100644 --- a/src/DynamicData/Experimental/IWatcher.cs +++ b/src/DynamicData/Experimental/IWatcher.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; namespace DynamicData.Experimental { diff --git a/src/DynamicData/Experimental/SubjectWithRefCount.cs b/src/DynamicData/Experimental/SubjectWithRefCount.cs index 64f7a8e02..a73ab8078 100644 --- a/src/DynamicData/Experimental/SubjectWithRefCount.cs +++ b/src/DynamicData/Experimental/SubjectWithRefCount.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Subjects; using System.Threading; diff --git a/src/DynamicData/Experimental/Watcher.cs b/src/DynamicData/Experimental/Watcher.cs index daa47ef43..a6364d7e8 100644 --- a/src/DynamicData/Experimental/Watcher.cs +++ b/src/DynamicData/Experimental/Watcher.cs @@ -1,4 +1,8 @@ -#region +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +#region using System; using System.Reactive.Concurrency; @@ -73,6 +77,7 @@ public IObservable> Watch(TKey key) var update = new Change(ChangeReason.Add, key, initial.Value); subject.OnNext(update); } + _subscribers.Edit(updater => updater.AddOrUpdate(subject, key)); } diff --git a/src/DynamicData/IChangeSet.cs b/src/DynamicData/IChangeSet.cs index f7beced6b..2cd127f9c 100644 --- a/src/DynamicData/IChangeSet.cs +++ b/src/DynamicData/IChangeSet.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData { /// diff --git a/src/DynamicData/Kernel/ConnectionStatus.cs b/src/DynamicData/Kernel/ConnectionStatus.cs index 1c9e24b54..4d0614aa3 100644 --- a/src/DynamicData/Kernel/ConnectionStatus.cs +++ b/src/DynamicData/Kernel/ConnectionStatus.cs @@ -1,3 +1,6 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. namespace DynamicData.Kernel { diff --git a/src/DynamicData/Kernel/DoubleCheck.cs b/src/DynamicData/Kernel/DoubleCheck.cs index 217f3e5f6..9118765c7 100644 --- a/src/DynamicData/Kernel/DoubleCheck.cs +++ b/src/DynamicData/Kernel/DoubleCheck.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; namespace DynamicData.Kernel @@ -30,12 +34,19 @@ public T Value { get { - if (_value != null) return _value; + if (_value != null) + { + return _value; + } + lock (_locker) + { if (_value == null) { _value = _factory(); } + } + return _value; } } diff --git a/src/DynamicData/Kernel/EnumerableEx.cs b/src/DynamicData/Kernel/EnumerableEx.cs index 73940208e..e844b2d3a 100644 --- a/src/DynamicData/Kernel/EnumerableEx.cs +++ b/src/DynamicData/Kernel/EnumerableEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -18,7 +22,11 @@ public static class EnumerableEx /// public static T[] AsArray([NotNull] this IEnumerable source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source as T[] ?? source.ToArray(); } @@ -30,7 +38,11 @@ public static T[] AsArray([NotNull] this IEnumerable source) /// public static List AsList([NotNull] this IEnumerable source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source as List ?? source.ToList(); } @@ -47,8 +59,16 @@ public static List AsList([NotNull] this IEnumerable source) public static IEnumerable Duplicates([NotNull] this IEnumerable source, [NotNull] Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.GroupBy(valueSelector) .Where(group => group.Count() > 1) .SelectMany(t => t); @@ -82,9 +102,20 @@ public static IEnumerable> IndexOfMany(this IEnumerable s /// public static IEnumerable IndexOfMany([NotNull] this IEnumerable source, [NotNull] IEnumerable itemsToFind, [NotNull] Func resultSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (itemsToFind == null) throw new ArgumentNullException(nameof(itemsToFind)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (itemsToFind == null) + { + throw new ArgumentNullException(nameof(itemsToFind)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } var indexed = source.Select((element, index) => new { Element = element, Index = index }); return itemsToFind @@ -106,7 +137,9 @@ internal static IEnumerable> WithIndex(this IEnumerable s internal static void ForEach(this IEnumerable source, Action action) { foreach (var item in source) + { action(item); + } } internal static void ForEach(this IEnumerable source, Action action) diff --git a/src/DynamicData/Kernel/EnumerableIList.cs b/src/DynamicData/Kernel/EnumerableIList.cs index 93d25fdcc..b6ad6d8ff 100644 --- a/src/DynamicData/Kernel/EnumerableIList.cs +++ b/src/DynamicData/Kernel/EnumerableIList.cs @@ -8,17 +8,14 @@ namespace DynamicData.Kernel { - - internal static class EnumerableIList { - + public static EnumerableIList Create(IList list) => new EnumerableIList(list); public static EnumerableIList> Create(IChangeSet changeset) => Create((IList>)changeset); } - internal struct EnumeratorIList : IEnumerator { private readonly IList _list; @@ -64,10 +61,8 @@ public EnumerableIList(IList list) public static implicit operator EnumerableIList(T[] array) => new EnumerableIList(array); - public static EnumerableIList Empty = default; - // IList pass through /// diff --git a/src/DynamicData/Kernel/Error.cs b/src/DynamicData/Kernel/Error.cs index ee1b3de31..0f90830ad 100644 --- a/src/DynamicData/Kernel/Error.cs +++ b/src/DynamicData/Kernel/Error.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; #pragma warning disable 1591 @@ -38,13 +42,11 @@ public Error(Exception exception, TObject value, TKey key) #region Equality members - public static bool operator ==(Error left, Error right) { return Equals(left, right); } - public static bool operator !=(Error left, Error right) { return !Equals(left, right); @@ -53,16 +55,32 @@ public Error(Exception exception, TObject value, TKey key) /// public bool Equals(Error other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Key, other.Key) && EqualityComparer.Default.Equals(Value, other.Value) && Equals(Exception, other.Exception); } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is Error && Equals((Error)obj); } diff --git a/src/DynamicData/Kernel/ISupportsCapcity.cs b/src/DynamicData/Kernel/ISupportsCapcity.cs index 5c86882b9..5b72200e0 100644 --- a/src/DynamicData/Kernel/ISupportsCapcity.cs +++ b/src/DynamicData/Kernel/ISupportsCapcity.cs @@ -1,4 +1,7 @@ - +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + namespace DynamicData.Kernel { internal interface ISupportsCapcity diff --git a/src/DynamicData/Kernel/InternalEx.cs b/src/DynamicData/Kernel/InternalEx.cs index ff39e63c6..6e0dbf6b3 100644 --- a/src/DynamicData/Kernel/InternalEx.cs +++ b/src/DynamicData/Kernel/InternalEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive; using System.Reactive.Concurrency; using System.Reactive.Linq; @@ -8,7 +12,7 @@ namespace DynamicData.Kernel { /// - /// + /// Extensions associated with times and intervals. /// public static class InternalEx { @@ -31,7 +35,9 @@ IObservable Retry(int failureCount) => source.Catch(error); + } return Observable.Timer(delay.Value).SelectMany(Retry(failureCount + 1)); }); @@ -57,9 +63,6 @@ internal static IObservable SelectTask(this IObservable }); } - - - /// /// Schedules a recurring action. /// @@ -96,6 +99,11 @@ public static IDisposable ScheduleRecurringAction(this IScheduler scheduler, Tim /// public static IDisposable ScheduleRecurringAction(this IScheduler scheduler, Func interval, Action action) { + if (interval == null) + { + throw new ArgumentNullException(nameof(interval)); + } + return scheduler.Schedule(interval(), scheduleNext => { action(); @@ -103,7 +111,7 @@ public static IDisposable ScheduleRecurringAction(this IScheduler scheduler, Fun scheduleNext(next); }); } - + internal static void Swap(ref TSwap t1, ref TSwap t2) { TSwap temp = t1; diff --git a/src/DynamicData/Kernel/ItemWithIndex.cs b/src/DynamicData/Kernel/ItemWithIndex.cs index 68ff052b0..c15fa62be 100644 --- a/src/DynamicData/Kernel/ItemWithIndex.cs +++ b/src/DynamicData/Kernel/ItemWithIndex.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; @@ -41,7 +45,11 @@ public bool Equals(ItemWithIndex other) /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is ItemWithIndex && Equals((ItemWithIndex) obj); } @@ -72,7 +80,6 @@ public override int GetHashCode() #endregion - /// public override string ToString() { diff --git a/src/DynamicData/Kernel/ItemWithValue.cs b/src/DynamicData/Kernel/ItemWithValue.cs index 2a1c27d6a..1d2673e4e 100644 --- a/src/DynamicData/Kernel/ItemWithValue.cs +++ b/src/DynamicData/Kernel/ItemWithValue.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; @@ -42,7 +46,11 @@ public bool Equals(ItemWithValue other) /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is ItemWithValue && Equals((ItemWithValue) obj); } diff --git a/src/DynamicData/Kernel/OptionElse.cs b/src/DynamicData/Kernel/OptionElse.cs index 21dcdf60a..aea879763 100644 --- a/src/DynamicData/Kernel/OptionElse.cs +++ b/src/DynamicData/Kernel/OptionElse.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; namespace DynamicData.Kernel { @@ -23,8 +27,15 @@ internal OptionElse(bool shouldRunAction = true) /// action public void Else(Action action) { - if (action == null) throw new ArgumentNullException(nameof(action)); - if (_shouldRunAction) action(); + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + + if (_shouldRunAction) + { + action(); + } } } } diff --git a/src/DynamicData/Kernel/OptionExtensions.cs b/src/DynamicData/Kernel/OptionExtensions.cs index 3c2e88847..47c6b3108 100644 --- a/src/DynamicData/Kernel/OptionExtensions.cs +++ b/src/DynamicData/Kernel/OptionExtensions.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; @@ -32,7 +36,11 @@ public static T ValueOr(this T? source, T defaultValue) /// valueSelector public static T ValueOr(this Optional source, Func valueSelector) { - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return source.HasValue ? source.Value : valueSelector(); } @@ -57,9 +65,15 @@ public static T ValueOrDefault(this Optional source) /// exceptionGenerator public static T ValueOrThrow(this Optional source, Func exceptionGenerator) { - if (exceptionGenerator == null) throw new ArgumentNullException(nameof(exceptionGenerator)); + if (exceptionGenerator == null) + { + throw new ArgumentNullException(nameof(exceptionGenerator)); + } + if (source.HasValue) + { return source.Value; + } throw exceptionGenerator(); } @@ -80,8 +94,15 @@ public static T ValueOrThrow(this Optional source, Func excepti /// public static TDestination ConvertOr(this Optional source, Func converter, Func fallbackConverter) { - if (converter == null) throw new ArgumentNullException(nameof(converter)); - if (fallbackConverter == null) throw new ArgumentNullException(nameof(fallbackConverter)); + if (converter == null) + { + throw new ArgumentNullException(nameof(converter)); + } + + if (fallbackConverter == null) + { + throw new ArgumentNullException(nameof(fallbackConverter)); + } return source.HasValue ? converter(source.Value) : fallbackConverter(); } @@ -97,7 +118,11 @@ public static TDestination ConvertOr(this Optionalconverter public static Optional Convert(this Optional source, Func converter) { - if (converter == null) throw new ArgumentNullException(nameof(converter)); + if (converter == null) + { + throw new ArgumentNullException(nameof(converter)); + } + return source.HasValue ? converter(source.Value) : Optional.None(); } @@ -124,6 +149,11 @@ public static IEnumerable SelectValues(this IEnumerable> sourc /// public static Optional Lookup(this IDictionary source, TKey key) { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + TValue contained; bool result = source.TryGetValue(key, out contained); return result ? contained : Optional.None(); @@ -139,6 +169,11 @@ public static Optional Lookup(this IDictionary public static bool RemoveIfContained(this IDictionary source, TKey key) { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.ContainsKey(key) && source.Remove(key); } @@ -153,6 +188,11 @@ public static bool RemoveIfContained(this IDictionary public static Optional FirstOrOptional(this IEnumerable source, Func selector) { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + var result = source.FirstOrDefault(selector); return !Equals(result, null) ? result : Optional.None(); } @@ -166,7 +206,16 @@ public static Optional FirstOrOptional(this IEnumerable source, Func public static OptionElse IfHasValue(this Optional source, Action action) { - if (!source.HasValue) return new OptionElse(); + if (!source.HasValue) + { + return new OptionElse(); + } + + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + action(source.Value); return OptionElse.NoAction; } diff --git a/src/DynamicData/Kernel/Optional.cs b/src/DynamicData/Kernel/Optional.cs index ff2b008be..b5664fd48 100644 --- a/src/DynamicData/Kernel/Optional.cs +++ b/src/DynamicData/Kernel/Optional.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; #pragma warning disable 1591 @@ -97,6 +101,7 @@ public T Value { throw new InvalidOperationException("Optional has no value"); } + return _value; } } @@ -108,7 +113,7 @@ public T Value /// public static implicit operator Optional(T value) { - return new Optional(value); + return ToOptional(value); } /// @@ -117,19 +122,27 @@ public static implicit operator Optional(T value) /// The value. /// public static explicit operator T(Optional value) + { + return FromOptional(value); + } + + public static T FromOptional(Optional value) { return value.Value; } - #region Equality members + public static Optional ToOptional(T value) + { + return new Optional(value); + } + #region Equality members public static bool operator ==(Optional left, Optional right) { return left.Equals(right); } - public static bool operator !=(Optional left, Optional right) { return !left.Equals(right); @@ -138,15 +151,27 @@ public static explicit operator T(Optional value) /// public bool Equals(Optional other) { - if (!HasValue) return !other.HasValue; - if (!other.HasValue) return false; + if (!HasValue) + { + return !other.HasValue; + } + + if (!other.HasValue) + { + return false; + } + return HasValue.Equals(other.HasValue) && EqualityComparer.Default.Equals(_value, other._value); } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is Optional && Equals((Optional)obj); } diff --git a/src/DynamicData/Kernel/ParallelEx.cs b/src/DynamicData/Kernel/ParallelEx.cs index 21b3eb5e7..fd26e52ba 100644 --- a/src/DynamicData/Kernel/ParallelEx.cs +++ b/src/DynamicData/Kernel/ParallelEx.cs @@ -1,5 +1,10 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; @@ -7,23 +12,31 @@ namespace DynamicData.Kernel { internal static class ParallelEx { + [SuppressMessage("Design", "CA2000: Dispose SemaphoreSlim", Justification = "Captured in lambda, can cause problems.")] public static async Task> SelectParallel(this IEnumerable source, Func> selector, int maximumThreads = 5) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (selector == null) throw new ArgumentNullException(nameof(selector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (selector == null) + { + throw new ArgumentNullException(nameof(selector)); + } var semaphore = new SemaphoreSlim(maximumThreads); var tasks = new List>(); foreach (var item in source) { - await semaphore.WaitAsync(); + await semaphore.WaitAsync().ConfigureAwait(false); tasks.Add(Task.Run(async () => { try { - return await selector(item); + return await selector(item).ConfigureAwait(false); } finally { @@ -31,7 +44,8 @@ public static async Task> SelectParallel items, int index = -1) { if (reason.GetChangeType() == ChangeType.Item) + { throw new IndexOutOfRangeException("ListChangeReason must be a range type for a range change"); + } //ignore this case because WhereReasonsAre removes the index //if (reason== ListChangeReason.RemoveRange && index < 0) @@ -81,10 +87,14 @@ public Change(ListChangeReason reason, IEnumerable items, int index = -1) public Change(T current, int currentIndex, int previousIndex) { if (currentIndex < 0) + { throw new ArgumentException("CurrentIndex must be greater than or equal to zero"); + } if (previousIndex < 0) + { throw new ArgumentException("PreviousIndex must be greater than or equal to zero"); + } Reason = ListChangeReason.Moved; Item = new ItemChange(Reason, current, Optional.None(), currentIndex, previousIndex); @@ -109,13 +119,19 @@ public Change(T current, int currentIndex, int previousIndex) public Change(ListChangeReason reason, T current, Optional previous, int currentIndex = -1, int previousIndex = -1) { if (reason == ListChangeReason.Add && previous.HasValue) + { throw new ArgumentException("For ChangeReason.Add, a previous value cannot be specified"); + } + if (reason == ListChangeReason.Replace && !previous.HasValue) + { throw new ArgumentException("For ChangeReason.Change, must supply previous value"); + } if (reason == ListChangeReason.Refresh && currentIndex < 0) + { throw new ArgumentException("For ChangeReason.Refresh, must supply and index"); - + } Reason = reason; Item = new ItemChange(Reason, current, previous, currentIndex, previousIndex); @@ -128,17 +144,37 @@ public Change(ListChangeReason reason, T current, Optional previous, int curr /// public bool Equals(Change other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return Reason == other.Reason && Item.Equals(other.Item) && Equals(Range, other.Range); } /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Change)obj); } @@ -154,10 +190,8 @@ public override int GetHashCode() } } - public static bool operator ==(Change left, Change right) => Equals(left, right); - public static bool operator !=(Change left, Change right) => !Equals(left, right); #endregion diff --git a/src/DynamicData/List/ChangeAwareList.cs b/src/DynamicData/List/ChangeAwareList.cs index 545b2aa43..ce10cda7e 100644 --- a/src/DynamicData/List/ChangeAwareList.cs +++ b/src/DynamicData/List/ChangeAwareList.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -13,8 +17,9 @@ namespace DynamicData /// Used for creating custom operators /// /// - public class ChangeAwareList: IExtendedList + public class ChangeAwareList : IExtendedList { + private readonly object _lockObject = new object(); private readonly List _innerList; private List> _changes = new List>(); @@ -26,13 +31,15 @@ public ChangeAwareList(int capacity = -1) _innerList = capacity > 0 ? new List(capacity) : new List(); } - /// /// Create a change aware list with the specified items /// public ChangeAwareList(IEnumerable items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } var list = items.ToList(); @@ -51,7 +58,10 @@ public ChangeAwareList(IEnumerable items) /// Should the list of changes also be copied over? public ChangeAwareList(ChangeAwareList list, bool copyChanges) { - if (list == null) throw new ArgumentNullException(nameof(list)); + if (list == null) + { + throw new ArgumentNullException(nameof(list)); + } _innerList = new List(list._innerList); @@ -66,21 +76,27 @@ public ChangeAwareList(ChangeAwareList list, bool copyChanges) /// public IChangeSet CaptureChanges() { - if (_changes.Count == 0) - return ChangeSet.Empty; - - var copy = new ChangeSet(_changes); - - //we can infer this is a Clear - if (_innerList.Count == 0 && copy.Removes == copy.TotalChanges && copy.TotalChanges > 1) + ChangeSet returnValue; + lock (_lockObject) { - _changes = new List>(); - var removed = copy.Unified().Select(u => u.Current); - return new ChangeSet { new Change(ListChangeReason.Clear, removed) }; + if (_changes.Count == 0) + { + return ChangeSet.Empty; + } + + returnValue = new ChangeSet(_changes); + + //we can infer this is a Clear + if (_innerList.Count == 0 && returnValue.Removes == returnValue.TotalChanges && returnValue.TotalChanges > 1) + { + var removed = returnValue.Unified().Select(u => u.Current); + returnValue = new ChangeSet { new Change(ListChangeReason.Clear, removed) }; + } } - _changes = new List>(); - return copy; + ClearChanges(); + + return returnValue; } /// @@ -88,11 +104,14 @@ public IChangeSet CaptureChanges() /// internal void ClearChanges() { - _changes = new List>(); + lock (_lockObject) + { + _changes = new List>(); + } } #region Range support - + /// /// Adds the elements of the specified collection to the end of the collection. /// @@ -102,9 +121,16 @@ public void AddRange(IEnumerable collection) { var args = new Change(ListChangeReason.AddRange, collection); - if (args.Range.Count == 0) return; - _changes.Add(args); - _innerList.AddRange(args.Range); + if (args.Range.Count == 0) + { + return; + } + + lock (_lockObject) + { + _changes.Add(args); + _innerList.AddRange(args.Range); + } } /// @@ -117,9 +143,17 @@ public void AddRange(IEnumerable collection) public void InsertRange(IEnumerable collection, int index) { var args = new Change(ListChangeReason.AddRange, collection, index); - if (args.Range.Count == 0) return; - _changes.Add(args); - _innerList.InsertRange(index, args.Range); + if (args.Range.Count == 0) + { + return; + } + + lock (_lockObject) + { + _changes.Add(args); + _innerList.InsertRange(index, args.Range); + } + OnInsertItems(index, args.Range); } @@ -130,15 +164,26 @@ public void InsertRange(IEnumerable collection, int index) public void RemoveRange(int index, int count) { - if (index >= _innerList.Count || index + count > _innerList.Count) - throw new ArgumentOutOfRangeException(nameof(index)); + Change args; + lock (_lockObject) + { + if (index >= _innerList.Count || index + count > _innerList.Count) + { + throw new ArgumentOutOfRangeException(nameof(index)); + } - var toremove = _innerList.Skip(index).Take(count).ToList(); - if (toremove.Count == 0) return; - var args = new Change(ListChangeReason.RemoveRange, toremove, index); + var toremove = _innerList.Skip(index).Take(count).ToList(); + if (toremove.Count == 0) + { + return; + } + + args = new Change(ListChangeReason.RemoveRange, toremove, index); + + _changes.Add(args); + _innerList.RemoveRange(index, count); + } - _changes.Add(args); - _innerList.RemoveRange(index, count); OnRemoveItems(index, args.Range); } @@ -147,17 +192,24 @@ public void RemoveRange(int index, int count) /// public virtual void Clear() { - if (_innerList.Count == 0) return; - var toremove = _innerList.ToList(); - _changes.Add(new Change(ListChangeReason.Clear, toremove)); - _innerList.Clear(); + lock (_lockObject) + { + if (_innerList.Count == 0) + { + return; + } + + var toremove = _innerList.ToList(); + + _changes.Add(new Change(ListChangeReason.Clear, toremove)); + _innerList.Clear(); + } } #endregion #region Subclass overrides - /// /// Override for custom Set /// @@ -190,10 +242,20 @@ protected virtual void OnRemoveItems(int startIndex, IEnumerable items) /// public void RefreshAt(int index) { - if (index < 0) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + if (index < 0) + { + throw new ArgumentException($"{nameof(index)} cannot be negative"); + } + + lock (_lockObject) + { + if (index > _innerList.Count) + { + throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + } - _changes.Add(new Change(ListChangeReason.Refresh, _innerList[index], index)); + _changes.Add(new Change(ListChangeReason.Refresh, _innerList[index], index)); + } } /// @@ -204,13 +266,23 @@ public void RefreshAt(int index) /// If the item is in the list, returns true public void Refresh(T item, int index) { - if (index < 0) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + if (index < 0) + { + throw new ArgumentException($"{nameof(index)} cannot be negative"); + } - var previous = _innerList[index]; - _innerList[index] = item; + lock (_lockObject) + { + if (index > _innerList.Count) + { + throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + } + + var previous = _innerList[index]; + _innerList[index] = item; - _changes.Add(new Change(ListChangeReason.Refresh, item, previous, index)); + _changes.Add(new Change(ListChangeReason.Refresh, item, previous, index)); + } } /// @@ -221,9 +293,15 @@ public void Refresh(T item, int index) public bool Refresh(T item) { var index = IndexOf(item); - if (index < 0) return false; + if (index < 0) + { + return false; + } - _changes.Add(new Change(ListChangeReason.Refresh, item, index)); + lock (_lockObject) + { + _changes.Add(new Change(ListChangeReason.Refresh, item, index)); + } return true; } @@ -235,7 +313,16 @@ public bool Refresh(T item) /// /// Gets the last change in the collection /// - private Optional> Last => _changes.Count == 0 ? Optional.None>() : _changes[_changes.Count - 1]; + private Optional> Last + { + get + { + lock (_lockObject) + { + return _changes.Count == 0 ? Optional.None>() : _changes[_changes.Count - 1]; + } + } + } /// /// Inserts an item at the specified index @@ -244,70 +331,83 @@ public bool Refresh(T item) /// protected virtual void InsertItem(int index, T item) { - if (index < 0 ) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); - - //attempt to batch updates as lists love to deal with ranges! (sorry if this code melts your mind) - var last = Last; + if (index < 0) + { + throw new ArgumentException($"{nameof(index)} cannot be negative"); + } - if (last.HasValue && last.Value.Reason == ListChangeReason.Add) + if (index > _innerList.Count) { - //begin a new batch if possible + throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + } - var firstOfBatch = _changes.Count - 1; - var previousItem = last.Value.Item; + lock (_lockObject) + { + //attempt to batch updates as lists love to deal with ranges! (sorry if this code melts your mind) + var last = Last; - if (index == previousItem.CurrentIndex) - { - _changes[firstOfBatch] = new Change(ListChangeReason.AddRange, new[] { item, previousItem.Current }, index); - } - else if (index == previousItem.CurrentIndex + 1) - { - _changes[firstOfBatch] = new Change(ListChangeReason.AddRange, new[] { previousItem.Current, item }, previousItem.CurrentIndex); - } - else + if (last.HasValue && last.Value.Reason == ListChangeReason.Add) { - _changes.Add(new Change(ListChangeReason.Add, item, index)); - } - } - else if (last.HasValue && last.Value.Reason == ListChangeReason.AddRange) - { - //check whether the new item is in the specified range - var range = last.Value.Range; + //begin a new batch if possible - var minimum = Math.Max(range.Index - 1, 0); - var maximum = range.Index + range.Count; - var isPartOfRange = index >= minimum && index <= maximum; + var firstOfBatch = _changes.Count - 1; + var previousItem = last.Value.Item; - if (!isPartOfRange) - { - _changes.Add(new Change(ListChangeReason.Add, item, index)); + if (index == previousItem.CurrentIndex) + { + _changes[firstOfBatch] = new Change(ListChangeReason.AddRange, new[] { item, previousItem.Current }, index); + } + else if (index == previousItem.CurrentIndex + 1) + { + _changes[firstOfBatch] = new Change(ListChangeReason.AddRange, new[] { previousItem.Current, item }, previousItem.CurrentIndex); + } + else + { + _changes.Add(new Change(ListChangeReason.Add, item, index)); + } } - else + else if (last.HasValue && last.Value.Reason == ListChangeReason.AddRange) { - var insertPosition = index - range.Index; - if (insertPosition < 0) + //check whether the new item is in the specified range + var range = last.Value.Range; + + var minimum = Math.Max(range.Index - 1, 0); + var maximum = range.Index + range.Count; + var isPartOfRange = index >= minimum && index <= maximum; + + if (!isPartOfRange) { - insertPosition = 0; + _changes.Add(new Change(ListChangeReason.Add, item, index)); } - else if (insertPosition >= range.Count) + else { - insertPosition = range.Count; + var insertPosition = index - range.Index; + if (insertPosition < 0) + { + insertPosition = 0; + } + else if (insertPosition >= range.Count) + { + insertPosition = range.Count; + } + + range.Insert(insertPosition, item); + + if (index < range.Index) + { + range.SetStartingIndex(index); + } } - range.Insert(insertPosition, item); - - if (index < range.Index) - range.SetStartingIndex(index); } - } - else - { - //first add, so cannot infer range - _changes.Add(new Change(ListChangeReason.Add, item, index)); - } + else + { + //first add, so cannot infer range + _changes.Add(new Change(ListChangeReason.Add, item, index)); + } - //finally, add the item - _innerList.Insert(index, item); + //finally, add the item + _innerList.Insert(index, item); + } } /// @@ -315,8 +415,11 @@ protected virtual void InsertItem(int index, T item) /// protected void RemoveItem(int index) { - var item = _innerList[index]; - RemoveItem(index, item); + lock (_lockObject) + { + var item = _innerList[index]; + RemoveItem(index, item); + } } /// @@ -324,62 +427,73 @@ protected void RemoveItem(int index) /// protected virtual void RemoveItem(int index, T item) { - if (index < 0) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); - - //attempt to batch updates as lists love to deal with ranges! (sorry if this code melts your mind) - var last = Last; - if (last.HasValue && last.Value.Reason == ListChangeReason.Remove) + if (index < 0) { - //begin a new batch - var firstOfBatch = _changes.Count - 1; - var previousItem = last.Value.Item; - - if (index == previousItem.CurrentIndex) - { - _changes[firstOfBatch] = new Change(ListChangeReason.RemoveRange, new[] { previousItem.Current, item }, index); - } - - else if (index == previousItem.CurrentIndex - 1) - { - //Nb: double check this one as it is the same as clause above. Can it be correct? - _changes[firstOfBatch] = new Change(ListChangeReason.RemoveRange, new[] { item, previousItem.Current }, index); - } - else - { - _changes.Add(new Change(ListChangeReason.Remove, item, index)); - } + throw new ArgumentException($"{nameof(index)} cannot be negative"); } - else if (last.HasValue && last.Value.Reason == ListChangeReason.RemoveRange) + + lock (_lockObject) { - //add to the end of the previous batch - var range = last.Value.Range; - if (range.Index == index) + if (index > _innerList.Count) { - //removed in order - range.Add(item); + throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); } - else if (range.Index == index - 1) + + //attempt to batch updates as lists love to deal with ranges! (sorry if this code melts your mind) + var last = Last; + if (last.HasValue && last.Value.Reason == ListChangeReason.Remove) { - _changes.Add(new Change(ListChangeReason.Remove, item, index)); + //begin a new batch + var firstOfBatch = _changes.Count - 1; + var previousItem = last.Value.Item; + + if (index == previousItem.CurrentIndex) + { + _changes[firstOfBatch] = new Change(ListChangeReason.RemoveRange, new[] { previousItem.Current, item }, index); + } + + else if (index == previousItem.CurrentIndex - 1) + { + //Nb: double check this one as it is the same as clause above. Can it be correct? + _changes[firstOfBatch] = new Change(ListChangeReason.RemoveRange, new[] { item, previousItem.Current }, index); + } + else + { + _changes.Add(new Change(ListChangeReason.Remove, item, index)); + } } - else if (range.Index == index + 1) + else if (last.HasValue && last.Value.Reason == ListChangeReason.RemoveRange) { - //removed in reverse order - range.Insert(0, item); - range.SetStartingIndex(index); + //add to the end of the previous batch + var range = last.Value.Range; + if (range.Index == index) + { + //removed in order + range.Add(item); + } + else if (range.Index == index - 1) + { + _changes.Add(new Change(ListChangeReason.Remove, item, index)); + } + else if (range.Index == index + 1) + { + //removed in reverse order + range.Insert(0, item); + range.SetStartingIndex(index); + } + else + { + _changes.Add(new Change(ListChangeReason.Remove, item, index)); + } } else { + //first remove, so cannot infer range _changes.Add(new Change(ListChangeReason.Remove, item, index)); } + + _innerList.RemoveAt(index); } - else - { - //first remove, so cannot infer range - _changes.Add(new Change(ListChangeReason.Remove, item, index)); - } - _innerList.RemoveAt(index); } /// @@ -387,12 +501,25 @@ protected virtual void RemoveItem(int index, T item) /// protected virtual void SetItem(int index, T item) { - if (index < 0) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + if (index < 0) + { + throw new ArgumentException($"{nameof(index)} cannot be negative"); + } + + T previous; + + lock (_lockObject) + { + if (index > _innerList.Count) + { + throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + } + + previous = _innerList[index]; + _changes.Add(new Change(ListChangeReason.Replace, item, previous, index, index)); + _innerList[index] = item; + } - var previous = _innerList[index]; - _changes.Add(new Change(ListChangeReason.Replace, item, previous, index, index)); - _innerList[index] = item; OnSetItem(index, item, previous); } @@ -403,11 +530,21 @@ protected virtual void SetItem(int index, T item) /// public virtual void Move(T item, int destination) { - if (destination < 0) throw new ArgumentException($"{nameof(destination)} cannot be negative"); - if (destination > _innerList.Count) throw new ArgumentException($"{nameof(destination)} cannot be greater than the size of the collection"); + if (destination < 0) + { + throw new ArgumentException($"{nameof(destination)} cannot be negative"); + } + + lock (_lockObject) + { + if (destination > _innerList.Count) + { + throw new ArgumentException($"{nameof(destination)} cannot be greater than the size of the collection"); + } - var index = _innerList.IndexOf(item); - Move(index, destination); + int index = _innerList.IndexOf(item); + Move(index, destination); + } } /// @@ -417,16 +554,33 @@ public virtual void Move(T item, int destination) /// The destination. public virtual void Move(int original, int destination) { - if (original < 0) throw new ArgumentException($"{nameof(original)} cannot be negative"); - if (original > _innerList.Count) throw new ArgumentException($"{nameof(original)} cannot be greater than the size of the collection"); + if (original < 0) + { + throw new ArgumentException($"{nameof(original)} cannot be negative"); + } - if (destination < 0) throw new ArgumentException($"{nameof(destination)} cannot be negative"); - if (destination > _innerList.Count) throw new ArgumentException($"{nameof(destination)} cannot be greater than the size of the collection"); + if (destination < 0) + { + throw new ArgumentException($"{nameof(destination)} cannot be negative"); + } - var item = _innerList[original]; - _innerList.RemoveAt(original); - _innerList.Insert(destination, item); - _changes.Add(new Change(item, destination, original)); + lock (_lockObject) + { + if (original > _innerList.Count) + { + throw new ArgumentException($"{nameof(original)} cannot be greater than the size of the collection"); + } + + if (destination > _innerList.Count) + { + throw new ArgumentException($"{nameof(destination)} cannot be greater than the size of the collection"); + } + + var item = _innerList[original]; + _innerList.RemoveAt(original); + _innerList.Insert(destination, item); + _changes.Add(new Change(item, destination, original)); + } } #endregion @@ -439,7 +593,13 @@ public virtual void Move(int original, int destination) public int Capacity { get => _innerList.Capacity; - set => _innerList.Capacity = value; + set + { + lock (_lockObject) + { + _innerList.Capacity = value; + } + } } /// @@ -456,14 +616,21 @@ public int Capacity /// public virtual bool Contains(T item) { - return _innerList.Contains(item); + lock (_lockObject) + { + return _innerList.Contains(item); + } } + /// /// Copies the entire collection to a compatible one-dimensional array, starting at the specified index of the target array. /// public void CopyTo(T[] array, int arrayIndex) { - _innerList.CopyTo(array, arrayIndex); + lock (_lockObject) + { + _innerList.CopyTo(array, arrayIndex); + } } /// @@ -471,7 +638,10 @@ public void CopyTo(T[] array, int arrayIndex) /// public int IndexOf(T item) { - return _innerList.IndexOf(item); + lock (_lockObject) + { + return _innerList.IndexOf(item); + } } /// @@ -479,7 +649,10 @@ public int IndexOf(T item) /// public int IndexOf(T item, IEqualityComparer equalityComparer) { - return _innerList.IndexOf(item, equalityComparer); + lock (_lockObject) + { + return _innerList.IndexOf(item, equalityComparer); + } } /// @@ -487,9 +660,20 @@ public int IndexOf(T item, IEqualityComparer equalityComparer) /// public void Insert(int index, T item) { - if (index < 0) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); - InsertItem(index, item); + if (index < 0) + { + throw new ArgumentException($"{nameof(index)} cannot be negative"); + } + + lock (_lockObject) + { + if (index > _innerList.Count) + { + throw new ArgumentException($"{nameof(index)} cannot be greater than the size of the collection"); + } + + InsertItem(index, item); + } } /// @@ -498,9 +682,20 @@ public void Insert(int index, T item) /// public void RemoveAt(int index) { - if (index < 0) throw new ArgumentException($"{nameof(index)} cannot be negative"); - if (index > _innerList.Count) throw new ArgumentOutOfRangeException($"{nameof(index)} cannot be greater than the size of the collection"); - RemoveItem(index); + if (index < 0) + { + throw new ArgumentException($"{nameof(index)} cannot be negative"); + } + + lock (_lockObject) + { + if (index > _innerList.Count) + { + throw new ArgumentOutOfRangeException($"{nameof(index)} cannot be greater than the size of the collection"); + } + + RemoveItem(index); + } } /// @@ -508,7 +703,10 @@ public void RemoveAt(int index) /// public void Add(T item) { - InsertItem(_innerList.Count, item); + lock (_lockObject) + { + InsertItem(_innerList.Count, item); + } } /// @@ -516,10 +714,17 @@ public void Add(T item) /// public bool Remove(T item) { - var index = _innerList.IndexOf(item); - if (index < 0) return false; - RemoveItem(index, item); - return true; + lock (_lockObject) + { + var index = _innerList.IndexOf(item); + if (index < 0) + { + return false; + } + + RemoveItem(index, item); + return true; + } } /// @@ -531,11 +736,13 @@ public T this[int index] set => SetItem(index, value); } - /// public IEnumerator GetEnumerator() { - return _innerList.GetEnumerator(); + lock (_lockObject) + { + return _innerList.ToList().GetEnumerator(); + } } /// @@ -547,7 +754,7 @@ IEnumerator IEnumerable.GetEnumerator() /// /// Is this collection read only /// - public bool IsReadOnly { get; } = false; + public bool IsReadOnly => false; #endregion } diff --git a/src/DynamicData/List/ChangeAwareListWithRefCounts.cs b/src/DynamicData/List/ChangeAwareListWithRefCounts.cs index 26646afdf..fd17744d7 100644 --- a/src/DynamicData/List/ChangeAwareListWithRefCounts.cs +++ b/src/DynamicData/List/ChangeAwareListWithRefCounts.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; using DynamicData.Kernel; using DynamicData.List.Internal; diff --git a/src/DynamicData/List/ChangeSet.cs b/src/DynamicData/List/ChangeSet.cs index b800a23fd..11c5e4b8c 100644 --- a/src/DynamicData/List/ChangeSet.cs +++ b/src/DynamicData/List/ChangeSet.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections; using System.Collections.Generic; @@ -39,12 +43,18 @@ public ChangeSet() /// items public ChangeSet([NotNull] IEnumerable> items) { - if (items == null) throw new ArgumentNullException(nameof(items)); + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } + var list = items as List> ?? items.ToList(); Items = list; foreach (var change in list) + { Add(change, true); + } } /// @@ -53,6 +63,11 @@ public ChangeSet([NotNull] IEnumerable> items) /// The item. public void Add(Change item) { + if (item == null) + { + throw new ArgumentNullException(nameof(item)); + } + Add(item, false); } @@ -90,9 +105,13 @@ private void Add(Change item, bool countOnly) _removes = _removes + item.Range.Count; break; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(item)); + } + + if (!countOnly) + { + Items.Add(item); } - if (!countOnly) Items.Add(item); } /// @@ -107,7 +126,7 @@ public int Capacity set => Items.Capacity = value; } - private List> Items { get; } + private List> Items { get; } /// /// Gets the number of additions diff --git a/src/DynamicData/List/ChangeSetEx.cs b/src/DynamicData/List/ChangeSetEx.cs index 0c87dd4f1..07b8db871 100644 --- a/src/DynamicData/List/ChangeSetEx.cs +++ b/src/DynamicData/List/ChangeSetEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -34,7 +38,11 @@ public static IEnumerable> YieldWithoutIndex(this IEnumerablesource internal static IEnumerable> Unified([NotNull] this IChangeSet source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new UnifiedChangeEnumerator(source); } @@ -47,7 +55,11 @@ internal static IEnumerable> Unified([NotNull] this IChangeS /// source public static IEnumerable> Flatten([NotNull] this IChangeSet source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new ItemChangeEnumerator(source); } @@ -73,7 +85,7 @@ public static ChangeType GetChangeType(this ListChangeReason source) case ListChangeReason.Clear: return ChangeType.Range; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(source)); } } @@ -93,8 +105,15 @@ public static ChangeType GetChangeType(this ListChangeReason source) public static IChangeSet Transform([NotNull] this IChangeSet source, [NotNull] Func transformer) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformer == null) throw new ArgumentNullException(nameof(transformer)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformer == null) + { + throw new ArgumentNullException(nameof(transformer)); + } var changes = source.Select(change => { @@ -106,6 +125,7 @@ public static IChangeSet Transform([NotNull change.Item.CurrentIndex, change.Item.PreviousIndex); } + return new Change(change.Reason, change.Range.Select(transformer), change.Range.Index); }); diff --git a/src/DynamicData/List/IChangeSet.cs b/src/DynamicData/List/IChangeSet.cs index 70649fdda..b441719c4 100644 --- a/src/DynamicData/List/IChangeSet.cs +++ b/src/DynamicData/List/IChangeSet.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/List/IExtendedList.cs b/src/DynamicData/List/IExtendedList.cs index b6fde78b9..b31b107a1 100644 --- a/src/DynamicData/List/IExtendedList.cs +++ b/src/DynamicData/List/IExtendedList.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; // ReSharper disable once CheckNamespace namespace DynamicData diff --git a/src/DynamicData/List/IGrouping.cs b/src/DynamicData/List/IGrouping.cs index 09f88ff31..fc9d5fd53 100644 --- a/src/DynamicData/List/IGrouping.cs +++ b/src/DynamicData/List/IGrouping.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections.Generic; namespace DynamicData.List diff --git a/src/DynamicData/List/IObservableList.cs b/src/DynamicData/List/IObservableList.cs index f5d3a44ca..3040e92a7 100644 --- a/src/DynamicData/List/IObservableList.cs +++ b/src/DynamicData/List/IObservableList.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; diff --git a/src/DynamicData/List/IPageChangeSet.cs b/src/DynamicData/List/IPageChangeSet.cs index c312275c7..7c18047b2 100644 --- a/src/DynamicData/List/IPageChangeSet.cs +++ b/src/DynamicData/List/IPageChangeSet.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using DynamicData.Operators; // ReSharper disable once CheckNamespace diff --git a/src/DynamicData/List/ISourceList.cs b/src/DynamicData/List/ISourceList.cs index 52e4f33cb..ad2a80ddc 100644 --- a/src/DynamicData/List/ISourceList.cs +++ b/src/DynamicData/List/ISourceList.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; // ReSharper disable once CheckNamespace diff --git a/src/DynamicData/List/Internal/AnonymousObservableList.cs b/src/DynamicData/List/Internal/AnonymousObservableList.cs index 2fd03adf2..757fc01a8 100644 --- a/src/DynamicData/List/Internal/AnonymousObservableList.cs +++ b/src/DynamicData/List/Internal/AnonymousObservableList.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; @@ -9,7 +13,11 @@ internal sealed class AnonymousObservableList : IObservableList public AnonymousObservableList(IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + _sourceList = new SourceList(source); } @@ -27,7 +35,7 @@ public AnonymousObservableList(ISourceList sourceList) public IObservable> Connect(Func predicate = null) => _sourceList.Connect(predicate); public IObservable> Preview(Func predicate = null) => _sourceList.Preview(predicate); - + public void Dispose() => _sourceList.Dispose(); } } diff --git a/src/DynamicData/List/Internal/AutoRefresh.cs b/src/DynamicData/List/Internal/AutoRefresh.cs index eec618324..964e0f079 100644 --- a/src/DynamicData/List/Internal/AutoRefresh.cs +++ b/src/DynamicData/List/Internal/AutoRefresh.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Concurrency; @@ -62,7 +66,6 @@ public IObservable> Run() return allItems.IndexOfMany(items, (t, idx) => new Change(ListChangeReason.Refresh, t, idx)); }).Select(changes => new ChangeSet(changes)); - //publish refreshes and underlying changes var publisher = shared .Merge(requiresRefresh) diff --git a/src/DynamicData/List/Internal/BufferIf.cs b/src/DynamicData/List/Internal/BufferIf.cs index 67cc5d777..7cebf645a 100644 --- a/src/DynamicData/List/Internal/BufferIf.cs +++ b/src/DynamicData/List/Internal/BufferIf.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Concurrency; @@ -50,9 +54,11 @@ public IObservable> Run() paused = true; //add pause timeout if required if (_timeOut != TimeSpan.Zero) + { timeoutSubscriber.Disposable = Observable.Timer(_timeOut, _scheduler) .Select(l => false) .SubscribeSafe(timeoutSubject); + } }); var resume = bufferSelector.Where(state => !state) @@ -60,7 +66,11 @@ public IObservable> Run() { paused = false; //publish changes and clear buffer - if (buffer.Count == 0) return; + if (buffer.Count == 0) + { + return; + } + observer.OnNext(new ChangeSet(buffer)); buffer.Clear(); diff --git a/src/DynamicData/List/Internal/Combiner.cs b/src/DynamicData/List/Internal/Combiner.cs index ebf2dd5ac..eb64e57df 100644 --- a/src/DynamicData/List/Internal/Combiner.cs +++ b/src/DynamicData/List/Internal/Combiner.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Disposables; @@ -32,7 +36,7 @@ public IObservable> Run() { var sourceLists = Enumerable.Range(0, _source.Count) .Select(_ => new ReferenceCountTracker()) - .ToList(); + .ToList(); foreach (var pair in _source.Zip(sourceLists, (item, list) => new { Item = item, List = list })) { @@ -42,7 +46,9 @@ public IObservable> Run() var notifications = UpdateResultList(changes, sourceLists, resultList); if (notifications.Count != 0) + { observer.OnNext(notifications); + } })); } } @@ -51,7 +57,7 @@ public IObservable> Run() }); } - private void CloneSourceList(ReferenceCountTracker tracker, IChangeSet changes) + private static void CloneSourceList(ReferenceCountTracker tracker, IChangeSet changes) { foreach (var change in changes) { @@ -62,7 +68,10 @@ private void CloneSourceList(ReferenceCountTracker tracker, IChangeSet cha break; case ListChangeReason.AddRange: foreach (var t in change.Range) + { tracker.Add(t); + } + break; case ListChangeReason.Replace: tracker.Remove(change.Item.Previous.Value); @@ -74,7 +83,10 @@ private void CloneSourceList(ReferenceCountTracker tracker, IChangeSet cha case ListChangeReason.RemoveRange: case ListChangeReason.Clear: foreach (var t in change.Range) + { tracker.Remove(t); + } + break; } } @@ -103,9 +115,12 @@ private IChangeSet UpdateResultList(IChangeSet changes, List> sourceLists, T ite { return sourceLists.All(s => s.Contains(item)); } + case CombineOperator.Or: { return sourceLists.Any(s => s.Contains(item)); } + case CombineOperator.Xor: { return sourceLists.Count(s => s.Contains(item)) == 1; } + case CombineOperator.Except: { var first = sourceLists[0].Contains(item); var others = sourceLists.Skip(1).Any(s => s.Contains(item)); return first && !others; } + default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(item)); } } } diff --git a/src/DynamicData/List/Internal/DeferUntilLoaded.cs b/src/DynamicData/List/Internal/DeferUntilLoaded.cs index d548b7acc..f87e5c84e 100644 --- a/src/DynamicData/List/Internal/DeferUntilLoaded.cs +++ b/src/DynamicData/List/Internal/DeferUntilLoaded.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Annotations; diff --git a/src/DynamicData/List/Internal/Distinct.cs b/src/DynamicData/List/Internal/Distinct.cs index f6b083e31..79853928e 100644 --- a/src/DynamicData/List/Internal/Distinct.cs +++ b/src/DynamicData/List/Internal/Distinct.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -9,7 +13,7 @@ namespace DynamicData.List.Internal { internal sealed class Distinct { - private readonly IObservable> _source; + private readonly IObservable> _source; private readonly Func _valueSelector; public Distinct([NotNull] IObservable> source, @@ -38,7 +42,7 @@ public IObservable> Run() }); } - private IChangeSet Process(Dictionary values, ChangeAwareList result, IChangeSet changes) + private static IChangeSet Process(Dictionary values, ChangeAwareList result, IChangeSet changes) { void AddAction(TValue value) => values.Lookup(value) .IfHasValue(count => values[value] = count + 1) @@ -51,12 +55,18 @@ void AddAction(TValue value) => values.Lookup(value) void RemoveAction(TValue value) { var counter = values.Lookup(value); - if (!counter.HasValue) return; + if (!counter.HasValue) + { + return; + } //decrement counter var newCount = counter.Value - 1; values[value] = newCount; - if (newCount != 0) return; + if (newCount != 0) + { + return; + } //if there are none, then remove and notify result.Remove(value); @@ -74,44 +84,54 @@ void RemoveAction(TValue value) AddAction(value); break; } + case ListChangeReason.AddRange: { change.Range.Select(item => item.Value).ForEach(AddAction); break; } - case ListChangeReason.Refresh: { var value = change.Item.Current.Value; var previous = change.Item.Current.Previous; - if (value.Equals(previous)) continue; + if (value.Equals(previous)) + { + continue; + } - RemoveAction(previous); + RemoveAction(previous); AddAction(value); break; } + case ListChangeReason.Replace: { var value = change.Item.Current.Value; var previous = change.Item.Previous.Value.Value; - if (value.Equals(previous)) continue; + if (value.Equals(previous)) + { + continue; + } RemoveAction(previous); AddAction(value); break; } + case ListChangeReason.Remove: { var previous = change.Item.Current.Value; RemoveAction(previous); break; } + case ListChangeReason.RemoveRange: { change.Range.Select(item => item.Value).ForEach(RemoveAction); break; } + case ListChangeReason.Clear: { result.Clear(); @@ -120,6 +140,7 @@ void RemoveAction(TValue value) } } } + return result.CaptureChanges(); } @@ -140,16 +161,36 @@ public ItemWithMatch(T item, TValue value, TValue previousValue) public bool Equals(ItemWithMatch other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Item, other.Item); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ItemWithMatch)obj); } diff --git a/src/DynamicData/List/Internal/DynamicCombiner.cs b/src/DynamicData/List/Internal/DynamicCombiner.cs index 6d2fa46f6..0039edc85 100644 --- a/src/DynamicData/List/Internal/DynamicCombiner.cs +++ b/src/DynamicData/List/Internal/DynamicCombiner.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -44,7 +48,9 @@ public IObservable> Run() //Populate result list and chck for changes var notifications = UpdateResultList(sourceLists.Items.AsArray(), resultList, changes); if (notifications.Count != 0) + { observer.OnNext(notifications); + } }); //when an list is removed, need to @@ -54,14 +60,18 @@ public IObservable> Run() //Remove items if required var notifications = ProcessChanges(sourceLists.Items.AsArray(), resultList, mc.Tracker.Items); if (notifications.Count != 0) + { observer.OnNext(notifications); + } if (_type == CombineOperator.And || _type == CombineOperator.Except) { var itemsToCheck = sourceLists.Items.SelectMany(mc2 => mc2.Tracker.Items).ToArray(); var notification2 = ProcessChanges(sourceLists.Items.AsArray(), resultList, itemsToCheck); if (notification2.Count != 0) + { observer.OnNext(notification2); + } } }) .Subscribe(); @@ -73,13 +83,17 @@ public IObservable> Run() { var notifications = ProcessChanges(sourceLists.Items.AsArray(), resultList, mc.Current.Tracker.Items); if (notifications.Count != 0) + { observer.OnNext(notifications); + } if (_type == CombineOperator.And || _type == CombineOperator.Except) { var notification2 = ProcessChanges(sourceLists.Items.AsArray(), resultList, resultList.ToArray()); if (notification2.Count != 0) + { observer.OnNext(notification2); + } } }) .Subscribe(); @@ -111,19 +125,25 @@ private void ProcessItem(MergeContainer[] sourceLists, ChangeAwareListWithRefCou if (shouldBeInResult) { if (!isInResult) + { resultingList.Add(item); + } } else { if (isInResult) + { resultingList.Remove(item); + } } } private bool MatchesConstraint(MergeContainer[] sourceLists, T item) { if (sourceLists.Length == 0) + { return false; + } switch (_type) { @@ -131,22 +151,26 @@ private bool MatchesConstraint(MergeContainer[] sourceLists, T item) { return sourceLists.All(s => s.Tracker.Contains(item)); } + case CombineOperator.Or: { return sourceLists.Any(s => s.Tracker.Contains(item)); } + case CombineOperator.Xor: { return sourceLists.Count(s => s.Tracker.Contains(item)) == 1; } + case CombineOperator.Except: { var first = sourceLists[0].Tracker.Contains(item); var others = sourceLists.Skip(1).Any(s => s.Tracker.Contains(item)); return first && !others; } + default: - throw new ArgumentOutOfRangeException(); + throw new IndexOutOfRangeException("Unknown CombineOperator " + _type); } } @@ -171,7 +195,10 @@ private void Clone(IChangeSet changes) break; case ListChangeReason.AddRange: foreach (var t in change.Range) + { Tracker.Add(t); + } + break; case ListChangeReason.Replace: Tracker.Remove(change.Item.Previous.Value); @@ -183,7 +210,10 @@ private void Clone(IChangeSet changes) case ListChangeReason.RemoveRange: case ListChangeReason.Clear: foreach (var t in change.Range) + { Tracker.Remove(t); + } + break; } } diff --git a/src/DynamicData/List/Internal/EditDiff.cs b/src/DynamicData/List/Internal/EditDiff.cs index e3aa807c1..0dcc5603a 100644 --- a/src/DynamicData/List/Internal/EditDiff.cs +++ b/src/DynamicData/List/Internal/EditDiff.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using DynamicData.Annotations; diff --git a/src/DynamicData/List/Internal/ExpirableItem.cs b/src/DynamicData/List/Internal/ExpirableItem.cs index 0fd468faf..a5ad5d41c 100644 --- a/src/DynamicData/List/Internal/ExpirableItem.cs +++ b/src/DynamicData/List/Internal/ExpirableItem.cs @@ -1,9 +1,13 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; namespace DynamicData.List.Internal { - + internal sealed class ExpirableItem : IEquatable> { public TObject Item { get; } @@ -21,15 +25,31 @@ public ExpirableItem(TObject value, DateTime dateTime, long index) public bool Equals(ExpirableItem other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Item, other.Item) && ExpireAt.Equals(other.ExpireAt) && Index == other.Index; } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is ExpirableItem item && Equals(item); } diff --git a/src/DynamicData/List/Internal/ExpireAfter.cs b/src/DynamicData/List/Internal/ExpireAfter.cs index 0ba0d928b..9f8895eb0 100644 --- a/src/DynamicData/List/Internal/ExpireAfter.cs +++ b/src/DynamicData/List/Internal/ExpireAfter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; @@ -89,6 +93,7 @@ void RemovalAction() }) .Subscribe(); } + return Disposable.Create(() => { removalSubscription.Dispose(); diff --git a/src/DynamicData/List/Internal/Filter.cs b/src/DynamicData/List/Internal/Filter.cs index b4a01f68a..27df951fb 100644 --- a/src/DynamicData/List/Internal/Filter.cs +++ b/src/DynamicData/List/Internal/Filter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -37,7 +41,7 @@ public IObservable> Run() return Observable.Create>(observer => { var locker = new object(); - + Func predicate = t => false; var all = new List(); var filtered = new ChangeAwareList(); @@ -78,10 +82,14 @@ public IObservable> Run() .Select(changes => { //keep track of all changes if filtering on an observable - if (!immutableFilter) all.Clone(changes); + if (!immutableFilter) + { + all.Clone(changes); + } + return Process(filtered, changes); }); - + return predicateChanged.Merge(filteredResult) .NotEmpty() .Select(changes => changes.Transform(iwm => iwm.Item)) // use convert, not transform @@ -89,7 +97,7 @@ public IObservable> Run() }); } - private IChangeSet Process(ChangeAwareList filtered, IChangeSet changes) + private static IChangeSet Process(ChangeAwareList filtered, IChangeSet changes) { //Maintain all items as well as filtered list. This enables us to a) requery when the predicate changes b) check the previous state when Refresh is called foreach (var item in changes) @@ -100,15 +108,20 @@ private IChangeSet Process(ChangeAwareList filtere { var change = item.Item; if (change.Current.IsMatch) - filtered.Add(change.Current); - break; + { + filtered.Add(change.Current); + } + + break; } + case ListChangeReason.AddRange: { var matches = item.Range.Where(t => t.IsMatch).ToList(); filtered.AddRange(matches); break; } + case ListChangeReason.Replace: { var change = item.Item; @@ -134,10 +147,14 @@ private IChangeSet Process(ChangeAwareList filtere else { if (wasMatch) - filtered.Remove(change.Previous.Value); - } + { + filtered.Remove(change.Previous.Value); + } + } + break; } + case ListChangeReason.Refresh: { var change = item.Item; @@ -162,20 +179,26 @@ private IChangeSet Process(ChangeAwareList filtere else { if (wasMatch) - filtered.Remove(change.Current); - } + { + filtered.Remove(change.Current); + } + } + break; } + case ListChangeReason.Remove: { filtered.Remove(item.Item.Current); break; } + case ListChangeReason.RemoveRange: { filtered.RemoveMany(item.Range); break; } + case ListChangeReason.Clear: { filtered.ClearOrRemoveMany(item); @@ -184,12 +207,16 @@ private IChangeSet Process(ChangeAwareList filtere } } + return filtered.CaptureChanges(); } private IChangeSet Requery(Func predicate, List all, ChangeAwareList filtered) { - if (all.Count == 0) return ChangeSet.Empty; + if (all.Count == 0) + { + return ChangeSet.Empty; + } if (_policy == ListFilterPolicy.ClearAndReplace) { @@ -198,13 +225,13 @@ private IChangeSet Requery(Func predicate, List iwm.IsMatch)); - + //reset state for all items all.Clear(); all.AddRange(itemsWithMatch); return filtered.CaptureChanges(); } - + var toAdd = new List(all.Count); var toRemove = new List(all.Count); @@ -254,8 +281,16 @@ public bool Equals(ItemWithMatch other) public override bool Equals(object obj) { - if (obj is null) return false; - if (obj.GetType() != GetType()) return false; + if (obj is null) + { + return false; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((ItemWithMatch) obj); } diff --git a/src/DynamicData/List/Internal/FilterOnObservable.cs b/src/DynamicData/List/Internal/FilterOnObservable.cs index b5a103bc7..b67dfee7a 100644 --- a/src/DynamicData/List/Internal/FilterOnObservable.cs +++ b/src/DynamicData/List/Internal/FilterOnObservable.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Concurrency; @@ -25,7 +29,7 @@ public FilterOnObservable(IObservable> source, _scheduler = scheduler; } - private readonly struct ObjWithFilterValue :IEquatable + private readonly struct ObjWithFilterValue : IEquatable { public readonly TObject Obj; public readonly bool Filter; @@ -53,12 +57,22 @@ public int GetHashCode(ObjWithFilterValue obj) } private static IEqualityComparer ObjComparer { get; } = new ObjEqualityComparer(); - + public bool Equals(ObjWithFilterValue other) { // default equality does _not_ include Filter value, as that would cause the Filter operator that is used later to fail return ObjComparer.Equals(this, other); } + + public override bool Equals(object obj) + { + return obj is ObjWithFilterValue value && Equals(value); + } + + public override int GetHashCode() + { + return ObjComparer.GetHashCode(this); + } } public IObservable> Run() @@ -117,15 +131,25 @@ public IObservable> Run() }); } - private static IEnumerable IndexOfMany(IEnumerable source, IEnumerable itemsToFind, - Func objectPropertyFunc, + Func objectPropertyFunc, Func resultSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (itemsToFind == null) throw new ArgumentNullException(nameof(itemsToFind)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (itemsToFind == null) + { + throw new ArgumentNullException(nameof(itemsToFind)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } var indexed = source.Select((element, index) => new { Element = element, Index = index }); return itemsToFind.Join(indexed, objectPropertyFunc, right => objectPropertyFunc(right.Element), diff --git a/src/DynamicData/List/Internal/FilterOnProperty.cs b/src/DynamicData/List/Internal/FilterOnProperty.cs index 86d79f103..dc82f440d 100644 --- a/src/DynamicData/List/Internal/FilterOnProperty.cs +++ b/src/DynamicData/List/Internal/FilterOnProperty.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.ComponentModel; using System.Linq.Expressions; using System.Reactive.Concurrency; diff --git a/src/DynamicData/List/Internal/FilterStatic.cs b/src/DynamicData/List/Internal/FilterStatic.cs index 1464b0de1..159f25e96 100644 --- a/src/DynamicData/List/Internal/FilterStatic.cs +++ b/src/DynamicData/List/Internal/FilterStatic.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using DynamicData.Annotations; using System; using System.Linq; @@ -38,15 +42,20 @@ private void Process(ChangeAwareList filtered, IChangeSet changes) { var change = item.Item; if (_predicate(change.Current)) - filtered.Add(change.Current); - break; + { + filtered.Add(change.Current); + } + + break; } + case ListChangeReason.AddRange: { var matches = item.Range.Where(t => _predicate(t)).ToList(); filtered.AddRange(matches); break; } + case ListChangeReason.Replace: { var change = item.Item; @@ -59,18 +68,22 @@ private void Process(ChangeAwareList filtered, IChangeSet changes) { filtered.Remove(change.Previous.Value); } + break; } + case ListChangeReason.Remove: { filtered.Remove(item.Item.Current); break; } + case ListChangeReason.RemoveRange: { filtered.RemoveMany(item.Range); break; } + case ListChangeReason.Clear: { filtered.ClearOrRemoveMany(item); diff --git a/src/DynamicData/List/Internal/Group.cs b/src/DynamicData/List/Internal/Group.cs index 23f0f3ed1..5ec92e970 100644 --- a/src/DynamicData/List/Internal/Group.cs +++ b/src/DynamicData/List/Internal/Group.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; @@ -17,16 +21,36 @@ internal class Group : IGroup, IDisposable, IE public bool Equals(Group other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(GroupKey, other.GroupKey); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((Group)obj); } diff --git a/src/DynamicData/List/Internal/GroupOn.cs b/src/DynamicData/List/Internal/GroupOn.cs index e838bf6d6..9c2db2eab 100644 --- a/src/DynamicData/List/Internal/GroupOn.cs +++ b/src/DynamicData/List/Internal/GroupOn.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -35,7 +39,7 @@ public IObservable>> Run() { return new ItemWithGroupKey(t, _groupSelector(t), previous.Convert(p=>p.Group)); },true); - + var locker = new object(); var shared = itemsWithGroup.Synchronize(locker).Publish(); @@ -72,8 +76,11 @@ private IChangeSet> Regroup(ChangeAwareList> Regroup(ChangeAwareList innerList.Add(itemWithValue.Item)); if (newGroupLookup.WasCreated) + { result.Add(newGroupCache); + } } return result.CaptureChanges(); } - private IChangeSet> Process(ChangeAwareList> result, IDictionary> groupCollection, IChangeSet changes) + private static IChangeSet> Process(ChangeAwareList> result, IDictionary> groupCollection, IChangeSet changes) { foreach (var grouping in changes.Unified().GroupBy(change => change.Current.Group)) { @@ -110,7 +119,9 @@ private IChangeSet> Process(ChangeAwareList> Process(ChangeAwareList> Process(ChangeAwareList { g.Edit(oldList => oldList.Remove(previousItem)); - if (g.List.Count != 0) return; + if (g.List.Count != 0) + { + return; + } + groupCollection.Remove(g.GroupKey); result.Remove(g); }); @@ -156,6 +172,7 @@ private IChangeSet> Process(ChangeAwareList> Process(ChangeAwareList { g.Edit(oldList => oldList.Remove(currentItem)); - if (g.List.Count != 0) return; + if (g.List.Count != 0) + { + return; + } + groupCollection.Remove(g.GroupKey); result.Remove(g); }); } + break; } @@ -192,6 +214,7 @@ private IChangeSet> Process(ChangeAwareList> Process(ChangeAwareList> groupCaches, TGroupKey key) + private static GroupWithAddIndicator GetCache(IDictionary> groupCaches, TGroupKey key) { var cache = groupCaches.Lookup(key); if (cache.HasValue) + { return new GroupWithAddIndicator(cache.Value, false); + } var newcache = new Group(key); groupCaches[key] = newcache; @@ -251,15 +277,31 @@ public ItemWithGroupKey(TObject item, TGroupKey group, Optional previ public bool Equals(ItemWithGroupKey other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Item, other.Item); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is ItemWithGroupKey && Equals((ItemWithGroupKey) obj); } @@ -288,7 +330,6 @@ public override int GetHashCode() #endregion - public override string ToString() => $"{Item} ({Group})"; } } diff --git a/src/DynamicData/List/Internal/GroupOnImmutable.cs b/src/DynamicData/List/Internal/GroupOnImmutable.cs index 8f8e55363..bd8f8d55f 100644 --- a/src/DynamicData/List/Internal/GroupOnImmutable.cs +++ b/src/DynamicData/List/Internal/GroupOnImmutable.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -7,7 +11,6 @@ using DynamicData.Annotations; using DynamicData.Kernel; - namespace DynamicData.List.Internal { internal sealed class GroupOnImmutable @@ -43,8 +46,6 @@ public IObservable>> Run() var locker = new object(); var shared = itemsWithGroup.Synchronize(locker).Publish(); - - var grouper = shared .Select(changes => Process(groupings, groupCache, changes)); @@ -77,12 +78,17 @@ private IChangeSet> Regroup(ChangeAwareList> Regroup(ChangeAwareList> Process(ChangeAwareList> result, IDictionary allGroupings, IChangeSet changes) + private static IChangeSet> Process(ChangeAwareList> result, IDictionary allGroupings, IChangeSet changes) { //need to keep track of effected groups to calculate correct notifications var initialStateOfGroups = new Dictionary>(); @@ -114,10 +123,11 @@ private IChangeSet> Process(ChangeAwareList { if (!initialStateOfGroups.ContainsKey(g.Key)) + { initialStateOfGroups[g.Key] = GetGroupState(g.Key, g.List); + } g.List.Remove(previousItem); }); } + break; } + case ListChangeReason.Replace: { GetInitialState(); @@ -180,19 +195,24 @@ void GetInitialState() .IfHasValue(g => { if (!initialStateOfGroups.ContainsKey(g.Key)) + { initialStateOfGroups[g.Key] = GetGroupState(g.Key, g.List); + } g.List.Remove(previousItem); }); } + break; } + case ListChangeReason.Remove: { GetInitialState(); listToModify.Remove(change.Current.Item); break; } + case ListChangeReason.Clear: { GetInitialState(); @@ -202,10 +222,11 @@ void GetInitialState() } } } + return CreateChangeSet(result, allGroupings, initialStateOfGroups); } - private IChangeSet> CreateChangeSet(ChangeAwareList> result, IDictionary allGroupings, IDictionary> initialStateOfGroups) + private static IChangeSet> CreateChangeSet(ChangeAwareList> result, IDictionary allGroupings, IDictionary> initialStateOfGroups) { //Now maintain target list foreach (var intialGroup in initialStateOfGroups) @@ -234,27 +255,30 @@ private IChangeSet> CreateChangeSet(ChangeAwareLis } } } + return result.CaptureChanges(); } - private IGrouping GetGroupState(GroupContainer grouping) + private static IGrouping GetGroupState(GroupContainer grouping) { return new ImmutableGroup(grouping.Key, grouping.List); } - private IGrouping GetGroupState(TGroupKey key, IList list) + private static IGrouping GetGroupState(TGroupKey key, IList list) { return new ImmutableGroup(key, list); } - private GroupContainer GetGroup(IDictionary groupCaches, TGroupKey key) + private static GroupContainer GetGroup(IDictionary groupCaches, TGroupKey key) { var cached = groupCaches.Lookup(key); if (cached.HasValue) + { return cached.Value; + } var newcache = new GroupContainer(key); - groupCaches[key] = newcache; + groupCaches[key] = newcache; return newcache; } @@ -274,7 +298,7 @@ private sealed class ItemWithGroupKey : IEquatable public TObject Item { get; } public TGroupKey Group { get; set; } public Optional PreviousGroup { get; } - + public ItemWithGroupKey(TObject item, TGroupKey group, Optional previousGroup) { Item = item; @@ -286,15 +310,31 @@ public ItemWithGroupKey(TObject item, TGroupKey group, Optional previ public bool Equals(ItemWithGroupKey other) { - if (other is null) return false; - if (ReferenceEquals(this, other)) return true; + if (other is null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Item, other.Item); } public override bool Equals(object obj) { - if (obj is null) return false; - if (ReferenceEquals(this, obj)) return true; + if (obj is null) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is ItemWithGroupKey && Equals((ItemWithGroupKey)obj); } diff --git a/src/DynamicData/List/Internal/GroupOnProperty.cs b/src/DynamicData/List/Internal/GroupOnProperty.cs index 069c3ec18..b38ff1ef9 100644 --- a/src/DynamicData/List/Internal/GroupOnProperty.cs +++ b/src/DynamicData/List/Internal/GroupOnProperty.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.ComponentModel; using System.Linq.Expressions; using System.Reactive.Concurrency; @@ -34,7 +38,9 @@ public IObservable>> Run() //add a throttle if specified if (_throttle != null) + { regrouper = regrouper.Throttle(_throttle.Value, _scheduler ?? Scheduler.Default); + } // Use property changes as a trigger to re-evaluate Grouping return shared.GroupOn(_groupSelector, regrouper); diff --git a/src/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs b/src/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs index ff08e3007..062e60f4c 100644 --- a/src/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs +++ b/src/DynamicData/List/Internal/GroupOnPropertyWithImmutableState.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.ComponentModel; using System.Linq.Expressions; using System.Reactive.Concurrency; @@ -34,7 +38,9 @@ public IObservable>> Run() //add a throttle if specified if (_throttle != null) + { regrouper = regrouper.Throttle(_throttle.Value, _scheduler ?? Scheduler.Default); + } // Use property changes as a trigger to re-evaluate Grouping return shared.GroupWithImmutableState(_groupSelector, regrouper); diff --git a/src/DynamicData/List/Internal/ImmutableGroup.cs b/src/DynamicData/List/Internal/ImmutableGroup.cs index 548e45bc1..ff50f949f 100644 --- a/src/DynamicData/List/Internal/ImmutableGroup.cs +++ b/src/DynamicData/List/Internal/ImmutableGroup.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -23,15 +27,31 @@ internal ImmutableGroup(TGroupKey key, IList items) public bool Equals(ImmutableGroup other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Key, other.Key); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + return obj is ImmutableGroup && Equals((ImmutableGroup) obj); } diff --git a/src/DynamicData/List/Internal/MergeMany.cs b/src/DynamicData/List/Internal/MergeMany.cs index c74712c53..45da5ee0d 100644 --- a/src/DynamicData/List/Internal/MergeMany.cs +++ b/src/DynamicData/List/Internal/MergeMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Annotations; diff --git a/src/DynamicData/List/Internal/OnBeingAdded.cs b/src/DynamicData/List/Internal/OnBeingAdded.cs index f2afc0498..52eb84709 100644 --- a/src/DynamicData/List/Internal/OnBeingAdded.cs +++ b/src/DynamicData/List/Internal/OnBeingAdded.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Linq; using DynamicData.Kernel; diff --git a/src/DynamicData/List/Internal/OnBeingRemoved.cs b/src/DynamicData/List/Internal/OnBeingRemoved.cs index 9edaf75a2..b310f9823 100644 --- a/src/DynamicData/List/Internal/OnBeingRemoved.cs +++ b/src/DynamicData/List/Internal/OnBeingRemoved.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -57,6 +61,7 @@ private void RegisterForRemoval(IList items, IChangeSet changes) break; } } + items.Clone(changes); } } diff --git a/src/DynamicData/List/Internal/Pager.cs b/src/DynamicData/List/Internal/Pager.cs index e05c16123..88b88e245 100644 --- a/src/DynamicData/List/Internal/Pager.cs +++ b/src/DynamicData/List/Internal/Pager.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -46,17 +50,22 @@ public IObservable> Run() }); } - private PageChangeSet CheckParametersAndPage(List all, ChangeAwareList paged, IPageRequest request) + private static PageChangeSet CheckParametersAndPage(List all, ChangeAwareList paged, IPageRequest request) { if (request == null || request.Page < 0 || request.Size < 1) + { return null; + } return Page(all, paged, request); } - private PageChangeSet Page(List all, ChangeAwareList paged, IPageRequest request, IChangeSet changeset = null) + private static PageChangeSet Page(List all, ChangeAwareList paged, IPageRequest request, IChangeSet changeset = null) { - if (changeset != null) all.Clone(changeset); + if (changeset != null) + { + all.Clone(changeset); + } var previous = paged; @@ -100,7 +109,9 @@ private PageChangeSet Page(List all, ChangeAwareList paged, IPageReques var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) + { continue; + } var index = paged.IndexOf(currentItem); paged.Move(i, index); @@ -111,7 +122,7 @@ private PageChangeSet Page(List all, ChangeAwareList paged, IPageReques return new PageChangeSet(changed, new PageResponse(paged.Count, page, all.Count, pages)); } - private int CalculatePages(List all, IPageRequest request) + private static int CalculatePages(List all, IPageRequest request) { if (request.Size >= all.Count || request.Size==0) { @@ -125,6 +136,7 @@ private int CalculatePages(List all, IPageRequest request) { return pages; } + return pages + 1; } } diff --git a/src/DynamicData/List/Internal/QueryWhenChanged.cs b/src/DynamicData/List/Internal/QueryWhenChanged.cs index 3250e7d4c..3dc2ca645 100644 --- a/src/DynamicData/List/Internal/QueryWhenChanged.cs +++ b/src/DynamicData/List/Internal/QueryWhenChanged.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Linq; diff --git a/src/DynamicData/List/Internal/ReaderWriter.cs b/src/DynamicData/List/Internal/ReaderWriter.cs index 37417ecd0..e9c2d82b1 100644 --- a/src/DynamicData/List/Internal/ReaderWriter.cs +++ b/src/DynamicData/List/Internal/ReaderWriter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using DynamicData.Kernel; @@ -11,7 +15,11 @@ internal sealed class ReaderWriter public IChangeSet Write(IChangeSet changes) { - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } + IChangeSet result; lock (_locker) @@ -19,12 +27,16 @@ public IChangeSet Write(IChangeSet changes) _data.Clone(changes); result = _data.CaptureChanges(); } + return result; } public IChangeSet Write(Action> updateAction) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } IChangeSet result; @@ -39,11 +51,18 @@ public IChangeSet Write(Action> updateAction) return result; } - + public IChangeSet WriteWithPreview(Action> updateAction, Action> previewHandler) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); - if (previewHandler == null) throw new ArgumentNullException(nameof(previewHandler)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } + + if (previewHandler == null) + { + throw new ArgumentNullException(nameof(previewHandler)); + } IChangeSet result; @@ -73,10 +92,12 @@ public IChangeSet WriteWithPreview(Action> updateAction, Act /// Changes are added to the topmost change tracker. /// Use only during an invocation of Write/WriteWithPreview. /// - public void WriteNested(Action> updateAction) + public void WriteNested(Action> updateAction) { if (updateAction == null) + { throw new ArgumentNullException(nameof(updateAction)); + } lock (_locker) { @@ -107,7 +128,9 @@ public int Count get { lock (_locker) + { return _data.Count; + } } } } diff --git a/src/DynamicData/List/Internal/RefCount.cs b/src/DynamicData/List/Internal/RefCount.cs index 7b6b19897..aae2f459e 100644 --- a/src/DynamicData/List/Internal/RefCount.cs +++ b/src/DynamicData/List/Internal/RefCount.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -21,8 +25,12 @@ public IObservable> Run() return Observable.Create>(observer => { lock (_locker) + { if (++_refCount == 1) + { _list = _source.AsObservableList(); + } + } var subscriber = _list.Connect().SubscribeSafe(observer); @@ -31,11 +39,14 @@ public IObservable> Run() subscriber.Dispose(); IDisposable listToDispose = null; lock (_locker) + { if (--_refCount == 0) { listToDispose = _list; _list = null; } + } + listToDispose?.Dispose(); }); }); diff --git a/src/DynamicData/List/Internal/ReferenceCountTracker.cs b/src/DynamicData/List/Internal/ReferenceCountTracker.cs index face12c2d..aaa1b391c 100644 --- a/src/DynamicData/List/Internal/ReferenceCountTracker.cs +++ b/src/DynamicData/List/Internal/ReferenceCountTracker.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; namespace DynamicData.List.Internal { diff --git a/src/DynamicData/List/Internal/SizeLimiter.cs b/src/DynamicData/List/Internal/SizeLimiter.cs index 704858a6f..7c5ff37bc 100644 --- a/src/DynamicData/List/Internal/SizeLimiter.cs +++ b/src/DynamicData/List/Internal/SizeLimiter.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -37,7 +41,9 @@ public IObservable> Run() { var numbertoExpire = list.Count - _sizeLimit; if (numbertoExpire < 0) + { return emptyResult; + } var dueForExpiry = list.OrderBy(exp => exp.ExpireAt).ThenBy(exp => exp.Index) .Take(numbertoExpire) diff --git a/src/DynamicData/List/Internal/Sort.cs b/src/DynamicData/List/Internal/Sort.cs index 16c03c1ca..4f108b52f 100644 --- a/src/DynamicData/List/Internal/Sort.cs +++ b/src/DynamicData/List/Internal/Sort.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive; @@ -43,7 +47,9 @@ public IObservable> Run() var changed = _source.Synchronize(locker).Select(changes => { if (_resetThreshold > 1) + { orginal.Clone(changes); + } return changes.TotalChanges > _resetThreshold && _comparer!=null ? Reset(orginal, target) : Process(target, changes); }); @@ -89,6 +95,7 @@ private IChangeSet ProcessImpl(ChangeAwareList target, IChangeSet chang Insert(target, current); break; } + case ListChangeReason.AddRange: { var ordered = change.Range.OrderBy(t => t, _comparer).ToList(); @@ -100,14 +107,17 @@ private IChangeSet ProcessImpl(ChangeAwareList target, IChangeSet chang { ordered.ForEach(item => Insert(target, item)); } + break; } + case ListChangeReason.Remove: { var current = change.Item.Current; Remove(target, current); break; } + case ListChangeReason.Refresh: { //add to refresh list so position can be calculated @@ -122,6 +132,7 @@ private IChangeSet ProcessImpl(ChangeAwareList target, IChangeSet chang target.Refresh(indexed.Item, indexed.Index); break; } + case ListChangeReason.Replace: { var current = change.Item.Current; @@ -137,6 +148,7 @@ private IChangeSet ProcessImpl(ChangeAwareList target, IChangeSet chang target.RemoveMany(change.Range); break; } + case ListChangeReason.Clear: { target.Clear(); @@ -149,17 +161,25 @@ private IChangeSet ProcessImpl(ChangeAwareList target, IChangeSet chang foreach (var item in refreshes) { var old = target.IndexOf(item); - if (old == -1) continue; + if (old == -1) + { + continue; + } int newposition = GetInsertPositionLinear(target, item); if (old < newposition) - newposition--; + { + newposition--; + } if (old == newposition) + { continue; + } target.Move(old, newposition); } + return target.CaptureChanges(); } @@ -173,7 +193,10 @@ private IChangeSet Reorder(ChangeAwareList target) var existing = target[index]; //if item is in the same place, - if (ReferenceEquals(item, existing)) continue; + if (ReferenceEquals(item, existing)) + { + continue; + } //Cannot use binary search as Resort is implicit of a mutable change var old = target.IndexOf(item); @@ -187,7 +210,9 @@ private IChangeSet ChangeComparer(ChangeAwareList target, IComparer com { _comparer = comparer; if (_resetThreshold > 0 && target.Count <= _resetThreshold) + { return Reorder(target); + } var sorted = target.OrderBy(t => t, _comparer).ToList(); target.Clear(); @@ -227,8 +252,11 @@ private int GetInsertPositionLinear(ChangeAwareList target, T item) for (var i = 0; i < target.Count; i++) { if (_comparer.Compare(item, target[i]) < 0) + { return i; + } } + return target.Count; } @@ -239,18 +267,23 @@ private int GetInsertPositionBinary(ChangeAwareList target, T item) //sort is not returning uniqueness if (insertIndex < 0) + { throw new SortException("Binary search has been specified, yet the sort does not yeild uniqueness"); + } + return insertIndex; } private int GetCurrentPosition(ChangeAwareList target, T item) { - var index = _sortOptions == SortOptions.UseBinarySearch - ? target.BinarySearch(item, _comparer) + var index = _sortOptions == SortOptions.UseBinarySearch + ? target.BinarySearch(item, _comparer) : target.IndexOf(item); if (index < 0) + { throw new SortException($"Cannot find item: {typeof(T).Name} -> {item}"); + } return index; } diff --git a/src/DynamicData/List/Internal/SubscribeMany.cs b/src/DynamicData/List/Internal/SubscribeMany.cs index 26565584d..66f6966ae 100644 --- a/src/DynamicData/List/Internal/SubscribeMany.cs +++ b/src/DynamicData/List/Internal/SubscribeMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Reactive.Disposables; using System.Reactive.Linq; diff --git a/src/DynamicData/List/Internal/Switch.cs b/src/DynamicData/List/Internal/Switch.cs index 5788c81cc..93c88ee60 100644 --- a/src/DynamicData/List/Internal/Switch.cs +++ b/src/DynamicData/List/Internal/Switch.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; @@ -25,11 +29,12 @@ public IObservable> Run() .Do(_ => { lock (locker) + { destination.Clear(); + } })) .Synchronize(locker) .PopulateInto(destination); - var publisher = destination.Connect().SubscribeSafe(observer); return new CompositeDisposable(destination, populator, publisher); diff --git a/src/DynamicData/List/Internal/ToObservableChangeSet.cs b/src/DynamicData/List/Internal/ToObservableChangeSet.cs index 3997e00d9..86c8c33da 100644 --- a/src/DynamicData/List/Internal/ToObservableChangeSet.cs +++ b/src/DynamicData/List/Internal/ToObservableChangeSet.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Concurrency; @@ -16,7 +20,7 @@ internal class ToObservableChangeSet private readonly int _limitSizeTo; private readonly IScheduler _scheduler; - public ToObservableChangeSet(IObservable source, + public ToObservableChangeSet(IObservable source, Func expireAfter, int limitSizeTo, IScheduler scheduler = null) @@ -35,7 +39,6 @@ public ToObservableChangeSet(IObservable> source, _scheduler = scheduler ?? Scheduler.Default; } - public IObservable> Run() { return Observable.Create>(observer => @@ -53,13 +56,13 @@ public IObservable> Run() { state.AddRange(items); } + return state; }) .Select(state => state.CaptureChanges()) .SubscribeSafe(observer); } - long orderItemWasAdded = -1; var locker = new object(); @@ -86,6 +89,7 @@ public IObservable> Run() var toRemove = state.Count - _limitSizeTo; state.RemoveRange(0, toRemove); } + return state; }) .Select(state => state.CaptureChanges()) @@ -94,7 +98,7 @@ public IObservable> Run() var timeLimited = (_expireAfter == null ? Observable.Never>>() : sizeLimited) .Filter(ei => ei.ExpireAt != DateTime.MaxValue) .GroupWithImmutableState(ei => ei.ExpireAt) - .MergeMany(grouping => + .MergeMany(grouping => { var expireAt = grouping.Key.Subtract(_scheduler.Now.DateTime); return Observable.Timer(expireAt, _scheduler).Select(_ => grouping); diff --git a/src/DynamicData/List/Internal/TransformAsync.cs b/src/DynamicData/List/Internal/TransformAsync.cs index f08b96da6..5300b8dd8 100644 --- a/src/DynamicData/List/Internal/TransformAsync.cs +++ b/src/DynamicData/List/Internal/TransformAsync.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -6,7 +10,6 @@ using System.Threading.Tasks; using DynamicData.Annotations; - namespace DynamicData.List.Internal { internal class TransformAsync @@ -16,12 +19,15 @@ internal class TransformAsync public TransformAsync([NotNull] IObservable> source, [NotNull] Func> factory) { - if (factory == null) throw new ArgumentNullException(nameof(factory)); + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } _source = source ?? throw new ArgumentNullException(nameof(source)); _containerFactory = async item => { - var destination = await factory(item); + var destination = await factory(item).ConfigureAwait(false); return new TransformedItemContainer(item, destination); }; } @@ -34,7 +40,7 @@ public IObservable> Run() var subscriber = _source.Select(async changes => { - await Transform(state, changes); + await Transform(state, changes).ConfigureAwait(false); return state; }) .Select(tasks => tasks.ToObservable()) @@ -49,11 +55,12 @@ public IObservable> Run() }); } - - private async Task Transform(ChangeAwareList transformed, IChangeSet changes) { - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } foreach (var item in changes) { @@ -64,27 +71,30 @@ private async Task Transform(ChangeAwareList transform var change = item.Item; if (change.CurrentIndex < 0 | change.CurrentIndex >= transformed.Count) { - var container = await _containerFactory(item.Item.Current); + var container = await _containerFactory(item.Item.Current).ConfigureAwait(false); transformed.Add(container); } else { - var container = await _containerFactory(item.Item.Current); + var container = await _containerFactory(item.Item.Current).ConfigureAwait(false); transformed.Insert(change.CurrentIndex, container); } + break; } + case ListChangeReason.AddRange: { var tasks = item.Range.Select(_containerFactory); - var containers = await Task.WhenAll(tasks); + var containers = await Task.WhenAll(tasks).ConfigureAwait(false); transformed.AddOrInsertRange(containers, item.Range.Index); break; } + case ListChangeReason.Replace: { var change = item.Item; - var container = await _containerFactory(item.Item.Current); + var container = await _containerFactory(item.Item.Current).ConfigureAwait(false); if (change.CurrentIndex == change.PreviousIndex) { @@ -98,6 +108,7 @@ private async Task Transform(ChangeAwareList transform break; } + case ListChangeReason.Remove: { var change = item.Item; @@ -112,11 +123,14 @@ private async Task Transform(ChangeAwareList transform var toremove = transformed.FirstOrDefault(t => ReferenceEquals(t.Source, t)); if (toremove != null) - transformed.Remove(toremove); - } + { + transformed.Remove(toremove); + } + } break; } + case ListChangeReason.RemoveRange: { if (item.Range.Index >= 0) @@ -131,6 +145,7 @@ private async Task Transform(ChangeAwareList transform break; } + case ListChangeReason.Clear: { //i.e. need to store transformed reference so we can correctly clear @@ -139,14 +154,17 @@ private async Task Transform(ChangeAwareList transform break; } + case ListChangeReason.Moved: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (!hasIndex) - throw new UnspecifiedIndexException("Cannot move as an index was not specified"); + { + throw new UnspecifiedIndexException("Cannot move as an index was not specified"); + } - var collection = transformed as IExtendedList; + var collection = transformed as IExtendedList; if (collection != null) { collection.Move(change.PreviousIndex, change.CurrentIndex); @@ -157,13 +175,13 @@ private async Task Transform(ChangeAwareList transform transformed.RemoveAt(change.PreviousIndex); transformed.Insert(change.CurrentIndex, current); } + break; } } } } - private class TransformedItemContainer : IEquatable { public TSource Source { get; } @@ -179,16 +197,36 @@ public TransformedItemContainer(TSource source, TDestination destination) public bool Equals(TransformedItemContainer other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Source, other.Source); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((TransformedItemContainer)obj); } diff --git a/src/DynamicData/List/Internal/TransformMany.cs b/src/DynamicData/List/Internal/TransformMany.cs index 75f7531d5..506390410 100644 --- a/src/DynamicData/List/Internal/TransformMany.cs +++ b/src/DynamicData/List/Internal/TransformMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections; using System.Collections.Generic; @@ -26,7 +30,9 @@ public TransformMany(IObservable> source, var subsequentChanges = manyselector(t).ToObservableChangeSet(); if (manyselector(t).Count > 0) + { return subsequentChanges; + } return Observable.Return(ChangeSet.Empty) .Concat(subsequentChanges); @@ -42,7 +48,9 @@ public TransformMany(IObservable> source, var subsequentChanges = manyselector(t).ToObservableChangeSet(); if (manyselector(t).Count > 0) + { return subsequentChanges; + } return Observable.Return(ChangeSet.Empty) .Concat(subsequentChanges); @@ -57,7 +65,9 @@ public TransformMany(IObservable> source, var subsequentChanges = manyselector(t).Connect(); if (manyselector(t).Count > 0) + { return subsequentChanges; + } return Observable.Return(ChangeSet.Empty) .Concat(subsequentChanges); @@ -79,12 +89,14 @@ public TransformMany([NotNull] IObservable> source, public IObservable> Run() { if (_childChanges != null) + { return CreateWithChangeset(); + } return _source.Transform(item => new ManyContainer(_manyselector(item).ToArray()), true) .Select(changes => new ChangeSet(new DestinationEnumerator(changes, _equalityComparer))).NotEmpty(); } - + private IObservable> CreateWithChangeset() { return Observable.Create>(observer => @@ -123,7 +135,6 @@ private IObservable> CreateWithChangeset() return result.CaptureChanges(); }); - var allChanges = init.Merge(subseq); return new CompositeDisposable(allChanges.SubscribeSafe(observer), transformed.Connect()); @@ -142,6 +153,7 @@ public ManyContainer(IEnumerable destination, IObservable> { @@ -163,13 +175,17 @@ public IEnumerator> GetEnumerator() { case ListChangeReason.Add: foreach (var destination in change.Item.Current.Destination) + { yield return new Change(change.Reason, destination); + } + break; case ListChangeReason.AddRange: { var items = change.Range.SelectMany(m => m.Destination); yield return new Change(change.Reason, items); } + break; case ListChangeReason.Replace: case ListChangeReason.Refresh: @@ -183,23 +199,34 @@ public IEnumerator> GetEnumerator() //I am not sure whether it is possible to translate the original change into a replace foreach (var destination in removes) - yield return new Change(ListChangeReason.Remove, destination); + { + yield return new Change(ListChangeReason.Remove, destination); + } + + foreach (var destination in adds) + { + yield return new Change(ListChangeReason.Add, destination); + } + } - foreach (var destination in adds) - yield return new Change(ListChangeReason.Add, destination); - } break; case ListChangeReason.Remove: foreach (var destination in change.Item.Current.Destination) + { yield return new Change(change.Reason, destination); + } + break; case ListChangeReason.RemoveRange: { var toRemove = change.Range.SelectMany(m => m.Destination); foreach (var destination in toRemove) - yield return new Change(ListChangeReason.Remove, destination); - } + { + yield return new Change(ListChangeReason.Remove, destination); + } + } + break; case ListChangeReason.Moved: //do nothing as the original index has no bearing on the destination index @@ -210,12 +237,14 @@ public IEnumerator> GetEnumerator() var items = change.Range.SelectMany(m => m.Destination); yield return new Change(change.Reason, items); } + break; default: - throw new ArgumentOutOfRangeException(); + throw new IndexOutOfRangeException("Unknown list reason " + change); } } } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); diff --git a/src/DynamicData/List/Internal/Transformer.cs b/src/DynamicData/List/Internal/Transformer.cs index 3444a4a91..5261457b0 100644 --- a/src/DynamicData/List/Internal/Transformer.cs +++ b/src/DynamicData/List/Internal/Transformer.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Linq; @@ -15,7 +19,11 @@ internal sealed class Transformer public Transformer([NotNull] IObservable> source, [NotNull] Func, int, TDestination> factory, bool transformOnRefresh) { - if (factory == null) throw new ArgumentNullException(nameof(factory)); + if (factory == null) + { + throw new ArgumentNullException(nameof(factory)); + } + _source = source ?? throw new ArgumentNullException(nameof(source)); _transformOnRefresh = transformOnRefresh; _containerFactory = (item, prev, index) => new TransformedItemContainer(item, factory(item, prev, index)); @@ -32,9 +40,9 @@ public IObservable> Run() { var changed = transformed.CaptureChanges(); return changed.Transform(container => container.Destination); - }); + }); } - + private sealed class TransformedItemContainer : IEquatable { public TSource Source { get; } @@ -50,16 +58,36 @@ public TransformedItemContainer(TSource source, TDestination destination) public bool Equals(TransformedItemContainer other) { - if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; + if (ReferenceEquals(null, other)) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + return EqualityComparer.Default.Equals(Source, other.Source); } public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + + if (ReferenceEquals(this, obj)) + { + return true; + } + + if (obj.GetType() != GetType()) + { + return false; + } + return Equals((TransformedItemContainer)obj); } @@ -83,7 +111,10 @@ public override int GetHashCode() private void Transform(ChangeAwareList transformed, IChangeSet changes) { - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } foreach (var item in changes) { @@ -101,8 +132,10 @@ private void Transform(ChangeAwareList transformed, IC var converted = _containerFactory(change.Current, Optional.None, change.CurrentIndex); transformed.Insert(change.CurrentIndex, converted); } + break; } + case ListChangeReason.AddRange: { var startIndex = item.Range.Index < 0 ? transformed.Count : item.Range.Index; @@ -113,6 +146,7 @@ private void Transform(ChangeAwareList transformed, IC break; } + case ListChangeReason.Refresh: { if (_transformOnRefresh) @@ -127,15 +161,17 @@ private void Transform(ChangeAwareList transformed, IC { transformed.RefreshAt(item.Item.CurrentIndex); } + break; } + case ListChangeReason.Replace: { var change = item.Item; Optional previous = transformed[change.PreviousIndex].Destination; if (change.CurrentIndex == change.PreviousIndex) { - + transformed[change.CurrentIndex] = _containerFactory(change.Current, previous, change.CurrentIndex); } else @@ -146,6 +182,7 @@ private void Transform(ChangeAwareList transformed, IC break; } + case ListChangeReason.Remove: { var change = item.Item; @@ -160,11 +197,14 @@ private void Transform(ChangeAwareList transformed, IC var toRemove = transformed.FirstOrDefault(t => ReferenceEquals(t.Source, change.Current)); if (toRemove != null) + { transformed.Remove(toRemove); + } } break; } + case ListChangeReason.RemoveRange: { if (item.Range.Index >= 0) @@ -179,6 +219,7 @@ private void Transform(ChangeAwareList transformed, IC break; } + case ListChangeReason.Clear: { //i.e. need to store transformed reference so we can correctly clear @@ -187,12 +228,15 @@ private void Transform(ChangeAwareList transformed, IC break; } + case ListChangeReason.Moved: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (!hasIndex) + { throw new UnspecifiedIndexException("Cannot move as an index was not specified"); + } transformed.Move(change.PreviousIndex, change.CurrentIndex); break; diff --git a/src/DynamicData/List/Internal/UnifiedChange.cs b/src/DynamicData/List/Internal/UnifiedChange.cs index 76bf83ee4..d05c1ec9c 100644 --- a/src/DynamicData/List/Internal/UnifiedChange.cs +++ b/src/DynamicData/List/Internal/UnifiedChange.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -31,7 +35,11 @@ public bool Equals(UnifiedChange other) public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is UnifiedChange && Equals((UnifiedChange)obj); } diff --git a/src/DynamicData/List/Internal/Virtualiser.cs b/src/DynamicData/List/Internal/Virtualiser.cs index 078314b90..cc7fe62c4 100644 --- a/src/DynamicData/List/Internal/Virtualiser.cs +++ b/src/DynamicData/List/Internal/Virtualiser.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using System.Reactive.Linq; @@ -12,7 +16,6 @@ internal sealed class Virtualiser private readonly IObservable> _source; private readonly IObservable _requests; - public Virtualiser([NotNull] IObservable> source, [NotNull] IObservable requests) { _source = source ?? throw new ArgumentNullException(nameof(source)); @@ -49,17 +52,22 @@ public IObservable> Run() }); } - private IChangeSet CheckParamsAndVirtualise(List all, ChangeAwareList virtualised, IVirtualRequest request) + private static IChangeSet CheckParamsAndVirtualise(List all, ChangeAwareList virtualised, IVirtualRequest request) { if (request == null || request.StartIndex < 0 || request.Size < 1) + { return null; + } return Virtualise(all, virtualised, request); } - private IChangeSet Virtualise(List all, ChangeAwareList virtualised, IVirtualRequest request, IChangeSet changeset = null) + private static IChangeSet Virtualise(List all, ChangeAwareList virtualised, IVirtualRequest request, IChangeSet changeset = null) { - if (changeset != null) all.Clone(changeset); + if (changeset != null) + { + all.Clone(changeset); + } var previous = virtualised; @@ -97,11 +105,14 @@ private IChangeSet Virtualise(List all, ChangeAwareList virtualised, IV var previousItem = previous[i]; if (ReferenceEquals(currentItem, previousItem)) + { continue; + } var index = virtualised.IndexOf(currentItem); virtualised.Move(i, index); } + return virtualised.CaptureChanges(); } } diff --git a/src/DynamicData/List/ItemChange.cs b/src/DynamicData/List/ItemChange.cs index 54d03460b..715fdef54 100644 --- a/src/DynamicData/List/ItemChange.cs +++ b/src/DynamicData/List/ItemChange.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Kernel; @@ -110,7 +114,11 @@ public bool Equals(ItemChange other) /// public override bool Equals(object obj) { - if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(null, obj)) + { + return false; + } + return obj is ItemChange && Equals((ItemChange)obj); } @@ -137,7 +145,6 @@ public override int GetHashCode() /// public static bool operator ==(ItemChange left, ItemChange right) => left.Equals(right); - /// /// Determines whether the specified objects are not equal /// diff --git a/src/DynamicData/List/Linq/AddKeyEnumerator.cs b/src/DynamicData/List/Linq/AddKeyEnumerator.cs index ec2f1db3a..e4e77d88c 100644 --- a/src/DynamicData/List/Linq/AddKeyEnumerator.cs +++ b/src/DynamicData/List/Linq/AddKeyEnumerator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections; using System.Collections.Generic; @@ -38,6 +42,7 @@ public IEnumerator> GetEnumerator() break; } + case ListChangeReason.AddRange: { foreach (var item in change.Range) @@ -48,6 +53,7 @@ public IEnumerator> GetEnumerator() break; } + case ListChangeReason.Replace: { //replace is a remove and add @@ -61,6 +67,7 @@ public IEnumerator> GetEnumerator() break; } + case ListChangeReason.Remove: { var item = change.Item.Current; @@ -69,6 +76,7 @@ public IEnumerator> GetEnumerator() break; } + case ListChangeReason.Clear: case ListChangeReason.RemoveRange: { @@ -80,14 +88,16 @@ public IEnumerator> GetEnumerator() break; } + case ListChangeReason.Moved: { var key = _keySelector(change.Item.Current); yield return new Change(ChangeReason.Moved, key, change.Item.Current, change.Item.Previous, change.Item.CurrentIndex, change.Item.PreviousIndex); break; } + default: - throw new ArgumentOutOfRangeException(); + throw new IndexOutOfRangeException("The changes are not of a supported type."); } } } diff --git a/src/DynamicData/List/Linq/ItemChangeEnumerator.cs b/src/DynamicData/List/Linq/ItemChangeEnumerator.cs index 991b3baf7..927a0c397 100644 --- a/src/DynamicData/List/Linq/ItemChangeEnumerator.cs +++ b/src/DynamicData/List/Linq/ItemChangeEnumerator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections; using System.Collections.Generic; @@ -44,6 +48,7 @@ public IEnumerator> GetEnumerator() default: yield break; } + index++; lastKnownIndex = index; } diff --git a/src/DynamicData/List/Linq/ReverseEnumerator.cs b/src/DynamicData/List/Linq/ReverseEnumerator.cs index 014f99eba..592c2b277 100644 --- a/src/DynamicData/List/Linq/ReverseEnumerator.cs +++ b/src/DynamicData/List/Linq/ReverseEnumerator.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System.Collections.Generic; using System.Linq; namespace DynamicData.List.Linq @@ -19,6 +23,7 @@ public IEnumerable> Reverse(IChangeSet changes) _length++; break; } + case ListChangeReason.AddRange: { var offset = change.Range.Index == -1 ? 0 : _length - change.Range.Index; @@ -27,18 +32,21 @@ public IEnumerable> Reverse(IChangeSet changes) break; } + case ListChangeReason.Replace: { var newIndex = _length - change.Item.CurrentIndex - 1; yield return new Change(ListChangeReason.Replace, change.Item.Current, change.Item.Previous.Value, newIndex, newIndex); break; } + case ListChangeReason.Remove: { yield return new Change(ListChangeReason.Remove, change.Item.Current, _length - change.Item.CurrentIndex - 1); _length--; break; } + case ListChangeReason.RemoveRange: { var offset = _length - change.Range.Index - change.Range.Count; @@ -47,6 +55,7 @@ public IEnumerable> Reverse(IChangeSet changes) break; } + case ListChangeReason.Moved: { var currentIndex = _length - change.Item.CurrentIndex - 1; @@ -55,6 +64,7 @@ public IEnumerable> Reverse(IChangeSet changes) break; } + case ListChangeReason.Clear: { yield return new Change(ListChangeReason.Clear, change.Range.Reverse()); diff --git a/src/DynamicData/List/Linq/UnifiedChangeEnumerator.cs b/src/DynamicData/List/Linq/UnifiedChangeEnumerator.cs index 12d651e64..e299ce8d3 100644 --- a/src/DynamicData/List/Linq/UnifiedChangeEnumerator.cs +++ b/src/DynamicData/List/Linq/UnifiedChangeEnumerator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections; using System.Collections.Generic; using DynamicData.List.Internal; diff --git a/src/DynamicData/List/Linq/WithoutIndexEnumerator.cs b/src/DynamicData/List/Linq/WithoutIndexEnumerator.cs index bd7455c7a..5e9664e56 100644 --- a/src/DynamicData/List/Linq/WithoutIndexEnumerator.cs +++ b/src/DynamicData/List/Linq/WithoutIndexEnumerator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections; using System.Collections.Generic; diff --git a/src/DynamicData/List/ListEx.cs b/src/DynamicData/List/ListEx.cs index 442015e80..eb51518ce 100644 --- a/src/DynamicData/List/ListEx.cs +++ b/src/DynamicData/List/ListEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Linq; using DynamicData.Annotations; @@ -18,7 +22,9 @@ public static class ListEx internal static bool MovedWithinRange(this Change source, int startIndex, int endIndex) { if (source.Reason != ListChangeReason.Moved) + { return false; + } var current = source.Item.CurrentIndex; var previous = source.Item.PreviousIndex; @@ -40,8 +46,15 @@ internal static bool MovedWithinRange(this Change source, int startIndex, /// public static void Clone(this IList source, IChangeSet changes) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (changes == null) throw new ArgumentNullException(nameof(changes)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (changes == null) + { + throw new ArgumentNullException(nameof(changes)); + } foreach (var item in changes) { @@ -67,18 +80,22 @@ private static void Clone(this IList source, Change item) { source.Add(change.Current); } + break; } + case ListChangeReason.AddRange: { source.AddOrInsertRange(item.Range, item.Range.Index); break; } + case ListChangeReason.Clear: { source.ClearOrRemoveMany(item); break; } + case ListChangeReason.Replace: { var change = item.Item; @@ -107,11 +124,11 @@ private static void Clone(this IList source, Change item) source.Insert(change.CurrentIndex, change.Current); } - } break; } + case ListChangeReason.Refresh: { if (changeAware != null) @@ -127,6 +144,7 @@ private static void Clone(this IList source, Change item) break; } + case ListChangeReason.Remove: { var change = item.Item; @@ -142,6 +160,7 @@ private static void Clone(this IList source, Change item) break; } + case ListChangeReason.RemoveRange: { //ignore this case because WhereReasonsAre removes the index [in which case call RemoveMany] @@ -159,14 +178,17 @@ private static void Clone(this IList source, Change item) break; } + case ListChangeReason.Moved: { var change = item.Item; bool hasIndex = change.CurrentIndex >= 0; if (!hasIndex) - throw new UnspecifiedIndexException("Cannot move as an index was not specified"); + { + throw new UnspecifiedIndexException("Cannot move as an index was not specified"); + } - var extendedList = source as IExtendedList; + var extendedList = source as IExtendedList; var observableCollection = source as ObservableCollection; if (extendedList != null) { @@ -182,6 +204,7 @@ private static void Clone(this IList source, Change item) source.RemoveAt(change.PreviousIndex); source.Insert(change.CurrentIndex, change.Current); } + break; } } @@ -234,6 +257,11 @@ public static int BinarySearch(this IList list, TItem value) /// public static int BinarySearch(this IList list, TItem value, IComparer comparer) { + if (comparer == null) + { + throw new ArgumentNullException(nameof(comparer)); + } + return list.BinarySearch(value, comparer.Compare); } @@ -251,10 +279,14 @@ public static int BinarySearch(this IList list, TItem value, IComp public static int BinarySearch(this IList list, TSearch value, Func comparer) { if (list == null) + { throw new ArgumentNullException(nameof(list)); + } if (comparer == null) + { throw new ArgumentNullException(nameof(comparer)); + } int lower = 0; int upper = list.Count - 1; @@ -317,18 +349,30 @@ public static int IndexOf(this IEnumerable source, T item) /// public static int IndexOf(this IEnumerable source, T item, IEqualityComparer equalityComparer) { + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (equalityComparer == null) + { + throw new ArgumentNullException(nameof(equalityComparer)); + } + int i = 0; foreach (var candidate in source) { if (equalityComparer.Equals(item, candidate)) + { return i; + } i++; } + return -1; } - #endregion #region Amendment @@ -346,8 +390,15 @@ public static int IndexOf(this IEnumerable source, T item, IEqualityCompar /// public static void Add(this IList source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (items == null) throw new ArgumentNullException(nameof(items)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } items.ForEach(source.Add); } @@ -365,8 +416,15 @@ public static void Add(this IList source, IEnumerable items) /// public static void AddRange(this IList source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (items == null) throw new ArgumentNullException(nameof(items)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } if (source is List) { @@ -393,8 +451,15 @@ public static void AddRange(this IList source, IEnumerable items) /// public static void AddRange(this IList source, IEnumerable items, int index) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (items == null) throw new ArgumentNullException(nameof(items)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } if (source is List) { @@ -421,8 +486,15 @@ public static void AddRange(this IList source, IEnumerable items, int i /// public static void AddOrInsertRange(this IList source, IEnumerable items, int index) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (items == null) throw new ArgumentNullException(nameof(items)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } if (source is List) { @@ -475,8 +547,15 @@ public static void RemoveMany(this IList source, [NotNull] IEnumerable across the source collection IndexOf lookups can result in very slow updates (especially for subsequent operators) */ - if (source == null) throw new ArgumentNullException(nameof(source)); - if (itemsToRemove == null) throw new ArgumentNullException(nameof(itemsToRemove)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (itemsToRemove == null) + { + throw new ArgumentNullException(nameof(itemsToRemove)); + } var toRemoveArray = itemsToRemove.AsArray(); @@ -493,7 +572,7 @@ across the source collection IndexOf lookups can result in very slow updates if (hasDuplicates) { //Slow remove but safe - toRemoveArray.ForEach(t => source.Remove(t)); + toRemoveArray?.ForEach(t => source.Remove(t)); } else { @@ -513,7 +592,10 @@ across the source collection IndexOf lookups can result in very slow updates /// Cannot remove range private static void RemoveRange(this IList source, int index, int count) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } if (source is List) { @@ -542,8 +624,15 @@ private static void RemoveRange(this IList source, int index, int count) /// public static void Remove(this IList source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (items == null) throw new ArgumentNullException(nameof(items)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (items == null) + { + throw new ArgumentNullException(nameof(items)); + } items.ForEach(t => source.Remove(t)); } @@ -560,17 +649,30 @@ public static void Remove(this IList source, IEnumerable items) /// items public static void Replace(this IList source, [NotNull] T original, [NotNull] T replacewith) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (original == null) throw new ArgumentNullException(nameof(original)); - if (replacewith == null) throw new ArgumentNullException(nameof(replacewith)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (original == null) + { + throw new ArgumentNullException(nameof(original)); + } + + if (replacewith == null) + { + throw new ArgumentNullException(nameof(replacewith)); + } var index = source.IndexOf(original); if (index==-1) + { throw new ArgumentException("Cannot find index of original item. Either it does not exist in the list or the hashcode has mutated"); + } + source[index] = replacewith; } - /// /// Replaces the item if found, otherwise the item is added to the list /// @@ -582,9 +684,20 @@ public static void Replace(this IList source, [NotNull] T original, [NotNu /// public static void ReplaceOrAdd(this IList source, [NotNull] T original, [NotNull] T replacewith) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (original == null) throw new ArgumentNullException(nameof(original)); - if (replacewith == null) throw new ArgumentNullException(nameof(replacewith)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (original == null) + { + throw new ArgumentNullException(nameof(original)); + } + + if (replacewith == null) + { + throw new ArgumentNullException(nameof(replacewith)); + } var index = source.IndexOf(original); if (index == -1) @@ -610,16 +723,36 @@ public static void ReplaceOrAdd(this IList source, [NotNull] T original, [ /// items public static void Replace(this IList source, [NotNull] T original, [NotNull] T replaceWith, IEqualityComparer comparer) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (original == null) throw new ArgumentNullException(nameof(original)); - if (replaceWith == null) throw new ArgumentNullException(nameof(replaceWith)); - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (original == null) + { + throw new ArgumentNullException(nameof(original)); + } + + if (replaceWith == null) + { + throw new ArgumentNullException(nameof(replaceWith)); + } + + if (comparer == null) + { + throw new ArgumentNullException(nameof(comparer)); + } var index = source.IndexOf(original); if (index == -1) + { throw new ArgumentException("Cannot find index of original item. Either it does not exist in the list or the hashcode has mutated"); + } + if (comparer.Equals(source[index], replaceWith)) + { source[index] = replaceWith; + } } #endregion diff --git a/src/DynamicData/List/ObservableListEx.cs b/src/DynamicData/List/ObservableListEx.cs index afd0be6aa..bd0117b7c 100644 --- a/src/DynamicData/List/ObservableListEx.cs +++ b/src/DynamicData/List/ObservableListEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; @@ -40,7 +44,10 @@ public static class ObservableListEx public static IObservable> ToObservableChangeSet(this IObservable source, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return ToObservableChangeSet(source, null, -1, scheduler); } @@ -60,8 +67,15 @@ public static IObservable> ToObservableChangeSet(this IObservab Func expireAfter, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (expireAfter == null) throw new ArgumentNullException(nameof(expireAfter)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (expireAfter == null) + { + throw new ArgumentNullException(nameof(expireAfter)); + } return ToObservableChangeSet(source, expireAfter, -1, scheduler); } @@ -81,7 +95,11 @@ public static IObservable> ToObservableChangeSet(this IObservab int limitSizeTo, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return ToObservableChangeSet(source, null, limitSizeTo, scheduler); } @@ -102,7 +120,11 @@ public static IObservable> ToObservableChangeSet(this IObservab int limitSizeTo, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new ToObservableChangeSet(source, expireAfter, limitSizeTo, scheduler).Run(); } @@ -175,7 +197,11 @@ public static IObservable> ToObservableChangeSet(this IObservab int limitSizeTo, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new ToObservableChangeSet(source, expireAfter, limitSizeTo, scheduler).Run(); } @@ -197,12 +223,17 @@ public static IObservable> AutoRefresh(this IObserv IScheduler scheduler = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } return source.AutoRefreshOnObservable(t => { if (propertyChangeThrottle == null) + { return t.WhenAnyPropertyChanged(); + } return t.WhenAnyPropertyChanged() .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); @@ -226,13 +257,22 @@ public static IObservable> AutoRefresh(t IScheduler scheduler = null) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertyAccessor == null) + { + throw new ArgumentNullException(nameof(propertyAccessor)); + } return source.AutoRefreshOnObservable(t => { if (propertyChangeThrottle == null) + { return t.WhenPropertyChanged(propertyAccessor, false); + } return t.WhenPropertyChanged(propertyAccessor,false) .Throttle(propertyChangeThrottle.Value, scheduler ?? Scheduler.Default); @@ -253,12 +293,19 @@ public static IObservable> AutoRefreshOnObservable(source, reevaluator, changeSetBuffer, scheduler).Run(); } - /// /// Supress refresh notifications /// @@ -269,7 +316,6 @@ public static IObservable> SupressRefresh(this IObservable> SupressRefresh(this IObservable public static IObservable> RemoveIndex([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Select(changes => new ChangeSet(changes.YieldWithoutIndex())); } @@ -305,8 +355,16 @@ public static IObservable> RemoveIndex([NotNull] this IObservab public static IObservable> AddKey( [NotNull] this IObservable> source, [NotNull] Func keySelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } + return source.Select(changes => new ChangeSet(new AddKeyEnumerator(changes, keySelector))); } @@ -327,12 +385,18 @@ public static IObservable> Convert> source, [NotNull] Func conversionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); - return source.Select(changes => changes.Transform(conversionFactory)); - } + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + if (conversionFactory == null) + { + throw new ArgumentNullException(nameof(conversionFactory)); + } + return source.Select(changes => changes.Transform(conversionFactory)); + } /// /// Cast the underlying type of an object. Use before a Cast function @@ -359,7 +423,11 @@ public static IObservable> CastToObject(this IObservable public static IObservable> Cast([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Select(changes => changes.Transform(t=>(TDestination)t)); } @@ -378,8 +446,16 @@ public static IObservable> Cast([NotNull] public static IObservable> Cast([NotNull] this IObservable> source, [NotNull] Func conversionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (conversionFactory == null) + { + throw new ArgumentNullException(nameof(conversionFactory)); + } + return source.Select(changes => changes.Transform(conversionFactory)); } @@ -403,8 +479,15 @@ public static IObservable> Cast( public static IObservable> Bind([NotNull] this IObservable> source, [NotNull] IObservableCollection targetCollection, int resetThreshold = 25) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (targetCollection == null) throw new ArgumentNullException(nameof(targetCollection)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (targetCollection == null) + { + throw new ArgumentNullException(nameof(targetCollection)); + } var adaptor = new ObservableCollectionAdaptor(targetCollection, resetThreshold); return source.Adapt(adaptor); @@ -423,7 +506,10 @@ public static IObservable> Bind([NotNull] this IObservable> Bind([NotNull] this IObservable> source, out ReadOnlyObservableCollection readOnlyObservableCollection, int resetThreshold = 25) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } var target = new ObservableCollectionExtended(); var result = new ReadOnlyObservableCollection(target); @@ -449,15 +535,21 @@ public static IObservable> Bind([NotNull] this IObservable> Bind([NotNull] this IObservable> source, [NotNull] BindingList bindingList, int resetThreshold = 25) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (bindingList == null) throw new ArgumentNullException(nameof(bindingList)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (bindingList == null) + { + throw new ArgumentNullException(nameof(bindingList)); + } return source.Adapt(new BindingListAdaptor(bindingList, resetThreshold)); } #endif - /// /// Injects a side effect into a changeset observable /// @@ -473,9 +565,15 @@ public static IObservable> Bind([NotNull] this IObservable> Adapt([NotNull] this IObservable> source, [NotNull] IChangeSetAdaptor adaptor) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (adaptor == null) throw new ArgumentNullException(nameof(adaptor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + if (adaptor == null) + { + throw new ArgumentNullException(nameof(adaptor)); + } return Observable.Create>(observer => { @@ -512,8 +610,15 @@ public static IObservable> Adapt([NotNull] this IObservable([NotNull] this IObservable> source, [NotNull] ISourceList destination) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (destination == null) + { + throw new ArgumentNullException(nameof(destination)); + } return source.Subscribe(changes => destination.Edit(updater => updater.Clone(changes))); } @@ -527,7 +632,11 @@ public static IDisposable PopulateInto([NotNull] this IObservablesource public static IObservableList AsObservableList([NotNull] this ISourceList source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new AnonymousObservableList(source); } @@ -540,7 +649,11 @@ public static IObservableList AsObservableList([NotNull] this ISourceList< /// source public static IObservableList AsObservableList([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new AnonymousObservableList(source); } @@ -553,7 +666,11 @@ public static IObservableList AsObservableList([NotNull] this IObservable< /// public static IObservable> RefCount([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new RefCount(source).Run(); } @@ -571,8 +688,16 @@ public static IObservable> RefCount([NotNull] this IObservable< /// source public static IObservable> Filter(this IObservable> source, Func predicate) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } + return new Filter(source, predicate).Run(); } @@ -589,13 +714,19 @@ public static IObservable> Filter(this IObservable public static IObservable> Filter([NotNull] this IObservable> source, [NotNull] IObservable> predicate, ListFilterPolicy filterPolicy = ListFilterPolicy.CalculateDiff) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (predicate == null) throw new ArgumentNullException(nameof(predicate)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (predicate == null) + { + throw new ArgumentNullException(nameof(predicate)); + } return new Filter(source, predicate, filterPolicy).Run(); } - /// /// Filters source on the specified property using the specified predicate. /// @@ -618,9 +749,21 @@ public static IObservable> FilterOnProperty(source, propertySelector, predicate, propertyChangedThrottle, scheduler).Run(); } @@ -642,7 +785,11 @@ public static IObservable> FilterOnObservable(this TimeSpan? propertyChangedThrottle = null, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new FilterOnObservable(source, objectFilterObservable, propertyChangedThrottle, scheduler).Run(); } @@ -660,7 +807,11 @@ public static IObservable> FilterOnObservable(this public static IObservable> Reverse(this IObservable> source) { var reverser = new Reverser(); - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Select(changes => new ChangeSet(reverser.Reverse(changes))); } @@ -678,12 +829,20 @@ public static IObservable> Reverse(this IObservable - public static IObservable> Transform(this IObservable> source, + public static IObservable> Transform(this IObservable> source, Func transformFactory, bool transformOnRefresh = false) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + return source.Transform((t, previous, idx) => transformFactory(t), transformOnRefresh); } @@ -701,12 +860,19 @@ public static IObservable> Transform - public static IObservable> Transform(this IObservable> source, + public static IObservable> Transform(this IObservable> source, Func transformFactory, bool transformOnRefresh = false) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.Transform((t, previous, idx) => transformFactory(t,idx),transformOnRefresh); } @@ -731,8 +897,15 @@ public static IObservable> Transform, TDestination> transformFactory, bool transformOnRefresh = false) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return source.Transform((t, previous, idx) => transformFactory(t, previous), transformOnRefresh); } @@ -756,13 +929,19 @@ public static IObservable> Transform> Transform(this IObservable> source, Func, int, TDestination> transformFactory, bool transformOnRefresh = false) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return new Transformer(source, transformFactory, transformOnRefresh).Run(); } - /// /// Projects each update item to a new form using the specified transform function /// @@ -779,8 +958,15 @@ public static IObservable> Transform> TransformAsync( this IObservable> source, Func> transformFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } return new TransformAsync(source, transformFactory).Run(); } @@ -803,8 +989,16 @@ public static IObservable> TransformMany> manyselector, IEqualityComparer equalityComparer = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (manyselector == null) throw new ArgumentNullException(nameof(manyselector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (manyselector == null) + { + throw new ArgumentNullException(nameof(manyselector)); + } + return new TransformMany(source, manyselector, equalityComparer).Run(); } @@ -870,8 +1064,16 @@ public static IObservable> DistinctValues( this IObservable> source, Func valueSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (valueSelector == null) throw new ArgumentNullException(nameof(valueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (valueSelector == null) + { + throw new ArgumentNullException(nameof(valueSelector)); + } + return new Distinct(source, valueSelector).Run(); } @@ -894,8 +1096,16 @@ public static IObservable>> GroupOn> source, Func groupSelector, IObservable regrouper = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelector == null) throw new ArgumentNullException(nameof(groupSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (groupSelector == null) + { + throw new ArgumentNullException(nameof(groupSelector)); + } + return new GroupOn(source, groupSelector, regrouper).Run(); } @@ -920,13 +1130,19 @@ public static IObservable>> GroupOn groupSelectorKey, IObservable regrouper = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (groupSelectorKey == null) throw new ArgumentNullException(nameof(groupSelectorKey)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (groupSelectorKey == null) + { + throw new ArgumentNullException(nameof(groupSelectorKey)); + } return new GroupOnImmutable(source, groupSelectorKey, regrouper).Run(); } - /// /// Groups the source using the property specified by the property selector. The resulting groupings contains an inner observable list. /// Groups are re-applied when the property value changed. @@ -948,8 +1164,16 @@ public static IObservable>> GroupOnProperty(source, propertySelector, propertyChangedThrottle, scheduler).Run(); } @@ -974,8 +1198,16 @@ public static IObservable>> GroupOnProperty(source, propertySelector, propertyChangedThrottle, scheduler).Run(); } @@ -988,7 +1220,11 @@ public static IObservable>> GroupOnPropertysource public static IObservable> NotEmpty(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Where(s => s.Count != 0); } @@ -1002,7 +1238,11 @@ public static IObservable> NotEmpty(this IObservablesource public static IObservable> Clone(this IObservable> source, IList target) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Do(target.Clone); } @@ -1031,8 +1271,15 @@ public static IObservable> Sort(this IObservable> IObservable> comparerChanged = null, int resetThreshold = 50) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparer == null) throw new ArgumentNullException(nameof(comparer)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (comparer == null) + { + throw new ArgumentNullException(nameof(comparer)); + } return new Sort(source, comparer, options, resort, comparerChanged, resetThreshold).Run(); } @@ -1056,8 +1303,15 @@ public static IObservable> Sort(this IObservable> IObservable resort = null, int resetThreshold = 50) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (comparerChanged == null) throw new ArgumentNullException(nameof(comparerChanged)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (comparerChanged == null) + { + throw new ArgumentNullException(nameof(comparerChanged)); + } return new Sort(source, null, options, resort, comparerChanged, resetThreshold).Run(); } @@ -1079,8 +1333,16 @@ public static IObservable> ForEachChange( [NotNull] this IObservable> source, [NotNull] Action> action) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (action == null) throw new ArgumentNullException(nameof(action)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + return source.Do(changes => changes.ForEach(action)); } @@ -1099,8 +1361,16 @@ public static IObservable> ForEachItemChange( [NotNull] this IObservable> source, [NotNull] Action> action) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (action == null) throw new ArgumentNullException(nameof(action)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (action == null) + { + throw new ArgumentNullException(nameof(action)); + } + return source.Do(changes => changes.Flatten().ForEach(action)); } @@ -1120,8 +1390,15 @@ public static IObservable MergeMany( [NotNull] this IObservable> source, [NotNull] Func> observableSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (observableSelector == null) throw new ArgumentNullException(nameof(observableSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (observableSelector == null) + { + throw new ArgumentNullException(nameof(observableSelector)); + } return new MergeMany(source, observableSelector).Run(); } @@ -1143,8 +1420,15 @@ public static IObservable WhenValueChanged( bool notifyOnInitialValue = true) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (propertyAccessor == null) throw new ArgumentNullException(nameof(propertyAccessor)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (propertyAccessor == null) + { + throw new ArgumentNullException(nameof(propertyAccessor)); + } var factory = propertyAccessor.GetFactory(); return source.MergeMany(t => factory(t, notifyOnInitialValue).Select(pv=>pv.Value)); @@ -1167,8 +1451,15 @@ public static IObservable> WhenPropertyChanged factory(t, notifyOnInitialValue)); @@ -1186,7 +1477,11 @@ public static IObservable> WhenPropertyChanged WhenAnyPropertyChanged([NotNull] this IObservable> source, params string[] propertiesToMonitor) where TObject : INotifyPropertyChanged { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.MergeMany(t => t.WhenAnyPropertyChanged(propertiesToMonitor)); } @@ -1206,8 +1501,16 @@ public static IObservable WhenAnyPropertyChanged([NotNull] thi public static IObservable> SubscribeMany(this IObservable> source, Func subscriptionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (subscriptionFactory == null) + { + throw new ArgumentNullException(nameof(subscriptionFactory)); + } + return new SubscribeMany(source, subscriptionFactory).Run(); } @@ -1247,8 +1550,15 @@ public static IObservable> DisposeMany(this IObservable> OnItemRemoved(this IObservable> source, Action removeAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (removeAction == null) throw new ArgumentNullException(nameof(removeAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (removeAction == null) + { + throw new ArgumentNullException(nameof(removeAction)); + } return new OnBeingRemoved(source, removeAction).Run(); } @@ -1263,8 +1573,16 @@ public static IObservable> OnItemRemoved(this IObservable> OnItemAdded(this IObservable> source, Action addAction) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (addAction == null) throw new ArgumentNullException(nameof(addAction)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (addAction == null) + { + throw new ArgumentNullException(nameof(addAction)); + } + return new OnBeingAdded(source, addAction).Run(); } @@ -1283,10 +1601,17 @@ public static IObservable> OnItemAdded(this IObservable> WhereReasonsAre(this IObservable> source, params ListChangeReason[] reasons) { + if (reasons == null) + { + throw new ArgumentNullException(nameof(reasons)); + } + if (reasons.Length == 0) + { throw new ArgumentException("Must enter at least 1 reason", nameof(reasons)); + } - var matches = new HashSet(reasons); + var matches = new HashSet(reasons); return source.Select(changes => { var filtered = changes.Where(change => matches.Contains(change.Reason)).YieldWithoutIndex(); @@ -1305,8 +1630,15 @@ public static IObservable> WhereReasonsAre(this IObservable> WhereReasonsAreNot(this IObservable> source, params ListChangeReason[] reasons) { + if (reasons == null) + { + throw new ArgumentNullException(nameof(reasons)); + } + if (reasons.Length == 0) + { throw new ArgumentException("Must enter at least 1 reason", nameof(reasons)); + } var matches = new HashSet(reasons); return source.Select(updates => @@ -1384,8 +1716,16 @@ public static IObservable> BufferIf([NotNull] this IObservable< bool intialPauseState = false, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (pauseIfTrueSelector == null) throw new ArgumentNullException(nameof(pauseIfTrueSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (pauseIfTrueSelector == null) + { + throw new ArgumentNullException(nameof(pauseIfTrueSelector)); + } + return BufferIf(source, pauseIfTrueSelector, intialPauseState, null, scheduler); } @@ -1426,8 +1766,16 @@ public static IObservable> BufferIf(this IObservable(source, pauseIfTrueSelector, intialPauseState, timeOut, scheduler).Run(); } @@ -1448,8 +1796,15 @@ public static IObservable QueryWhenChanged( this IObservable> source, Func, TDestination> resultSelector) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (resultSelector == null) throw new ArgumentNullException(nameof(resultSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (resultSelector == null) + { + throw new ArgumentNullException(nameof(resultSelector)); + } return source.QueryWhenChanged().Select(resultSelector); } @@ -1463,7 +1818,11 @@ public static IObservable QueryWhenChanged( /// source public static IObservable> QueryWhenChanged([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new QueryWhenChanged(source).Run(); } @@ -1513,7 +1872,6 @@ public static IObservable> ToSortedCollection /// Defer the subscribtion until loaded and skip initial changeset /// @@ -1523,7 +1881,11 @@ public static IObservable> ToSortedCollectionsource public static IObservable> SkipInitial(this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.DeferUntilLoaded().Skip(1); } @@ -1535,7 +1897,11 @@ public static IObservable> SkipInitial(this IObservable public static IObservable> DeferUntilLoaded([NotNull] this IObservable> source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return new DeferUntilLoaded(source).Run(); } @@ -1547,7 +1913,11 @@ public static IObservable> DeferUntilLoaded([NotNull] this IObs /// public static IObservable> DeferUntilLoaded(this IObservableList source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + return source.Connect().DeferUntilLoaded(); } @@ -1565,8 +1935,16 @@ public static IObservable> DeferUntilLoaded(this IObservableLis public static IObservable> Virtualise([NotNull] this IObservable> source, [NotNull] IObservable requests) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (requests == null) throw new ArgumentNullException(nameof(requests)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (requests == null) + { + throw new ArgumentNullException(nameof(requests)); + } + return new Virtualiser(source, requests).Run(); } @@ -1580,15 +1958,20 @@ public static IObservable> Virtualise([NotNull] this IOb public static IObservable> Top([NotNull] this IObservable> source, int numberOfItems) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + if (numberOfItems <= 0) + { throw new ArgumentOutOfRangeException(nameof(numberOfItems), "Number of items should be greater than zero"); + } return source.Virtualise(Observable.Return(new VirtualRequest(0, numberOfItems))); } - /// /// Applies paging to the the data source /// @@ -1599,8 +1982,16 @@ public static IObservable> Top([NotNull] this IObservable> Page([NotNull] this IObservable> source, [NotNull] IObservable requests) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (requests == null) throw new ArgumentNullException(nameof(requests)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (requests == null) + { + throw new ArgumentNullException(nameof(requests)); + } + return new Pager(source, requests).Run(); } @@ -1623,8 +2014,15 @@ public static IObservable> Page([NotNull] this IObservable> LimitSizeTo([NotNull] this ISourceList source, int sizeLimit, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (sizeLimit <= 0) throw new ArgumentException("sizeLimit cannot be zero", nameof(sizeLimit)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (sizeLimit <= 0) + { + throw new ArgumentException("sizeLimit cannot be zero", nameof(sizeLimit)); + } var locker = new object(); var limiter = new LimitSizeTo(source, sizeLimit, scheduler ?? Scheduler.Default, locker); @@ -1662,8 +2060,15 @@ public static IObservable> ExpireAfter([NotNull] this ISourceL public static IObservable> ExpireAfter([NotNull] this ISourceList source, [NotNull] Func timeSelector, TimeSpan? pollingInterval = null, IScheduler scheduler = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (timeSelector == null) throw new ArgumentNullException(nameof(timeSelector)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (timeSelector == null) + { + throw new ArgumentNullException(nameof(timeSelector)); + } var locker = new object(); var limiter = new ExpireAfter(source, timeSelector, pollingInterval, scheduler ?? Scheduler.Default, @@ -1930,7 +2335,10 @@ private static IObservable> Combine( [NotNull] this ICollection>> sources, CombineOperator type) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return new Combiner(sources, type).Run(); } @@ -1939,9 +2347,20 @@ private static IObservable> Combine([NotNull] this IObservable< CombineOperator type, params IObservable>[] others) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (others == null) + { + throw new ArgumentNullException(nameof(others)); + } + if (others.Length == 0) + { throw new ArgumentException("Must be at least one item to combine with", nameof(others)); + } var items = source.EnumerateOne().Union(others).ToList(); return new Combiner(items, type).Run(); @@ -1950,7 +2369,10 @@ private static IObservable> Combine([NotNull] this IObservable< private static IObservable> Combine([NotNull] this IObservableList> sources, CombineOperator type) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return Observable.Create>(observer => { @@ -1963,7 +2385,10 @@ private static IObservable> Combine([NotNull] this IObservableL private static IObservable> Combine([NotNull] this IObservableList> sources, CombineOperator type) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } return Observable.Create>(observer => { @@ -1976,7 +2401,11 @@ private static IObservable> Combine([NotNull] this IObservableL private static IObservable> Combine( [NotNull] this IObservableList>> sources, CombineOperator type) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + return new DynamicCombiner(sources, type).Run(); } @@ -1999,7 +2428,11 @@ private static IObservable> Combine( /// is null. public static IObservable> Switch(this IObservable> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + return sources.Select(cache => cache.Connect()).Switch(); } @@ -2018,13 +2451,16 @@ public static IObservable> Switch(this IObservable is null. public static IObservable> Switch(this IObservable>> sources) { - if (sources == null) throw new ArgumentNullException(nameof(sources)); + if (sources == null) + { + throw new ArgumentNullException(nameof(sources)); + } + return new Switch(sources).Run(); } #endregion - #region Start with /// @@ -2035,8 +2471,6 @@ public static IObservable> StartWithEmpty(this IObservable.Empty); } - - #endregion } } diff --git a/src/DynamicData/List/PageChangeSet.cs b/src/DynamicData/List/PageChangeSet.cs index bc5f5c178..259a73280 100644 --- a/src/DynamicData/List/PageChangeSet.cs +++ b/src/DynamicData/List/PageChangeSet.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections; using System.Collections.Generic; @@ -21,7 +25,6 @@ public PageChangeSet(IChangeSet virtualChangeSet, IPageResponse response) #region Delegating members - int IChangeSet.Adds => _virtualChangeSet.Adds; int IChangeSet.Removes => _virtualChangeSet.Removes; @@ -40,7 +43,6 @@ int IChangeSet.Capacity int IChangeSet.TotalChanges => _virtualChangeSet.TotalChanges; - public int Refreshes => _virtualChangeSet.Refreshes; #endregion diff --git a/src/DynamicData/List/RangeChange.cs b/src/DynamicData/List/RangeChange.cs index e22b7613b..f2c8066c4 100644 --- a/src/DynamicData/List/RangeChange.cs +++ b/src/DynamicData/List/RangeChange.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Collections; using System.Collections.Generic; using DynamicData.Kernel; diff --git a/src/DynamicData/List/SortException.cs b/src/DynamicData/List/SortException.cs index 2834883a3..934a6a355 100644 --- a/src/DynamicData/List/SortException.cs +++ b/src/DynamicData/List/SortException.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; // ReSharper disable once CheckNamespace @@ -6,6 +10,7 @@ namespace DynamicData /// /// Thrown when an exception occurs within the sort operators /// + [Serializable] public class SortException : Exception { /// @@ -16,5 +21,19 @@ public SortException(string message) : base(message) { } + + public SortException() + { + } + + public SortException(string message, Exception innerException) + : base(message, innerException) + { + } + + protected SortException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } } } diff --git a/src/DynamicData/List/SortOptions.cs b/src/DynamicData/List/SortOptions.cs index ad58f460a..fe59fe359 100644 --- a/src/DynamicData/List/SortOptions.cs +++ b/src/DynamicData/List/SortOptions.cs @@ -1,10 +1,14 @@  // ReSharper disable once CheckNamespace + +using System.Diagnostics.CodeAnalysis; + namespace DynamicData { /// /// Options for sorting /// + [SuppressMessage("Design", "CA1717: Only flags should have plural names", Justification = "Backwards compatibility")] public enum SortOptions { /// diff --git a/src/DynamicData/List/SourceList.cs b/src/DynamicData/List/SourceList.cs index a7f6c0c54..ecdfd00f5 100644 --- a/src/DynamicData/List/SourceList.cs +++ b/src/DynamicData/List/SourceList.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -55,7 +59,10 @@ private IDisposable LoadFromSource(IObservable> source) /// public void Edit([NotNull] Action> updateAction) { - if (updateAction == null) throw new ArgumentNullException(nameof(updateAction)); + if (updateAction == null) + { + throw new ArgumentNullException(nameof(updateAction)); + } lock (_writeLock) { @@ -78,7 +85,7 @@ public void Edit([NotNull] Action> updateAction) { _readerWriter.WriteNested(updateAction); } - + _editLevel--; if (_editLevel == 0) @@ -90,7 +97,10 @@ public void Edit([NotNull] Action> updateAction) private void InvokeNextPreview(IChangeSet changes) { - if (changes.Count == 0) return; + if (changes.Count == 0) + { + return; + } lock (_locker) { @@ -100,18 +110,22 @@ private void InvokeNextPreview(IChangeSet changes) private void InvokeNext(IChangeSet changes) { - if (changes.Count == 0) return; + if (changes.Count == 0) + { + return; + } lock (_locker) { _changes.OnNext(changes); if (_countChanged.IsValueCreated) + { _countChanged.Value.OnNext(_readerWriter.Count); + } } } - private void OnCompleted() { lock (_locker) @@ -147,7 +161,11 @@ public IObservable> Connect(Func predicate = null) lock (_locker) { var initial = new ChangeSet(new[] {new Change(ListChangeReason.AddRange, _readerWriter.Items)}); - if (initial.TotalChanges > 0) observer.OnNext(initial); + if (initial.TotalChanges > 0) + { + observer.OnNext(initial); + } + var source = _changes.Finally(observer.OnCompleted); return source.SubscribeSafe(observer); @@ -155,7 +173,9 @@ public IObservable> Connect(Func predicate = null) }); if (predicate != null) + { observable = new FilterStatic(observable, predicate).Run(); + } return observable; } @@ -166,7 +186,9 @@ public IObservable> Preview(Func predicate = null) IObservable> observable = _changesPreview; if (predicate != null) + { observable = new FilterStatic(observable, predicate).Run(); + } return observable; } @@ -175,6 +197,7 @@ public IObservable> Preview(Func predicate = null) public void Dispose() { _cleanUp.Dispose(); + _changesPreview?.Dispose(); } } } diff --git a/src/DynamicData/List/SourceListEditConvenienceEx.cs b/src/DynamicData/List/SourceListEditConvenienceEx.cs index 9dd1d4fcf..7e276e214 100644 --- a/src/DynamicData/List/SourceListEditConvenienceEx.cs +++ b/src/DynamicData/List/SourceListEditConvenienceEx.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using DynamicData.Annotations; @@ -24,8 +28,16 @@ public static void EditDiff([NotNull] this ISourceList source, [NotNull] IEnumerable allItems, IEqualityComparer equalityComparer = null) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (allItems == null) throw new ArgumentNullException(nameof(allItems)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (allItems == null) + { + throw new ArgumentNullException(nameof(allItems)); + } + var editDiff = new EditDiff(source, equalityComparer); editDiff.Edit(allItems); } @@ -37,7 +49,11 @@ public static void EditDiff([NotNull] this ISourceList source, /// The source. public static void Clear([NotNull] this ISourceList source) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.Clear()); } @@ -49,7 +65,11 @@ public static void Clear([NotNull] this ISourceList source) /// The item. public static void Add([NotNull] this ISourceList source, T item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.Add(item)); } @@ -62,7 +82,11 @@ public static void Add([NotNull] this ISourceList source, T item) /// The index. public static void Insert([NotNull] this ISourceList source, int index, T item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.Insert(index, item)); } @@ -74,7 +98,11 @@ public static void Insert([NotNull] this ISourceList source, int index, T /// The items. public static void AddRange([NotNull] this ISourceList source, IEnumerable items) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.AddRange(items)); } @@ -87,7 +115,11 @@ public static void AddRange([NotNull] this ISourceList source, IEnumerable /// The zero-based index at which the new elements should be inserted. public static void InsertRange([NotNull] this ISourceList source, IEnumerable items, int index) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.AddRange(items, index)); } @@ -100,7 +132,11 @@ public static void InsertRange([NotNull] this ISourceList source, IEnumera public static bool Remove([NotNull] this ISourceList source, T item) { bool removed = false; - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => removed = list.Remove(item)); return removed; } @@ -114,7 +150,11 @@ public static bool Remove([NotNull] this ISourceList source, T item) /// public static void RemoveMany([NotNull] this ISourceList source, IEnumerable itemsToRemove) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.RemoveMany(itemsToRemove)); } @@ -126,7 +166,11 @@ public static void RemoveMany([NotNull] this ISourceList source, IEnumerab /// The destination. public static void Move([NotNull] this ISourceList source, int original, int destination) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.Move(original, destination)); } @@ -141,7 +185,11 @@ public static void Move([NotNull] this ISourceList source, int original, i /// and do not denote a valid range of elements in the . public static void RemoveRange([NotNull] this ISourceList source, int index, int count) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.RemoveRange(index, count)); } @@ -153,7 +201,11 @@ public static void RemoveRange([NotNull] this ISourceList source, int inde /// The index. public static void RemoveAt([NotNull] this ISourceList source, int index) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.RemoveAt(index)); } @@ -166,7 +218,11 @@ public static void RemoveAt([NotNull] this ISourceList source, int index) /// The destination. public static void Replace([NotNull] this ISourceList source, T original, T destination) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list.Replace(original, destination)); } @@ -179,7 +235,11 @@ public static void Replace([NotNull] this ISourceList source, T original, /// The item. public static void ReplaceAt([NotNull] this ISourceList source, int index, T item) { - if (source == null) throw new ArgumentNullException(nameof(source)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + source.Edit(list => list[index] = item); } } diff --git a/src/DynamicData/List/SourceListEx.cs b/src/DynamicData/List/SourceListEx.cs index 838ba8e28..550e992a2 100644 --- a/src/DynamicData/List/SourceListEx.cs +++ b/src/DynamicData/List/SourceListEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using DynamicData.Annotations; @@ -25,8 +29,16 @@ public static class SourceListEx public static IObservable> Cast([NotNull] this ISourceList source, [NotNull] Func conversionFactory) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (conversionFactory == null) throw new ArgumentNullException(nameof(conversionFactory)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (conversionFactory == null) + { + throw new ArgumentNullException(nameof(conversionFactory)); + } + return source.Connect().Cast(conversionFactory); } } diff --git a/src/DynamicData/List/Tests/ChangeSetAggregator.cs b/src/DynamicData/List/Tests/ChangeSetAggregator.cs index 65d33d058..4fea26a4d 100644 --- a/src/DynamicData/List/Tests/ChangeSetAggregator.cs +++ b/src/DynamicData/List/Tests/ChangeSetAggregator.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections.Generic; using System.Reactive.Disposables; @@ -16,6 +20,7 @@ public class ChangeSetAggregator : IDisposable private readonly IDisposable _disposer; private readonly IList> _messages = new List>(); private Exception _error; + private bool _isDisposed; /// /// Initializes a new instance of the class. @@ -29,7 +34,7 @@ public ChangeSetAggregator(IObservable> source) var results = published.Subscribe(updates => _messages.Add(updates), ex => _error = ex); var connected = published.Connect(); - + _disposer = Disposable.Create(() => { Data.Dispose(); @@ -48,11 +53,28 @@ public ChangeSetAggregator(IObservable> source) /// public IList> Messages => _messages; - - /// + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// public void Dispose() { - _disposer.Dispose(); + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool isDisposing) + { + if (_isDisposed) + { + return; + } + + _isDisposed = true; + + if (isDisposing) + { + _disposer?.Dispose(); + } } } } diff --git a/src/DynamicData/List/Tests/TestEx.cs b/src/DynamicData/List/Tests/TestEx.cs index 63db46981..42435d7dc 100644 --- a/src/DynamicData/List/Tests/TestEx.cs +++ b/src/DynamicData/List/Tests/TestEx.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; // ReSharper disable once CheckNamespace namespace DynamicData.Tests diff --git a/src/DynamicData/List/UnspecifiedIndexException.cs b/src/DynamicData/List/UnspecifiedIndexException.cs index 5c56343a5..c2b1e2253 100644 --- a/src/DynamicData/List/UnspecifiedIndexException.cs +++ b/src/DynamicData/List/UnspecifiedIndexException.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; // ReSharper disable once CheckNamespace @@ -6,6 +10,7 @@ namespace DynamicData /// /// Thrown when an index is expected but not specified /// + [Serializable] public class UnspecifiedIndexException : Exception { /// @@ -33,5 +38,10 @@ public UnspecifiedIndexException(string message, Exception innerException) : base(message, innerException) { } + + protected UnspecifiedIndexException(System.Runtime.Serialization.SerializationInfo serializationInfo, System.Runtime.Serialization.StreamingContext streamingContext) + : base(serializationInfo, streamingContext) + { + } } } \ No newline at end of file diff --git a/src/DynamicData/List/VirtualChangeSet.cs b/src/DynamicData/List/VirtualChangeSet.cs index 59b003bcc..26361755c 100644 --- a/src/DynamicData/List/VirtualChangeSet.cs +++ b/src/DynamicData/List/VirtualChangeSet.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System; using System.Collections; using System.Collections.Generic; @@ -20,7 +24,6 @@ public VirtualChangeSet(IChangeSet virtualChangeSet, IVirtualResponse respons #region Delegating members - int IChangeSet.Adds => _virtualChangeSet.Adds; int IChangeSet.Removes => _virtualChangeSet.Removes; @@ -39,7 +42,6 @@ int IChangeSet.Capacity int IChangeSet.TotalChanges => _virtualChangeSet.TotalChanges; - public int Refreshes => _virtualChangeSet.Refreshes; #endregion diff --git a/src/DynamicData/ObservableChangeSet.cs b/src/DynamicData/ObservableChangeSet.cs index 56b4dd3d9..4c19857ef 100644 --- a/src/DynamicData/ObservableChangeSet.cs +++ b/src/DynamicData/ObservableChangeSet.cs @@ -1,4 +1,8 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +using System; using System.Reactive.Disposables; using System.Reactive.Linq; using System.Threading; @@ -24,8 +28,16 @@ public static class ObservableChangeSet /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Action> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } + return Create(cache => { var action = subscribe(cache); @@ -43,8 +55,15 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, IDisposable> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return Observable.Create>(observer => { @@ -62,7 +81,7 @@ public static IObservable> Create(Func< return new CompositeDisposable(disposable, Disposable.Create(observer.OnCompleted), - cache.Connect().SubscribeSafe(observer), + cache.Connect().SubscribeSafe(observer), cache); }); } @@ -77,10 +96,17 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Task> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } - return Create(async (list, ct) => await subscribe(list), keySelector); + return Create(async (list, _) => await subscribe(list).ConfigureAwait(false), keySelector); } /// @@ -93,8 +119,15 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return Observable.Create>(async (observer, ct) => { @@ -103,16 +136,16 @@ public static IObservable> Create(Func< try { - disposable.Disposable = await subscribe(cache, ct); + disposable.Disposable = await subscribe(cache, ct).ConfigureAwait(false); } catch (Exception e) { observer.OnError(e); } - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, - disposable, + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + cache, + disposable, Disposable.Create(observer.OnCompleted)); }); } @@ -127,12 +160,18 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Task> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } - return Create(async (list, ct) => await subscribe(list),keySelector); - } + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } + return Create((list, ct) => subscribe(list), keySelector); + } /// /// Creates an observable cache from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. @@ -144,8 +183,15 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return Observable.Create>(async (observer, ct) => { @@ -154,14 +200,14 @@ public static IObservable> Create(Func< try { - disposeAction = await subscribe(cache, ct); + disposeAction = await subscribe(cache, ct).ConfigureAwait(false); } catch (Exception e) { observer.OnError(e); } - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), cache, Disposable.Create(() => { observer.OnCompleted(); @@ -180,8 +226,15 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Task> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return Observable.Create>(async observer => { @@ -189,7 +242,7 @@ public static IObservable> Create(Func< try { - await subscribe(cache); + await subscribe(cache).ConfigureAwait(false); } catch (Exception e) { @@ -197,7 +250,7 @@ public static IObservable> Create(Func< } return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, + cache, Disposable.Create(observer.OnCompleted)); }); } @@ -212,8 +265,15 @@ public static IObservable> Create(Func< /// The observable cache with the specified implementation for the Subscribe method. public static IObservable> Create(Func, CancellationToken, Task> subscribe, Func keySelector) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + if (keySelector == null) + { + throw new ArgumentNullException(nameof(keySelector)); + } return Observable.Create>(async (observer, ct) => { @@ -221,20 +281,19 @@ public static IObservable> Create(Func< try { - await subscribe(cache, ct); + await subscribe(cache, ct).ConfigureAwait(false); } catch (Exception e) { observer.OnError(e); } - return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), - cache, + return new CompositeDisposable(cache.Connect().SubscribeSafe(observer), + cache, Disposable.Create(observer.OnCompleted)); }); } - #endregion #region List Create Methods @@ -247,7 +306,10 @@ public static IObservable> Create(Func< /// The observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Action> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } return Create(list => { @@ -264,7 +326,10 @@ public static IObservable> Create(Func, Action> /// The observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, IDisposable> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } return Observable.Create>(observer => { @@ -296,8 +361,12 @@ public static IObservable> Create(Func, IDisposa /// The observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Task> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - return Create(async (list, ct) => await subscribe(list)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + + return Create((list, ct) => subscribe(list)); } /// @@ -308,7 +377,10 @@ public static IObservable> Create(Func, TaskThe observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, CancellationToken, Task> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } return Observable.Create>(async (observer, ct) => { @@ -318,7 +390,7 @@ public static IObservable> Create(Func, Cancella try { - disposeAction = await subscribe(list, ct); + disposeAction = await subscribe(list, ct).ConfigureAwait(false); } catch (Exception e) { @@ -341,10 +413,13 @@ public static IObservable> Create(Func, Cancella /// The observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Task> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); - return Create(async (list, ct) => await subscribe(list)); - } + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } + return Create(async (list, ct) => await subscribe(list).ConfigureAwait(false)); + } /// /// Creates an observable list from a specified cancellable asynchronous Subscribe method. The CancellationToken passed to the asynchronous Subscribe method is tied to the returned disposable subscription, allowing best-effort cancellation. @@ -354,7 +429,10 @@ public static IObservable> Create(Func, TaskThe observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, CancellationToken, Task> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } return Observable.Create>(async (observer, ct) => { @@ -363,7 +441,7 @@ public static IObservable> Create(Func, Cancella try { - disposeAction = await subscribe(list, ct); + disposeAction = await subscribe(list, ct).ConfigureAwait(false); } catch (Exception e) { @@ -386,7 +464,10 @@ public static IObservable> Create(Func, Cancella /// The observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, Task> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } return Observable.Create>(async observer => { @@ -394,7 +475,7 @@ public static IObservable> Create(Func, Task> su try { - await subscribe(list); + await subscribe(list).ConfigureAwait(false); } catch (Exception e) { @@ -413,7 +494,10 @@ public static IObservable> Create(Func, Task> su /// The observable list with the specified implementation for the Subscribe method. public static IObservable> Create(Func, CancellationToken, Task> subscribe) { - if (subscribe == null) throw new ArgumentNullException(nameof(subscribe)); + if (subscribe == null) + { + throw new ArgumentNullException(nameof(subscribe)); + } return Observable.Create>(async (observer, ct) => { @@ -421,7 +505,7 @@ public static IObservable> Create(Func, Cancella try { - await subscribe(list,ct); + await subscribe(list,ct).ConfigureAwait(false); } catch (Exception e) { @@ -432,7 +516,6 @@ public static IObservable> Create(Func, Cancella }); } - #endregion } } \ No newline at end of file diff --git a/src/DynamicData/ObsoleteEx.cs b/src/DynamicData/ObsoleteEx.cs index 31579d9a7..eee6b93b7 100644 --- a/src/DynamicData/ObsoleteEx.cs +++ b/src/DynamicData/ObsoleteEx.cs @@ -1,4 +1,8 @@ -namespace DynamicData +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +namespace DynamicData { internal static class Constants { diff --git a/src/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs b/src/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs index a86e8bd1e..bf3c8a77a 100644 --- a/src/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs +++ b/src/DynamicData/Platforms/net45/PLinqFilteredUpdater.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + #if P_LINQ using System; @@ -61,13 +65,11 @@ protected override IEnumerable GetChangesWithFilter(IChangeSet return updates.Parallelise(_parallelisationOptions) .Select(u => new UpdateWithFilter(Filter(u.Current), u)).ToArray(); } + return updates.Select(u => new UpdateWithFilter(Filter(u.Current), u)).ToArray(); } } } - - - } #endif diff --git a/src/DynamicData/Platforms/net45/PSubscribeMany.cs b/src/DynamicData/Platforms/net45/PSubscribeMany.cs index 07af6993c..4d1e0f2ad 100644 --- a/src/DynamicData/Platforms/net45/PSubscribeMany.cs +++ b/src/DynamicData/Platforms/net45/PSubscribeMany.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + #if P_LINQ using System; @@ -12,7 +16,7 @@ internal class PSubscribeMany private readonly ParallelisationOptions _parallelisationOptions; private readonly IObservable> _source; private readonly Func _subscriptionFactory; - + public PSubscribeMany(IObservable> source, Func subscriptionFactory, ParallelisationOptions parallelisationOptions) { diff --git a/src/DynamicData/Platforms/net45/PTransform.cs b/src/DynamicData/Platforms/net45/PTransform.cs index 968af59f7..835121013 100644 --- a/src/DynamicData/Platforms/net45/PTransform.cs +++ b/src/DynamicData/Platforms/net45/PTransform.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + #if P_LINQ using System; @@ -41,7 +45,6 @@ private IChangeSet DoTransform(ChangeAwareCache change) var destination = _transformFactory(change.Current, change.Previous, change.Key); return new TransformResult(change, destination); } + return new TransformResult(change); } catch (Exception ex) { //only handle errors if a handler has been specified if (_exceptionCallback != null) + { return new TransformResult(change, ex); + } + throw; } } @@ -117,7 +124,6 @@ public TransformResult(Change change, TDestination destination) Key = change.Key; } - public TransformResult(Change change) : this() { diff --git a/src/DynamicData/Platforms/net45/ParallelEx.cs b/src/DynamicData/Platforms/net45/ParallelEx.cs index 1fd95ca08..c7ad428e3 100644 --- a/src/DynamicData/Platforms/net45/ParallelEx.cs +++ b/src/DynamicData/Platforms/net45/ParallelEx.cs @@ -1,10 +1,13 @@ -#if P_LINQ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. +#if P_LINQ using System; using System.Collections.Generic; using System.Linq; - + // ReSharper disable once CheckNamespace namespace DynamicData.PLinq { @@ -64,6 +67,7 @@ internal static IEnumerable Parallelise(this IEnumerable source, Parall { return parallelise.AsParallel(); } + return parallelise; } @@ -74,8 +78,10 @@ internal static IEnumerable Parallelise(this IEnumerable source, Parall { return parallelise.AsParallel().AsOrdered(); } + return parallelise; } + default: return source; } diff --git a/src/DynamicData/Platforms/net45/ParallelOperators.cs b/src/DynamicData/Platforms/net45/ParallelOperators.cs index ecf6bc201..aa16dcdf8 100644 --- a/src/DynamicData/Platforms/net45/ParallelOperators.cs +++ b/src/DynamicData/Platforms/net45/ParallelOperators.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + #if P_LINQ using System; @@ -33,9 +37,20 @@ public static IObservable> SubscribeMany subscriptionFactory, ParallelisationOptions parallelisationOptions) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); - if (parallelisationOptions == null) throw new ArgumentNullException(nameof(parallelisationOptions)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (subscriptionFactory == null) + { + throw new ArgumentNullException(nameof(subscriptionFactory)); + } + + if (parallelisationOptions == null) + { + throw new ArgumentNullException(nameof(parallelisationOptions)); + } return new PSubscribeMany(source,(t,v)=> subscriptionFactory(t),parallelisationOptions).Run(); } @@ -58,9 +73,20 @@ public static IObservable> SubscribeMany> SubscribeMany(this IObservable> source, Func subscriptionFactory, ParallelisationOptions parallelisationOptions) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (subscriptionFactory == null) throw new ArgumentNullException(nameof(subscriptionFactory)); - if (parallelisationOptions == null) throw new ArgumentNullException(nameof(parallelisationOptions)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (subscriptionFactory == null) + { + throw new ArgumentNullException(nameof(subscriptionFactory)); + } + + if (parallelisationOptions == null) + { + throw new ArgumentNullException(nameof(parallelisationOptions)); + } return new PSubscribeMany(source, subscriptionFactory, parallelisationOptions).Run(); } @@ -88,9 +114,20 @@ public static IObservable> Transform transformFactory, ParallelisationOptions parallelisationOptions) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (parallelisationOptions == null) throw new ArgumentNullException(nameof(parallelisationOptions)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (parallelisationOptions == null) + { + throw new ArgumentNullException(nameof(parallelisationOptions)); + } return new PTransform(source, (t, p, k) => transformFactory(t, k), parallelisationOptions).Run(); } @@ -141,10 +178,25 @@ public static IObservable> TransformSafe> errorHandler, ParallelisationOptions parallelisationOptions) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (transformFactory == null) throw new ArgumentNullException(nameof(transformFactory)); - if (errorHandler == null) throw new ArgumentNullException(nameof(errorHandler)); - if (parallelisationOptions == null) throw new ArgumentNullException(nameof(parallelisationOptions)); + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (transformFactory == null) + { + throw new ArgumentNullException(nameof(transformFactory)); + } + + if (errorHandler == null) + { + throw new ArgumentNullException(nameof(errorHandler)); + } + + if (parallelisationOptions == null) + { + throw new ArgumentNullException(nameof(parallelisationOptions)); + } return new PTransform(source, (t, p, k) => transformFactory(t), parallelisationOptions, errorHandler).Run(); } @@ -192,8 +244,15 @@ public static IObservable> TransformSafesource public static IObservable> Filter(this IObservable> source, Func filter, ParallelisationOptions parallelisationOptions) { - if (source == null) throw new ArgumentNullException(nameof(source)); - if (filter == null) return source; + if (source == null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (filter == null) + { + return source; + } return new PFilter(source, filter, parallelisationOptions).Run(); } diff --git a/src/DynamicData/Platforms/net45/ParallelisationOptions.cs b/src/DynamicData/Platforms/net45/ParallelisationOptions.cs index caef2c986..14b55d094 100644 --- a/src/DynamicData/Platforms/net45/ParallelisationOptions.cs +++ b/src/DynamicData/Platforms/net45/ParallelisationOptions.cs @@ -1,3 +1,7 @@ +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + using System.Diagnostics.CodeAnalysis; #if P_LINQ @@ -27,6 +31,7 @@ public ParallelisationOptions(ParallelType type = ParallelType.None, int thresho { Type = type; Threshold = threshold; + MaxDegreeOfParallisation = maxDegreeOfParallisation; } /// @@ -37,7 +42,12 @@ public ParallelisationOptions(ParallelType type = ParallelType.None, int thresho /// /// Gets the threshold. /// - public int Threshold { get; } = 0; + public int Threshold { get; } + + /// + /// Gets the maximum degree of parallisation. + /// + public int MaxDegreeOfParallisation { get; } } } #endif diff --git a/src/DynamicData/Properties/Annotations.cs b/src/DynamicData/Properties/Annotations.cs index 19d3ff734..82ec7f511 100644 --- a/src/DynamicData/Properties/Annotations.cs +++ b/src/DynamicData/Properties/Annotations.cs @@ -1,4 +1,9 @@ -using System; +// Copyright (c) 2011-2019 Roland Pheasant. All rights reserved. +// Roland Pheasant licenses this file to you under the MIT license. +// See the LICENSE file in the project root for full license information. + +// +using System; using System.Diagnostics; #pragma warning disable 1591 diff --git a/src/analyzers.ruleset b/src/analyzers.ruleset new file mode 100644 index 000000000..23cb2aa88 --- /dev/null +++ b/src/analyzers.ruleset @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/global.json b/src/global.json index 1e4edb34a..12fd38086 100644 --- a/src/global.json +++ b/src/global.json @@ -1,5 +1,8 @@ { + "sdk": { + "version": "3.0.100-preview" + }, "msbuild-sdks": { - "MSBuild.Sdk.Extras": "1.6.68" + "MSBuild.Sdk.Extras": "2.0.29" } } diff --git a/version.json b/version.json new file mode 100644 index 000000000..a9c3a867f --- /dev/null +++ b/version.json @@ -0,0 +1,17 @@ +{ + "version": "6.12", + "publicReleaseRefSpec": [ + "^refs/heads/master$", // we release out of master + "^refs/heads/preview/.*", // we release previews + "^refs/heads/rel/\\d+\\.\\d+\\.\\d+" // we also release branches starting with rel/N.N.N + ], + "nugetPackageVersion":{ + "semVer": 2 + }, + "cloudBuild": { + "setVersionVariables": true, + "buildNumber": { + "enabled": false + } + } +}