示例#1
0
 public static void OnPipelineComplete(PipelineEventArgs e)
 {
     if (PipelineComplete != null)
     {
         PipelineComplete(null, e);
     }
 }
示例#2
0
        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);
        }
示例#3
0
        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);
        }
示例#4
0
 private static void OnPipelineCreated(PipelineEventArgs e) => PipelineCreated?.Invoke(null, e);
示例#5
0
 private static void OnPipelineComplete(PipelineEventArgs e) => PipelineComplete?.Invoke(null, e);
示例#6
0
        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);
        }