MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4#nullable enable5 6using System;7using System.Collections.Generic;8using System.Collections.ObjectModel;9using System.Management.Automation.Internal;10using Microsoft.PowerShell.Telemetry;11 12namespace System.Management.Automation.Subsystem13{14 /// <summary>15 /// Class used to represent the metadata and state of a subsystem.16 /// </summary>17 public abstract class SubsystemInfo18 {19 #region "Metadata of a Subsystem (public)"20 21 /// <summary>22 /// Gets the kind of a concrete subsystem.23 /// </summary>24 public SubsystemKind Kind { get; }25 26 /// <summary>27 /// Gets the type of a concrete subsystem.28 /// </summary>29 public Type SubsystemType { get; }30 31 /// <summary>32 /// Gets a value indicating whether the subsystem allows to unregister an implementation.33 /// </summary>34 public bool AllowUnregistration { get; private set; }35 36 /// <summary>37 /// Gets a value indicating whether the subsystem allows to have multiple implementations registered.38 /// </summary>39 public bool AllowMultipleRegistration { get; private set; }40 41 /// <summary>42 /// Gets the names of the required cmdlets that have to be implemented by the subsystem implementation.43 /// </summary>44 public ReadOnlyCollection<string> RequiredCmdlets { get; private set; }45 46 /// <summary>47 /// Gets the names of the required functions that have to be implemented by the subsystem implementation.48 /// </summary>49 public ReadOnlyCollection<string> RequiredFunctions { get; private set; }50 51 // /// <summary>52 // /// A subsystem may depend on or more other subsystems.53 // /// Maybe add a 'DependsOn' member?54 // /// This can be validated when registering a subsystem implementation,55 // /// to make sure its prerequisites have already been registered.56 // /// </summary>57 // public ReadOnlyCollection<SubsystemKind> DependsOn { get; private set; }58 59 #endregion60 61 #region "State of a Subsystem (public)"62 63 /// <summary>64 /// Indicate whether there is any implementation registered to the subsystem.65 /// </summary>66 public bool IsRegistered => _cachedImplInfos.Count > 0;67 68 /// <summary>69 /// Get the information about the registered implementations.70 /// </summary>71 public ReadOnlyCollection<ImplementationInfo> Implementations => _cachedImplInfos;72 73 #endregion74 75 #region "private/internal instance members"76 77 private protected readonly object _syncObj;78 private protected ReadOnlyCollection<ImplementationInfo> _cachedImplInfos;79 80 private protected SubsystemInfo(SubsystemKind kind, Type subsystemType)81 {82 _syncObj = new object();83 _cachedImplInfos = Utils.EmptyReadOnlyCollection<ImplementationInfo>();84 85 Kind = kind;86 SubsystemType = subsystemType;87 AllowUnregistration = false;88 AllowMultipleRegistration = false;89 RequiredCmdlets = Utils.EmptyReadOnlyCollection<string>();90 RequiredFunctions = Utils.EmptyReadOnlyCollection<string>();91 }92 93 private protected abstract void AddImplementation(ISubsystem rawImpl);94 95 private protected abstract ISubsystem RemoveImplementation(Guid id);96 97 internal void RegisterImplementation(ISubsystem impl)98 {99 AddImplementation(impl);100 ApplicationInsightsTelemetry.SendUseTelemetry(ApplicationInsightsTelemetry.s_subsystemRegistration, impl.Name);101 }102 103 internal ISubsystem UnregisterImplementation(Guid id)104 {105 return RemoveImplementation(id);106 }107 108 #endregion109 110 #region "Static factory overloads"111 112 internal static SubsystemInfo Create<TConcreteSubsystem>(SubsystemKind kind)113 where TConcreteSubsystem : class, ISubsystem114 {115 return new SubsystemInfoImpl<TConcreteSubsystem>(kind);116 }117 118 internal static SubsystemInfo Create<TConcreteSubsystem>(119 SubsystemKind kind,120 bool allowUnregistration,121 bool allowMultipleRegistration) where TConcreteSubsystem : class, ISubsystem122 {123 return new SubsystemInfoImpl<TConcreteSubsystem>(kind)124 {125 AllowUnregistration = allowUnregistration,126 AllowMultipleRegistration = allowMultipleRegistration,127 };128 }129 130 internal static SubsystemInfo Create<TConcreteSubsystem>(131 SubsystemKind kind,132 bool allowUnregistration,133 bool allowMultipleRegistration,134 ReadOnlyCollection<string> requiredCmdlets,135 ReadOnlyCollection<string> requiredFunctions) where TConcreteSubsystem : class, ISubsystem136 {137 if (allowMultipleRegistration &&138 (requiredCmdlets.Count > 0 || requiredFunctions.Count > 0))139 {140 throw new ArgumentException(141 StringUtil.Format(142 SubsystemStrings.InvalidSubsystemInfo,143 kind.ToString()));144 }145 146 return new SubsystemInfoImpl<TConcreteSubsystem>(kind)147 {148 AllowUnregistration = allowUnregistration,149 AllowMultipleRegistration = allowMultipleRegistration,150 RequiredCmdlets = requiredCmdlets,151 RequiredFunctions = requiredFunctions,152 };153 }154 155 #endregion156 157 #region "ImplementationInfo"158 159 /// <summary>160 /// Information about an implementation of a subsystem.161 /// </summary>162 public class ImplementationInfo163 {164 internal ImplementationInfo(SubsystemKind kind, ISubsystem implementation)165 {166 Id = implementation.Id;167 Kind = kind;168 Name = implementation.Name;169 Description = implementation.Description;170 ImplementationType = implementation.GetType();171 }172 173 /// <summary>174 /// Gets the unique identifier for a subsystem implementation.175 /// </summary>176 public Guid Id { get; }177 178 /// <summary>179 /// Gets the kind of subsystem.180 /// </summary>181 public SubsystemKind Kind { get; }182 183 /// <summary>184 /// Gets the name of a subsystem implementation.185 /// </summary>186 public string Name { get; }187 188 /// <summary>189 /// Gets the description of a subsystem implementation.190 /// </summary>191 public string Description { get; }192 193 /// <summary>194 /// Gets the implementation type.195 /// </summary>196 public Type ImplementationType { get; }197 }198 199 #endregion200 }201 202 internal sealed class SubsystemInfoImpl<TConcreteSubsystem> : SubsystemInfo203 where TConcreteSubsystem : class, ISubsystem204 {205 private ReadOnlyCollection<TConcreteSubsystem> _registeredImpls;206 207 internal SubsystemInfoImpl(SubsystemKind kind)208 : base(kind, typeof(TConcreteSubsystem))209 {210 _registeredImpls = Utils.EmptyReadOnlyCollection<TConcreteSubsystem>();211 }212 213 /// <summary>214 /// The 'add' and 'remove' operations are implemented in a way to optimize the 'reading' operation,215 /// so that reading is lock-free and allocation-free, at the cost of O(n) copy in 'add' and 'remove'216 /// ('n' is the number of registered implementations).217 /// </summary>218 /// <remarks>219 /// In the subsystem scenario, registration operations will be minimum, and in most cases, the registered220 /// implementation will never be unregistered, so optimization for reading is more important.221 /// </remarks>222 /// <param name="rawImpl">The subsystem implementation to be added.</param>223 private protected override void AddImplementation(ISubsystem rawImpl)224 {225 lock (_syncObj)226 {227 var impl = (TConcreteSubsystem)rawImpl;228 229 if (_registeredImpls.Count == 0)230 {231 _registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(new[] { impl });232 _cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(new[] { new ImplementationInfo(Kind, impl) });233 return;234 }235 236 if (!AllowMultipleRegistration)237 {238 throw new InvalidOperationException(239 StringUtil.Format(240 SubsystemStrings.MultipleRegistrationNotAllowed,241 Kind.ToString()));242 }243 244 foreach (TConcreteSubsystem item in _registeredImpls)245 {246 if (item.Id == impl.Id)247 {248 throw new InvalidOperationException(249 StringUtil.Format(250 SubsystemStrings.ImplementationAlreadyRegistered,251 impl.Id,252 Kind.ToString()));253 }254 }255 256 int newCapacity = _registeredImpls.Count + 1;257 var implList = new List<TConcreteSubsystem>(newCapacity);258 implList.AddRange(_registeredImpls);259 implList.Add(impl);260 261 var implInfo = new List<ImplementationInfo>(newCapacity);262 implInfo.AddRange(_cachedImplInfos);263 implInfo.Add(new ImplementationInfo(Kind, impl));264 265 _registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(implList);266 _cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(implInfo);267 }268 }269 270 /// <summary>271 /// The 'add' and 'remove' operations are implemented in a way to optimize the 'reading' operation,272 /// so that reading is lock-free and allocation-free, at the cost of O(n) copy in 'add' and 'remove'273 /// ('n' is the number of registered implementations).274 /// </summary>275 /// <remarks>276 /// In the subsystem scenario, registration operations will be minimum, and in most cases, the registered277 /// implementation will never be unregistered, so optimization for reading is more important.278 /// </remarks>279 /// <param name="id">The id of the subsystem implementation to be removed.</param>280 /// <returns>The subsystem implementation that was removed.</returns>281 private protected override ISubsystem RemoveImplementation(Guid id)282 {283 if (!AllowUnregistration)284 {285 throw new InvalidOperationException(286 StringUtil.Format(287 SubsystemStrings.UnregistrationNotAllowed,288 Kind.ToString()));289 }290 291 lock (_syncObj)292 {293 if (_registeredImpls.Count == 0)294 {295 throw new InvalidOperationException(296 StringUtil.Format(297 SubsystemStrings.NoImplementationRegistered,298 Kind.ToString()));299 }300 301 int index = -1;302 for (int i = 0; i < _registeredImpls.Count; i++)303 {304 if (_registeredImpls[i].Id == id)305 {306 index = i;307 break;308 }309 }310 311 if (index == -1)312 {313 throw new InvalidOperationException(314 StringUtil.Format(315 SubsystemStrings.ImplementationNotFound,316 id.ToString()));317 }318 319 ISubsystem target = _registeredImpls[index];320 if (_registeredImpls.Count == 1)321 {322 _registeredImpls = Utils.EmptyReadOnlyCollection<TConcreteSubsystem>();323 _cachedImplInfos = Utils.EmptyReadOnlyCollection<ImplementationInfo>();324 }325 else326 {327 int newCapacity = _registeredImpls.Count - 1;328 var implList = new List<TConcreteSubsystem>(newCapacity);329 var implInfo = new List<ImplementationInfo>(newCapacity);330 331 for (int i = 0; i < _registeredImpls.Count; i++)332 {333 if (index == i)334 {335 continue;336 }337 338 implList.Add(_registeredImpls[i]);339 implInfo.Add(_cachedImplInfos[i]);340 }341 342 _registeredImpls = new ReadOnlyCollection<TConcreteSubsystem>(implList);343 _cachedImplInfos = new ReadOnlyCollection<ImplementationInfo>(implInfo);344 }345 346 return target;347 }348 }349 350 internal TConcreteSubsystem? GetImplementation()351 {352 var localRef = _registeredImpls;353 return localRef.Count > 0 ? localRef[localRef.Count - 1] : null;354 }355 356 internal ReadOnlyCollection<TConcreteSubsystem> GetAllImplementations()357 {358 return _registeredImpls;359 }360 }361}362 