PageRenderTime 58ms CodeModel.GetById 26ms app.highlight 23ms RepoModel.GetById 2ms app.codeStats 0ms

/Spss/SpssDataDocument.cs

#
C# | 325 lines | 148 code | 35 blank | 142 comment | 19 complexity | 6537cca9b457c9075cc420681203e1f0 MD5 | raw file
  1//-----------------------------------------------------------------------
  2// <copyright file="SpssDataDocument.cs" company="Andrew Arnott">
  3//     Copyright (c) Andrew Arnott. All rights reserved.
  4//     Copyright (c) Brigham Young University
  5// </copyright>
  6//-----------------------------------------------------------------------
  7
  8namespace Spss {
  9	using System;
 10	using System.Collections.Generic;
 11	using System.Data;
 12	using System.IO;
 13	using System.Linq;
 14
 15	/// <summary>
 16	/// The three levels of file access supported by SPSS.
 17	/// </summary>
 18	public enum SpssFileAccess {
 19		/// <summary>
 20		/// A new file is being written.
 21		/// </summary>
 22		Create,
 23
 24		/// <summary>
 25		/// Read-only access to metadata and data.
 26		/// </summary>
 27		Read,
 28
 29		/// <summary>
 30		/// Data is being added to an existing file.
 31		/// </summary>
 32		Append,
 33	}
 34
 35	/// <summary>
 36	/// Manages reading from and writing to SPSS data files.
 37	/// </summary>
 38	public class SpssDataDocument : IDisposable {
 39		/// <summary>
 40		/// Initializes a new instance of the <see cref="SpssDataDocument"/> class
 41		/// and opens an existing SPSS file, or creates a new one.
 42		/// </summary>
 43		/// <param name="filename">The path of the file to open/create.</param>
 44		/// <param name="access">The desired file access.</param>
 45		protected SpssDataDocument(string filename, SpssFileAccess access) {
 46			this.Filename = filename;
 47			this.AccessMode = access;
 48			int handle;
 49			switch (access) {
 50				case SpssFileAccess.Read:
 51					SpssException.ThrowOnFailure(SpssSafeWrapper.spssOpenRead(filename, out handle), "spssOpenRead");
 52					break;
 53				case SpssFileAccess.Append:
 54					SpssException.ThrowOnFailure(SpssSafeWrapper.spssOpenAppend(filename, out handle), "spssOpenAppend");
 55					break;
 56				case SpssFileAccess.Create:
 57					SpssException.ThrowOnFailure(SpssSafeWrapper.spssOpenWrite(filename, out handle), "spssOpenWrite");
 58					break;
 59				default:
 60					throw new ApplicationException("Unrecognized access level: " + access);
 61			}
 62
 63			this.Handle = new SpssSafeHandle(handle, access);
 64			if (access == SpssFileAccess.Create) {
 65				this.IsCompressed = true;
 66			}
 67			this.IsAuthoringDictionary = true;
 68			this.Variables = new SpssVariablesCollection(this);
 69			this.Cases = new SpssCasesCollection(this);
 70			this.IsAuthoringDictionary = access == SpssFileAccess.Create;
 71		}
 72
 73		/// <summary>
 74		/// An event raised when the dictionary has just been committed.
 75		/// </summary>
 76		protected internal event EventHandler DictionaryCommitted;
 77
 78		/// <summary>
 79		/// Gets a value indicating whether the data file is in a dictionary writing state.
 80		/// </summary>
 81		/// <remarks>
 82		/// Dictionary writing is the first step in authoring an SPSS data file,
 83		/// and must be completed before any data rows is written to the file.
 84		/// </remarks>
 85		public bool IsAuthoringDictionary { get; private set; }
 86
 87		/// <summary>
 88		/// Gets a value indicating whether this document is open for read or write access.
 89		/// </summary>
 90		public SpssFileAccess AccessMode { get; private set; }
 91
 92		/// <summary>
 93		/// Gets the filename of the open document.
 94		/// </summary>
 95		public string Filename { get; private set; }
 96
 97		/// <summary>
 98		/// Gets a value indicating whether this document has been closed.
 99		/// </summary>
100		public bool IsClosed {
101			get {
102				return this.Handle.IsClosed;
103			}
104		}
105
106		/// <summary>
107		/// Gets or sets a value indicating whether the SPSS file is compressed on disk.
108		/// </summary>
109		public bool IsCompressed {
110			get {
111				int compressed;
112				SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetCompressionImpl(this.Handle, out compressed), "spssGetCompression");
113				return compressed != 0;
114			}
115
116			set {
117				SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetCompression(this.Handle, value ? 1 : 0), "spssSetCompression");
118			}
119		}
120
121		/// <summary>
122		/// Gets the set of variables defined in the SPSS data file.
123		/// </summary>
124		public SpssVariablesCollection Variables { get; private set; }
125
126		/// <summary>
127		/// Gets the set of all cases saved in the data file.
128		/// </summary>
129		/// <value>
130		/// In SPSS, a case is a row of data.
131		/// </value>
132		public SpssCasesCollection Cases { get; private set; }
133
134		/// <summary>
135		/// Gets the SPSS-defined system missing value.
136		/// </summary>
137		/// <remarks>
138		/// Setting a numeric variable to this value is equivalent in purpose
139		/// to setting DBNull.Value in a database.
140		/// </remarks>
141		protected internal static double SystemMissingValue {
142			get {
143				return SpssThinWrapper.spssSysmisValImpl();
144			}
145		}
146
147		/// <summary>
148		/// Gets the SPSS file handle for the open document.
149		/// </summary>
150		protected internal SpssSafeHandle Handle { get; private set; }
151
152		/// <summary>
153		/// Opens an SPSS data document for reading or appending.
154		/// </summary>
155		/// <param name="filename">
156		/// The filename of the existing document to open.
157		/// </param>
158		/// <param name="access">
159		/// <see cref="FileAccess.Read"/> for read only access, or 
160		/// <see cref="FileAccess.Write"/> for append access.
161		/// </param>
162		/// <returns>
163		/// The newly opened <see cref="SpssDataDocument">SPSS data document</see>.
164		/// </returns>
165		/// <remarks>
166		/// This method is only for opening existing data documents.
167		/// To create a new document, use the <see cref="Create(string)"/> method.
168		/// </remarks>
169		public static SpssDataDocument Open(string filename, SpssFileAccess access) {
170			if (access == SpssFileAccess.Create) {
171				throw new ArgumentOutOfRangeException("access", access, "Use Create method to create a new file.");
172			}
173			return new SpssDataDocument(filename, access);
174		}
175
176		/// <summary>
177		/// Creates a new SPSS data document.
178		/// </summary>
179		/// <param name="filename">
180		/// The filename of the new document to create.
181		/// </param>
182		/// <returns>
183		/// The newly created <see cref="SpssDataDocument">SPSS data document</see>.
184		/// </returns>
185		public static SpssDataDocument Create(string filename) {
186			if (File.Exists(filename)) {
187				throw new InvalidOperationException("File to create already exists.");
188			}
189			return new SpssDataDocument(filename, SpssFileAccess.Create);
190		}
191
192		/// <summary>
193		/// Creates a new SPSS data document, initializing its dictionary 
194		/// by copying the dictionary from an existing SPSS data file.
195		/// </summary>
196		/// <param name="filename">
197		/// The filename of the new document to create.
198		/// </param>
199		/// <param name="copyDictionaryFromFileName">
200		/// The filename of the existing SPSS data file to copy the dictionary from.
201		/// </param>
202		/// <returns>
203		/// The newly created <see cref="SpssDataDocument">SPSS data document</see>.
204		/// </returns>
205		public static SpssDataDocument Create(string filename, string copyDictionaryFromFileName) {
206			if (File.Exists(filename)) {
207				throw new InvalidOperationException("File to create already exists.");
208			}
209			if (!File.Exists(copyDictionaryFromFileName)) {
210				throw new FileNotFoundException("File to copy does not exist.", copyDictionaryFromFileName);
211			}
212			using (SpssDataDocument read = SpssDataDocument.Open(copyDictionaryFromFileName, SpssFileAccess.Read)) {
213				SpssDataDocument toReturn = new SpssDataDocument(filename, SpssFileAccess.Create);
214
215				foreach (SpssVariable var in read.Variables) {
216					toReturn.Variables.Add(var.Clone());
217				}
218
219				toReturn.CommitDictionary();
220				return toReturn;
221			}
222		}
223
224		/// <summary>
225		/// Closes the SAV that is open for reading or writing.  
226		/// If no file is open, close() simply returns.
227		/// </summary>
228		public void Close() {
229			this.Handle.Close();
230		}
231
232		/// <summary>
233		/// Commits the dictionary of a newly created SPSS file.
234		/// </summary>
235		/// <remarks>
236		/// This method should be called after all variables
237		/// have been added to the data file.
238		/// </remarks>
239		public void CommitDictionary() {
240			this.EnsureAuthoringDictionary();
241			this.Variables.Commit();
242
243			SpssException.ThrowOnFailure(SpssSafeWrapper.spssCommitHeaderImpl(this.Handle), "spssCommitHeader");
244
245			this.IsAuthoringDictionary = false;
246
247			this.OnDictionaryCommitted();
248		}
249
250		/// <summary>
251		/// Imports data (and optionally metadata) into the document.
252		/// </summary>
253		/// <param name="table">The table.</param>
254		/// <param name="data">The data, which may just be <paramref name="table"/>.Rows.</param>
255		public void ImportData(DataTable table, IEnumerable<DataRow> data) {
256			if (this.IsAuthoringDictionary) {
257				// First import schema
258				this.Variables.ImportSchema(table);
259				this.CommitDictionary();
260			}
261
262			System.Collections.IEnumerable dataRows = data;
263			if (dataRows == null) {
264				dataRows = table.Rows;
265			}
266			foreach (DataRow row in dataRows) {
267				SpssCase caseRow = this.Cases.New();
268				for (int col = 0; col < table.Columns.Count; col++) {
269					caseRow.SetDBValue(this.Variables.GenerateColumnName(table.Columns[col].ColumnName), row[col]);
270				}
271				caseRow.Commit();
272			}
273		}
274
275		/// <summary>
276		/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
277		/// </summary>
278		public void Dispose() {
279			this.Dispose(true);
280			GC.SuppressFinalize(this);
281		}
282
283		/// <summary>
284		/// Throws an InvalidOperationException if the document has been closed.
285		/// </summary>
286		internal void EnsureNotClosed() {
287			if (this.IsClosed) {
288				throw new InvalidOperationException("Cannot perform this operation after the document has been closed.");
289			}
290		}
291
292		/// <summary>
293		/// Throws an InvalidOperationException if we're not in authoring dictionary mode.
294		/// </summary>
295		internal void EnsureAuthoringDictionary() {
296			this.EnsureNotClosed();
297			if (!this.IsAuthoringDictionary) {
298				throw new InvalidOperationException("Cannot perform this operation unless the file has just been created.");
299			}
300		}
301
302		/// <summary>
303		/// Raises the <see cref="DictionaryCommitted"/> event.
304		/// </summary>
305		protected void OnDictionaryCommitted() {
306			EventHandler dictionaryCommitted = this.DictionaryCommitted;
307			if (dictionaryCommitted != null) {
308				dictionaryCommitted(this, null);
309			}
310		}
311
312		/// <summary>
313		/// Releases unmanaged and - optionally - managed resources
314		/// </summary>
315		/// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
316		protected virtual void Dispose(bool disposing) {
317			if (disposing) {
318				this.Close();
319			}
320
321			// We don't clean up our SPSS handle if !disposing because it's a SafeHandle, which will take care of itself.
322			// And SafeHandle is a class, and we should never touch references during finalization.
323		}
324	}
325}