Skip to content

Commit

Permalink
Add clone methods
Browse files Browse the repository at this point in the history
  • Loading branch information
wherewhere committed Feb 14, 2024
1 parent 12cdb1b commit 7268203
Show file tree
Hide file tree
Showing 12 changed files with 178 additions and 29 deletions.
17 changes: 15 additions & 2 deletions AdvancedSharpAdbClient/AdbClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ namespace AdvancedSharpAdbClient
/// <para><seealso href="https://github.com/android/platform_system_core/blob/master/adb/adb_client.c">adb_client.c</seealso></para>
/// <para><seealso href="https://github.com/android/platform_system_core/blob/master/adb/adb.c">adb.c</seealso></para>
/// </remarks>
public partial class AdbClient : IAdbClient
public partial class AdbClient : IAdbClient, ICloneable<IAdbClient>, ICloneable
#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
, IAdbClient.IWinRT
#endif
Expand Down Expand Up @@ -143,7 +143,7 @@ public AdbClient(Func<EndPoint, IAdbSocket> adbSocketFactory)
/// <summary>
/// Gets the <see cref="System.Net.EndPoint"/> at which the adb server is listening.
/// </summary>
public EndPoint EndPoint { get; protected set; }
public EndPoint EndPoint { get; init; }

/// <summary>
/// Create an ASCII string preceded by four hex digits. The opening "####"
Expand Down Expand Up @@ -1118,6 +1118,19 @@ public IEnumerable<string> GetFeatureSet(DeviceData device)
return featureList;
}

/// <summary>
/// Creates a new <see cref="AdbClient"/> object that is a copy of the current instance with new <see cref="EndPoint"/>.
/// </summary>
/// <param name="endPoint">The new <see cref="EndPoint"/> to use.</param>
/// <returns>A new <see cref="AdbClient"/> object that is a copy of this instance with new <see cref="EndPoint"/>.</returns>
public AdbClient Clone(EndPoint endPoint) => new(endPoint, AdbSocketFactory);

/// <inheritdoc/>
public IAdbClient Clone() => new AdbClient(EndPoint, AdbSocketFactory);

/// <inheritdoc/>
object ICloneable.Clone() => Clone();

/// <summary>
/// Sets default encoding (default - UTF8).
/// </summary>
Expand Down
2 changes: 1 addition & 1 deletion AdvancedSharpAdbClient/AdbCommandLineClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public AdbCommandLineClient(string adbPath, bool isForce = false, ILogger<AdbCom
/// <summary>
/// Gets the path to the <c>adb.exe</c> executable.
/// </summary>
public string AdbPath { get; protected set; }
public string AdbPath { get; init; }

/// <inheritdoc/>
public AdbCommandLineStatus GetVersion()
Expand Down
17 changes: 15 additions & 2 deletions AdvancedSharpAdbClient/AdbServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ namespace AdvancedSharpAdbClient
/// giant multiplexing loop whose purpose is to orchestrate the exchange of data
/// between clients and devices.</para>
/// </summary>
public partial class AdbServer : IAdbServer
public partial class AdbServer : IAdbServer, ICloneable<IAdbServer>, ICloneable
{
/// <summary>
/// The minimum version of <c>adb.exe</c> that is supported by this library.
Expand Down Expand Up @@ -162,7 +162,7 @@ public AdbServer(Func<EndPoint, IAdbSocket> adbSocketFactory, Func<string, IAdbC
/// <summary>
/// Gets the <see cref="System.Net.EndPoint"/> at which the adb server is listening.
/// </summary>
public EndPoint EndPoint { get; protected set; }
public EndPoint EndPoint { get; init; }

/// <inheritdoc/>
public StartServerResult StartServer(string adbPath, bool restartServerIfNewer = false)
Expand Down Expand Up @@ -261,5 +261,18 @@ public AdbServerStatus GetStatus()
}
}
}

/// <summary>
/// Creates a new <see cref="AdbServer"/> object that is a copy of the current instance with new <see cref="EndPoint"/>.
/// </summary>
/// <param name="endPoint">The new <see cref="EndPoint"/> to use.</param>
/// <returns>A new <see cref="AdbServer"/> object that is a copy of this instance with new <see cref="EndPoint"/>.</returns>
public AdbServer Clone(EndPoint endPoint) => new(endPoint, AdbSocketFactory, AdbCommandLineClientFactory);

/// <inheritdoc/>
public IAdbServer Clone() => new AdbServer(EndPoint, AdbSocketFactory, AdbCommandLineClientFactory);

