bin/projects/dbatools/dbatools/Parameter/DbaSelectParameter.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
using System;
using System.Collections;
using System.Linq;
using System.Management.Automation;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace Sqlcollaborative.Dbatools.Parameter
{
    /// <summary>
    /// Class that automatically parses input chosen for the -Property parameter of Select-PSUObject
    /// </summary>
    public class DbaSelectParameter
    {
        /// <summary>
        /// The original input object
        /// </summary>
        public object InputObject;

        /// <summary>
        /// The value as Select-Object wants it
        /// </summary>
        public object Value;

        /// <summary>
        /// Builds a property parameter from string
        /// </summary>
        /// <param name="Value">The string to interpret</param>
        public DbaSelectParameter(string Value)
        {
            InputObject = Value;

            if (!Value.Contains(" "))
            {
                this.Value = Value;
                return;
            }

            #region Process Input
            // Runtime properties
            string valueName = "";
            string propertyName = "";
            string castType = "";
            string fromName = "_";
            string wherePropInput = "";
            string wherePropOutput = "";
            string sizeName = "";
            uint sizeDecimals = 0;
            bool sizeShow = false;

            string tempValue = Value.Trim();
            propertyName = tempValue.Split(' ')[0];
            valueName = propertyName;
            tempValue = tempValue.Substring(propertyName.Length);

            if (Regex.IsMatch(tempValue, @" as \w+", RegexOptions.IgnoreCase))
            {
                propertyName = Regex.Match(tempValue, @" as (\w+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" as \w+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" from [\w_]+", RegexOptions.IgnoreCase))
            {
                fromName = Regex.Match(tempValue, @" from ([\w_]+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" from [\w_]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" where [\w_]+ = [\w_]+", RegexOptions.IgnoreCase))
            {
                Match match = Regex.Match(tempValue, @" where ([\w_]+) = ([\w_]+)", RegexOptions.IgnoreCase);
                wherePropOutput = match.Groups[1].Value;
                wherePropInput = match.Groups[2].Value;
                tempValue = Regex.Replace(tempValue, @" where [\w_]+ = [\w_]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" to [\w\.]+", RegexOptions.IgnoreCase))
            {
                castType = Regex.Match(tempValue, @" to ([\w\.]+)", RegexOptions.IgnoreCase).Groups[1].Value;
                tempValue = Regex.Replace(tempValue, @" to [\w\.]+", "", RegexOptions.IgnoreCase);
            }

            if (Regex.IsMatch(tempValue, @" size \w+(:\d){1,2}", RegexOptions.IgnoreCase))
            {
                Match match = Regex.Match(tempValue, @" size (\w+)(:\d){1,2}", RegexOptions.IgnoreCase);
                sizeName = match.Groups[1].Value;
                sizeDecimals = UInt32.Parse(match.Groups[2].Captures[0].Value.Trim(':'));
                if (match.Groups[2].Captures.Count > 1)
                    sizeShow = match.Groups[2].Captures[1].Value == ":1";
                tempValue = Regex.Replace(tempValue, @" size \w+(:\d){1,2}", "", RegexOptions.IgnoreCase);
            }

            if (!String.IsNullOrEmpty(tempValue))
                throw new ArgumentException(String.Format("Failed to parse input! Original input: {0} | Unprocessed leftovers: {1}", Value, tempValue));
            #endregion Process Input

            #region Build Hashtable
            Hashtable table = new Hashtable();
            table["Name"] = propertyName;

            // Process cast strings
            string stringCast = "";
            if (!String.IsNullOrEmpty(castType))
                stringCast = String.Format("[{0}]", castType);

            // Process size strings
            string stringSizeStart = "";
            string stringSizeEnd = "";
            if (sizeName != "")
            {
                stringSizeStart = "[System.Math]::Round((";
                stringSizeEnd = String.Format(" / 1{0}), {1})", sizeName, sizeDecimals);
                if (sizeShow)
                {
                    stringSizeStart = String.Format("\"$({0}", stringSizeStart);
                    stringSizeEnd = String.Format("
{0}) {1}\"", stringSizeEnd, sizeName);
                }
            }

            // Process value strings
            string stringGuidVar = "${" + Guid.NewGuid().ToString() + "}";
            string preLine = String.Format("{0} = $_\n", stringGuidVar);
            string stringValue = String.Format("${0}", fromName);
            if (fromName != "_" && wherePropInput != "")
            {
                stringValue = String.Format("({1} | Where-Object {2} -eq {0}.{3})", stringGuidVar, stringValue, wherePropOutput, wherePropInput);
            }

            if (propertyName != ".")
                stringValue = String.Format("{0}.{1}", stringValue, valueName);

            // <guid> = $_
            // "(<size>(<cast>(<value>))<sizeName>)"
            string script = String.Format("{0}({1}({2}({3})){4})", preLine, stringSizeStart, stringCast, stringValue, stringSizeEnd);

            table["Expression"] = ScriptBlock.Create(script);
            this.Value = table;
            #endregion Build Hashtable
        }

        /// <summary>
        /// Builds a select parameter from a hashtable (pretty straightforward)
        /// </summary>
        /// <param name="Hash">The hashtable to accept</param>
        public DbaSelectParameter(Hashtable Hash)
        {
            InputObject = Hash;
            Value = Hash;
        }
    }
}