/Spss/SpssDataDocument.cs
C# | 325 lines | 148 code | 35 blank | 142 comment | 19 complexity | 6537cca9b457c9075cc420681203e1f0 MD5 | raw file
Possible License(s): LGPL-2.1
- //-----------------------------------------------------------------------
- // <copyright file="SpssDataDocument.cs" company="Andrew Arnott">
- // Copyright (c) Andrew Arnott. All rights reserved.
- // Copyright (c) Brigham Young University
- // </copyright>
- //-----------------------------------------------------------------------
-
- namespace Spss {
- using System;
- using System.Collections.Generic;
- using System.Data;
- using System.IO;
- using System.Linq;
-
- /// <summary>
- /// The three levels of file access supported by SPSS.
- /// </summary>
- public enum SpssFileAccess {
- /// <summary>
- /// A new file is being written.
- /// </summary>
- Create,
-
- /// <summary>
- /// Read-only access to metadata and data.
- /// </summary>
- Read,
-
- /// <summary>
- /// Data is being added to an existing file.
- /// </summary>
- Append,
- }
-
- /// <summary>
- /// Manages reading from and writing to SPSS data files.
- /// </summary>
- public class SpssDataDocument : IDisposable {
- /// <summary>
- /// Initializes a new instance of the <see cref="SpssDataDocument"/> class
- /// and opens an existing SPSS file, or creates a new one.
- /// </summary>
- /// <param name="filename">The path of the file to open/create.</param>
- /// <param name="access">The desired file access.</param>
- protected SpssDataDocument(string filename, SpssFileAccess access) {
- this.Filename = filename;
- this.AccessMode = access;
- int handle;
- switch (access) {
- case SpssFileAccess.Read:
- SpssException.ThrowOnFailure(SpssSafeWrapper.spssOpenRead(filename, out handle), "spssOpenRead");
- break;
- case SpssFileAccess.Append:
- SpssException.ThrowOnFailure(SpssSafeWrapper.spssOpenAppend(filename, out handle), "spssOpenAppend");
- break;
- case SpssFileAccess.Create:
- SpssException.ThrowOnFailure(SpssSafeWrapper.spssOpenWrite(filename, out handle), "spssOpenWrite");
- break;
- default:
- throw new ApplicationException("Unrecognized access level: " + access);
- }
-
- this.Handle = new SpssSafeHandle(handle, access);
- if (access == SpssFileAccess.Create) {
- this.IsCompressed = true;
- }
- this.IsAuthoringDictionary = true;
- this.Variables = new SpssVariablesCollection(this);
- this.Cases = new SpssCasesCollection(this);
- this.IsAuthoringDictionary = access == SpssFileAccess.Create;
- }
-
- /// <summary>
- /// An event raised when the dictionary has just been committed.
- /// </summary>
- protected internal event EventHandler DictionaryCommitted;
-
- /// <summary>
- /// Gets a value indicating whether the data file is in a dictionary writing state.
- /// </summary>
- /// <remarks>
- /// Dictionary writing is the first step in authoring an SPSS data file,
- /// and must be completed before any data rows is written to the file.
- /// </remarks>
- public bool IsAuthoringDictionary { get; private set; }
-
- /// <summary>
- /// Gets a value indicating whether this document is open for read or write access.
- /// </summary>
- public SpssFileAccess AccessMode { get; private set; }
-
- /// <summary>
- /// Gets the filename of the open document.
- /// </summary>
- public string Filename { get; private set; }
-
- /// <summary>
- /// Gets a value indicating whether this document has been closed.
- /// </summary>
- public bool IsClosed {
- get {
- return this.Handle.IsClosed;
- }
- }
-
- /// <summary>
- /// Gets or sets a value indicating whether the SPSS file is compressed on disk.
- /// </summary>
- public bool IsCompressed {
- get {
- int compressed;
- SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetCompressionImpl(this.Handle, out compressed), "spssGetCompression");
- return compressed != 0;
- }
-
- set {
- SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetCompression(this.Handle, value ? 1 : 0), "spssSetCompression");
- }
- }
-
- /// <summary>
- /// Gets the set of variables defined in the SPSS data file.
- /// </summary>
- public SpssVariablesCollection Variables { get; private set; }
-
- /// <summary>
- /// Gets the set of all cases saved in the data file.
- /// </summary>
- /// <value>
- /// In SPSS, a case is a row of data.
- /// </value>
- public SpssCasesCollection Cases { get; private set; }
-
- /// <summary>
- /// Gets the SPSS-defined system missing value.
- /// </summary>
- /// <remarks>
- /// Setting a numeric variable to this value is equivalent in purpose
- /// to setting DBNull.Value in a database.
- /// </remarks>
- protected internal static double SystemMissingValue {
- get {
- return SpssThinWrapper.spssSysmisValImpl();
- }
- }
-
- /// <summary>
- /// Gets the SPSS file handle for the open document.
- /// </summary>
- protected internal SpssSafeHandle Handle { get; private set; }
-
- /// <summary>
- /// Opens an SPSS data document for reading or appending.
- /// </summary>
- /// <param name="filename">
- /// The filename of the existing document to open.
- /// </param>
- /// <param name="access">
- /// <see cref="FileAccess.Read"/> for read only access, or
- /// <see cref="FileAccess.Write"/> for append access.
- /// </param>
- /// <returns>
- /// The newly opened <see cref="SpssDataDocument">SPSS data document</see>.
- /// </returns>
- /// <remarks>
- /// This method is only for opening existing data documents.
- /// To create a new document, use the <see cref="Create(string)"/> method.
- /// </remarks>
- public static SpssDataDocument Open(string filename, SpssFileAccess access) {
- if (access == SpssFileAccess.Create) {
- throw new ArgumentOutOfRangeException("access", access, "Use Create method to create a new file.");
- }
- return new SpssDataDocument(filename, access);
- }
-
- /// <summary>
- /// Creates a new SPSS data document.
- /// </summary>
- /// <param name="filename">
- /// The filename of the new document to create.
- /// </param>
- /// <returns>
- /// The newly created <see cref="SpssDataDocument">SPSS data document</see>.
- /// </returns>
- public static SpssDataDocument Create(string filename) {
- if (File.Exists(filename)) {
- throw new InvalidOperationException("File to create already exists.");
- }
- return new SpssDataDocument(filename, SpssFileAccess.Create);
- }
-
- /// <summary>
- /// Creates a new SPSS data document, initializing its dictionary
- /// by copying the dictionary from an existing SPSS data file.
- /// </summary>
- /// <param name="filename">
- /// The filename of the new document to create.
- /// </param>
- /// <param name="copyDictionaryFromFileName">
- /// The filename of the existing SPSS data file to copy the dictionary from.
- /// </param>
- /// <returns>
- /// The newly created <see cref="SpssDataDocument">SPSS data document</see>.
- /// </returns>
- public static SpssDataDocument Create(string filename, string copyDictionaryFromFileName) {
- if (File.Exists(filename)) {
- throw new InvalidOperationException("File to create already exists.");
- }
- if (!File.Exists(copyDictionaryFromFileName)) {
- throw new FileNotFoundException("File to copy does not exist.", copyDictionaryFromFileName);
- }
- using (SpssDataDocument read = SpssDataDocument.Open(copyDictionaryFromFileName, SpssFileAccess.Read)) {
- SpssDataDocument toReturn = new SpssDataDocument(filename, SpssFileAccess.Create);
-
- foreach (SpssVariable var in read.Variables) {
- toReturn.Variables.Add(var.Clone());
- }
-
- toReturn.CommitDictionary();
- return toReturn;
- }
- }
-
- /// <summary>
- /// Closes the SAV that is open for reading or writing.
- /// If no file is open, close() simply returns.
- /// </summary>
- public void Close() {
- this.Handle.Close();
- }
-
- /// <summary>
- /// Commits the dictionary of a newly created SPSS file.
- /// </summary>
- /// <remarks>
- /// This method should be called after all variables
- /// have been added to the data file.
- /// </remarks>
- public void CommitDictionary() {
- this.EnsureAuthoringDictionary();
- this.Variables.Commit();
-
- SpssException.ThrowOnFailure(SpssSafeWrapper.spssCommitHeaderImpl(this.Handle), "spssCommitHeader");
-
- this.IsAuthoringDictionary = false;
-
- this.OnDictionaryCommitted();
- }
-
- /// <summary>
- /// Imports data (and optionally metadata) into the document.
- /// </summary>
- /// <param name="table">The table.</param>
- /// <param name="data">The data, which may just be <paramref name="table"/>.Rows.</param>
- public void ImportData(DataTable table, IEnumerable<DataRow> data) {
- if (this.IsAuthoringDictionary) {
- // First import schema
- this.Variables.ImportSchema(table);
- this.CommitDictionary();
- }
-
- System.Collections.IEnumerable dataRows = data;
- if (dataRows == null) {
- dataRows = table.Rows;
- }
- foreach (DataRow row in dataRows) {
- SpssCase caseRow = this.Cases.New();
- for (int col = 0; col < table.Columns.Count; col++) {
- caseRow.SetDBValue(this.Variables.GenerateColumnName(table.Columns[col].ColumnName), row[col]);
- }
- caseRow.Commit();
- }
- }
-
- /// <summary>
- /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
- /// </summary>
- public void Dispose() {
- this.Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Throws an InvalidOperationException if the document has been closed.
- /// </summary>
- internal void EnsureNotClosed() {
- if (this.IsClosed) {
- throw new InvalidOperationException("Cannot perform this operation after the document has been closed.");
- }
- }
-
- /// <summary>
- /// Throws an InvalidOperationException if we're not in authoring dictionary mode.
- /// </summary>
- internal void EnsureAuthoringDictionary() {
- this.EnsureNotClosed();
- if (!this.IsAuthoringDictionary) {
- throw new InvalidOperationException("Cannot perform this operation unless the file has just been created.");
- }
- }
-
- /// <summary>
- /// Raises the <see cref="DictionaryCommitted"/> event.
- /// </summary>
- protected void OnDictionaryCommitted() {
- EventHandler dictionaryCommitted = this.DictionaryCommitted;
- if (dictionaryCommitted != null) {
- dictionaryCommitted(this, null);
- }
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool disposing) {
- if (disposing) {
- this.Close();
- }
-
- // We don't clean up our SPSS handle if !disposing because it's a SafeHandle, which will take care of itself.
- // And SafeHandle is a class, and we should never touch references during finalization.
- }
- }
- }