PageRenderTime 40ms CodeModel.GetById 20ms app.highlight 15ms RepoModel.GetById 1ms app.codeStats 0ms

/Spss/SpssVariable.cs

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