aboutsummaryrefslogtreecommitdiff
path: root/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs')
-rw-r--r--src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs228
1 files changed, 228 insertions, 0 deletions
diff --git a/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
new file mode 100644
index 00000000..1fc7d068
--- /dev/null
+++ b/src/WixToolset.Core.WindowsInstaller/Bind/CreateIdtFileCommand.cs
@@ -0,0 +1,228 @@
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.Core.WindowsInstaller.Bind
4{
5 using System;
6 using System.Globalization;
7 using System.IO;
8 using System.Text;
9 using WixToolset.Data;
10 using WixToolset.Data.WindowsInstaller;
11
12 internal class CreateIdtFileCommand
13 {
14 public CreateIdtFileCommand(Table table, int codepage, string intermediateFolder, bool keepAddedColumns)
15 {
16 this.Table = table;
17 this.Codepage = codepage;
18 this.IntermediateFolder = intermediateFolder;
19 this.KeepAddedColumns = keepAddedColumns;
20 }
21
22 private Table Table { get; }
23
24 private int Codepage { get; set; }
25
26 private string IntermediateFolder { get; }
27
28 private bool KeepAddedColumns { get; }
29
30 public string IdtPath { get; private set; }
31
32 public void Execute()
33 {
34 // write out the table to an IDT file
35 Encoding encoding;
36
37 // If UTF8 encoding, use the UTF8-specific constructor to avoid writing
38 // the byte order mark at the beginning of the file
39 if (this.Codepage == Encoding.UTF8.CodePage)
40 {
41 encoding = new UTF8Encoding(false, true);
42 }
43 else
44 {
45 if (this.Codepage == 0)
46 {
47 this.Codepage = Encoding.ASCII.CodePage;
48 }
49
50 encoding = Encoding.GetEncoding(this.Codepage, new EncoderExceptionFallback(), new DecoderExceptionFallback());
51 }
52
53 this.IdtPath = Path.Combine(this.IntermediateFolder, String.Concat(this.Table.Name, ".idt"));
54
55 using (var idtWriter = new StreamWriter(this.IdtPath, false, encoding))
56 {
57 this.TableToIdtDefinition(this.Table, idtWriter, this.KeepAddedColumns);
58 }
59 }
60
61 private void TableToIdtDefinition(Table table, StreamWriter writer, bool keepAddedColumns)
62 {
63 if (table.Definition.Unreal)
64 {
65 return;
66 }
67
68 if (TableDefinition.MaxColumnsInRealTable < table.Definition.Columns.Count)
69 {
70 throw new WixException(WixDataErrors.TooManyColumnsInRealTable(table.Definition.Name, table.Definition.Columns.Count, TableDefinition.MaxColumnsInRealTable));
71 }
72
73 // Tack on the table header, and flush before we start writing bytes directly to the stream.
74 var header = this.TableDefinitionToIdtDefinition(table.Definition, keepAddedColumns);
75 writer.Write(header);
76 writer.Flush();
77
78 using (var binary = new BinaryWriter(writer.BaseStream, writer.Encoding, true))
79 {
80 // Create an encoding that replaces characters with question marks, and doesn't throw. We'll
81 // use this in case of errors
82 Encoding convertEncoding = Encoding.GetEncoding(writer.Encoding.CodePage);
83
84 foreach (Row row in table.Rows)
85 {
86 if (row.Redundant)
87 {
88 continue;
89 }
90
91 string rowString = this.RowToIdtDefinition(row, keepAddedColumns);
92 byte[] rowBytes;
93
94 try
95 {
96 // GetBytes will throw an exception if any character doesn't match our current encoding
97 rowBytes = writer.Encoding.GetBytes(rowString);
98 }
99 catch (EncoderFallbackException)
100 {
101 Messaging.Instance.OnMessage(WixDataErrors.InvalidStringForCodepage(row.SourceLineNumbers, Convert.ToString(writer.Encoding.WindowsCodePage, CultureInfo.InvariantCulture)));
102
103 rowBytes = convertEncoding.GetBytes(rowString);
104 }
105
106 binary.Write(rowBytes, 0, rowBytes.Length);
107 }
108 }
109 }
110
111 private string TableDefinitionToIdtDefinition(TableDefinition definition, bool keepAddedColumns)
112 {
113 var first = true;
114 var columnString = new StringBuilder();
115 var dataString = new StringBuilder();
116 var tableString = new StringBuilder();
117
118 tableString.Append(definition.Name);
119 foreach (var column in definition.Columns)
120 {
121 // conditionally keep columns added in a transform; otherwise,
122 // break because columns can only be added at the end
123 if (column.Added && !keepAddedColumns)
124 {
125 break;
126 }
127
128 if (!first)
129 {
130 columnString.Append('\t');
131 dataString.Append('\t');
132 }
133
134 columnString.Append(column.Name);
135 dataString.Append(ColumnIdtType(column));
136
137 if (column.PrimaryKey)
138 {
139 tableString.AppendFormat("\t{0}", column.Name);
140 }
141
142 first = false;
143 }
144 columnString.Append("\r\n");
145 columnString.Append(dataString);
146 columnString.Append("\r\n");
147 columnString.Append(tableString);
148 columnString.Append("\r\n");
149
150 return columnString.ToString();
151 }
152
153 private string RowToIdtDefinition(Row row, bool keepAddedColumns)
154 {
155 var first = true;
156 var sb = new StringBuilder();
157
158 foreach (var field in row.Fields)
159 {
160 // Conditionally keep columns added in a transform; otherwise,
161 // break because columns can only be added at the end.
162 if (field.Column.Added && !keepAddedColumns)
163 {
164 break;
165 }
166
167 if (first)
168 {
169 first = false;
170 }
171 else
172 {
173 sb.Append('\t');
174 }
175
176 sb.Append(this.FieldToIdtValue(field));
177 }
178 sb.Append("\r\n");
179
180 return sb.ToString();
181 }
182
183 private string FieldToIdtValue(Field field)
184 {
185 var data = field.AsString();
186
187 if (String.IsNullOrEmpty(data))
188 {
189 return data;
190 }
191
192 // Special field value idt-specific escaping.
193 return data.Replace('\t', '\x10')
194 .Replace('\r', '\x11')
195 .Replace('\n', '\x19');
196 }
197
198
199 /// <summary>
200 /// Gets the type of the column in IDT format.
201 /// </summary>
202 /// <value>IDT format for column type.</value>
203 private static string ColumnIdtType(ColumnDefinition column)
204 {
205 char typeCharacter;
206 switch (column.Type)
207 {
208 case ColumnType.Number:
209 typeCharacter = column.Nullable ? 'I' : 'i';
210 break;
211 case ColumnType.Preserved:
212 case ColumnType.String:
213 typeCharacter = column.Nullable ? 'S' : 's';
214 break;
215 case ColumnType.Localized:
216 typeCharacter = column.Nullable ? 'L' : 'l';
217 break;
218 case ColumnType.Object:
219 typeCharacter = column.Nullable ? 'V' : 'v';
220 break;
221 default:
222 throw new InvalidOperationException(String.Format(CultureInfo.CurrentUICulture, WixDataStrings.EXP_UnknownColumnType, column.Type));
223 }
224
225 return String.Concat(typeCharacter, column.Length);
226 }
227 }
228}