aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data/WindowsInstaller/Field.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2017-12-07 14:17:39 -0800
committerRob Mensching <rob@firegiant.com>2017-12-07 14:17:39 -0800
commit221da62c05ef2b515eb507c77655514cd0ec32a4 (patch)
treefabec5b8ac871f17de6fe0aad3e6188b9af42bfb /src/WixToolset.Data/WindowsInstaller/Field.cs
parentca376995792d2e2a1a7f39760989496702a8f603 (diff)
downloadwix-221da62c05ef2b515eb507c77655514cd0ec32a4.tar.gz
wix-221da62c05ef2b515eb507c77655514cd0ec32a4.tar.bz2
wix-221da62c05ef2b515eb507c77655514cd0ec32a4.zip
Reintegrate MSI constructs into WxToolset.Data.WindowsInstaller namespace
Diffstat (limited to 'src/WixToolset.Data/WindowsInstaller/Field.cs')
-rw-r--r--src/WixToolset.Data/WindowsInstaller/Field.cs312
1 files changed, 312 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..11cd2c33
--- /dev/null
+++ b/src/WixToolset.Data/WindowsInstaller/Field.cs
@@ -0,0 +1,312 @@
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
3namespace WixToolset.Data.WindowsInstaller
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.ValidateValue(this.Column, 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.ValidateValue(this.Column, 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 /// Validate a value for this column.
145 /// </summary>
146 /// <param name="value">The value to validate.</param>
147 /// <returns>Validated value.</returns>
148 internal object ValidateValue(ColumnDefinition column, object value)
149 {
150 if (null == value)
151 {
152 if (!column.Nullable)
153 {
154 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with a null value because this is a required field.", column.Name));
155 }
156 }
157 else // check numerical values against their specified minimum and maximum values.
158 {
159 if (ColumnType.Number == column.Type && !column.IsLocalizable)
160 {
161 // For now all enums in the tables can be represented by integers. This if statement would need to
162 // be enhanced if that ever changes.
163 if (value is int || value.GetType().IsEnum)
164 {
165 int intValue = (int)value;
166
167 // validate the value against the minimum allowed value
168 if (column.MinValue.HasValue && column.MinValue > intValue)
169 {
170 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is less than the minimum allowed value for this column, {2}.", column.Name, intValue, column.MinValue));
171 }
172
173 // validate the value against the maximum allowed value
174 if (column.MaxValue.HasValue && column.MaxValue < intValue)
175 {
176 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is greater than the maximum allowed value for this column, {2}.", column.Name, intValue, column.MaxValue));
177 }
178
179 return intValue;
180 }
181 else if (value is long longValue)
182 {
183 // validate the value against the minimum allowed value
184 if (column.MinValue.HasValue && column.MinValue > longValue)
185 {
186 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is less than the minimum allowed value for this column, {2}.", column.Name, longValue, column.MinValue));
187 }
188
189 // validate the value against the maximum allowed value
190 if (column.MaxValue.HasValue && column.MaxValue < longValue)
191 {
192 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set column '{0}' with value {1} because it is greater than the maximum allowed value for this column, {2}.", column.Name, longValue, column.MaxValue));
193 }
194
195 return longValue;
196 }
197 else
198 {
199 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set number column '{0}' with a value of type '{1}'.", column.Name, value.GetType().ToString()));
200 }
201 }
202 else
203 {
204 if (!(value is string))
205 {
206 //throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Cannot set string column '{0}' with a value of type '{1}'.", this.name, value.GetType().ToString()));
207 return value.ToString();
208 }
209 }
210 }
211
212 return value;
213 }
214
215 /// <summary>
216 /// Parse a field from the xml.
217 /// </summary>
218 /// <param name="reader">XmlReader where the intermediate is persisted.</param>
219 internal virtual void Read(XmlReader reader)
220 {
221 Debug.Assert("field" == reader.LocalName);
222
223 bool empty = reader.IsEmptyElement;
224
225 while (reader.MoveToNextAttribute())
226 {
227 switch (reader.LocalName)
228 {
229 case "modified":
230 this.Modified = reader.Value.Equals("yes");
231 break;
232 case "previousData":
233 this.PreviousData = reader.Value;
234 break;
235 }
236 }
237
238 if (!empty)
239 {
240 bool done = false;
241
242 while (!done && reader.Read())
243 {
244 switch (reader.NodeType)
245 {
246 case XmlNodeType.Element:
247 throw new XmlException();
248 case XmlNodeType.CDATA:
249 case XmlNodeType.Text:
250 case XmlNodeType.SignificantWhitespace:
251 if (0 < reader.Value.Length)
252 {
253 if (ColumnType.Number == this.Column.Type && !this.Column.IsLocalizable)
254 {
255 // older wix files could persist data as a long value (which would overflow an int)
256 // since the Convert class always throws exceptions for overflows, read in integral
257 // values as a long to avoid the overflow, then cast it to an int (this operation can
258 // overflow without throwing an exception inside an unchecked block)
259 this.data = unchecked((int)Convert.ToInt64(reader.Value, CultureInfo.InvariantCulture));
260 }
261 else
262 {
263 this.data = reader.Value;
264 }
265 }
266 break;
267 case XmlNodeType.EndElement:
268 done = true;
269 break;
270 }
271 }
272
273 if (!done)
274 {
275 throw new XmlException();
276 }
277 }
278 }
279
280 /// <summary>
281 /// Persists a field in an XML format.
282 /// </summary>
283 /// <param name="writer">XmlWriter where the Field should persist itself as XML.</param>
284 internal virtual void Write(XmlWriter writer)
285 {
286 writer.WriteStartElement("field", Intermediate.XmlNamespaceUri);
287
288 if (this.Modified)
289 {
290 writer.WriteAttributeString("modified", "yes");
291 }
292
293 if (null != this.PreviousData)
294 {
295 writer.WriteAttributeString("previousData", this.PreviousData);
296 }
297
298 // Convert the data to a string that will persist nicely (nulls as String.Empty).
299 string text = Convert.ToString(this.data, CultureInfo.InvariantCulture);
300 if (this.Column.UseCData)
301 {
302 writer.WriteCData(text);
303 }
304 else
305 {
306 writer.WriteString(text);
307 }
308
309 writer.WriteEndElement();
310 }
311 }
312}