public static void OnPipelineComplete(PipelineEventArgs e) { if (PipelineComplete != null) { PipelineComplete(null, e); } }
public Pipeline(string commandString = null) { // Add this assembly to initialze the filters // I moved this to the static constructor so it only inits once and always inits when accessed statically // (I'm leaving this comment here in case I get confused later...) // AddAssembly(Assembly.GetExecutingAssembly()); if (!string.IsNullOrWhiteSpace(commandString)) { AddCommand(commandString); } var configSection = (PipelineConfigSection)ConfigurationManager.GetSection("denina"); if (configSection != null) { foreach (PipelineConfigVariable configVariable in configSection.Variables) { SetGlobalVariable(configVariable.Key, configVariable.Value, true); } } LogEntries = new List <ExecutionLog>(); // Raise the PipelineCreated event var eventArgs = new PipelineEventArgs(this, null); OnPipelineCreated(eventArgs); }
public string Execute(string input = null) { // Clear the debug data DebugData.Clear(); // Add a pass-through command at the end just to hold a label called "end". if (commands.Any(c => c.Label == FINAL_COMMAND_LABEL)) { commands.Remove(commands.First(c => c.Label == FINAL_COMMAND_LABEL)); } commands.Add( new PipelineCommand() { CommandName = LABEL_COMMAND, Label = FINAL_COMMAND_LABEL, CommandArgs = new Dictionary <object, string>() { { 0, FINAL_COMMAND_LABEL } } } ); // We set the global variable to the incoming string. It will be modified and eventually returned from this variable slot. SetVariable(GLOBAL_VARIABLE_NAME, input); // We're going to set up a linked list of commands. Each command holds a reference to the next command in its SendToLabel property. The last command is NULL. var commandQueue = new Dictionary <string, PipelineCommand>(); for (var index = 0; index < commands.Count; index++) { var thisCommand = commands[index]; // If this is a "Label" command, then we need to get the label "out" to a property if (thisCommand.NormalizedCommandName == LABEL_COMMAND) { thisCommand.Label = thisCommand.DefaultArgument; } // If (1) this command doesn't already have a SendToLabel (it...shouldn't...I don't think), and (2) we're not on the last command, then set the SendToLabel of this command to the Label of the next command if (thisCommand.SendToLabel == null && index < commands.Count - 1) { thisCommand.SendToLabel = commands[index + 1].Label; } // Add this command to the queue, keyed to its Label commandQueue.Add(thisCommand.Label.ToLower(), thisCommand); } // We're going to stay in this loop, resetting "command" each iteration, until SendToLabel is NULL NextCommandLabel = commandQueue.First().Value.Label; while (true) { // Do we have a next command? if (NextCommandLabel == null) { // Stick a fork in us, we're done break; } // Does the specified next command exist? if (!commandQueue.ContainsKey(NextCommandLabel.ToLower())) { throw new Exception(string.Format("Specified command label \"{0}\" does not exist in the command queue.", NextCommandLabel)); } // Get the next command var command = commandQueue[NextCommandLabel.ToLower()]; // Create the debug entry var debugData = new DebugEntry(command); // Are we writing to a variable? if (command.NormalizedCommandName == WRITE_TO_VARIABLE_COMMAND) { // Get the active text and copy it to a different variable SetVariable(command.OutputVariable, GetVariable(GLOBAL_VARIABLE_NAME)); NextCommandLabel = command.SendToLabel; continue; } // Are we reading from a variable? if (command.NormalizedCommandName == READ_FROM_VARIABLE_COMMAND) { // Get the variable and copy it into the active text SetVariable(GLOBAL_VARIABLE_NAME, GetVariable(command.InputVariable)); NextCommandLabel = command.SendToLabel; continue; } // Is this a label? if (command.NormalizedCommandName == LABEL_COMMAND) { NextCommandLabel = command.SendToLabel; continue; } // Note that the above commands will never actually execute. This is why their methods are just empty shells... // Do we a method for this command? if (!CommandMethods.ContainsKey(command.NormalizedCommandName)) { // This command doesn't exist. We're going to try to be helpful and let the user know if it's becaue of a missing dependency. var errorString = hiddenCommandMethods.ContainsKey(command.NormalizedCommandName) ? string.Format(hiddenCommandMethods[command.NormalizedCommandName]) // This should be the reason the command is hidden : string.Format(@"No command loaded for ""{0}""", command.CommandName); throw new DeninaException(errorString); } // Set a pipeline reference which can be accessed inside the filter method command.Pipeline = this; // Resolve any arguments that are actually variable names command.ResolveArguments(); // Execute var method = CommandMethods[command.NormalizedCommandName]; try { timer.Reset(); timer.Start(); // This is where we make the actual method call. We get the text out of the InputVariable slot, and we put it back into the OutputVariable slot. (These are usually the same slot...) // We're going to "SafeSet" this, so they can't pipe output to a read-only variable debugData.InputValue = GetVariable(command.InputVariable).ToString(); var output = method.Invoke(null, new[] { GetVariable(command.InputVariable), command }); debugData.OutputValue = output.ToString(); // If we're appending, tack this onto what was passed in (really, prepend was was passed in) if (command.AppendToLast) { output = string.Concat(GetVariable(command.InputVariable), output); } SafeSetVariable(command.OutputVariable, output); command.ElapsedTime = timer.ElapsedMilliseconds; } catch (Exception e) { if (e.InnerException is DeninaException) { // Since this was reflected, the "outer" exception is "an exception was thrown by the target of an invocation" // Hence, the "real" exception is the inner exception var exception = (DeninaException)e.InnerException; exception.CurrentCommandText = command.OriginalText; exception.CurrentCommandName = command.NormalizedCommandName; throw exception; } else { throw; } } // Set the pointer to the next command NextCommandLabel = command.SendToLabel; DebugData.Add(debugData); } var finalOutput = GetVariable(GLOBAL_VARIABLE_NAME).ToString(); // Raise the PipelineCompleted event var eventArgs = new PipelineEventArgs(this, finalOutput); OnPipelineComplete(eventArgs); finalOutput = eventArgs.Value; return(finalOutput); }
private static void OnPipelineCreated(PipelineEventArgs e) => PipelineCreated?.Invoke(null, e);
private static void OnPipelineComplete(PipelineEventArgs e) => PipelineComplete?.Invoke(null, e);
public string Execute(string input = null) { // Clear the debug data LogEntries.Clear(); // Have any commandFactories been defined? if (commandFactories != null) { // We need to execute any command factories and modify the command list // We can't do a foreach, because we're going to modify this list // Also: _this does not account for recursion or circular inclusions!!!!_ // The GetLogicHashCode method can be used to compare command logic to ensure uniqueness for (var i = 0; i < commands.Count(); i++) { var thisCommand = commands[i]; var factoryCommand = commandFactories.FirstOrDefault(c => Regex.IsMatch(thisCommand.NormalizedCommandName, StringUtilities.ConvertWildcardToRegex(c.Key), RegexOptions.IgnoreCase)); if (factoryCommand.Value != null) // This test is a little weird. KeyValue is a struct, so a normal null check doesn't work, so I do the null check against the Value... { // Run the factory, passing the entire command var commandsToInclude = factoryCommand.Value(thisCommand); // Include the source, for logging commandsToInclude.ToList().ForEach(c => { c.CommandFactorySource = thisCommand.OriginalText; }); // Insert these commands AFTER the factory command commands.InsertRange(i + 1, commandsToInclude); // Delete the factory command commands.Remove(thisCommand); // Inserted commands will also be processed for command factories, since the next iteration of this loop will pickup at the first inserted command } } } // At this point, all command factories should be resolved for this and subsequent executions // Add a pass-through command at the end just to hold a label called "end". if (commands.Any(c => c.Label == FINAL_COMMAND_LABEL)) { commands.Remove(commands.First(c => c.Label == FINAL_COMMAND_LABEL)); } commands.Add( new PipelineCommand() { FullyQualifiedCommandName = LABEL_COMMAND, Label = FINAL_COMMAND_LABEL, CommandArgs = new Dictionary <object, string>() { { 0, FINAL_COMMAND_LABEL } } } ); // We set the global variable to the incoming string. It will be modified and eventually returned from this variable slot. SetVariable(GLOBAL_VARIABLE_NAME, input); // We're going to set up a linked list of commands. Each command holds a reference to the next command in its SendToLabel property. The last command is NULL. var commandQueue = new Dictionary <string, PipelineCommand>(); for (var index = 0; index < commands.Count; index++) { var thisCommand = commands[index]; // If this is a "Label" command, then we need to get the label "out" to a property if (thisCommand.NormalizedCommandName == LABEL_COMMAND) { thisCommand.Label = thisCommand.DefaultArgument; } // If (1) this command doesn't already have a SendToLabel (it...shouldn't...I don't think), and (2) we're not on the last command, then set the SendToLabel of this command to the Label of the next command if (thisCommand.SendToLabel == null && index < commands.Count - 1) { thisCommand.SendToLabel = commands[index + 1].Label; } // Add this command to the queue, keyed to its Label commandQueue.Add(thisCommand.Label.ToLower(), thisCommand); } // We're going to stay in this loop, resetting "command" each iteration, until SendToLabel is NULL NextCommandLabel = commandQueue.First().Value.Label; while (true) { // Do we have a next command? if (NextCommandLabel == null) { // Stick a fork in us, we're done break; } // Does the specified next command exist? if (!commandQueue.ContainsKey(NextCommandLabel.ToLower())) { throw new Exception(string.Format("Specified command label \"{0}\" does not exist in the command queue.", NextCommandLabel)); } // Get the next command var command = commandQueue[NextCommandLabel.ToLower()]; // Create the debug entry var executionLog = new ExecutionLog(command, Variables); // Are we writing to a variable? if (command.NormalizedCommandName == WRITE_TO_VARIABLE_COMMAND) { // Get the active text and copy it to a different variable SetVariable(command.OutputVariable, GetVariable(GLOBAL_VARIABLE_NAME)); NextCommandLabel = command.SendToLabel; continue; } // Are we reading from a variable? if (command.NormalizedCommandName == READ_FROM_VARIABLE_COMMAND) { // Get the variable and copy it into the active text SetVariable(GLOBAL_VARIABLE_NAME, GetVariable(command.InputVariable)); NextCommandLabel = command.SendToLabel; continue; } // Is this a label? if (command.NormalizedCommandName == LABEL_COMMAND) { NextCommandLabel = command.SendToLabel; continue; } // Note that the above commands will never actually execute. This is why their methods are just empty shells... // Do we a method for this command? if (!CommandMethods.ContainsKey(command.NormalizedCommandName)) { // This command doesn't exist. We're going to try to be helpful and let the user know if it's becaue of a missing dependency. var errorString = hiddenCommandMethods.ContainsKey(command.NormalizedCommandName) ? string.Format(hiddenCommandMethods[command.NormalizedCommandName]) // This should be the reason the command is hidden : string.Format(@"No command loaded for ""{0}""", command.FullyQualifiedCommandName); throw new DeninaException(errorString); } // Set a pipeline reference which can be accessed inside the filter method command.Pipeline = this; // Resolve any arguments that are actually variable names command.ResolveArguments(); // Execute var method = CommandMethods[command.NormalizedCommandName]; try { timer.Reset(); timer.Start(); // Get the input from the designated variable var filterInput = (string)GetVariable(command.InputVariable); // Process the input through the event var executingFilterEventArgs = new FilterEventArgs(command, filterInput, null); OnFilterExecuting(executingFilterEventArgs); filterInput = executingFilterEventArgs.Input; command = executingFilterEventArgs.Command; // I'm not sure I need to do this, but just to be explicit... // TO DO: How do we track changes made to the input in an event? executionLog.InputValue = GetVariable(command.InputVariable).ToString(); // This is where we make the actual method call. We get the text out of the InputVariable slot, and we put it back into the OutputVariable slot. (These are usually the same slot...) // We create a delete so that we can use anonymous functions. Since non-anonymous functions are static, and anonymous functions aren't static, we have to create a // delegate so we can handle both var filter = (FilterDelegate)Delegate.CreateDelegate(typeof(FilterDelegate), null, method); var filterOutput = filter(filterInput, command, executionLog); // Process the output through the event var executedFilterEventArgs = new FilterEventArgs(command, null, filterOutput); OnFilterExecuted(executedFilterEventArgs); filterOutput = executedFilterEventArgs.Output; // TO DO: How do we track changes made to the output in an event? executionLog.OutputValue = filterOutput.ToString(); // If we're appending, tack this onto what was passed in (really, prepend was was passed in) if (command.AppendToOutput) { filterOutput = string.Concat(GetVariable(command.OutputVariable), filterOutput); } // We're going to "SafeSet" this, so they can't pipe output to a read-only variable SafeSetVariable(command.OutputVariable, filterOutput); executionLog.ElapsedTime = timer.ElapsedMilliseconds; // If we got here with no exception executionLog.SuccessfullyExecuted = true; } catch (DeninaException e) { e.CurrentCommandText = command.OriginalText; e.CurrentCommandName = command.NormalizedCommandName; throw; } // We are not going to handle a non-DeninaException. We'll just let that bubble up to the implementation's error handler // Set the pointer to the next command NextCommandLabel = command.SendToLabel; LogEntries.Add(executionLog); } var finalOutput = GetVariable(GLOBAL_VARIABLE_NAME).ToString(); // Raise the PipelineCompleted event var eventArgs = new PipelineEventArgs(this, finalOutput); OnPipelineComplete(eventArgs); finalOutput = eventArgs.Value; return(finalOutput); }