PageRenderTime 30ms CodeModel.GetById 17ms app.highlight 8ms RepoModel.GetById 1ms app.codeStats 0ms

/Spss/SpssVariablesCollection.cs

#
C# | 346 lines | 186 code | 51 blank | 109 comment | 43 complexity | f93fa8f492fa53133c3403ab12f6b2ac MD5 | raw file
  1namespace Spss {
  2	using System;
  3	using System.IO;
  4	using System.Data;
  5	using System.Collections;
  6	using System.Diagnostics;
  7	using System.Globalization;
  8	using System.Collections.Generic;
  9	using System.Collections.ObjectModel;
 10
 11	/// <summary>
 12	/// Class to manage the metadata of variables in an <see cref="SpssDataDocument">SPSS data document</see>.
 13	/// </summary>
 14	public sealed class SpssVariablesCollection : IList<SpssVariable> {
 15		/// <summary>
 16		/// The list of variables in the file, or that will shortly be committed to the file.
 17		/// </summary>
 18		private List<SpssVariable> variables;
 19
 20		private KeyedCollection<string, SpssVariable> variablesLookup;
 21
 22		/// <summary>
 23		/// Initializes a new instance of the <see cref="SpssVariablesCollection"/> class.
 24		/// </summary>
 25		/// <param name="document">The hosting SPSS data document whose variables will be managed by
 26		/// this instance.</param>
 27		internal SpssVariablesCollection(SpssDataDocument document) {
 28			if (document == null) {
 29				throw new ArgumentNullException("document");
 30			}
 31
 32			this.Document = document;
 33
 34			InitializeVariablesList();
 35		}
 36
 37		/// <summary>
 38		/// Gets the variable with a given name.
 39		/// </summary>
 40		public SpssVariable this[string varName] {
 41			get { return variablesLookup[varName]; }
 42		}
 43
 44		/// <summary>
 45		/// The SPSS data document whose variables are being managed.
 46		/// </summary>
 47		public SpssDataDocument Document { get; private set; }
 48
 49		/// <summary>
 50		/// The file handle of the SPSS data document whose variables are being managed.
 51		/// </summary>
 52		private Int32 FileHandle {
 53			get { return this.Document.Handle; }
 54		}
 55
 56		/// <summary>
 57		/// Gets a value indicating whether a variable has been added to this document.
 58		/// </summary>
 59		public bool Contains(SpssVariable variable) {
 60			if (variable == null) throw new ArgumentNullException("variable");
 61
 62			return variables.Contains(variable);
 63		}
 64
 65		/// <summary>
 66		/// Gets a value indicating whether a variable exists in this document.
 67		/// </summary>
 68		/// <param name="varName">
 69		/// The name of the variable in question.
 70		/// </param>
 71		/// <returns>
 72		/// True if the variable already exists in the document.  False otherwise.
 73		/// </returns>
 74		public bool Contains(string varName) {
 75			if (varName == null || varName.Length == 0)
 76				throw new ArgumentNullException("varName");
 77
 78			return variablesLookup.Contains(varName);
 79		}
 80
 81		/// <summary>
 82		/// Gets the position of a variable.
 83		/// </summary>
 84		/// <returns>
 85		/// The index of the variable, or -1 if not found.
 86		/// </returns>
 87		public int IndexOf(SpssVariable variable) {
 88			if (variable == null) throw new ArgumentNullException("variable");
 89			return variables.IndexOf(variable);
 90		}
 91
 92		/// <summary>
 93		/// Gets the position of the variable with a given name.
 94		/// </summary>
 95		/// <returns>
 96		/// The index of the variable, or -1 if not found.
 97		/// </returns>
 98		public int IndexOf(string varName) {
 99			if (varName == null || varName.Length == 0) throw new ArgumentNullException("varName");
100			return IndexOf(variablesLookup[varName]);
101		}
102
103		/// <summary>
104		/// Adds a variable to the document at a specific index.
105		/// </summary>
106		public void Insert(int index, SpssVariable variable) {
107			if (variable == null) throw new ArgumentNullException("variable");
108			EnsureAuthoringDictionary();
109			variable.AddToCollection(this);
110			variablesLookup.Add(variable);
111			variables.Insert(index, variable);
112		}
113
114		/// <summary>
115		/// Adds a variable to the document.
116		/// </summary>
117		/// <returns>
118		/// The index of the newly added variable.
119		/// </returns>
120		public void Add(SpssVariable variable) {
121			if (variable == null) {
122				throw new ArgumentNullException("variable");
123			}
124			EnsureAuthoringDictionary();
125			variable.AddToCollection(this);
126			variablesLookup.Add(variable);
127			variables.Add(variable);
128		}
129
130		/// <summary>
131		/// Removes a variable from the document.
132		/// </summary>
133		public bool Remove(SpssVariable variable) {
134			if (variable == null) throw new ArgumentNullException("variable");
135			EnsureAuthoringDictionary();
136			try {
137				variable.RemoveFromCollection(this);
138			} catch (ArgumentException) {
139				return false;
140			}
141			variables.Remove(variable);
142			variablesLookup.Remove(variable.Name);
143			return true;
144		}
145
146		/// <summary>
147		/// Copies the definition of variables from this file to another.
148		/// </summary>
149		public void CopyTo(SpssVariablesCollection other, int index) {
150			if (other == null) throw new ArgumentNullException("other");
151			if (other == this) throw new ArgumentException("Must be a different variables collection.", "other");
152
153			throw new NotImplementedException();
154		}
155
156		/// <summary>
157		/// Defines the variables in the SPSS data file so that they mirror
158		/// those defined in a <see cref="DataTable"/>.
159		/// </summary>
160		/// <param name="table">
161		/// The DataTable whose list of columns are the ones we want to copy.
162		/// </param>
163		/// <param name="fillInMetadataCallback">
164		/// The callback method to use to retrieve the additional metadata 
165		/// to put into the SPSS data document, that is not included in a DataTable.
166		/// Optional.
167		/// </param>
168		public void ImportSchema(DataTable table, Action<SpssVariable> fillInMetadataCallback) {
169			foreach (DataColumn column in table.Columns) {
170				try {
171					SpssVariable var;
172					if (column.DataType == typeof(string)) {
173						var = new SpssStringVariable();
174						((SpssStringVariable)var).Length = (column.MaxLength < 0 || column.MaxLength > SpssSafeWrapper.SPSS_MAX_LONGSTRING) ? SpssSafeWrapper.SPSS_MAX_LONGSTRING : column.MaxLength;
175					} else if (column.DataType == typeof(DateTime))
176						var = new SpssDateVariable();
177					else {
178						var = new SpssNumericVariable();
179						if (column.DataType == typeof(float) || column.DataType == typeof(double)) {
180							((SpssNumericVariable)var).PrintDecimal = 2;
181							((SpssNumericVariable)var).WriteDecimal = 2;
182						}
183					}
184
185					var.Name = GenerateColumnName(column.ColumnName);
186					Add(var);
187
188					// Provide opportunity for callback function to fill in variable-specific metadata
189					if (fillInMetadataCallback != null) {
190						try {
191							fillInMetadataCallback(var);
192						} catch (Exception ex) {
193							throw new ApplicationException("Exception in metadata filler callback function on column " + column.ColumnName + ".", ex);
194						}
195					}
196				} catch (Exception ex) {
197					throw new ApplicationException("Error adding column " + column.ColumnName + " schema information to the SPSS .SAV data file.", ex);
198				}
199			}
200		}
201
202		/// <summary>
203		/// Defines the variables in the SPSS data file so that they mirror
204		/// those defined in a <see cref="DataTable"/>.
205		/// </summary>
206		/// <param name="table">
207		/// The DataTable whose list of columns are the ones we want to copy.
208		/// </param>
209		public void ImportSchema(DataTable table) {
210			ImportSchema(table, null);
211		}
212
213		/// <summary>
214		/// Writes the variables to the dictionary.
215		/// </summary>
216		internal void Commit() {
217			EnsureAuthoringDictionary();
218
219			// Write the variables we have been caching into the data file.
220			foreach (SpssVariable var in this) {
221				var.CommitToDictionary();
222			}
223		}
224
225		/// <summary>
226		/// Comes up with a variable name guaranteed to be short enough 
227		/// to fit within the limits of SPSS.
228		/// </summary>
229		/// <param name="colName">
230		/// The initially suggested name for a variable.
231		/// </param>
232		/// <returns>
233		/// The original variable name, if it was within SPSS limits.
234		/// Otherwise, it applies a string shortening algorithm and returns
235		/// the shorter variable name.
236		/// </returns>
237		/// <remarks>
238		/// The shortening algorithm takes the first and last several characters of the 
239		/// variable name and concatenates them together such that the resulting
240		/// string is exactly the allowed length for a variable name.
241		/// The process is not guaranteed to produce a unique variable name.
242		/// </remarks>
243		internal string GenerateColumnName(string colName) {
244			if (colName.Length > SpssThinWrapper.SPSS_MAX_VARNAME)
245				colName = colName.Substring(0, SpssThinWrapper.SPSS_MAX_VARNAME / 2) + colName.Substring(colName.Length - SpssThinWrapper.SPSS_MAX_VARNAME / 2);
246			return colName;
247		}
248
249		/// <summary>
250		/// Called when a <see cref="SpssVariable.Name"/> changes so that the lookup
251		/// table can be updated.
252		/// </summary>
253		internal void ColumnNameUpdated(SpssVariable variable, string oldName) {
254			variablesLookup.Remove(oldName);
255			variablesLookup.Add(variable);
256		}
257
258		private void EnsureAuthoringDictionary() {
259			Document.EnsureAuthoringDictionary();
260		}
261
262		private void InitializeVariablesList() {
263			Debug.Assert(FileHandle >= 0, "Must be working with an open file.");
264			int initialSize;
265			SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetNumberofVariables(FileHandle, out initialSize), "spssGetNumberofVariables");
266			variables = new List<SpssVariable>(initialSize);
267			variablesLookup = new SpssVariableKeyedCollection();
268
269			string[] varNames;
270			int[] varTypes;
271			ReturnCode result = SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarNames(FileHandle, out varNames, out varTypes), "spssGetVarNames", ReturnCode.SPSS_INVALID_FILE);
272			if (result == ReturnCode.SPSS_INVALID_FILE) {
273				// brand new file
274				return;
275			}
276			Debug.Assert(varNames.Length == varTypes.Length);
277			for (int i = 0; i < varNames.Length; i++) {
278				this.Add(SpssVariable.LoadVariable(this, varNames[i], varTypes[i]));
279			}
280		}
281
282		#region IList<SpssVariable> Members
283
284		/// <summary>
285		/// Gets the variable at some 0-based index.
286		/// </summary>
287		public SpssVariable this[int index] {
288			get { return variables[index]; }
289			set { variables[index] = value; }
290		}
291
292		#endregion
293
294		#region ICollection<SpssVariable> Members
295
296		public void CopyTo(SpssVariable[] array, int arrayIndex) {
297			throw new NotImplementedException();
298		}
299
300		#endregion
301
302		#region IEnumerable<SpssVariable> Members
303
304		IEnumerator<SpssVariable> IEnumerable<SpssVariable>.GetEnumerator() {
305			return this.variables.GetEnumerator();
306		}
307
308		#endregion
309
310		#region IList<SpssVariable> Members
311
312
313		public void RemoveAt(int index) {
314			throw new NotImplementedException();
315		}
316
317		#endregion
318
319		#region ICollection<SpssVariable> Members
320
321		public void Clear() {
322			EnsureAuthoringDictionary();
323			while (this.variables.Count > 0) {
324				this.Remove(this.variables[0]);
325			}
326		}
327
328		public int Count {
329			get { return this.variables.Count; }
330		}
331
332		public bool IsReadOnly {
333			get { return !this.Document.IsAuthoringDictionary; }
334		}
335
336		#endregion
337
338		#region IEnumerable Members
339
340		IEnumerator IEnumerable.GetEnumerator() {
341			return this.variables.GetEnumerator();
342		}
343
344		#endregion
345	}
346}