aboutsummaryrefslogtreecommitdiff
path: root/src/dtf/WixToolset.Dtf.WindowsInstaller/ColumnCollection.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/dtf/WixToolset.Dtf.WindowsInstaller/ColumnCollection.cs333
1 files changed, 333 insertions, 0 deletions
diff --git a/src/dtf/WixToolset.Dtf.WindowsInstaller/ColumnCollection.cs b/src/dtf/WixToolset.Dtf.WindowsInstaller/ColumnCollection.cs
new file mode 100644
index 00000000..9a452da1
--- /dev/null
+++ b/src/dtf/WixToolset.Dtf.WindowsInstaller/ColumnCollection.cs
@@ -0,0 +1,333 @@
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.Dtf.WindowsInstaller
4{
5 using System;
6 using System.IO;
7 using System.Collections;
8 using System.Collections.Generic;
9 using System.Text;
10 using System.Diagnostics.CodeAnalysis;
11
12 /// <summary>
13 /// Collection of column information related to a <see cref="TableInfo"/> or
14 /// <see cref="View"/>.
15 /// </summary>
16 public sealed class ColumnCollection : ICollection<ColumnInfo>
17 {
18 private IList<ColumnInfo> columns;
19 private string formatString;
20
21 /// <summary>
22 /// Creates a new ColumnCollection based on a specified list of columns.
23 /// </summary>
24 /// <param name="columns">columns to be added to the new collection</param>
25 public ColumnCollection(ICollection<ColumnInfo> columns)
26 {
27 if (columns == null)
28 {
29 throw new ArgumentNullException("columns");
30 }
31
32 this.columns = new List<ColumnInfo>(columns);
33 }
34
35 /// <summary>
36 /// Creates a new ColumnCollection that is associated with a database table.
37 /// </summary>
38 /// <param name="view">view that contains the columns</param>
39 internal ColumnCollection(View view)
40 {
41 if (view == null)
42 {
43 throw new ArgumentNullException("view");
44 }
45
46 this.columns = ColumnCollection.GetViewColumns(view);
47 }
48
49 /// <summary>
50 /// Gets the number of columns in the collection.
51 /// </summary>
52 /// <value>number of columns in the collection</value>
53 public int Count
54 {
55 get
56 {
57 return this.columns.Count;
58 }
59 }
60
61 /// <summary>
62 /// Gets a boolean value indicating whether the collection is read-only.
63 /// A ColumnCollection is read-only if it is associated with a <see cref="View"/>
64 /// or a read-only <see cref="Database"/>.
65 /// </summary>
66 /// <value>read-only status of the collection</value>
67 public bool IsReadOnly
68 {
69 get
70 {
71 return true;
72 }
73 }
74
75 /// <summary>
76 /// Gets information about a specific column in the collection.
77 /// </summary>
78 /// <param name="columnIndex">1-based index into the column collection</param>
79 /// <exception cref="ArgumentOutOfRangeException"><paramref name="columnIndex"/> is less
80 /// than 1 or greater than the number of columns in the collection</exception>
81 public ColumnInfo this[int columnIndex]
82 {
83 get
84 {
85 if (columnIndex >= 0 && columnIndex < this.columns.Count)
86 {
87 return this.columns[columnIndex];
88 }
89 else
90 {
91 throw new ArgumentOutOfRangeException("columnIndex");
92 }
93 }
94 }
95
96 /// <summary>
97 /// Gets information about a specific column in the collection.
98 /// </summary>
99 /// <param name="columnName">case-sensitive name of a column collection</param>
100 /// <exception cref="ArgumentOutOfRangeException"><paramref name="columnName"/> does
101 /// not exist in the collection</exception>
102 public ColumnInfo this[string columnName]
103 {
104 get
105 {
106 if (String.IsNullOrEmpty(columnName))
107 {
108 throw new ArgumentNullException("columnName");
109 }
110
111 foreach (ColumnInfo colInfo in this.columns)
112 {
113 if (colInfo.Name == columnName)
114 {
115 return colInfo;
116 }
117 }
118
119 throw new ArgumentOutOfRangeException("columnName");
120 }
121 }
122
123 /// <summary>
124 /// Not supported because the collection is read-only.
125 /// </summary>
126 /// <param name="item">information about the column being added</param>
127 /// <exception cref="InvalidOperationException">the collection is read-only</exception>
128 public void Add(ColumnInfo item)
129 {
130 throw new InvalidOperationException();
131 }
132
133 /// <summary>
134 /// Not supported because the collection is read-only.
135 /// </summary>
136 /// <exception cref="InvalidOperationException">the collection is read-only</exception>
137 public void Clear()
138 {
139 throw new InvalidOperationException();
140 }
141
142 /// <summary>
143 /// Checks if a column with a given name exists in the collection.
144 /// </summary>
145 /// <param name="columnName">case-sensitive name of the column to look for</param>
146 /// <returns>true if the column exists in the collection, false otherwise</returns>
147 public bool Contains(string columnName)
148 {
149 return this.IndexOf(columnName) >= 0;
150 }
151
152 /// <summary>
153 /// Checks if a column with a given name exists in the collection.
154 /// </summary>
155 /// <param name="column">column to look for, with case-sensitive name</param>
156 /// <returns>true if the column exists in the collection, false otherwise</returns>
157 bool ICollection<ColumnInfo>.Contains(ColumnInfo column)
158 {
159 return this.Contains(column.Name);
160 }
161
162 /// <summary>
163 /// Gets the index of a column within the collection.
164 /// </summary>
165 /// <param name="columnName">case-sensitive name of the column to look for</param>
166 /// <returns>0-based index of the column, or -1 if not found</returns>
167 public int IndexOf(string columnName)
168 {
169 if (String.IsNullOrEmpty(columnName))
170 {
171 throw new ArgumentNullException("columnName");
172 }
173
174 for (int index = 0; index < this.columns.Count; index++)
175 {
176 if (this.columns[index].Name == columnName)
177 {
178 return index;
179 }
180 }
181 return -1;
182 }
183
184 /// <summary>
185 /// Copies the columns from this collection into an array.
186 /// </summary>
187 /// <param name="array">destination array to be filed</param>
188 /// <param name="arrayIndex">offset into the destination array where copying begins</param>
189 public void CopyTo(ColumnInfo[] array, int arrayIndex)
190 {
191 if (array == null)
192 {
193 throw new ArgumentNullException("array");
194 }
195
196 this.columns.CopyTo(array, arrayIndex);
197 }
198
199 /// <summary>
200 /// Not supported because the collection is read-only.
201 /// </summary>
202 /// <param name="column">column to remove</param>
203 /// <returns>true if the column was removed, false if it was not found</returns>
204 /// <exception cref="InvalidOperationException">the collection is read-only</exception>
205 [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "column")]
206 [SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")]
207 bool ICollection<ColumnInfo>.Remove(ColumnInfo column)
208 {
209 throw new InvalidOperationException();
210 }
211
212 /// <summary>
213 /// Gets an enumerator over the columns in the collection.
214 /// </summary>
215 /// <returns>An enumerator of ColumnInfo objects.</returns>
216 public IEnumerator<ColumnInfo> GetEnumerator()
217 {
218 return this.columns.GetEnumerator();
219 }
220
221 /// <summary>
222 /// Gets a string suitable for printing all the values of a record containing these columns.
223 /// </summary>
224 public string FormatString
225 {
226 get
227 {
228 if (this.formatString == null)
229 {
230 this.formatString = CreateFormatString(this.columns);
231 }
232 return this.formatString;
233 }
234 }
235
236 private static string CreateFormatString(IList<ColumnInfo> columns)
237 {
238 StringBuilder sb = new StringBuilder();
239 for (int i = 0; i < columns.Count; i++)
240 {
241 if (columns[i].Type == typeof(Stream))
242 {
243 sb.AppendFormat("{0} = [Binary Data]", columns[i].Name);
244 }
245 else
246 {
247 sb.AppendFormat("{0} = [{1}]", columns[i].Name, i + 1);
248 }
249
250 if (i < columns.Count - 1)
251 {
252 sb.Append(", ");
253 }
254 }
255 return sb.ToString();
256 }
257
258 /// <summary>
259 /// Gets an enumerator over the columns in the collection.
260 /// </summary>
261 /// <returns>An enumerator of ColumnInfo objects.</returns>
262 IEnumerator IEnumerable.GetEnumerator()
263 {
264 return this.GetEnumerator();
265 }
266
267 /// <summary>
268 /// Creates ColumnInfo objects for the associated view.
269 /// </summary>
270 /// <returns>dynamically-generated list of columns</returns>
271 private static IList<ColumnInfo> GetViewColumns(View view)
272 {
273 IList<string> columnNames = ColumnCollection.GetViewColumns(view, false);
274 IList<string> columnTypes = ColumnCollection.GetViewColumns(view, true);
275
276 int count = columnNames.Count;
277 if (columnTypes[count - 1] == "O0")
278 {
279 // Weird.. the "_Tables" table returns a second column with type "O0" -- ignore it.
280 count--;
281 }
282
283 IList<ColumnInfo> columnsList = new List<ColumnInfo>(count);
284 for (int i = 0; i < count; i++)
285 {
286 columnsList.Add(new ColumnInfo(columnNames[i], columnTypes[i]));
287 }
288
289 return columnsList;
290 }
291
292 /// <summary>
293 /// Gets a list of column names or column-definition-strings for the
294 /// associated view.
295 /// </summary>
296 /// <param name="view">the view to that defines the columns</param>
297 /// <param name="types">true to return types (column definition strings),
298 /// false to return names</param>
299 /// <returns>list of column names or types</returns>
300 private static IList<string> GetViewColumns(View view, bool types)
301 {
302 int recordHandle;
303 int typesFlag = types ? 1 : 0;
304 uint ret = RemotableNativeMethods.MsiViewGetColumnInfo(
305 (int) view.Handle, (uint) typesFlag, out recordHandle);
306 if (ret != 0)
307 {
308 throw InstallerException.ExceptionFromReturnCode(ret);
309 }
310
311 using (Record rec = new Record((IntPtr) recordHandle, true, null))
312 {
313 int count = rec.FieldCount;
314 IList<string> columnsList = new List<string>(count);
315
316 // Since we must be getting all strings of limited length,
317 // this code is faster than calling rec.GetString(field).
318 for (int field = 1; field <= count; field++)
319 {
320 uint bufSize = 256;
321 StringBuilder buf = new StringBuilder((int) bufSize);
322 ret = RemotableNativeMethods.MsiRecordGetString((int) rec.Handle, (uint) field, buf, ref bufSize);
323 if (ret != 0)
324 {
325 throw InstallerException.ExceptionFromReturnCode(ret);
326 }
327 columnsList.Add(buf.ToString());
328 }
329 return columnsList;
330 }
331 }
332 }
333}