/// <inheritdoc/>
object ICloneable.Clone() => Clone();
}
}
16 changes: 15 additions & 1 deletion AdvancedSharpAdbClient/AdbSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ namespace AdvancedSharpAdbClient
/// </summary>
/// <param name="socket">The <see cref="ITcpSocket"/> at which the Android Debug Bridge is listening for clients.</param>
/// <param name="logger">The logger to use when logging.</param>
public partial class AdbSocket(ITcpSocket socket, ILogger<AdbSocket>? logger = null) : IAdbSocket
public partial class AdbSocket(ITcpSocket socket, ILogger<AdbSocket>? logger = null) : IAdbSocket, ICloneable<IAdbSocket>, ICloneable
{
/// <summary>
/// The logger to use when logging messages.
Expand Down Expand Up @@ -538,6 +538,20 @@ public void Dispose()
/// <inheritdoc/>
public void Close() => Socket.Dispose();

/// <inheritdoc/>
public IAdbSocket Clone()
{
if (Socket is not ICloneable<ITcpSocket> cloneable)
{
throw new NotSupportedException($"{Socket.GetType()} does not support cloning.");
}
ITcpSocket socket = cloneable.Clone();
return new AdbSocket(socket, logger);
}

/// <inheritdoc/>
object ICloneable.Clone() => Clone();

/// <summary>
/// Creates a new <see cref="TcpSocket"/> instance based on the specified <see cref="EndPoint"/>.
/// </summary>
Expand Down
28 changes: 14 additions & 14 deletions AdvancedSharpAdbClient/DeviceCommands/DeviceClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -401,34 +401,34 @@ public void Deconstruct(out IAdbClient client, out DeviceData device)
device = Device;
}

#if !NET40_OR_GREATER && !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_0_OR_GREATER && !UAP10_0_15138_0
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(EqualityContract, AdbClient, Device);

/// <inheritdoc/>
public virtual bool Equals(DeviceClient? other) =>
(object?)this == other ||
(other != (object?)null
&& EqualityComparer<Type>.Default.Equals(EqualityContract, other.EqualityContract)
&& EqualityComparer<IAdbClient>.Default.Equals(AdbClient, other.AdbClient)
&& EqualityComparer<DeviceData>.Default.Equals(Device, other.Device));
#endif

/// <summary>
/// Throws an <see cref="ArgumentNullException"/> if the <paramref name="device"/>
/// parameter is <see langword="null"/>, and a <see cref="ArgumentOutOfRangeException"/>
/// if <paramref name="device"/> does not have a valid serial number.
/// </summary>
/// <param name="device">A <see cref="DeviceData"/> object to validate.</param>
/// <returns>The <paramref name="device"/> parameter, if it is valid.</returns>
protected static DeviceData EnsureDevice([NotNull] DeviceData? device)
private static DeviceData EnsureDevice([NotNull] DeviceData? device)
{
ExceptionExtensions.ThrowIfNull(device);
return device.IsEmpty
? throw new ArgumentOutOfRangeException(nameof(device), "You must specific a serial number for the device")
: device;
}

#if !NET40_OR_GREATER && !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_0_OR_GREATER && !UAP10_0_15138_0
/// <inheritdoc/>
public override int GetHashCode() => HashCode.Combine(EqualityContract, AdbClient, Device);

/// <inheritdoc/>
public virtual bool Equals(DeviceClient? other) =>
(object?)this == other ||
(other != (object?)null
&& EqualityComparer<Type>.Default.Equals(EqualityContract, other.EqualityContract)
&& EqualityComparer<IAdbClient>.Default.Equals(AdbClient, other.AdbClient)
&& EqualityComparer<DeviceData>.Default.Equals(Device, other.Device));
#endif

#if NET7_0_OR_GREATER
[GeneratedRegex("<\\?xml(.?)*")]
private static partial Regex GetXmlRegex();
Expand Down
15 changes: 14 additions & 1 deletion AdvancedSharpAdbClient/DeviceMonitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ namespace AdvancedSharpAdbClient
/// }
/// </code>
/// </example>
public partial class DeviceMonitor : IDeviceMonitor
public partial class DeviceMonitor : IDeviceMonitor, ICloneable<IDeviceMonitor>, ICloneable
#if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
, IAsyncDisposable
#endif
Expand Down Expand Up @@ -399,5 +399,18 @@ protected virtual void UpdateDevices(IEnumerable<DeviceData> collection)
}
}
}

/// <inheritdoc/>
public IDeviceMonitor Clone()
{
if (Socket is not ICloneable<IAdbSocket> cloneable)
{
throw new NotSupportedException($"{Socket.GetType()} does not support cloning.");
}
return new DeviceMonitor(cloneable.Clone(), logger);
}

/// <inheritdoc/>
object ICloneable.Clone() => Clone();
}
}
57 changes: 57 additions & 0 deletions AdvancedSharpAdbClient/Polyfills/Interfaces/ICloneable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// <copyright file="ICloneable.cs" company="The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere">
// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved.
// </copyright>

using System;

#if !NETFRAMEWORK && !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_0_OR_GREATER && !UAP10_0_15138_0
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace System
{
/// <summary>
/// Supports cloning, which creates a new instance of a class with the same value as an existing instance.
/// </summary>
internal interface ICloneable
{
/// <summary>
/// Creates a new object that is a copy of the current instance.
/// </summary>
/// <returns>A new object that is a copy of this instance.</returns>
object Clone();
}
}

