diff options
Diffstat (limited to 'src/WixToolset.Data.WindowsInstaller/Row.cs')
-rw-r--r-- | src/WixToolset.Data.WindowsInstaller/Row.cs | 620 |
1 files changed, 0 insertions, 620 deletions
diff --git a/src/WixToolset.Data.WindowsInstaller/Row.cs b/src/WixToolset.Data.WindowsInstaller/Row.cs deleted file mode 100644 index 962ed0f4..00000000 --- a/src/WixToolset.Data.WindowsInstaller/Row.cs +++ /dev/null | |||
@@ -1,620 +0,0 @@ | |||
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.Collections.Generic; | ||
7 | using System.Diagnostics; | ||
8 | using System.Diagnostics.CodeAnalysis; | ||
9 | using System.Globalization; | ||
10 | using System.Text; | ||
11 | using System.Text.RegularExpressions; | ||
12 | using System.Xml; | ||
13 | |||
14 | /// <summary> | ||
15 | /// Row containing data for a table. | ||
16 | /// </summary> | ||
17 | public class Row | ||
18 | { | ||
19 | private static long rowCount; | ||
20 | |||
21 | private Field[] fields; | ||
22 | |||
23 | /// <summary> | ||
24 | /// Creates a row that belongs to a table. | ||
25 | /// </summary> | ||
26 | /// <param name="sourceLineNumbers">Original source lines for this row.</param> | ||
27 | /// <param name="table">Table this row belongs to and should get its column definitions from.</param> | ||
28 | /// <remarks>The compiler should use this constructor exclusively.</remarks> | ||
29 | public Row(SourceLineNumber sourceLineNumbers, Table table) | ||
30 | : this(sourceLineNumbers, table.Definition) | ||
31 | { | ||
32 | this.Table = table; | ||
33 | } | ||
34 | |||
35 | /// <summary> | ||
36 | /// Creates a row that does not belong to a table. | ||
37 | /// </summary> | ||
38 | /// <param name="sourceLineNumbers">Original source lines for this row.</param> | ||
39 | /// <param name="tableDefinition">TableDefinition this row should get its column definitions from.</param> | ||
40 | /// <remarks>This constructor is used in cases where there isn't a clear owner of the row. The linker uses this constructor for the rows it generates.</remarks> | ||
41 | public Row(SourceLineNumber sourceLineNumbers, TableDefinition tableDefinition) | ||
42 | { | ||
43 | this.Number = rowCount++; | ||
44 | this.SourceLineNumbers = sourceLineNumbers; | ||
45 | this.fields = new Field[tableDefinition.Columns.Count]; | ||
46 | this.TableDefinition = tableDefinition; | ||
47 | |||
48 | for (int i = 0; i < this.fields.Length; ++i) | ||
49 | { | ||
50 | this.fields[i] = Field.Create(this.TableDefinition.Columns[i]); | ||
51 | } | ||
52 | } | ||
53 | |||
54 | /// <summary> | ||
55 | /// Creates a shallow copy of a row from another row. | ||
56 | /// </summary> | ||
57 | /// <param name="source">The row the data is copied from.</param> | ||
58 | protected Row(Row source) | ||
59 | { | ||
60 | this.Table = source.Table; | ||
61 | this.TableDefinition = source.TableDefinition; | ||
62 | this.Number = source.Number; | ||
63 | this.Access = source.Access; | ||
64 | this.Operation = source.Operation; | ||
65 | this.Redundant = source.Redundant; | ||
66 | this.SectionId = source.SectionId; | ||
67 | this.SourceLineNumbers = source.SourceLineNumbers; | ||
68 | this.fields = source.fields; | ||
69 | } | ||
70 | |||
71 | /// <summary> | ||
72 | /// Gets or sets the access to the row's primary key. | ||
73 | /// </summary> | ||
74 | /// <value>The row access modifier.</value> | ||
75 | public AccessModifier Access { get; set; } | ||
76 | |||
77 | /// <summary> | ||
78 | /// Gets or sets the row transform operation. | ||
79 | /// </summary> | ||
80 | /// <value>The row transform operation.</value> | ||
81 | public RowOperation Operation { get; set; } | ||
82 | |||
83 | /// <summary> | ||
84 | /// Gets or sets wether the row is a duplicate of another row thus redundant. | ||
85 | /// </summary> | ||
86 | public bool Redundant { get; set; } | ||
87 | |||
88 | /// <summary> | ||
89 | /// Gets or sets the SectionId property on the row. | ||
90 | /// </summary> | ||
91 | /// <value>The SectionId property on the row.</value> | ||
92 | public string SectionId { get; set; } | ||
93 | |||
94 | /// <summary> | ||
95 | /// Gets the source file and line number for the row. | ||
96 | /// </summary> | ||
97 | /// <value>Source file and line number.</value> | ||
98 | public SourceLineNumber SourceLineNumbers { get; private set; } | ||
99 | |||
100 | /// <summary> | ||
101 | /// Gets the table this row belongs to. | ||
102 | /// </summary> | ||
103 | /// <value>null if Row does not belong to a Table, or owner Table otherwise.</value> | ||
104 | public Table Table { get; private set; } | ||
105 | |||
106 | /// <summary> | ||
107 | /// Gets the table definition for this row. | ||
108 | /// </summary> | ||
109 | /// <remarks>A Row always has a TableDefinition, even if the Row does not belong to a Table.</remarks> | ||
110 | /// <value>TableDefinition for Row.</value> | ||
111 | public TableDefinition TableDefinition { get; private set; } | ||
112 | |||
113 | /// <summary> | ||
114 | /// Gets the fields contained by this row. | ||
115 | /// </summary> | ||
116 | /// <value>Array of field objects</value> | ||
117 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] | ||
118 | public Field[] Fields | ||
119 | { | ||
120 | get { return this.fields; } | ||
121 | } | ||
122 | |||
123 | /// <summary> | ||
124 | /// Gets the unique number for the row. | ||
125 | /// </summary> | ||
126 | /// <value>Number for row.</value> | ||
127 | public long Number { get; private set; } | ||
128 | |||
129 | /// <summary> | ||
130 | /// Gets or sets the value of a particular field in the row. | ||
131 | /// </summary> | ||
132 | /// <param name="field">field index.</param> | ||
133 | /// <value>Value of a field in the row.</value> | ||
134 | public object this[int field] | ||
135 | { | ||
136 | get { return this.fields[field].Data; } | ||
137 | set { this.fields[field].Data = value; } | ||
138 | } | ||
139 | |||
140 | /// <summary> | ||
141 | /// Gets the field as an integer. | ||
142 | /// </summary> | ||
143 | /// <returns>Field's data as an integer.</returns> | ||
144 | public int FieldAsInteger(int field) | ||
145 | { | ||
146 | return this.fields[field].AsInteger(); | ||
147 | } | ||
148 | |||
149 | /// <summary> | ||
150 | /// Gets the field as an integer that could be null. | ||
151 | /// </summary> | ||
152 | /// <returns>Field's data as an integer that could be null.</returns> | ||
153 | public int? FieldAsNullableInteger(int field) | ||
154 | { | ||
155 | return this.fields[field].AsNullableInteger(); | ||
156 | } | ||
157 | |||
158 | /// <summary> | ||
159 | /// Gets the field as a string. | ||
160 | /// </summary> | ||
161 | /// <returns>Field's data as a string.</returns> | ||
162 | public string FieldAsString(int field) | ||
163 | { | ||
164 | return this.fields[field].AsString(); | ||
165 | } | ||
166 | |||
167 | /// <summary> | ||
168 | /// Sets the value of a particular field in the row without validating. | ||
169 | /// </summary> | ||
170 | /// <param name="field">field index.</param> | ||
171 | /// <param name="value">Value of a field in the row.</param> | ||
172 | /// <returns>True if successful, false if validation failed.</returns> | ||
173 | public bool BestEffortSetField(int field, object value) | ||
174 | { | ||
175 | return this.fields[field].BestEffortSet(value); | ||
176 | } | ||
177 | |||
178 | /// <summary> | ||
179 | /// Get the value used to represent the row in a keyed row collection. | ||
180 | /// </summary> | ||
181 | /// <returns>Primary key or row number if no primary key is available.</returns> | ||
182 | public string GetKey() | ||
183 | { | ||
184 | return this.GetPrimaryKey() ?? Convert.ToString(this.Number, CultureInfo.InvariantCulture); | ||
185 | } | ||
186 | |||
187 | /// <summary> | ||
188 | /// Get the primary key of this row. | ||
189 | /// </summary> | ||
190 | /// <param name="delimiter">Delimiter character for multiple column primary keys.</param> | ||
191 | /// <returns>The primary key or null if the row's table has no primary key columns.</returns> | ||
192 | public string GetPrimaryKey(char delimiter = '/') | ||
193 | { | ||
194 | return this.GetPrimaryKey(delimiter, String.Empty); | ||
195 | } | ||
196 | |||
197 | /// <summary> | ||
198 | /// Get the primary key of this row. | ||
199 | /// </summary> | ||
200 | /// <param name="delimiter">Delimiter character for multiple column primary keys.</param> | ||
201 | /// <param name="nullReplacement">String to represent null values in the primary key.</param> | ||
202 | /// <returns>The primary key or null if the row's table has no primary key columns.</returns> | ||
203 | public string GetPrimaryKey(char delimiter, string nullReplacement) | ||
204 | { | ||
205 | bool foundPrimaryKey = false; | ||
206 | StringBuilder primaryKey = new StringBuilder(); | ||
207 | |||
208 | foreach (Field field in this.fields) | ||
209 | { | ||
210 | if (field.Column.PrimaryKey) | ||
211 | { | ||
212 | if (foundPrimaryKey) | ||
213 | { | ||
214 | primaryKey.Append(delimiter); | ||
215 | } | ||
216 | |||
217 | primaryKey.Append((null == field.Data) ? nullReplacement : Convert.ToString(field.Data, CultureInfo.InvariantCulture)); | ||
218 | |||
219 | foundPrimaryKey = true; | ||
220 | } | ||
221 | else // primary keys must be the first columns of a row so the first non-primary key means we can stop looking. | ||
222 | { | ||
223 | break; | ||
224 | } | ||
225 | } | ||
226 | |||
227 | return foundPrimaryKey ? primaryKey.ToString() : null; | ||
228 | } | ||
229 | |||
230 | /// <summary> | ||
231 | /// Returns true if the specified field is null or an empty string. | ||
232 | /// </summary> | ||
233 | /// <param name="field">Index of the field to check.</param> | ||
234 | /// <returns>true if the specified field is null or an empty string, false otherwise.</returns> | ||
235 | public bool IsColumnEmpty(int field) | ||
236 | { | ||
237 | if (null == this.fields[field].Data) | ||
238 | { | ||
239 | return true; | ||
240 | } | ||
241 | |||
242 | string dataString = this.fields[field].Data as string; | ||
243 | if (null != dataString && 0 == dataString.Length) | ||
244 | { | ||
245 | return true; | ||
246 | } | ||
247 | |||
248 | return false; | ||
249 | } | ||
250 | |||
251 | /// <summary> | ||
252 | /// Tests if the passed in row is identical. | ||
253 | /// </summary> | ||
254 | /// <param name="row">Row to compare against.</param> | ||
255 | /// <returns>True if two rows are identical.</returns> | ||
256 | public bool IsIdentical(Row row) | ||
257 | { | ||
258 | bool identical = (this.TableDefinition.Name == row.TableDefinition.Name && this.fields.Length == row.fields.Length); | ||
259 | |||
260 | for (int i = 0; identical && i < this.fields.Length; ++i) | ||
261 | { | ||
262 | if (!(this.fields[i].IsIdentical(row.fields[i]))) | ||
263 | { | ||
264 | identical = false; | ||
265 | } | ||
266 | } | ||
267 | |||
268 | return identical; | ||
269 | } | ||
270 | |||
271 | /// <summary> | ||
272 | /// Returns a string representation of the Row. | ||
273 | /// </summary> | ||
274 | /// <returns>A string representation of the Row.</returns> | ||
275 | public override string ToString() | ||
276 | { | ||
277 | return String.Join("/", (object[])this.fields); | ||
278 | } | ||
279 | |||
280 | /// <summary> | ||
281 | /// Creates a Row from the XmlReader. | ||
282 | /// </summary> | ||
283 | /// <param name="reader">Reader to get data from.</param> | ||
284 | /// <param name="table">Table for this row.</param> | ||
285 | /// <returns>New row object.</returns> | ||
286 | internal static Row Read(XmlReader reader, Table table) | ||
287 | { | ||
288 | Debug.Assert("row" == reader.LocalName); | ||
289 | |||
290 | bool empty = reader.IsEmptyElement; | ||
291 | AccessModifier access = AccessModifier.Public; | ||
292 | RowOperation operation = RowOperation.None; | ||
293 | bool redundant = false; | ||
294 | string sectionId = null; | ||
295 | SourceLineNumber sourceLineNumbers = null; | ||
296 | |||
297 | while (reader.MoveToNextAttribute()) | ||
298 | { | ||
299 | switch (reader.LocalName) | ||
300 | { | ||
301 | case "access": | ||
302 | access = (AccessModifier)Enum.Parse(typeof(AccessModifier), reader.Value, true); | ||
303 | break; | ||
304 | case "op": | ||
305 | operation = (RowOperation)Enum.Parse(typeof(RowOperation), reader.Value, true); | ||
306 | break; | ||
307 | case "redundant": | ||
308 | redundant = reader.Value.Equals("yes"); | ||
309 | break; | ||
310 | case "sectionId": | ||
311 | sectionId = reader.Value; | ||
312 | break; | ||
313 | case "sourceLineNumber": | ||
314 | sourceLineNumbers = SourceLineNumber.CreateFromEncoded(reader.Value); | ||
315 | break; | ||
316 | } | ||
317 | } | ||
318 | |||
319 | Row row = table.CreateRow(sourceLineNumbers); | ||
320 | row.Access = access; | ||
321 | row.Operation = operation; | ||
322 | row.Redundant = redundant; | ||
323 | row.SectionId = sectionId; | ||
324 | |||
325 | // loop through all the fields in a row | ||
326 | if (!empty) | ||
327 | { | ||
328 | bool done = false; | ||
329 | int field = 0; | ||
330 | |||
331 | // loop through all the fields in a row | ||
332 | while (!done && reader.Read()) | ||
333 | { | ||
334 | switch (reader.NodeType) | ||
335 | { | ||
336 | case XmlNodeType.Element: | ||
337 | switch (reader.LocalName) | ||
338 | { | ||
339 | case "field": | ||
340 | if (row.Fields.Length <= field) | ||
341 | { | ||
342 | if (!reader.IsEmptyElement) | ||
343 | { | ||
344 | throw new XmlException(); | ||
345 | } | ||
346 | } | ||
347 | else | ||
348 | { | ||
349 | row.fields[field].Read(reader); | ||
350 | } | ||
351 | ++field; | ||
352 | break; | ||
353 | default: | ||
354 | throw new XmlException(); | ||
355 | } | ||
356 | break; | ||
357 | case XmlNodeType.EndElement: | ||
358 | done = true; | ||
359 | break; | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if (!done) | ||
364 | { | ||
365 | throw new XmlException(); | ||
366 | } | ||
367 | } | ||
368 | |||
369 | return row; | ||
370 | } | ||
371 | |||
372 | /// <summary> | ||
373 | /// Returns the row in a format usable in IDT files. | ||
374 | /// </summary> | ||
375 | /// <param name="keepAddedColumns">Whether to keep columns added in a transform.</param> | ||
376 | /// <returns>String with tab delimited field values.</returns> | ||
377 | internal string ToIdtDefinition(bool keepAddedColumns) | ||
378 | { | ||
379 | bool first = true; | ||
380 | StringBuilder sb = new StringBuilder(); | ||
381 | |||
382 | foreach (Field field in this.fields) | ||
383 | { | ||
384 | // Conditionally keep columns added in a transform; otherwise, | ||
385 | // break because columns can only be added at the end. | ||
386 | if (field.Column.Added && !keepAddedColumns) | ||
387 | { | ||
388 | break; | ||
389 | } | ||
390 | |||
391 | if (first) | ||
392 | { | ||
393 | first = false; | ||
394 | } | ||
395 | else | ||
396 | { | ||
397 | sb.Append('\t'); | ||
398 | } | ||
399 | |||
400 | sb.Append(field.ToIdtValue()); | ||
401 | } | ||
402 | sb.Append("\r\n"); | ||
403 | |||
404 | return sb.ToString(); | ||
405 | } | ||
406 | |||
407 | /// <summary> | ||
408 | /// Gets the modularized version of the field data. | ||
409 | /// </summary> | ||
410 | /// <param name="field">The field to modularize.</param> | ||
411 | /// <param name="modularizationGuid">String containing the GUID of the Merge Module to append the the field value, if appropriate.</param> | ||
412 | /// <param name="suppressModularizationIdentifiers">Optional collection of identifiers that should not be modularized.</param> | ||
413 | /// <remarks>moduleGuid is expected to be null when not being used to compile a Merge Module.</remarks> | ||
414 | /// <returns>The modularized version of the field data.</returns> | ||
415 | internal string GetModularizedValue(Field field, string modularizationGuid, ISet<string> suppressModularizationIdentifiers) | ||
416 | { | ||
417 | Debug.Assert(null != field.Data && 0 < ((string)field.Data).Length); | ||
418 | string fieldData = Convert.ToString(field.Data, CultureInfo.InvariantCulture); | ||
419 | |||
420 | if (null != modularizationGuid && ColumnModularizeType.None != field.Column.ModularizeType && !(WindowsInstallerStandard.IsStandardAction(fieldData) || WindowsInstallerStandard.IsStandardProperty(fieldData))) | ||
421 | { | ||
422 | StringBuilder sb; | ||
423 | int start; | ||
424 | ColumnModularizeType modularizeType = field.Column.ModularizeType; | ||
425 | |||
426 | // special logic for the ControlEvent table's Argument column | ||
427 | // this column requires different modularization methods depending upon the value of the Event column | ||
428 | if (ColumnModularizeType.ControlEventArgument == field.Column.ModularizeType) | ||
429 | { | ||
430 | switch (this[2].ToString()) | ||
431 | { | ||
432 | case "CheckExistingTargetPath": // redirectable property name | ||
433 | case "CheckTargetPath": | ||
434 | case "DoAction": // custom action name | ||
435 | case "NewDialog": // dialog name | ||
436 | case "SelectionBrowse": | ||
437 | case "SetTargetPath": | ||
438 | case "SpawnDialog": | ||
439 | case "SpawnWaitDialog": | ||
440 | if (Common.IsIdentifier(fieldData)) | ||
441 | { | ||
442 | modularizeType = ColumnModularizeType.Column; | ||
443 | } | ||
444 | else | ||
445 | { | ||
446 | modularizeType = ColumnModularizeType.Property; | ||
447 | } | ||
448 | break; | ||
449 | default: // formatted | ||
450 | modularizeType = ColumnModularizeType.Property; | ||
451 | break; | ||
452 | } | ||
453 | } | ||
454 | else if (ColumnModularizeType.ControlText == field.Column.ModularizeType) | ||
455 | { | ||
456 | // icons are stored in the Binary table, so they get column-type modularization | ||
457 | if (("Bitmap" == this[2].ToString() || "Icon" == this[2].ToString()) && Common.IsIdentifier(fieldData)) | ||
458 | { | ||
459 | modularizeType = ColumnModularizeType.Column; | ||
460 | } | ||
461 | else | ||
462 | { | ||
463 | modularizeType = ColumnModularizeType.Property; | ||
464 | } | ||
465 | } | ||
466 | |||
467 | switch (modularizeType) | ||
468 | { | ||
469 | case ColumnModularizeType.Column: | ||
470 | // ensure the value is an identifier (otherwise it shouldn't be modularized this way) | ||
471 | if (!Common.IsIdentifier(fieldData)) | ||
472 | { | ||
473 | throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixDataStrings.EXP_CannotModularizeIllegalID, fieldData)); | ||
474 | } | ||
475 | |||
476 | // if we're not supposed to suppress modularization of this identifier | ||
477 | if (null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(fieldData)) | ||
478 | { | ||
479 | fieldData = String.Concat(fieldData, ".", modularizationGuid); | ||
480 | } | ||
481 | break; | ||
482 | |||
483 | case ColumnModularizeType.Property: | ||
484 | case ColumnModularizeType.Condition: | ||
485 | Regex regex; | ||
486 | if (ColumnModularizeType.Property == modularizeType) | ||
487 | { | ||
488 | regex = new Regex(@"\[(?<identifier>[#$!]?[a-zA-Z_][a-zA-Z0-9_\.]*)]", RegexOptions.Singleline | RegexOptions.ExplicitCapture); | ||
489 | } | ||
490 | else | ||
491 | { | ||
492 | Debug.Assert(ColumnModularizeType.Condition == modularizeType); | ||
493 | |||
494 | // This heinous looking regular expression is actually quite an elegant way | ||
495 | // to shred the entire condition into the identifiers that need to be | ||
496 | // modularized. Let's break it down piece by piece: | ||
497 | // | ||
498 | // 1. Look for the operators: NOT, EQV, XOR, OR, AND, IMP (plus a space). Note that the | ||
499 | // regular expression is case insensitive so we don't have to worry about | ||
500 | // all the permutations of these strings. | ||
501 | // 2. Look for quoted strings. Quoted strings are just text and are ignored | ||
502 | // outright. | ||
503 | // 3. Look for environment variables. These look like identifiers we might | ||
504 | // otherwise be interested in but start with a percent sign. Like quoted | ||
505 | // strings these enviroment variable references are ignored outright. | ||
506 | // 4. Match all identifiers that are things that need to be modularized. Note | ||
507 | // the special characters (!, $, ?, &) that denote Component and Feature states. | ||
508 | regex = new Regex(@"NOT\s|EQV\s|XOR\s|OR\s|AND\s|IMP\s|"".*?""|%[a-zA-Z_][a-zA-Z0-9_\.]*|(?<identifier>[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); | ||
509 | |||
510 | // less performant version of the above with captures showing where everything lives | ||
511 | // regex = new Regex(@"(?<operator>NOT|EQV|XOR|OR|AND|IMP)|(?<string>"".*?"")|(?<environment>%[a-zA-Z_][a-zA-Z0-9_\.]*)|(?<identifier>[!$\?&]?[a-zA-Z_][a-zA-Z0-9_\.]*)",RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); | ||
512 | } | ||
513 | |||
514 | MatchCollection matches = regex.Matches(fieldData); | ||
515 | |||
516 | sb = new StringBuilder(fieldData); | ||
517 | |||
518 | // notice how this code walks backward through the list | ||
519 | // because it modifies the string as we through it | ||
520 | for (int i = matches.Count - 1; 0 <= i; i--) | ||
521 | { | ||
522 | Group group = matches[i].Groups["identifier"]; | ||
523 | if (group.Success) | ||
524 | { | ||
525 | string identifier = group.Value; | ||
526 | if (!WindowsInstallerStandard.IsStandardProperty(identifier) && (null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(identifier))) | ||
527 | { | ||
528 | sb.Insert(group.Index + group.Length, '.'); | ||
529 | sb.Insert(group.Index + group.Length + 1, modularizationGuid); | ||
530 | } | ||
531 | } | ||
532 | } | ||
533 | |||
534 | fieldData = sb.ToString(); | ||
535 | break; | ||
536 | |||
537 | case ColumnModularizeType.CompanionFile: | ||
538 | // if we're not supposed to ignore this identifier and the value does not start with | ||
539 | // a digit, we must have a companion file so modularize it | ||
540 | if ((null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(fieldData)) && | ||
541 | 0 < fieldData.Length && !Char.IsDigit(fieldData, 0)) | ||
542 | { | ||
543 | fieldData = String.Concat(fieldData, ".", modularizationGuid); | ||
544 | } | ||
545 | break; | ||
546 | |||
547 | case ColumnModularizeType.Icon: | ||
548 | if (null == suppressModularizationIdentifiers || !suppressModularizationIdentifiers.Contains(fieldData)) | ||
549 | { | ||
550 | start = fieldData.LastIndexOf(".", StringComparison.Ordinal); | ||
551 | if (-1 == start) | ||
552 | { | ||
553 | fieldData = String.Concat(fieldData, ".", modularizationGuid); | ||
554 | } | ||
555 | else | ||
556 | { | ||
557 | fieldData = String.Concat(fieldData.Substring(0, start), ".", modularizationGuid, fieldData.Substring(start)); | ||
558 | } | ||
559 | } | ||
560 | break; | ||
561 | |||
562 | case ColumnModularizeType.SemicolonDelimited: | ||
563 | string[] keys = fieldData.Split(';'); | ||
564 | for (int i = 0; i < keys.Length; ++i) | ||
565 | { | ||
566 | keys[i] = String.Concat(keys[i], ".", modularizationGuid); | ||
567 | } | ||
568 | fieldData = String.Join(";", keys); | ||
569 | break; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | return fieldData; | ||
574 | } | ||
575 | |||
576 | /// <summary> | ||
577 | /// Persists a row in an XML format. | ||
578 | /// </summary> | ||
579 | /// <param name="writer">XmlWriter where the Row should persist itself as XML.</param> | ||
580 | [SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Justification = "Changing the way this string normalizes would result " + | ||
581 | "in a change to the way intermediate files are generated, potentially causing extra churn in patches on an MSI built from an older version of WiX. " + | ||
582 | "Furthermore, there is no security hole here, as the strings won't need to make a round trip")] | ||
583 | internal void Write(XmlWriter writer) | ||
584 | { | ||
585 | writer.WriteStartElement("row", Intermediate.XmlNamespaceUri); | ||
586 | |||
587 | if (AccessModifier.Public != this.Access) | ||
588 | { | ||
589 | writer.WriteAttributeString("access", this.Access.ToString().ToLowerInvariant()); | ||
590 | } | ||
591 | |||
592 | if (RowOperation.None != this.Operation) | ||
593 | { | ||
594 | writer.WriteAttributeString("op", this.Operation.ToString().ToLowerInvariant()); | ||
595 | } | ||
596 | |||
597 | if (this.Redundant) | ||
598 | { | ||
599 | writer.WriteAttributeString("redundant", "yes"); | ||
600 | } | ||
601 | |||
602 | if (null != this.SectionId) | ||
603 | { | ||
604 | writer.WriteAttributeString("sectionId", this.SectionId); | ||
605 | } | ||
606 | |||
607 | if (null != this.SourceLineNumbers) | ||
608 | { | ||
609 | writer.WriteAttributeString("sourceLineNumber", this.SourceLineNumbers.GetEncoded()); | ||
610 | } | ||
611 | |||
612 | for (int i = 0; i < this.fields.Length; ++i) | ||
613 | { | ||
614 | this.fields[i].Write(writer); | ||
615 | } | ||
616 | |||
617 | writer.WriteEndElement(); | ||
618 | } | ||
619 | } | ||
620 | } | ||