PageRenderTime 57ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/Spss/SpssVariablesCollection.cs

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