PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/Spss/SpssDataDocument.cs

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