/// <summary>
 /// Validates <see cref="ActionPlan"/>
 /// </summary>
 /// <param name="plan"><see cref="ActionPlan"/> to validate.</param>
 public void Validate(ActionPlan plan)
 {
     throw new NotImplementedException();
 }
        /// <summary>Asynchronously executes all actions in the <see cref="ActionPlan"/> that is specified in <paramref name="plan"/>.</summary>
        /// <param name="plan">The <see cref="ActionPlan"/> to execute.</param>
        /// <param name="properties">Collection of global properties. Names should match those returned by <see cref="ActionPlan.Properties"/>.</param>
        /// <param name="token">The cancellation token that can be used for cancelling execution.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        public async Task ExecuteAsync(ActionPlan plan, Dictionary<string, object> properties, CancellationToken token)
        {
            this.ThrowErrorIfDisposed();

            if (properties == null)
            {
                throw new ArgumentNullException("properties");
            }

            this.Validate(plan);

            // Validate global properties values
            foreach (var property in properties.Where(property => (property.Value == null)))
            {
                throw new ArgumentOutOfRangeException("properties", string.Format("Property '{0}' must contain a value.", property.Key));
            }

            // inject values for non-mandatory properties if not specified.
            foreach (var property in plan.Properties.Where(property => property.Optional && !properties.ContainsKey(property.Name)))
            {
                properties.Add(property.Name, property.Value);
            }

            var propertyNames = plan.Properties.Select(propertyRow => propertyRow.Name).ToArray();

            if (propertyNames.Any(propertyName => !properties.ContainsKey(propertyName)))
            {
                const string MessageFormat = "The properties '{0}' are required. Properties that has been provided are '{1}'";
                string message = string.Format(MessageFormat, string.Join(", ", propertyNames), string.Join(", ", properties.Keys));
                
                throw new ArgumentOutOfRangeException("properties", message);
            }

            int progress = 0;

            var propertyCache = properties.ToDictionary(
                    globalProperty => GetPropertyMacroName(plan.GetType().Name, globalProperty.Key),
                    globalProperty => globalProperty.Value);

            IActionExecutionContext context = new ActionExecutionContext();

            foreach (var actionDefinition in plan.Actions)
            {
                this.ReportProgress(string.Format("Executing '{0}' operation ... ", actionDefinition.Id), progress);

                string actionName = actionDefinition.Name;
                IAction action = this.GetActionByName(actionName);

                // Set input properties
                SetActionInputPropertyValues(actionDefinition, action, propertyCache);

                // Execute action
                try
                {
                    await action.ExecuteAsync(context);
                }
                catch (Exception e)
                {
                    string message = string.Format("Execution of action '{0}' failed. Details: {1}", actionDefinition.Id, e.Message);
                    throw new InvalidOperationException(message, e);
                }

                // Collect output properties to be available for macro expansion
                GetActionOutputPropertyValues(actionDefinition, action, propertyCache);

                // Check if validation should be done 
                foreach (var property in actionDefinition.OutputProperties.Where(property => property.Validate))
                {
                    var propertyInfo = GetGetPropertyInfo(action.GetType(), property.Name, actionDefinition.Id);
                    var propertyValue = propertyInfo.GetValue(action);
                    var expectedValue = System.Convert.ChangeType(property.ExpectedValue, propertyInfo.PropertyType);

                    if (!propertyValue.Equals(expectedValue))
                    {
                        string message = property.ValidationMessage;
                        if (message == null)
                        {
                            string.Format("Action {0} fails validation, property {1} expected {2} but returned {3}", actionDefinition.Id, property.Name, property.ExpectedValue, propertyValue);
                        }
                        else
                        {
                            message = ExpandMacros(message, propertyCache).ToString();
                        }

                        throw new ArgumentOutOfRangeException("OutputProperties", message);
                    }
                }

                progress += 100 / plan.Actions.Length;
                token.ThrowIfCancellationRequested();
            }

            this.ReportProgress("Execution completed", 100);
        }
 public Task ExecuteAsync(ActionPlan plan, Dictionary<string, object> properties, CancellationToken token)
 {
     return null;
 }
        /// <summary>
        /// Validates <see cref="ActionPlan"/>
        /// </summary>
        /// <param name="plan"><see cref="ActionPlan"/> to validate.</param>
        /// <exception cref="ArgumentNullException">Value of the <paramref name="plan"/> argument is null.</exception>
        public void Validate(ActionPlan plan)
        {
            if (plan == null)
            {
                throw new ArgumentNullException("plan");
            }

            this.ThrowErrorIfDisposed();

            plan.Validate();

            foreach (var actionDefinition in plan.Actions)
            {
                string actionName = actionDefinition.Name;
                IAction action = this.GetActionByName(actionName);

                // Validate input properties
                foreach (var inPropertyRow in actionDefinition.InputProperties)
                {
                    GetSetPropertyInfo(action.GetType(), inPropertyRow.Name, actionDefinition.Id);
                }

                // Validate output properties
                foreach (var outPropertyRow in actionDefinition.OutputProperties)
                {
                    GetGetPropertyInfo(action.GetType(), outPropertyRow.Name, actionDefinition.Id);
                }
            }
        }