This repository was archived by the owner on Nov 6, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 82
Implement a CombinedFileProvider #142
Closed
Closed
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
ad7154e
Add CombinedFileProvider
mgrosperrin d1b8237
Add tests for the combined file provider (GetFileInfo and GetDirector…
mgrosperrin e58209c
Add tests for the Watch method
mgrosperrin 7de02de
Add documentation to explicit how the combinaison is done.
mgrosperrin 0bfeaaa
Replace IEnumerable by more concrete type
mgrosperrin 1043d92
Rewrite the CombinedDirectoryContents to be lazy in the enumeration o…
mgrosperrin 1b8b8b1
Un-nest internal files
mgrosperrin 6cd24d2
Deferring evaluation of files when combining mutiple directories (ins…
mgrosperrin 093fc42
Rename CombiendFileProvider to CompositeFileProvider
mgrosperrin 3576eac
Make CompositeXXX types public
mgrosperrin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
95 changes: 95 additions & 0 deletions
95
src/Microsoft.AspNet.FileProviders.Composite/CompositeDirectoryContents.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
|
||
namespace Microsoft.AspNet.FileProviders | ||
{ | ||
/// <summary> | ||
/// Represents the result of a call composition of <see cref="IFileProvider.GetDirectoryContents(string)"/> for a list of <see cref="IFileProvider"/> and a path. | ||
/// </summary> | ||
public class CompositeDirectoryContents : IDirectoryContents | ||
{ | ||
private readonly IList<IFileProvider> _fileProviders; | ||
private readonly string _subPath; | ||
private List<IFileInfo> _files; | ||
private bool _exists; | ||
private List<IDirectoryContents> _directories; | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="CompositeDirectoryContents"/> to represents the result of a call composition of <see cref="IFileProvider.GetDirectoryContents(string)"/>. | ||
/// </summary> | ||
/// <param name="fileProviders">The list of <see cref="IFileProvider"/> for which the results have to be composed.</param> | ||
/// <param name="subpath">The path.</param> | ||
public CompositeDirectoryContents(IList<IFileProvider> fileProviders, string subpath) | ||
{ | ||
if(fileProviders == null) | ||
{ | ||
throw new ArgumentNullException(nameof(fileProviders)); | ||
} | ||
_fileProviders = fileProviders; | ||
_subPath = subpath; | ||
} | ||
|
||
private void EnsureDirectoriesAreInitialized() | ||
{ | ||
if (_directories == null) | ||
{ | ||
_directories = new List<IDirectoryContents>(); | ||
foreach (var fileProvider in _fileProviders) | ||
{ | ||
var directoryContents = fileProvider.GetDirectoryContents(_subPath); | ||
if (directoryContents != null && directoryContents.Exists) | ||
{ | ||
_exists = true; | ||
_directories.Add(directoryContents); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void EnsureFilesAreInitialized() | ||
{ | ||
EnsureDirectoriesAreInitialized(); | ||
if (_files == null) | ||
{ | ||
_files = new List<IFileInfo>(); | ||
var names = new HashSet<string>(); | ||
for (var i = 0; i < _directories.Count; i++) | ||
{ | ||
var directoryContents = _directories[i]; | ||
foreach (var file in directoryContents) | ||
{ | ||
if (names.Add(file.Name)) | ||
{ | ||
_files.Add(file); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
public IEnumerator<IFileInfo> GetEnumerator() | ||
{ | ||
EnsureFilesAreInitialized(); | ||
return _files.GetEnumerator(); | ||
} | ||
|
||
IEnumerator IEnumerable.GetEnumerator() | ||
{ | ||
EnsureFilesAreInitialized(); | ||
return _files.GetEnumerator(); | ||
} | ||
|
||
public bool Exists | ||
{ | ||
get | ||
{ | ||
EnsureDirectoriesAreInitialized(); | ||
return _exists; | ||
} | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
src/Microsoft.AspNet.FileProviders.Composite/CompositeDisposable.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace Microsoft.AspNet.FileProviders | ||
{ | ||
/// <summary> | ||
/// Represents a composition of <see cref="IDisposable"/>. | ||
/// </summary> | ||
public class CompositeDisposable : IDisposable | ||
{ | ||
private readonly IList<IDisposable> _disposables; | ||
/// <summary> | ||
/// Creates a new instance of <see cref="CompositeDisposable"/>. | ||
/// </summary> | ||
/// <param name="disposables">The list of <see cref="IDisposable"/> to compose.</param> | ||
public CompositeDisposable(IList<IDisposable> disposables) | ||
{ | ||
if(disposables == null) | ||
{ | ||
throw new ArgumentNullException(nameof(disposables)); | ||
} | ||
_disposables = disposables; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
for (var i = 0; i < _disposables.Count; i++) | ||
{ | ||
_disposables[i].Dispose(); | ||
} | ||
} | ||
} | ||
} |
56 changes: 56 additions & 0 deletions
56
src/Microsoft.AspNet.FileProviders.Composite/CompositeFileChangeToken.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using Microsoft.Extensions.Primitives; | ||
|
||
namespace Microsoft.AspNet.FileProviders | ||
{ | ||
/// <summary> | ||
/// Represents a composition of <see cref="IChangeToken"/>. | ||
/// </summary> | ||
public class CompositeFileChangeToken : IChangeToken | ||
{ | ||
private readonly IList<IChangeToken> _changeTokens; | ||
|
||
/// <summary> | ||
/// Creates a new instance of <see cref="CompositeFileChangeToken"/>. | ||
/// </summary> | ||
/// <param name="changeTokens">The list of <see cref="IChangeToken"/> to compose.</param> | ||
public CompositeFileChangeToken(IList<IChangeToken> changeTokens) | ||
{ | ||
if(changeTokens == null) | ||
{ | ||
throw new ArgumentNullException(nameof(changeTokens)); | ||
} | ||
_changeTokens = changeTokens; | ||
} | ||
|
||
public IDisposable RegisterChangeCallback(Action<object> callback, object state) | ||
{ | ||
var disposables = new List<IDisposable>(); | ||
for (var i = 0; i < _changeTokens.Count; i++) | ||
{ | ||
var changeToken = _changeTokens[i]; | ||
if (changeToken.ActiveChangeCallbacks) | ||
{ | ||
var disposable = _changeTokens[i].RegisterChangeCallback(callback, state); | ||
disposables.Add(disposable); | ||
} | ||
} | ||
return new CompositeDisposable(disposables); | ||
} | ||
|
||
public bool HasChanged | ||
{ | ||
get { return _changeTokens.Any(token => token.HasChanged); } | ||
} | ||
|
||
public bool ActiveChangeCallbacks | ||
{ | ||
get { return _changeTokens.Any(token => token.ActiveChangeCallbacks); } | ||
} | ||
} | ||
} |
99 changes: 99 additions & 0 deletions
99
src/Microsoft.AspNet.FileProviders.Composite/CompositeFileProvider.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Collections.Generic; | ||
using Microsoft.Extensions.Primitives; | ||
using System; | ||
using System.Linq; | ||
|
||
namespace Microsoft.AspNet.FileProviders | ||
{ | ||
/// <summary> | ||
/// Looks up files using a list of <see cref="IFileProvider"/>. | ||
/// </summary> | ||
public class CompositeFileProvider : IFileProvider | ||
{ | ||
private readonly IFileProvider[] _fileProviders; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CompositeFileProvider" /> class using a list of file provider. | ||
/// </summary> | ||
/// <param name="fileProviders"></param> | ||
public CompositeFileProvider(params IFileProvider[] fileProviders) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we settle on using |
||
{ | ||
_fileProviders = fileProviders ?? new IFileProvider[0]; | ||
} | ||
/// <summary> | ||
/// Initializes a new instance of the <see cref="CompositeFileProvider" /> class using a list of file provider. | ||
/// </summary> | ||
/// <param name="fileProviders"></param> | ||
public CompositeFileProvider(IEnumerable<IFileProvider> fileProviders) | ||
{ | ||
if(fileProviders == null) | ||
{ | ||
throw new ArgumentNullException(nameof(fileProviders)); | ||
} | ||
_fileProviders = fileProviders.ToArray(); | ||
} | ||
|
||
/// <summary> | ||
/// Locates a file at the given path. | ||
/// </summary> | ||
/// <param name="subpath">The path that identifies the file. </param> | ||
/// <returns>The file information. Caller must check Exists property. This will be the first existing <see cref="IFileInfo"/> returned by the provided <see cref="IFileProvider"/> or a not found <see cref="IFileInfo"/> if no existing files is found.</returns> | ||
public IFileInfo GetFileInfo(string subpath) | ||
{ | ||
foreach (var fileProvider in _fileProviders) | ||
{ | ||
var fileInfo = fileProvider.GetFileInfo(subpath); | ||
if (fileInfo != null && fileInfo.Exists) | ||
{ | ||
return fileInfo; | ||
} | ||
} | ||
return new NotFoundFileInfo(subpath); | ||
} | ||
|
||
/// <summary> | ||
/// Enumerate a directory at the given path, if any. | ||
/// </summary> | ||
/// <param name="subpath">The path that identifies the directory</param> | ||
/// <returns>Contents of the directory. Caller must check Exists property. | ||
/// The content is a merge of the contents of the provided <see cref="IFileProvider"/>. | ||
/// When there is multiple <see cref="IFileInfo"/> with the same Name property, only the first one is included on the results.</returns> | ||
public IDirectoryContents GetDirectoryContents(string subpath) | ||
{ | ||
var directoryContents = new CompositeDirectoryContents(_fileProviders, subpath); | ||
return directoryContents; | ||
} | ||
|
||
/// <summary> | ||
/// Creates a <see cref="IChangeToken"/> for the specified <paramref name="filter"/>. | ||
/// </summary> | ||
/// <remarks></remarks> | ||
/// <param name="pattern">Filter string used to determine what files or folders to monitor. Example: **/*.cs, *.*, subFolder/**/*.cshtml.</param> | ||
/// <returns>An <see cref="IChangeToken"/> that is notified when a file matching <paramref name="filter"/> is added, modified or deleted. | ||
/// The change token will be notified when one of the change token returned by the provided <see cref="IFileProvider"/> will be notified.</returns> | ||
public IChangeToken Watch(string pattern) | ||
{ | ||
// Watch all file providers | ||
var changeTokens = new List<IChangeToken>(); | ||
foreach (var fileProvider in _fileProviders) | ||
{ | ||
var changeToken = fileProvider.Watch(pattern); | ||
if (changeToken != null) | ||
{ | ||
changeTokens.Add(changeToken); | ||
} | ||
} | ||
|
||
// There is no change token with active change callbacks | ||
if (changeTokens.Count == 0) | ||
{ | ||
return NoopChangeToken.Singleton; | ||
} | ||
var CompositeFileChangeToken = new CompositeFileChangeToken(changeTokens); | ||
return CompositeFileChangeToken; | ||
} | ||
} | ||
} |
19 changes: 19 additions & 0 deletions
19
src/Microsoft.AspNet.FileProviders.Composite/Microsoft.AspNet.FileProviders.Composite.xproj
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<?xml version="1.0" encoding="utf-8"?> | ||
<Project ToolsVersion="__ToolsVersion__" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | ||
<PropertyGroup> | ||
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion> | ||
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath> | ||
</PropertyGroup> | ||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.Props" Condition="'$(VSToolsPath)' != ''" /> | ||
<PropertyGroup Label="Globals"> | ||
<ProjectGuid>caaf52ef-f91b-474d-ac56-fe9d96ff8254</ProjectGuid> | ||
</PropertyGroup> | ||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x86'" Label="Configuration"> | ||
</PropertyGroup> | ||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x86'" Label="Configuration"> | ||
</PropertyGroup> | ||
<PropertyGroup> | ||
<SchemaVersion>2.0</SchemaVersion> | ||
</PropertyGroup> | ||
<Import Project="$(VSToolsPath)\AspNet\Microsoft.Web.AspNet.targets" Condition="'$(VSToolsPath)' != ''" /> | ||
</Project> |
8 changes: 8 additions & 0 deletions
8
src/Microsoft.AspNet.FileProviders.Composite/Properties/AssemblyInfo.cs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.Reflection; | ||
using System.Resources; | ||
|
||
[assembly: AssemblyMetadata("Serviceable", "True")] | ||
[assembly: NeutralResourcesLanguage("en-us")] |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This would need a null check (
throw new ArgumentNullException
)