namespace AdvancedSharpAdbClient.Polyfills
{
/// <summary>
/// Supports cloning, which creates a new instance of a class with the same value as an existing instance.
/// </summary>
/// <typeparam name="T">The type of the class.</typeparam>
public interface ICloneable<T>
{
/// <summary>
/// Creates a new <typeparamref name="T"/> object that is a copy of the current instance.
/// </summary>
/// <returns>A new <typeparamref name="T"/> object that is a copy of this instance.</returns>
T Clone();
}
}
#else
namespace AdvancedSharpAdbClient.Polyfills
{
/// <summary>
/// Supports cloning, which creates a new instance of a class with the same value as an existing instance.
/// </summary>
/// <typeparam name="T">The type of the class.</typeparam>
public interface ICloneable<T> : ICloneable
{
/// <summary>
/// Creates a new <typeparamref name="T"/> object that is a copy of the current instance.
/// </summary>
/// <returns>A new <typeparamref name="T"/> object that is a copy of this instance.</returns>
new T Clone();
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#if HAS_TASK
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// <copyright file="IDisposableWithTask.cs" company="The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere">
// Copyright (c) The Android Open Source Project, Ryan Conrad, Quamotion, yungd1plomat, wherewhere. All rights reserved.
// </copyright>

using System.ComponentModel;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.ComponentModel;

namespace AdvancedSharpAdbClient.Polyfills
namespace System.Collections.Generic
{
/// <summary>
/// Represents a strongly-typed, read-only collection of elements.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

using System.ComponentModel;

namespace AdvancedSharpAdbClient.Polyfills
namespace System.Collections.Generic
{
/// <summary>
/// Represents a read-only collection of elements that can be accessed by index.
Expand Down
31 changes: 29 additions & 2 deletions AdvancedSharpAdbClient/SyncService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ namespace AdvancedSharpAdbClient
/// }
/// </code>
/// </example>
public partial class SyncService : ISyncService
public partial class SyncService : ISyncService, ICloneable<ISyncService>, ICloneable
#if WINDOWS_UWP || WINDOWS10_0_17763_0_OR_GREATER
, ISyncService.IWinRT
#endif
Expand Down Expand Up @@ -106,7 +106,7 @@ public SyncService(IAdbSocket socket, DeviceData device)
public IAdbSocket Socket { get; protected set; }

/// <inheritdoc/>
public bool IsOpen => Socket != null && Socket.Connected;
public bool IsOpen => Socket?.Connected == true;

/// <inheritdoc/>
/// <remarks>This method has been invoked by the constructor.
Expand Down Expand Up @@ -351,6 +351,33 @@ public void Dispose()
GC.SuppressFinalize(this);
}

/// <summary>
/// Creates a new <see cref="AdbServer"/> object that is a copy of the current instance with new <see cref="Device"/>.
/// </summary>
/// <param name="device">The new <see cref="Device"/> to use.</param>
/// <returns>A new <see cref="AdbServer"/> object that is a copy of this instance with new <see cref="Device"/>.</returns>
public SyncService Clone(DeviceData device)
{
if (Socket is not ICloneable<IAdbSocket> cloneable)
{
throw new NotSupportedException($"{Socket.GetType()} does not support cloning.");
}
return new SyncService(cloneable.Clone(), device);
}

/// <inheritdoc/>
public ISyncService Clone()
{
if (Socket is not ICloneable<IAdbSocket> cloneable)
{
throw new NotSupportedException($"{Socket.GetType()} does not support cloning.");
}
return new SyncService(cloneable.Clone(), Device);
}

/// <inheritdoc/>
object ICloneable.Clone() => Clone();

/// <summary>
/// Reads the statistics of a file from the socket.
/// </summary>
Expand Down
14 changes: 13 additions & 1 deletion AdvancedSharpAdbClient/TcpSocket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ namespace AdvancedSharpAdbClient
/// <summary>
/// Implements the <see cref="ITcpSocket"/> interface using the standard <see cref="System.Net.Sockets.Socket"/> class.
/// </summary>
public sealed partial class TcpSocket : ITcpSocket
public sealed partial class TcpSocket : ITcpSocket, ICloneable<ITcpSocket>, ICloneable
{
/// <summary>
/// Initializes a new instance of the <see cref="TcpSocket"/> class.
Expand Down Expand Up @@ -125,5 +125,17 @@ public void Dispose()

/// <inheritdoc/>
public Stream GetStream() => new NetworkStream(Socket);

/// <inheritdoc/>
public ITcpSocket Clone()
{
TcpSocket socket = new();
socket.Connect(EndPoint!);
socket.ReceiveBufferSize = ReceiveBufferSize;
return socket;
}

/// <inheritdoc/>
object ICloneable.Clone() => Clone();
}
}

0 comments on commit 7268203

Please sign in to comment.