Skip to content

Commit

Permalink
Net6 support (#184)
Browse files Browse the repository at this point in the history
* Replace classic FieldOffset interop method with framework stream implementation. Using the same chunking method as before, for now

* Add disposal of SteamReference.

* using-> await using

* Just some cleanup before rewriting demo project

* Update demo wasm project for net6

* Fixes annoying map error

* Server-side project

* Fix some using error
  • Loading branch information
Tewr authored Dec 14, 2021
1 parent 3440775 commit a15a1c2
Show file tree
Hide file tree
Showing 91 changed files with 3,055 additions and 109 deletions.
25 changes: 23 additions & 2 deletions src/Blazor.FileReader.sln
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.28531.58
# Visual Studio Version 17
VisualStudioVersion = 17.0.31808.319
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.FileReader", "Blazor.FileReader\Blazor.FileReader.csproj", "{5F8084DD-9D88-4407-BCFC-15C71C5AF11E}"
EndProject
Expand All @@ -27,6 +27,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tewr.Blazor.FileReader.E2ET
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tewr.Blazor.FileReader.UnitTests", "..\tests\Tewr.Blazor.FileReader.UnitTests\Tewr.Blazor.FileReader.UnitTests.csproj", "{38F452ED-A8E3-4CED-890C-E2E6DB6F54AF}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Net6", "Net6", "{C3DC632D-7249-4B55-A0F4-68742064C71F}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Blazor.FileReader.Wasm.Demo", "Demo\Net6\Blazor.FileReader.Wasm.Demo\Blazor.FileReader.Wasm.Demo.csproj", "{96778444-7F44-4CF8-B8B2-4124865A826A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Blazor.FileReader.ServerSide.Demo", "Demo\Net6\Blazor.FileReader.ServerSide.Demo\Blazor.FileReader.ServerSide.Demo.csproj", "{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -81,6 +87,18 @@ Global
{38F452ED-A8E3-4CED-890C-E2E6DB6F54AF}.Ghpages|Any CPU.Build.0 = Debug|Any CPU
{38F452ED-A8E3-4CED-890C-E2E6DB6F54AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{38F452ED-A8E3-4CED-890C-E2E6DB6F54AF}.Release|Any CPU.Build.0 = Release|Any CPU
{96778444-7F44-4CF8-B8B2-4124865A826A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{96778444-7F44-4CF8-B8B2-4124865A826A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{96778444-7F44-4CF8-B8B2-4124865A826A}.Ghpages|Any CPU.ActiveCfg = Debug|Any CPU
{96778444-7F44-4CF8-B8B2-4124865A826A}.Ghpages|Any CPU.Build.0 = Debug|Any CPU
{96778444-7F44-4CF8-B8B2-4124865A826A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{96778444-7F44-4CF8-B8B2-4124865A826A}.Release|Any CPU.Build.0 = Release|Any CPU
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}.Ghpages|Any CPU.ActiveCfg = Debug|Any CPU
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}.Ghpages|Any CPU.Build.0 = Debug|Any CPU
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -95,6 +113,9 @@ Global
{AEA2CBDF-329E-4B17-9B02-4178D045F528} = {856A3F73-2C95-4E2E-819C-41F6456EE049}
{14C576CC-9CFA-49E7-B48F-2C8E03A9B76D} = {9A262CB5-8352-42D0-830C-D87131D9060B}
{38F452ED-A8E3-4CED-890C-E2E6DB6F54AF} = {9A262CB5-8352-42D0-830C-D87131D9060B}
{C3DC632D-7249-4B55-A0F4-68742064C71F} = {8AA482CE-0130-43A1-84BC-92215AF25AED}
{96778444-7F44-4CF8-B8B2-4124865A826A} = {C3DC632D-7249-4B55-A0F4-68742064C71F}
{4AFCC00A-7CC4-4143-9D12-0E0E3CA4A630} = {C3DC632D-7249-4B55-A0F4-68742064C71F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7BCBB258-C07A-4039-838A-09F3B45F7E58}
Expand Down
15 changes: 10 additions & 5 deletions src/Blazor.FileReader/Blazor.FileReader.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@


<PropertyGroup>
<TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
<TargetFrameworks>netstandard2.0;net5.0;net6.0</TargetFrameworks>
<BlazorLinkOnBuild>true</BlazorLinkOnBuild>
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked>
<LangVersion>latest</LangVersion>
Expand All @@ -14,15 +14,15 @@
<PackageTags>blazor blazor-component stream filestream file-stream read-file filereader</PackageTags>
<Configurations>Debug;Release;Ghpages</Configurations>
<PackageId>Tewr.Blazor.FileReader</PackageId>
<PackageReleaseNotes>Copy-paste API</PackageReleaseNotes>
<PackageReleaseNotes>net6 support</PackageReleaseNotes>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>icon.png</PackageIcon>
<RazorLangVersion>3.0</RazorLangVersion>
<AssemblyName>Tewr.Blazor.FileReader</AssemblyName>
<RootNamespace>Tewr.Blazor.FileReader</RootNamespace>
<AssemblyVersion>3.2.0.21211</AssemblyVersion>
<Version>3.2.0.21211</Version>
<FileVersion>3.2.0.21211</FileVersion>
<AssemblyVersion>3.3.0.21348</AssemblyVersion>
<Version>3.3.0.21348</Version>
<FileVersion>3.3.0.21348</FileVersion>
</PropertyGroup>
<ItemGroup>
<None Include="icon.png" Pack="true" Visible="false" PackagePath="" />
Expand All @@ -44,6 +44,11 @@
<EmbeddedResource Include="wwwroot\**\*.js" LogicalName="blazor:js:%(RecursiveDir)%(Filename)%(Extension)" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net6.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="6.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="6.0.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)'=='net5.0'">
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="5.0.0" />
<PackageReference Include="Microsoft.Bcl.AsyncInterfaces" Version="5.0.0" />
Expand Down
68 changes: 53 additions & 15 deletions src/Blazor.FileReader/FileReaderJsInterop.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ public partial class FileReaderJsInterop
private static readonly Dictionary<long, TaskCompletionSource<int>> _readFileUnmarshalledCalls
= new Dictionary<long, TaskCompletionSource<int>>();



internal IJSRuntime CurrentJSRuntime;
internal IJSUnmarshalledRuntime UnmarshalledRuntime;

Expand Down Expand Up @@ -53,25 +55,25 @@ internal void Initialize()
internal async Task<bool> RegisterDropEvents(ElementReference elementReference, DropEventsOptions options)
{
await EnsureInitializedAsync();
return await CurrentJSRuntime.InvokeAsync<bool>($"FileReaderComponent.RegisterDropEvents", elementReference, options);
return await CurrentJSRuntime.InvokeAsync<bool>(FileReaderComponent.RegisterDropEvents, elementReference, options);
}

public async Task<bool> UnregisterDropEvents(ElementReference elementReference)
{
await EnsureInitializedAsync();
return await CurrentJSRuntime.InvokeAsync<bool>($"FileReaderComponent.UnregisterDropEvents", elementReference);
return await CurrentJSRuntime.InvokeAsync<bool>(FileReaderComponent.UnregisterDropEvents, elementReference);
}

internal async Task<bool> RegisterPasteEvent(ElementReference elementReference, PasteEventOptions options)
{
await EnsureInitializedAsync();
return await CurrentJSRuntime.InvokeAsync<bool>($"FileReaderComponent.RegisterPasteEvent", elementReference, options);
return await CurrentJSRuntime.InvokeAsync<bool>(FileReaderComponent.RegisterPasteEvent, elementReference, options);
}

public async Task<bool> UnregisterPasteEvent(ElementReference elementReference)
{
await EnsureInitializedAsync();
return await CurrentJSRuntime.InvokeAsync<bool>($"FileReaderComponent.UnregisterPasteEvent", elementReference);
return await CurrentJSRuntime.InvokeAsync<bool>(FileReaderComponent.UnregisterPasteEvent, elementReference);
}

public async Task<AsyncDisposableStream> OpenFileStream(ElementReference elementReference, int index, IFileInfo fileInfo)
Expand All @@ -87,18 +89,18 @@ public async Task<IBase64Stream> OpenBase64Stream(ElementReference elementRefere
public async Task<int> GetFileCount(ElementReference elementReference)
{
await EnsureInitializedAsync();
return (int)await CurrentJSRuntime.InvokeAsync<long>($"FileReaderComponent.GetFileCount", elementReference);
return (int)await CurrentJSRuntime.InvokeAsync<long>(FileReaderComponent.GetFileCount, elementReference);
}

public async Task<int> ClearValue(ElementReference elementReference)
{
await EnsureInitializedAsync();
return (int)await CurrentJSRuntime.InvokeAsync<long>($"FileReaderComponent.ClearValue", elementReference);
return (int)await CurrentJSRuntime.InvokeAsync<long>(FileReaderComponent.ClearValue, elementReference);
}

public async Task<IFileInfo> GetFileInfoFromElement(ElementReference elementReference, int index)
{
return await CurrentJSRuntime.InvokeAsync<FileInfo>($"FileReaderComponent.GetFileInfoFromElement", elementReference, index);
return await CurrentJSRuntime.InvokeAsync<FileInfo>(FileReaderComponent.GetFileInfoFromElement, elementReference, index);
}

public async Task EnsureInitializedAsync(bool force = false)
Expand All @@ -113,19 +115,23 @@ public async Task EnsureInitializedAsync(bool force = false)

private async Task<int> OpenReadAsync(ElementReference elementReference, int fileIndex)
{
return (int)await CurrentJSRuntime.InvokeAsync<long>($"FileReaderComponent.OpenRead", elementReference, fileIndex, _options.UseWasmSharedBuffer);
return (int)await CurrentJSRuntime.InvokeAsync<long>(FileReaderComponent.OpenRead, elementReference, fileIndex, _options.UseWasmSharedBuffer);
}

private async Task<bool> DisposeStream(int fileRef)
{
return await CurrentJSRuntime.InvokeAsync<bool>($"FileReaderComponent.Dispose", fileRef);
return await CurrentJSRuntime.InvokeAsync<bool>(FileReaderComponent.Dispose, fileRef);
}

private async Task<int> ReadFileAsync(int fileRef, byte[] buffer, long position, long bufferOffset, int count, CancellationToken cancellationToken)
{
if (this._options.UseWasmSharedBuffer)
{
#if NET6_0_OR_GREATER
return await ReadFileSliceAsync(fileRef, buffer, position, bufferOffset, count, cancellationToken);
#else
return await ReadFileUnmarshalledAsync(fileRef, buffer, position, bufferOffset, count, cancellationToken);
#endif
}

return await ReadFileMarshalledAsync(fileRef, buffer, position, bufferOffset, count, cancellationToken);
Expand Down Expand Up @@ -189,13 +195,25 @@ private async Task<string> ReadFileMarshalledBase64Async(
{
cancellationToken.ThrowIfCancellationRequested();
var data = await CurrentJSRuntime.InvokeAsync<string>(
$"FileReaderComponent.ReadFileMarshalledAsync", cancellationToken,
FileReaderComponent.ReadFileMarshalledAsync, cancellationToken,
new { position, count, fileRef });

return data;

}

#if NET6_0_OR_GREATER
private async Task<int> ReadFileSliceAsync(
int fileRef, byte[] buffer, long position, long bufferOffset, int count,
CancellationToken cancellationToken)
{
await using var streamReference = await this.CurrentJSRuntime.InvokeAsync<IJSStreamReference>(
FileReaderComponent.ReadFileSliceAsync, cancellationToken, fileRef, position, count);
using var readStream = await streamReference.OpenReadStreamAsync(cancellationToken: cancellationToken);
return await readStream.ReadAsync(buffer.AsMemory((int)bufferOffset, count), cancellationToken);
}
#endif

private async Task<int> ReadFileUnmarshalledAsync(
int fileRef, byte[] buffer, long position, long bufferOffset, int count,
CancellationToken cancellationToken)
Expand All @@ -207,7 +225,7 @@ private async Task<int> ReadFileUnmarshalledAsync(

// Buffer is not allocated here,
UnmarshalledRuntime.InvokeUnmarshalled<ReadFileParams, int>(
$"FileReaderComponent.ReadFileUnmarshalledAsync",
FileReaderComponent.ReadFileUnmarshalledAsync,
new ReadFileParams {
BufferOffset = bufferOffset,
Count = count,
Expand All @@ -221,7 +239,7 @@ private async Task<int> ReadFileUnmarshalledAsync(

// It is safely filled up here instead
var bytesRead = UnmarshalledRuntime.InvokeUnmarshalled<BufferParams, int>(
$"FileReaderComponent.FillBufferUnmarshalled",
FileReaderComponent.FillBufferUnmarshalled,
new BufferParams
{
TaskId = id,
Expand Down Expand Up @@ -260,10 +278,10 @@ public static void EndReadFileUnmarshalledAsyncError(long taskId, string error)
taskCompletionSource.SetException(new BrowserFileReaderException(error));
}

#if NET5
#if NET5_0_OR_GREATER
internal async Task<IJSObjectReference> GetJSObjectReferenceAsync(ElementReference elementRef, int fileIndex)
{
return await CurrentJSRuntime.InvokeAsync<IJSObjectReference>("FileReaderComponent.GetJSObjectReference", elementRef, fileIndex);
return await CurrentJSRuntime.InvokeAsync<IJSObjectReference>(FileReaderComponent.GetJSObjectReference, elementRef, fileIndex);
}
#endif

Expand Down Expand Up @@ -300,7 +318,7 @@ private async Task InitializeAsync()

private async Task<bool> IsLoaded()
{
return await CurrentJSRuntime.InvokeAsync<bool>("window.hasOwnProperty", "FileReaderComponent");
return await CurrentJSRuntime.InvokeAsync<bool>("window.hasOwnProperty", FileReaderComponent.InstanceName);
}

private async Task<T> ExecuteRawScriptAsync<T>(string scriptContent)
Expand All @@ -311,4 +329,24 @@ private async Task<T> ExecuteRawScriptAsync<T>(string scriptContent)
return await CurrentJSRuntime.InvokeAsync<T>("eval", bootStrapScript);
}
}

internal static class FileReaderComponent
{
public static readonly string InstanceName = nameof(FileReaderComponent);

public static readonly string ClearValue = $"{InstanceName}.{nameof(ClearValue)}";
public static readonly string Dispose = $"{InstanceName}.{nameof(Dispose)}";
public static readonly string FillBufferUnmarshalled = $"{InstanceName}.{nameof(FillBufferUnmarshalled)}";
public static readonly string GetFileCount = $"{InstanceName}.{nameof(GetFileCount)}";
public static readonly string GetFileInfoFromElement = $"{InstanceName}.{nameof(GetFileInfoFromElement)}";
public static readonly string GetJSObjectReference = $"{InstanceName}.{nameof(GetJSObjectReference)}";
public static readonly string OpenRead = $"{InstanceName}.{nameof(OpenRead)}";
public static readonly string ReadFileMarshalledAsync = $"{InstanceName}.{nameof(ReadFileMarshalledAsync)}";
public static readonly string ReadFileSliceAsync = $"{InstanceName}.{nameof(ReadFileSliceAsync)}";
public static readonly string ReadFileUnmarshalledAsync = $"{InstanceName}.{nameof(ReadFileUnmarshalledAsync)}";
public static readonly string RegisterDropEvents = $"{InstanceName}.{nameof(RegisterDropEvents)}";
public static readonly string RegisterPasteEvent = $"{InstanceName}.{nameof(RegisterPasteEvent)}";
public static readonly string UnregisterDropEvents = $"{InstanceName}.{nameof(UnregisterDropEvents)}";
public static readonly string UnregisterPasteEvent = $"{InstanceName}.{nameof(UnregisterPasteEvent)}";
}
}
4 changes: 2 additions & 2 deletions src/Blazor.FileReader/FileReaderRef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ public interface IFileReaderRef
public interface IFileReference
{

#if NET5
#if NET5_0_OR_GREATER
/// <summary>
/// Returns the underlying file object as an <see cref="IJSObjectReference"/>
/// </summary>
Expand Down Expand Up @@ -332,7 +332,7 @@ private async Task<MemoryStream> InnerCreateMemoryStreamAsync(int bufferSize, Ca
return memoryStream;
}

#if NET5
#if NET5_0_OR_GREATER
public Task<IJSObjectReference> GetJSObjectReferenceAsync()
{
return this.fileLoaderRef.FileReaderJsInterop.GetJSObjectReferenceAsync(fileLoaderRef.ElementRef, this.index);
Expand Down
2 changes: 1 addition & 1 deletion src/Blazor.FileReader/ObjectUrl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Tewr.Blazor.FileReader
{
#if NET5
#if NET5_0_OR_GREATER

/// <summary>
/// Represents an object url for a file.
Expand Down
56 changes: 0 additions & 56 deletions src/Blazor.FileReader/Tewr.Blazor.FileReader.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,9 @@
- [CreateMemoryStreamAsync()](#M-Tewr-Blazor-FileReader-IFileReference-CreateMemoryStreamAsync-System-Threading-CancellationToken- 'Tewr.Blazor.FileReader.IFileReference.CreateMemoryStreamAsync(System.Threading.CancellationToken)')
- [CreateMemoryStreamAsync()](#M-Tewr-Blazor-FileReader-IFileReference-CreateMemoryStreamAsync-System-Int32- 'Tewr.Blazor.FileReader.IFileReference.CreateMemoryStreamAsync(System.Int32)')
- [CreateMemoryStreamAsync()](#M-Tewr-Blazor-FileReader-IFileReference-CreateMemoryStreamAsync-System-Int32,System-Threading-CancellationToken- 'Tewr.Blazor.FileReader.IFileReference.CreateMemoryStreamAsync(System.Int32,System.Threading.CancellationToken)')
- [GetJSObjectReferenceAsync()](#M-Tewr-Blazor-FileReader-IFileReference-GetJSObjectReferenceAsync 'Tewr.Blazor.FileReader.IFileReference.GetJSObjectReferenceAsync')
- [GetObjectUrlAsync()](#M-Tewr-Blazor-FileReader-IFileReference-GetObjectUrlAsync 'Tewr.Blazor.FileReader.IFileReference.GetObjectUrlAsync')
- [OpenReadAsync()](#M-Tewr-Blazor-FileReader-IFileReference-OpenReadAsync 'Tewr.Blazor.FileReader.IFileReference.OpenReadAsync')
- [OpenReadBase64Async()](#M-Tewr-Blazor-FileReader-IFileReference-OpenReadBase64Async 'Tewr.Blazor.FileReader.IFileReference.OpenReadBase64Async')
- [ReadFileInfoAsync()](#M-Tewr-Blazor-FileReader-IFileReference-ReadFileInfoAsync 'Tewr.Blazor.FileReader.IFileReference.ReadFileInfoAsync')
- [IObjectUrl](#T-Tewr-Blazor-FileReader-IObjectUrl 'Tewr.Blazor.FileReader.IObjectUrl')
- [Url](#P-Tewr-Blazor-FileReader-IObjectUrl-Url 'Tewr.Blazor.FileReader.IObjectUrl.Url')
- [PasteEventOptions](#T-Tewr-Blazor-FileReader-DropEvents-PasteEventOptions 'Tewr.Blazor.FileReader.DropEvents.PasteEventOptions')
- [Additive](#P-Tewr-Blazor-FileReader-DropEvents-PasteEventOptions-Additive 'Tewr.Blazor.FileReader.DropEvents.PasteEventOptions.Additive')
- [PlatformConfig](#T-Tewr-Blazor-FileReader-PlatformConfig 'Tewr.Blazor.FileReader.PlatformConfig')
Expand Down Expand Up @@ -779,36 +775,6 @@ The length of the resulting [MemoryStream](http://msdn.microsoft.com/query/dev14

A [MemoryStream](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.IO.MemoryStream 'System.IO.MemoryStream') representing the full file, with [Position](http://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k:System.IO.MemoryStream.Position 'System.IO.MemoryStream.Position') set to 0.

##### Parameters

This method has no parameters.

<a name='M-Tewr-Blazor-FileReader-IFileReference-GetJSObjectReferenceAsync'></a>
### GetJSObjectReferenceAsync() `method`

##### Summary

Returns the underlying file object as an [IJSObjectReference](#T-Microsoft-JSInterop-IJSObjectReference 'Microsoft.JSInterop.IJSObjectReference')

##### Returns



##### Parameters

This method has no parameters.

<a name='M-Tewr-Blazor-FileReader-IFileReference-GetObjectUrlAsync'></a>
### GetObjectUrlAsync() `method`

##### Summary

Returns an object url for a file.

##### Returns



##### Parameters

This method has no parameters.
Expand Down Expand Up @@ -858,28 +824,6 @@ An object containing the file metadata

This method has no parameters.

<a name='T-Tewr-Blazor-FileReader-IObjectUrl'></a>
## IObjectUrl `type`

##### Namespace

Tewr.Blazor.FileReader

##### Summary

Represents an object url for a file.

##### Remarks

https://developer.mozilla.org/en-US/docs/Web/API/URL/createObjectURL

<a name='P-Tewr-Blazor-FileReader-IObjectUrl-Url'></a>
### Url `property`

##### Summary

Returns the Object Url.

<a name='T-Tewr-Blazor-FileReader-DropEvents-PasteEventOptions'></a>
## PasteEventOptions `type`

Expand Down
Loading

0 comments on commit a15a1c2

Please sign in to comment.