MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Collections.Specialized;7using System.Management.Automation;8using System.Management.Automation.Internal;9 10namespace Microsoft.PowerShell.Commands.Internal.Format11{12 /// <summary>13 /// Inner command class used to manage the sub pipelines14 /// it determines which command should process the incoming objects15 /// based on the object type16 ///17 /// This class is the implementation class for out-console and out-file.18 /// </summary>19 internal sealed class OutputManagerInner : ImplementationCommandBase20 {21 #region tracer22 [TraceSource("format_out_OutputManagerInner", "OutputManagerInner")]23 internal static readonly PSTraceSource tracer = PSTraceSource.GetTracer("format_out_OutputManagerInner", "OutputManagerInner");24 #endregion tracer25 26 #region LineOutput27 internal LineOutput LineOutput28 {29 set30 {31 lock (_syncRoot)32 {33 _lo = value;34 35 if (_isStopped)36 {37 _lo.StopProcessing();38 }39 }40 }41 }42 43 private LineOutput _lo = null;44 #endregion45 46 /// <summary>47 /// Handler for processing each object coming through the pipeline48 /// it forwards the call to the pipeline manager object.49 /// </summary>50 internal override void ProcessRecord()51 {52 PSObject so = this.ReadObject();53 54 if (so == null || so == AutomationNull.Value)55 {56 return;57 }58 59 // on demand initialization when the first pipeline60 // object is initialized61 if (_mgr == null)62 {63 _mgr = new SubPipelineManager();64 _mgr.Initialize(_lo, this.OuterCmdlet().Context);65 }66 67#if false68 // if the object supports IEnumerable,69 // unpack the object and process each member separately70 IEnumerable e = PSObjectHelper.GetEnumerable (so);71 72 if (e == null)73 {74 this.mgr.Process (so);75 }76 else77 {78 foreach (object obj in e)79 {80 this.mgr.Process (PSObjectHelper.AsPSObject (obj));81 }82 }83#else84 _mgr.Process(so);85#endif86 }87 88 /// <summary>89 /// Handler for processing shut down. It forwards the call to the90 /// pipeline manager object.91 /// </summary>92 internal override void EndProcessing()93 {94 // shut down only if we ever processed a pipeline object95 _mgr?.ShutDown();96 }97 98 internal override void StopProcessing()99 {100 lock (_syncRoot)101 {102 _lo?.StopProcessing();103 _isStopped = true;104 }105 }106 107 /// <summary>108 /// Make sure we dispose of the sub pipeline manager.109 /// </summary>110 protected override void InternalDispose()111 {112 base.InternalDispose();113 if (_mgr != null)114 {115 _mgr.Dispose();116 _mgr = null;117 }118 }119 120 /// <summary>121 /// Instance of the pipeline manager object.122 /// </summary>123 private SubPipelineManager _mgr = null;124 125 /// <summary>126 /// True if the cmdlet has been stopped.127 /// </summary>128 private bool _isStopped = false;129 130 /// <summary>131 /// Lock object.132 /// </summary>133 private readonly object _syncRoot = new object();134 }135 136 /// <summary>137 /// Object managing the sub-pipelines that execute138 /// different output commands (or different instances of the139 /// default one)140 /// </summary>141 internal sealed class SubPipelineManager : IDisposable142 {143 /// <summary>144 /// Entry defining a command to be run in a separate pipeline.145 /// </summary>146 private sealed class CommandEntry : IDisposable147 {148 /// <summary>149 /// Instance of pipeline wrapper object.150 /// </summary>151 internal CommandWrapper command = new CommandWrapper();152 153 /// <summary>154 /// </summary>155 /// <param name="typeName">ETS type name of the object to process.</param>156 /// <returns>True if there is a match.</returns>157 internal bool AppliesToType(string typeName)158 {159 foreach (string s in _applicableTypes)160 {161 if (string.Equals(s, typeName, StringComparison.OrdinalIgnoreCase))162 return true;163 }164 165 return false;166 }167 168 /// <summary>169 /// Just dispose of the inner command wrapper.170 /// </summary>171 public void Dispose()172 {173 if (this.command == null)174 return;175 176 this.command.Dispose();177 this.command = null;178 }179 180 /// <summary>181 /// Ordered list of ETS type names this object is handling.182 /// </summary>183 private readonly StringCollection _applicableTypes = new StringCollection();184 }185 186 /// <summary>187 /// Initialize the pipeline manager before any object is processed.188 /// </summary>189 /// <param name="lineOutput">LineOutput to pass to the child pipelines.</param>190 /// <param name="context">ExecutionContext to pass to the child pipelines.</param>191 internal void Initialize(LineOutput lineOutput, ExecutionContext context)192 {193 _lo = lineOutput;194 InitializeCommandsHardWired(context);195 }196 197 /// <summary>198 /// Hard wired registration helper for specialized types.199 /// </summary>200 /// <param name="context">ExecutionContext to pass to the child pipeline.</param>201 private void InitializeCommandsHardWired(ExecutionContext context)202 {203 // set the default handler204 RegisterCommandDefault(context, "out-lineoutput", typeof(OutLineOutputCommand));205 /*206 NOTE:207 This is the spot where we could add new specialized handlers for208 additional types. Adding a handler here would cause a new sub-pipeline209 to be created.210 211 For example, the following line would add a new handler named "out-example"212 to be invoked when the incoming object type is "MyNamespace.Whatever.Example"213 214 RegisterCommandForTypes (context, "out-example", new string[] { "MyNamespace.Whatever.Example" });215 216 And the method can be like this:217 private void RegisterCommandForTypes (ExecutionContext context, string commandName, Type commandType, string[] types)218 {219 CommandEntry ce = new CommandEntry ();220 221 ce.command.Initialize (context, commandName, commandType);222 ce.command.AddNamedParameter ("LineOutput", this.lo);223 for (int k = 0; k < types.Length; k++)224 {225 ce.AddApplicableType (types[k]);226 }227 228 this.commandEntryList.Add (ce);229 }230 */231 }232 233 /// <summary>234 /// Register the default output command.235 /// </summary>236 /// <param name="context">ExecutionContext to pass to the child pipeline.</param>237 /// <param name="commandName">Name of the command to execute.</param>238 /// <param name="commandType">Type of the command to execute.</param>239 private void RegisterCommandDefault(ExecutionContext context, string commandName, Type commandType)240 {241 CommandEntry ce = new CommandEntry();242 243 ce.command.Initialize(context, commandName, commandType);244 ce.command.AddNamedParameter("LineOutput", _lo);245 _defaultCommandEntry = ce;246 }247 248 /// <summary>249 /// Process an incoming parent pipeline object.250 /// </summary>251 /// <param name="so">Pipeline object to process.</param>252 internal void Process(PSObject so)253 {254 // select which pipeline should handle the object255 CommandEntry ce = this.GetActiveCommandEntry(so);256 257 Diagnostics.Assert(ce != null, "CommandEntry ce must not be null");258 259 // delegate the processing260 ce.command.Process(so);261 }262 263 /// <summary>264 /// Shut down the child pipelines.265 /// </summary>266 internal void ShutDown()267 {268 // we assume that command entries are never null269 foreach (CommandEntry ce in _commandEntryList)270 {271 Diagnostics.Assert(ce != null, "ce != null");272 ce.command.ShutDown();273 ce.command = null;274 }275 276 // we assume we always have a default command entry277 Diagnostics.Assert(_defaultCommandEntry != null, "defaultCommandEntry != null");278 _defaultCommandEntry.command.ShutDown();279 _defaultCommandEntry.command = null;280 }281 282 public void Dispose()283 {284 // we assume that command entries are never null285 foreach (CommandEntry ce in _commandEntryList)286 {287 Diagnostics.Assert(ce != null, "ce != null");288 ce.Dispose();289 }290 291 // we assume we always have a default command entry292 Diagnostics.Assert(_defaultCommandEntry != null, "defaultCommandEntry != null");293 _defaultCommandEntry.Dispose();294 }295 296 /// <summary>297 /// It selects the applicable out command (it can be the default one)298 /// to process the current pipeline object.299 /// </summary>300 /// <param name="so">Pipeline object to be processed.</param>301 /// <returns>Applicable command entry.</returns>302 private CommandEntry GetActiveCommandEntry(PSObject so)303 {304 string typeName = PSObjectHelper.PSObjectIsOfExactType(so.InternalTypeNames);305 foreach (CommandEntry ce in _commandEntryList)306 {307 if (ce.AppliesToType(typeName))308 return ce;309 }310 311 // failed any match: return the default handler312 return _defaultCommandEntry;313 }314 315 private LineOutput _lo = null;316 317 /// <summary>318 /// List of command entries, each with a set of applicable types.319 /// </summary>320 private readonly List<CommandEntry> _commandEntryList = new List<CommandEntry>();321 322 /// <summary>323 /// Default command entry to be executed when all type matches fail.324 /// </summary>325 private CommandEntry _defaultCommandEntry = new CommandEntry();326 }327}328 