PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 1ms

/Spss/SpssVariable.cs

#
C# | 381 lines | 258 code | 29 blank | 94 comment | 74 complexity | b9ecdcb8641214941efc4cdfcd35d939 MD5 | raw file
Possible License(s): LGPL-2.1
  1. using System;
  2. using System.Linq;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.Diagnostics;
  6. namespace Spss
  7. {
  8. /// <summary>
  9. /// Represents an SPSS data variable.
  10. /// </summary>
  11. public abstract class SpssVariable
  12. {
  13. /// <summary>
  14. /// Creates an instance of the <see cref="SpssNumericVariable"/> class.
  15. /// </summary>
  16. protected SpssVariable()
  17. {
  18. }
  19. /// <summary>
  20. /// Creates an instance of the <see cref="SpssNumericVariable"/> class.
  21. /// </summary>
  22. /// <param name="variables">The containing collection.</param>
  23. /// <param name="varName">The name of the variable.</param>
  24. protected SpssVariable(SpssVariablesCollection variables, string varName)
  25. {
  26. if( variables == null ) throw new ArgumentNullException("variables");
  27. if( varName == null || varName.Length == 0 )
  28. throw new ArgumentNullException("varName");
  29. this.variables = variables;
  30. AssumeIdentity(varName);
  31. }
  32. private void AssumeIdentity( string varName )
  33. {
  34. if( varName == null || varName.Length == 0 )
  35. throw new ArgumentNullException("varName");
  36. ReturnCode result = SpssSafeWrapper.spssGetVarHandle( FileHandle, varName, out variableHandle );
  37. switch( result )
  38. {
  39. case ReturnCode.SPSS_OK:
  40. break;
  41. case ReturnCode.SPSS_DICT_NOTCOMMIT:
  42. // Header not yet saved, but that's ok.
  43. // Just remember the name of the variable
  44. break;
  45. case ReturnCode.SPSS_VAR_NOTFOUND:
  46. throw new ArgumentOutOfRangeException("varName", varName, "SPSS returned: " + result);
  47. default:
  48. throw new SpssException(result, "spssGetVarHandle");
  49. }
  50. name = varName;
  51. }
  52. internal static SpssVariable LoadVariable(SpssVariablesCollection parent, string varName, int varType) {
  53. FormatTypeCode writeFormat, printFormat;
  54. int writeDecimal, writeWidth, printDecimal, printWidth;
  55. SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarWriteFormat(parent.Document.Handle, varName, out writeFormat, out writeDecimal, out writeWidth), "spssGetVarWriteFormat");
  56. SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarPrintFormat(parent.Document.Handle, varName, out printFormat, out printDecimal, out printWidth), "spssGetVarPrintFormat");
  57. SpssVariable variable;
  58. switch (varType) {
  59. case 0:
  60. // This may be a date or a numeric
  61. if (SpssDateVariable.IsDateVariable(writeFormat))
  62. variable = new SpssDateVariable(parent, varName, writeFormat, writeWidth, printFormat, printWidth);
  63. else
  64. variable = new SpssNumericVariable(parent, varName, writeFormat, writeDecimal, writeWidth, printFormat, printDecimal, printWidth);
  65. break;
  66. default:
  67. Debug.Assert(varType == printWidth);
  68. variable = new SpssStringVariable(parent, varName, varType);
  69. break;
  70. }
  71. return variable;
  72. }
  73. #region Attributes
  74. private bool committedThisSession = false;
  75. internal bool CommittedThisSession { get { return committedThisSession; } }
  76. /// <summary>
  77. /// Gets a value indicating whether this variable has been added to a collection yet.
  78. /// </summary>
  79. protected internal bool IsInCollection { get { return Variables != null; } }
  80. /// <summary>
  81. /// Gets a value indicating whether this variable has been committed to the SPSS data file.
  82. /// </summary>
  83. protected internal bool IsCommitted { get { return Handle >= 0; } }
  84. private SpssVariablesCollection variables;
  85. /// <summary>
  86. /// The collection of variables to which this one belongs.
  87. /// </summary>
  88. public SpssVariablesCollection Variables { get { return variables; } }
  89. /// <summary>
  90. /// The file handle of the SPSS data document whose variables are being managed.
  91. /// </summary>
  92. protected Int32 FileHandle
  93. {
  94. get
  95. {
  96. if( !IsInCollection )
  97. throw new InvalidOperationException("This variable is not associated with a SPSS data file.");
  98. return Variables.Document.Handle;
  99. }
  100. }
  101. private double variableHandle = -1;
  102. /// <summary>
  103. /// The variable handle assigned by SPSS for this variable.
  104. /// </summary>
  105. protected double Handle
  106. {
  107. get
  108. {
  109. return variableHandle;
  110. }
  111. }
  112. private string name;
  113. /// <summary>
  114. /// Gets the name of the variable.
  115. /// </summary>
  116. public string Name
  117. {
  118. get
  119. {
  120. return name;
  121. }
  122. set
  123. {
  124. if( value == null || value.Length == 0 ) throw new ArgumentNullException("Name");
  125. if( value.Length > SpssSafeWrapper.SPSS_MAX_VARNAME )
  126. throw new ArgumentOutOfRangeException("Name", value, "Too long. Maximum variable name is " + SpssSafeWrapper.SPSS_MAX_VARNAME + " characters.");
  127. VerifyNotCommittedVariable();
  128. // Ensure that this new name will not conflict with another variable.
  129. if( IsInCollection && Variables.Contains(value) )
  130. throw new SpssVariableNameConflictException(value, name);
  131. // Ensures that the look up table in SpssVariablesCollection are renamed as well.
  132. string oldName = name;
  133. name = value;
  134. if (Variables != null)
  135. Variables.ColumnNameUpdated(this, oldName);
  136. }
  137. }
  138. private string label = null;
  139. /// <summary>
  140. /// Gets or sets the variable label.
  141. /// </summary>
  142. public string Label
  143. {
  144. get
  145. {
  146. // If this variable was read from an existing data file, the label
  147. // may not have been loaded yet.
  148. if( label == null && IsCommitted )
  149. {
  150. SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarLabel(FileHandle, Name, out label), "spssGetVarLabel", ReturnCode.SPSS_NO_LABEL);
  151. }
  152. return label ?? string.Empty;
  153. }
  154. set
  155. {
  156. if( value == null ) value = string.Empty;
  157. // Check to make sure the label is not too long
  158. if( value.Length > SpssSafeWrapper.SPSS_MAX_VARLABEL )
  159. throw new ArgumentOutOfRangeException("Label", value, "Label length maximum is " + SpssSafeWrapper.SPSS_MAX_VARLABEL);
  160. label = value;
  161. }
  162. }
  163. /// <summary>
  164. /// Gets or sets the data value of this variable within a specific case.
  165. /// </summary>
  166. internal object Value
  167. {
  168. get
  169. {
  170. if( this is SpssNumericVariable )
  171. return ((SpssNumericVariable)this).Value;
  172. else if( this is SpssStringVariable )
  173. return ((SpssStringVariable)this).Value;
  174. else if( this is SpssDateVariable )
  175. return ((SpssDateVariable)this).Value;
  176. else
  177. throw new NotSupportedException("Specific type of SpssVariable could not be determined and is not supported.");
  178. }
  179. set
  180. {
  181. if( this is SpssNumericVariable )
  182. ((SpssNumericVariable)this).Value = (value == null) ? (double?)null : Convert.ToDouble(value);
  183. else if( this is SpssStringVariable )
  184. ((SpssStringVariable)this).Value = (string) value;
  185. else if( this is SpssDateVariable )
  186. ((SpssDateVariable)this).Value = (value == null) ? (DateTime?)null : (DateTime) value;
  187. else
  188. throw new NotSupportedException("Specific type of SpssVariable could not be determined and is not supported.");
  189. }
  190. }
  191. /// <summary>
  192. /// Gets the SPSS type for the variable.
  193. /// </summary>
  194. /// <value>For numeric/date types, this is 0. For strings, this is the length of the column.</value>
  195. public abstract int SpssType { get; }
  196. protected const int ColumnWidthDefault = 8;
  197. private int columnWidth = -1;
  198. /// <summary>
  199. /// The width to reserve for this variable when printed.
  200. /// </summary>
  201. public int ColumnWidth
  202. {
  203. get
  204. {
  205. // If this variable was read from an existing file, and
  206. // width has not yet been retrieved, get it.
  207. if( columnWidth < 0 && Handle >= 0 )
  208. SpssSafeWrapper.spssGetVarColumnWidth(FileHandle, Name, out columnWidth);
  209. return columnWidth >= 0 ? columnWidth : ColumnWidthDefault;
  210. }
  211. set
  212. {
  213. if( value <= 0 ) throw new ArgumentOutOfRangeException("ColumnWidth", value, "Must be a positive integer.");
  214. columnWidth = value;
  215. }
  216. }
  217. private MeasurementLevelCode measurementLevel = MeasurementLevelCode.SPSS_MLVL_UNK;
  218. /// <summary>
  219. /// Gets or sets the measurement level.
  220. /// </summary>
  221. /// <value>The measurement level.</value>
  222. public MeasurementLevelCode MeasurementLevel {
  223. get {
  224. // If this variable was read from an existing file, and
  225. // width has not yet been retrieved, get it.
  226. if (measurementLevel == MeasurementLevelCode.SPSS_MLVL_UNK && Handle >= 0) {
  227. SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarMeasureLevel(this.FileHandle, this.Name, out measurementLevel), "spssGetVarMeasureLevel");
  228. }
  229. return measurementLevel;
  230. }
  231. set {
  232. measurementLevel = value;
  233. }
  234. }
  235. private AlignmentCode alignment = AlignmentCode.SPSS_ALIGN_LEFT;
  236. /// <summary>
  237. /// Gets or sets the alignment of the variable.
  238. /// </summary>
  239. /// <value>The alignment.</value>
  240. public AlignmentCode Alignment {
  241. get {
  242. // If this variable was read from an existing file, get it.
  243. if (this.Handle >= 0) {
  244. SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarAlignment(this.FileHandle, this.Name, out alignment), "spssGetVarAlignment");
  245. }
  246. return alignment;
  247. }
  248. set {
  249. alignment = value;
  250. }
  251. }
  252. #endregion
  253. #region Operations
  254. protected abstract bool IsApplicableFormatTypeCode(FormatTypeCode formatType);
  255. public IEnumerable<KeyValuePair<string, string>> GetValueLabels() {
  256. var numeric = this as SpssNumericVariable;
  257. if (numeric != null) {
  258. return numeric.ValueLabels.Select(pair => new KeyValuePair<string, string>(pair.Key.ToString(CultureInfo.InvariantCulture), pair.Value));
  259. }
  260. var str = this as SpssStringVariable;
  261. if (str != null) {
  262. return str.ValueLabels;
  263. }
  264. return Enumerable.Empty<KeyValuePair<string, string>>();
  265. }
  266. /// <summary>
  267. /// Clones the variable for use in another <see cref="SpssDataDocument"/>.
  268. /// </summary>
  269. /// <returns></returns>
  270. public abstract SpssVariable Clone();
  271. /// <summary>
  272. /// Copies the fields from this variable into another previously created
  273. /// <see cref="SpssVariable"/>.
  274. /// </summary>
  275. protected virtual void CloneTo(SpssVariable other)
  276. {
  277. if (other == null) throw new ArgumentNullException("other");
  278. other.Name = Name;
  279. other.Label = Label;
  280. other.ColumnWidth = ColumnWidth;
  281. }
  282. /// <summary>
  283. /// Throws an <see cref="InvalidOperationException"/> when called
  284. /// after the variable has been committed to the dictionary.
  285. /// </summary>
  286. protected void VerifyNotCommittedVariable()
  287. {
  288. if( IsCommitted )
  289. throw new InvalidOperationException("Cannot perform this operation after the variable has been committed.");
  290. }
  291. /// <summary>
  292. /// Writes the variable's metadata out to the dictionary of the SPSS data file.
  293. /// </summary>
  294. protected internal void CommitToDictionary()
  295. {
  296. if( Handle >= 0 ) throw new InvalidOperationException("Already committed.");
  297. // Create the variable.
  298. SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetVarName(FileHandle, Name, SpssType), "spssSetVarName");
  299. // Call the descending class to finish the details.
  300. Update();
  301. committedThisSession = true;
  302. }
  303. /// <summary>
  304. /// Updates the changed attributes of the variable within SPSS.
  305. /// </summary>
  306. protected virtual void Update() {
  307. if (!IsInCollection) return; // we'll get to do this later
  308. SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetVarLabel(FileHandle, Name, Label), "spssSetVarLabel");
  309. SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetVarColumnWidth(FileHandle, Name, ColumnWidth), "spssSetVarColumnWidth");
  310. SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetVarMeasureLevel(this.FileHandle, this.Name, this.MeasurementLevel), "spssSetVarMeasureLevel");
  311. SpssException.ThrowOnFailure(SpssSafeWrapper.spssSetVarAlignment(this.FileHandle, this.Name, this.Alignment), "spssSetVarAlignment");
  312. }
  313. /// <summary>
  314. /// Informs this variable that it is being added to a <see cref="SpssVariablesCollection"/>.
  315. /// </summary>
  316. /// <exception cref="InvalidOperationException">
  317. /// Thrown when this variable already belongs to a different collection.
  318. /// </exception>
  319. internal void AddToCollection(SpssVariablesCollection variables)
  320. {
  321. if( variables == null ) throw new ArgumentNullException("variables");
  322. if( Variables != null && Variables != variables )
  323. throw new InvalidOperationException("Already belongs to a different collection.");
  324. if( Name == null || Name.Length == 0 )
  325. throw new InvalidOperationException("SpssVariable.Name must be set first.");
  326. // Make sure that a variable with this same name has not already been added to the collection.
  327. if( variables.Contains(Name) && !variables.Contains(this) ) // and not this one
  328. throw new SpssVariableNameConflictException(Name);
  329. this.variables = variables;
  330. Variables.Document.DictionaryCommitted += new EventHandler(Document_DictionaryCommitted);
  331. }
  332. /// <summary>
  333. /// Informs this variable that it is being removed from a <see cref="SpssVariablesCollection"/>.
  334. /// </summary>
  335. internal void RemoveFromCollection(SpssVariablesCollection variables)
  336. {
  337. if( variables == null ) throw new ArgumentNullException("variables");
  338. if( variables != Variables )
  339. throw new ArgumentException("The variables collection being removed from does not match the collection this variable belongs to.");
  340. Variables.Document.DictionaryCommitted -= new EventHandler(Document_DictionaryCommitted);
  341. this.variables = null; // remove reference to owning collection
  342. }
  343. #endregion
  344. #region Events
  345. private void Document_DictionaryCommitted(object sender, EventArgs e)
  346. {
  347. // Set the variable handle
  348. SpssException.ThrowOnFailure(SpssSafeWrapper.spssGetVarHandle(FileHandle, Name, out variableHandle), "spssGetVarHandle");
  349. }
  350. #endregion
  351. }
  352. }