diff options
Diffstat (limited to 'src/WixToolset.Data.WindowsInstaller/Field.cs')
-rw-r--r-- | src/WixToolset.Data.WindowsInstaller/Field.cs | 266 |
1 files changed, 266 insertions, 0 deletions
diff --git a/src/WixToolset.Data.WindowsInstaller/Field.cs b/src/WixToolset.Data.WindowsInstaller/Field.cs new file mode 100644 index 00000000..74b78229 --- /dev/null +++ b/src/WixToolset.Data.WindowsInstaller/Field.cs | |||
@@ -0,0 +1,266 @@ | |||
1 | // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. | ||
2 | |||
3 | namespace WixToolset.Data | ||
4 | { | ||
5 | using System; | ||
6 | using System.Diagnostics; | ||
7 | using System.Globalization; | ||
8 | using System.Xml; | ||
9 | |||
10 | /// <summary> | ||
11 | /// Field containing data for a column in a row. | ||
12 | /// </summary> | ||
13 | public class Field | ||
14 | { | ||
15 | private object data; | ||
16 | |||
17 | /// <summary> | ||
18 | /// Instantiates a new Field. | ||
19 | /// </summary> | ||
20 | /// <param name="columnDefinition">Column definition for this field.</param> | ||
21 | protected Field(ColumnDefinition columnDefinition) | ||
22 | { | ||
23 | this.Column = columnDefinition; | ||
24 | } | ||
25 | |||
26 | /// <summary> | ||
27 | /// Gets or sets the column definition for this field. | ||
28 | /// </summary> | ||
29 | /// <value>Column definition.</value> | ||
30 | public ColumnDefinition Column { get; private set; } | ||
31 | |||
32 | /// <summary> | ||
33 | /// Gets or sets the data for this field. | ||
34 | /// </summary> | ||
35 | /// <value>Data in the field.</value> | ||
36 | public object Data | ||
37 | { | ||
38 | get | ||
39 | { | ||
40 | return this.data; | ||
41 | } | ||
42 | |||
43 | set | ||
44 | { | ||
45 | // Validate the value before setting it. | ||
46 | this.data = this.Column.ValidateValue(value); | ||
47 | } | ||
48 | } | ||
49 | |||
50 | /// <summary> | ||
51 | /// Gets or sets whether this field is modified. | ||
52 | /// </summary> | ||
53 | /// <value>Whether this field is modified.</value> | ||
54 | public bool Modified { get; set; } | ||
55 | |||
56 | /// <summary> | ||
57 | /// Gets or sets the previous data. | ||
58 | /// </summary> | ||
59 | /// <value>The previous data.</value> | ||
60 | public string PreviousData { get; set; } | ||
61 | |||
62 | /// <summary> | ||
63 | /// Instantiate a new Field object of the correct type. | ||
64 | /// </summary> | ||
65 | /// <param name="columnDefinition">The column definition for the field.</param> | ||
66 | /// <returns>The new Field object.</returns> | ||
67 | public static Field Create(ColumnDefinition columnDefinition) | ||
68 | { | ||
69 | return (ColumnType.Object == columnDefinition.Type) ? new ObjectField(columnDefinition) : new Field(columnDefinition); | ||
70 | } | ||
71 | |||
72 | /// <summary> | ||
73 | /// Sets the value of a particular field in the row without validating. | ||
74 | /// </summary> | ||
75 | /// <param name="field">field index.</param> | ||
76 | /// <param name="value">Value of a field in the row.</param> | ||
77 | /// <returns>True if successful, false if validation failed.</returns> | ||
78 | public bool BestEffortSet(object value) | ||
79 | { | ||
80 | bool success = true; | ||
81 | object bestEffortValue = value; | ||
82 | |||
83 | try | ||
84 | { | ||
85 | bestEffortValue = this.Column.ValidateValue(value); | ||
86 | } | ||
87 | catch (InvalidOperationException) | ||
88 | { | ||
89 | success = false; | ||
90 | } | ||
91 | |||
92 | this.data = bestEffortValue; | ||
93 | return success; | ||
94 | } | ||
95 | |||
96 | /// <summary> | ||
97 | /// Determine if this field is identical to another field. | ||
98 | /// </summary> | ||
99 | /// <param name="field">The other field to compare to.</param> | ||
100 | /// <returns>true if they are equal; false otherwise.</returns> | ||
101 | public bool IsIdentical(Field field) | ||
102 | { | ||
103 | return (this.Column.Name == field.Column.Name && | ||
104 | ((null != this.data && this.data.Equals(field.data)) || (null == this.data && null == field.data))); | ||
105 | } | ||
106 | |||
107 | /// <summary> | ||
108 | /// Overrides the built in object implementation to return the field's data as a string. | ||
109 | /// </summary> | ||
110 | /// <returns>Field's data as a string.</returns> | ||
111 | public override string ToString() | ||
112 | { | ||
113 | return this.AsString(); | ||
114 | } | ||
115 | |||
116 | /// <summary> | ||
117 | /// Gets the field as an integer. | ||
118 | /// </summary> | ||
119 | /// <returns>Field's data as an integer.</returns> | ||
120 | public int AsInteger() | ||
121 | { | ||
122 | return (this.data is int) ? (int)this.data : Convert.ToInt32(this.data, CultureInfo.InvariantCulture); | ||
123 | } | ||
124 | |||
125 | /// <summary> | ||
126 | /// Gets the field as an integer that could be null. | ||
127 | /// </summary> | ||
128 | /// <returns>Field's data as an integer that could be null.</returns> | ||
129 | public int? AsNullableInteger() | ||
130 | { | ||
131 | return (null == this.data) ? (int?)null : (this.data is int) ? (int)this.data : Convert.ToInt32(this.data, CultureInfo.InvariantCulture); | ||
132 | } | ||
133 | |||
134 | /// <summary> | ||
135 | /// Gets the field as a string. | ||
136 | /// </summary> | ||
137 | /// <returns>Field's data as a string.</returns> | ||
138 | public string AsString() | ||
139 | { | ||
140 | return (null == this.data) ? null : Convert.ToString(this.data, CultureInfo.InvariantCulture); | ||
141 | } | ||
142 | |||
143 | /// <summary> | ||
144 | /// Parse a field from the xml. | ||
145 | /// </summary> | ||
146 | /// <param name="reader">XmlReader where the intermediate is persisted.</param> | ||
147 | internal virtual void Read(XmlReader reader) | ||
148 | { | ||
149 | Debug.Assert("field" == reader.LocalName); | ||
150 | |||
151 | bool empty = reader.IsEmptyElement; | ||
152 | |||
153 | while (reader.MoveToNextAttribute()) | ||
154 | { | ||
155 | switch (reader.LocalName) | ||
156 | { | ||
157 | case "modified": | ||
158 | this.Modified = reader.Value.Equals("yes"); | ||
159 | break; | ||
160 | case "previousData": | ||
161 | this.PreviousData = reader.Value; | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | if (!empty) | ||
167 | { | ||
168 | bool done = false; | ||
169 | |||
170 | while (!done && reader.Read()) | ||
171 | { | ||
172 | switch (reader.NodeType) | ||
173 | { | ||
174 | case XmlNodeType.Element: | ||
175 | throw new XmlException(); | ||
176 | case XmlNodeType.CDATA: | ||
177 | case XmlNodeType.Text: | ||
178 | case XmlNodeType.SignificantWhitespace: | ||
179 | if (0 < reader.Value.Length) | ||
180 | { | ||
181 | if (ColumnType.Number == this.Column.Type && !this.Column.IsLocalizable) | ||
182 | { | ||
183 | // older wix files could persist data as a long value (which would overflow an int) | ||
184 | // since the Convert class always throws exceptions for overflows, read in integral | ||
185 | // values as a long to avoid the overflow, then cast it to an int (this operation can | ||
186 | // overflow without throwing an exception inside an unchecked block) | ||
187 | this.data = unchecked((int)Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture)); | ||
188 | } | ||
189 | else | ||
190 | { | ||
191 | this.data = reader.Value; | ||
192 | } | ||
193 | } | ||
194 | break; | ||
195 | case XmlNodeType.EndElement: | ||
196 | done = true; | ||
197 | break; | ||
198 | } | ||
199 | } | ||
200 | |||
201 | if (!done) | ||
202 | { | ||
203 | throw new XmlException(); | ||
204 | } | ||
205 | } | ||
206 | } | ||
207 | |||
208 | /// <summary> | ||
209 | /// Persists a field in an XML format. | ||
210 | /// </summary> | ||
211 | /// <param name="writer">XmlWriter where the Field should persist itself as XML.</param> | ||
212 | internal virtual void Write(XmlWriter writer) | ||
213 | { | ||
214 | writer.WriteStartElement("field", Intermediate.XmlNamespaceUri); | ||
215 | |||
216 | if (this.Modified) | ||
217 | { | ||
218 | writer.WriteAttributeString("modified", "yes"); | ||
219 | } | ||
220 | |||
221 | if (null != this.PreviousData) | ||
222 | { | ||
223 | writer.WriteAttributeString("previousData", this.PreviousData); | ||
224 | } | ||
225 | |||
226 | // Convert the data to a string that will persist nicely (nulls as String.Empty). | ||
227 | string text = Convert.ToString(this.data, CultureInfo.InvariantCulture); | ||
228 | if (this.Column.UseCData) | ||
229 | { | ||
230 | writer.WriteCData(text); | ||
231 | } | ||
232 | else | ||
233 | { | ||
234 | writer.WriteString(text); | ||
235 | } | ||
236 | |||
237 | writer.WriteEndElement(); | ||
238 | } | ||
239 | |||
240 | /// <summary> | ||
241 | /// Returns the field data in a format usable in IDT files. | ||
242 | /// </summary> | ||
243 | /// <returns>Field data in string IDT format.</returns> | ||
244 | internal string ToIdtValue() | ||
245 | { | ||
246 | if (null == this.data) | ||
247 | { | ||
248 | return null; | ||
249 | } | ||
250 | else | ||
251 | { | ||
252 | string fieldData = Convert.ToString(this.data, CultureInfo.InvariantCulture); | ||
253 | |||
254 | // special idt-specific escaping | ||
255 | if (this.Column.EscapeIdtCharacters) | ||
256 | { | ||
257 | fieldData = fieldData.Replace('\t', '\x10'); | ||
258 | fieldData = fieldData.Replace('\r', '\x11'); | ||
259 | fieldData = fieldData.Replace('\n', '\x19'); | ||
260 | } | ||
261 | |||
262 | return fieldData; | ||
263 | } | ||
264 | } | ||
265 | } | ||
266 | } | ||