aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Data.WindowsInstaller/Field.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Data.WindowsInstaller/Field.cs')
-rw-r--r--src/WixToolset.Data.WindowsInstaller/Field.cs266
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
3namespace 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}