/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Management.Automation;
using System.Management.Automation.Internal;
namespace Microsoft.PowerShell.Commands.Internal.Format
{
///
/// inner command class used to manage the sub pipelines
/// it determines which command should process the incoming objects
/// based on the object type
///
/// This class is the implementation class for out-console and out-file
///
internal sealed class OutputManagerInner : ImplementationCommandBase
{
#region tracer
[TraceSource("format_out_OutputManagerInner", "OutputManagerInner")]
internal static PSTraceSource tracer = PSTraceSource.GetTracer("format_out_OutputManagerInner", "OutputManagerInner");
#endregion tracer
#region LineOutput
internal LineOutput LineOutput
{
set
{
lock (_syncRoot)
{
_lo = value;
if (_isStopped)
{
_lo.StopProcessing();
}
}
}
}
private LineOutput _lo = null;
#endregion
///
/// handler for processing each object coming through the pipeline
/// it forwards the call to the pipeline manager object
///
internal override void ProcessRecord()
{
PSObject so = this.ReadObject();
if (so == null || so == AutomationNull.Value)
{
return;
}
// on demand initialization when the first pipeline
// object is initialized
if (_mgr == null)
{
_mgr = new SubPipelineManager();
_mgr.Initialize(_lo, this.OuterCmdlet().Context);
}
#if false
// if the object supports IEnumerable,
// unpack the object and process each member separately
IEnumerable e = PSObjectHelper.GetEnumerable (so);
if (e == null)
{
this.mgr.Process (so);
}
else
{
foreach (object obj in e)
{
this.mgr.Process (PSObjectHelper.AsPSObject (obj));
}
}
#else
_mgr.Process(so);
#endif
}
///
/// handler for processing shut down. It forwards the call to the
/// pipeline manager object
///
internal override void EndProcessing()
{
// shut down only if we ever processed a pipeline object
if (_mgr != null)
_mgr.ShutDown();
}
internal override void StopProcessing()
{
lock (_syncRoot)
{
if (_lo != null)
{
_lo.StopProcessing();
}
_isStopped = true;
}
}
///
/// make sure we dispose of the sub pipeline manager
///
protected override void InternalDispose()
{
base.InternalDispose();
if (_mgr != null)
{
_mgr.Dispose();
_mgr = null;
}
}
///
/// instance of the pipeline manager object
///
private SubPipelineManager _mgr = null;
///
/// True if the cmdlet has been stopped
///
private bool _isStopped = false;
///
/// Lock object
///
private object _syncRoot = new object();
}
///
/// object managing the sub-pipelines that execute
/// different output commands (or different instances of the
/// default one)
///
internal sealed class SubPipelineManager : IDisposable
{
///
/// entry defining a command to be run in a separate pipeline
///
private sealed class CommandEntry : IDisposable
{
///
/// instance of pipeline wrapper object
///
internal CommandWrapper command = new CommandWrapper();
///
///
///
/// ETS type name of the object to process
/// true if there is a match
internal bool AppliesToType(string typeName)
{
foreach (String s in _applicableTypes)
{
if (string.Equals(s, typeName, StringComparison.OrdinalIgnoreCase))
return true;
}
return false;
}
///
/// just dispose of the inner command wrapper
///
public void Dispose()
{
if (this.command == null)
return;
this.command.Dispose();
this.command = null;
}
///
/// ordered list of ETS type names this object is handling
///
private StringCollection _applicableTypes = new StringCollection();
}
///
/// Initialize the pipeline manager before any object is processed
///
/// LineOutput to pass to the child pipelines
/// ExecutionContext to pass to the child pipelines
internal void Initialize(LineOutput lineOutput, ExecutionContext context)
{
_lo = lineOutput;
InitializeCommandsHardWired(context);
}
///
/// hard wired registration helper for specialized types
///
/// ExecutionContext to pass to the child pipeline
private void InitializeCommandsHardWired(ExecutionContext context)
{
// set the default handler
RegisterCommandDefault(context, "out-lineoutput", typeof(OutLineOutputCommand));
/*
NOTE:
This is the spot where we could add new specialized handlers for
additional types. Adding a handler here would cause a new sub-pipeline
to be created.
For example, the following line would add a new handler named "out-foobar"
to be invoked when the incoming object type is "MyNamespace.Whatever.FooBar"
RegisterCommandForTypes (context, "out-foobar", new string[] { "MyNamespace.Whatever.FooBar" });
And the method can be like this:
private void RegisterCommandForTypes (ExecutionContext context, string commandName, Type commandType, string[] types)
{
CommandEntry ce = new CommandEntry ();
ce.command.Initialize (context, commandName, commandType);
ce.command.AddNamedParameter ("LineOutput", this.lo);
for (int k = 0; k < types.Length; k++)
{
ce.AddApplicableType (types[k]);
}
this.commandEntryList.Add (ce);
}
*/
}
///
/// register the default output command
///
/// ExecutionContext to pass to the child pipeline
/// name of the command to execute
/// Type of the command to execute
private void RegisterCommandDefault(ExecutionContext context, string commandName, Type commandType)
{
CommandEntry ce = new CommandEntry();
ce.command.Initialize(context, commandName, commandType);
ce.command.AddNamedParameter("LineOutput", _lo);
_defaultCommandEntry = ce;
}
///
/// process an incoming parent pipeline object
///
/// pipeline object to process
internal void Process(PSObject so)
{
// select which pipeline should handle the object
CommandEntry ce = this.GetActiveCommandEntry(so);
Diagnostics.Assert(ce != null, "CommandEntry ce must not be null");
// delegate the processing
ce.command.Process(so);
}
///
/// shut down the child pipelines
///
internal void ShutDown()
{
// we assume that command entries are never null
foreach (CommandEntry ce in _commandEntryList)
{
Diagnostics.Assert(ce != null, "ce != null");
ce.command.ShutDown();
ce.command = null;
}
// we assume we always have a default command entry
Diagnostics.Assert(_defaultCommandEntry != null, "defaultCommandEntry != null");
_defaultCommandEntry.command.ShutDown();
_defaultCommandEntry.command = null;
}
public void Dispose()
{
// we assume that command entries are never null
foreach (CommandEntry ce in _commandEntryList)
{
Diagnostics.Assert(ce != null, "ce != null");
ce.Dispose();
}
// we assume we always have a default command entry
Diagnostics.Assert(_defaultCommandEntry != null, "defaultCommandEntry != null");
_defaultCommandEntry.Dispose();
}
///
/// it selects the applicable out command (it can be the default one)
/// to process the current pipeline object
///
/// pipeline object to be processed
/// applicable command entry
private CommandEntry GetActiveCommandEntry(PSObject so)
{
string typeName = PSObjectHelper.PSObjectIsOfExactType(so.InternalTypeNames);
foreach (CommandEntry ce in _commandEntryList)
{
if (ce.AppliesToType(typeName))
return ce;
}
// failed any match: return the default handler
return _defaultCommandEntry;
}
private LineOutput _lo = null;
///
/// list of command entries, each with a set of applicable types
///
private List _commandEntryList = new List();
///
/// default command entry to be executed when all type matches fail
///
private CommandEntry _defaultCommandEntry = new CommandEntry();
}
}