1 /***************************************************************************
2                           rkvariable  -  description
3                              -------------------
4     begin                : Thu Aug 12 2004
5     copyright            : (C) 2004, 2007, 2010, 2011, 2012 by Thomas Friedrichsmeier
6     email                : thomas.friedrichsmeier@kdemail.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 #ifndef RKVARIABLE_H
18 #define RKVARIABLE_H
19 
20 #include <QStringList>
21 #include <QHash>
22 
23 #include "robject.h"
24 
25 class RContainerObject;
26 
27 /** Abstract representation of a variable. A variable in this diction is an RObject, which is a vector of data. It may internally be a factor or a vector.
28 RKVariables are so far the only type of object that is really editable (data.frames are just a bundle of RKVariables). Therefore, for most practical purposes, the RKVariable represents a column in a table.
29 
30 TODO: actually, for now, the data is always given to the backend as strings. Change that!
31 TODO: there should be "chunks" of column-data. This should be done at the level of rows, i.e. across columns. After all, if a row gets added/removed in one column, all other columns of the same table will also be affected.
32 TODO: which functions should do syncing by themselves, which should not? Or should all set... ()-functions have an extra parameter for this?
33 
34 @author Thomas Friedrichsmeier
35 */
36 class RKVariable : public RObject {
37 public:
38 /** constructs a new RKVariable as a child of the given parent and with the given name. Do not call directly, but let RContainerObject / RObjectList handle creation of new variables. */
39 	RKVariable (RContainerObject *parent, const QString &name);
40 
41 	~RKVariable ();
42 
43 /** set the VarType. If sync, the change will be communicated to the backend immediately. See RObject::RDataType */
44 	void setVarType (RObject::RDataType, bool sync=true);
45 
46 /** reimplemented from RObject to also store value labels/factor levels (and in the future probably further info) */
47 	void writeMetaData (RCommandChain *chain) override;
48 friend class RContainerObject;
49 	void rCommandDone (RCommand *command) override;
50 public:
51 ////////////// BEGIN: data handling ////////////////////////
52 /** the Status enum is used for both keeping track of the entire row and individual cells. For single cells the meaning should be obvious. The entire row
53 is set to Unused, if _no_ cell in the row is used, Valid if _all_ cells in the row are valid and Invalid if _one or more_ cells in the row are invalid, Unknown if _all_ cells in the row are unknown/updating. */
54 	enum Status { ValueUnused=0, ValueValid=1, ValueInvalid=2, ValueUnknown=4 };
55 
56 /** sets whether changed data should be synced immediately or not. Set this to off for large paste operations. Remember to call setSyncing (true) and syncDataToR () after the paste is complete */
57 	void lockSyncing (bool lock);
58 /** syncs pending data changes to the backend */
59 	void syncDataToR ();
60 /** reimplemented from RObject */
61 	void updateDataFromR (RCommandChain *chain) override;
62 
63 	bool hasInvalidFields () const;
64 
65 /** get the value at the given row in text-form - regardless of the storage mode.
66 @param pretty: get the text in pretty form, e.g. rounding numbers to a certain number of digits, replacing numeric values with value labels if available, etc. Formatting is done according to the meta-information stored in the RObject and global user preferences */
67 	QString getText (int row, bool pretty=false) const;
68 /** get the value at the given row in text-form suitable for submission to R. I.e. strings are quoted, numbers are not, empty values are returned as NA */
69 	QString getRText (int row) const;
70 /** set the value at the given row in text-form. Will try to convert the given string to the internal storage format if possible. */
71 	virtual void setText (int row, const QString &text);
72 
73 /** get a copy of the text values of rows from from_index to to_index. TODO: This could be made, but currently is not, more efficient than calling getText in a loop. */
74 	QString *getCharacter (int from_row, int to_row) const;
75 
76 /** returns the current status of the given cell */
77 	Status cellStatus (int row) const;
78 
79 /** entirely remove the given rows (i.e. the cells). Will also take care of updating the state (are there any invalid cells left?). Does not sync with the backend for technical reasons! You have to remove the row in the backend explicitly. */
80 	virtual void removeRows (int from_row, int to_row);
81 /** inserts count rows (with empty values) just above the given index. Does not sync with the backend for technical reasons! You have to insert the row in the backend explicitly. */
82 	virtual void insertRows (int row, int count);
83 /** Tells the object it has (data) length len. Usually this will only be called directly after creating a new object */
84 	void setLength (int len);
85 
86 /** returns (a copy of) the map of value labels for this variable or and empty map, if no labels/levels are assigned. */
87 	ValueLabels getValueLabels () const;
88 /** assigns a new map of labels. Also takes care of syncing with the backend. */
89 	void setValueLabels (const ValueLabels& labels);
90 /** re-check a factor variable after editing its value labels, and sync labels to R */
91 	void updateValueLabels ();
92 /** get value labels as string (for display) */
93 	QString getValueLabelString () const;
94 /** set value labels from string (for paste operations) */
95 	void setValueLabelString (const QString &string);
96 
97 /** Restores the variable including data and meta-data */
98 	void restore (RCommandChain *chain=0);
99 
100 /** Stores formatting options set for this variable */
101 	struct FormattingOptions {
102 		enum Alignment { AlignDefault=0, AlignLeft=1, AlignRight=2 };
103 		enum Precision { PrecisionDefault=0, PrecisionRequired=1, PrecisionFixed=2 };
104 
105 		Alignment alignment;
106 		Precision precision_mode;
107 		int precision;
108 	};
109 
110 /** assigns new formatting options. Ownership of the FormattingOptions -struct is transferred to the variable. Use setFormatting (0) to remove all options */
111 	void setFormattingOptions (const FormattingOptions new_options);
112 /** get the formatting options for this variable */
113 	FormattingOptions getFormattingOptions () const;
114 /** get formatting options as a string (for display) TODO: redundant -> remove */
115 	QString getFormattingOptionsString () const;
116 /** parse formatting options from the given string TODO: redundant -> remove */
117 	void setFormattingOptionsString (const QString &string);
118 
119 /** This enum describes the alignment of text inside a table cell */
120 	enum CellAlign { AlignCellLeft=0, AlignCellRight=1 };
121 /** returns alignment to use for this variable */
122 	CellAlign getAlignment () const;
123 
124 /** creates/parses formatting options from the stored meta-property string. See also: getFormattingOptions () */
125 	static FormattingOptions parseFormattingOptionsString (const QString &string);
126 /** inverse of parseFormattingOptionsString () */
127 	static QString formattingOptionsToString (const FormattingOptions& options);
128 /** changes the allocated storage to contain a least length elements. More data may be allocated than actually needed. This function only ever does upsizing. */
129 	void extendToLength (int length);
130 protected:
131 /** Discards pending unsynced changes. */
132 	void discardUnsyncedChanges ();
133 /** like setNumeric, but sets chars. If internalStorage () is numeric, attempts to convert the given strings to numbers. I.e. the function behaves essentially like setText (), but operates on a range of cells. Code may assume that all data comes directly from R, is entirely valid in R. */
134 	virtual void setCharacterFromR (int from_row, int to_row, const QStringList &data);
135 /** set numeric values in the given range. Assumes you provide enough values for the range. If internalStorage is String, all values will be converted to strings, so you should use this function only, if you know you are dealing with a numeric object. Code may assume that all data comes directly from R, is entirely valid in R. */
136 	void setNumericFromR (int from_row, int to_row, const QVector<double> &data);
137 /** reimplemented from RObject to change the internal data storage mode, if the var is being edited */
138 	bool updateType (RData *new_data) override;
139 /** Extended from RObject::EditData to actually contain data. */
140 	struct RKVarEditData {
141 		QStringList cell_strings;
142 		QList<double> cell_doubles;
143 		enum CellState {
144 			Unknown=0,
145 			Invalid=1,
146 			NA=2,
147 			Valid=4,
148 			UnsyncedInvalidState=8
149 		};
150 		QList<int> cell_states;
151 
152 /// see setSyncing
153 		int sync_locks;
154 /// stores changes if syncing is not immediate
155 		ChangeSet changes;
156 /// stores whether there were preivously invalid cells. If so, and there are no longer, now, we may change the mode in the backend.
157 		bool previously_valid;
158 /** the value-labels or factor levels assigned to this variable. 0 if no values/levels given. TODO: Should this be made a regular (non-pointer) member, or is the saved mem really worth the trouble? */
159 		ValueLabels *value_labels;
160 /// the formatting options set for this var (see FormattingOptions) */
161 		FormattingOptions formatting_options;
162 /// storage for invalid fields
163 		QHash<int, QString> invalid_fields;
164 /// how many models need our data?
165 		int num_listeners;
166 	};
167 	RKVarEditData* data;
168 
169 /** reimplemented from RObject */
170 	void beginEdit () override;
171 /** reimplemented from RObject */
172 	void endEdit () override;
173 
174 /** takes care of syncing the given range of cells */
175 	void cellsChanged (int from_row, int to_row);
176 /** writes the given range of cells to the backend (regardless of whether syncing should be immediate) */
177 	virtual void writeData (int from_row, int to_row, RCommandChain *chain=0);
178 	void writeInvalidFields (QList<int> rows, RCommandChain *chain=0);
179 /** writes the values labels to the backend */
180 	void writeValueLabels (RCommandChain *chain) const;
181 
182 /** allocate edit data (cells initialized to NAs) */
183 	void allocateEditData ();
184 /** discard edit data */
185 	void discardEditData ();
186 /////////////////// END: data-handling //////////////////////
187 };
188 
189 #endif
190