summaryrefslogtreecommitdiff
path: root/noncore/apps/checkbook/kmolcalc.cpp
Unidiff
Diffstat (limited to 'noncore/apps/checkbook/kmolcalc.cpp') (more/less context) (ignore whitespace changes)
-rw-r--r--noncore/apps/checkbook/kmolcalc.cpp233
1 files changed, 233 insertions, 0 deletions
diff --git a/noncore/apps/checkbook/kmolcalc.cpp b/noncore/apps/checkbook/kmolcalc.cpp
new file mode 100644
index 0000000..e5ab736
--- a/dev/null
+++ b/noncore/apps/checkbook/kmolcalc.cpp
@@ -0,0 +1,233 @@
1/*
2 * kmolcalc.cpp
3 *
4 * Copyright (C) 2000 Tomislav Gountchev <tomi@idiom.com>
5 */
6
7/**
8 * KMOLCALC is the calculation engine. It knows about a hashtable of user defined atomic
9 * weights and group definitions ELSTABLE, and the currently processed formula, stored
10 * as a list of elements and their coefficients, ELEMENTS.
11 */
12
13#include "kmolcalc.h"
14#include <qdict.h>
15#include <qdir.h>
16#include <iostream.h>
17
18
19/**
20 * Construct a new calculator object.
21 */
22KMolCalc::KMolCalc() {
23 elements = new ElementList;
24 elstable = NULL;
25 readElstable();
26}
27
28KMolCalc::~KMolCalc() {
29 delete elements;
30}
31
32void KMolCalc::readElstable() {
33 weight = -1; // not calculated yet
34 if (elstable) delete elstable;
35 elstable = new QDict<SubUnit> (197, TRUE);
36 elstable->setAutoDelete(TRUE);
37 QStringList files;// = // KGlobal::dirs()->findAllResources("appdata", "kmolweights");
38// mwfile = locateLocal("data", "kmol")+"/kmolweights";
39 QFile f(mwfile);
40 QString* latest_f = &mwfile;
41 for (uint i=0; i<files.count(); i++) {
42 if (QFileInfo(QFile(files[i])).lastModified() > QFileInfo(QFile(*latest_f)).lastModified()) {
43 latest_f = &files[i];
44 }
45 }
46 QFile lf(*latest_f);
47 if (f.exists()) readMwfile(f);
48 if (!f.exists()) {
49 readMwfile(lf);
50 writeElstable();
51 } else if (QFileInfo(f).lastModified() < QFileInfo(lf).lastModified()) {
52 // announce
53 QMessageBox::information
54 (0, "Warning:", "Found new global Mw file.\nLocal definitions will be updated.", QMessageBox::Ok);
55 readMwfile(lf);
56 writeElstable();
57 }
58
59}
60
61
62/**
63 * Parse a string S and construct the ElementList this->ELEMENTS, representing the
64 * composition of S. Returns 0 if successful, or an error code (currently -1) if
65 * parsing failed.
66 * The elements is S must be valid element or group symbols, as stored in this->ELSTABLE.
67 * See help files for correct formula syntax.
68 */
69QString KMolCalc::readFormula(const QString& s) {
70 weight = -1;
71 if (elements) delete elements;
72 elements = new ElementList;
73 return KMolCalc::readGroup(s, elements);
74}
75
76// read a formula group recursively. Called by readFormula.
77QString KMolCalc::readGroup(const QString& s, ElementList* els) {
78 if (s.isEmpty()) return QString ("Enter a formula."); //ERROR
79 int sl = s.length();
80 int i = 0;
81 QString errors ("OK");
82 bool ok = TRUE;
83 while (i < sl && ((s[i] <= '9' && s[i] >= '0') || s[i] == '.')) i++;
84 double prefix = (i == 0 ? 1 : s.left(i).toDouble(&ok));
85 if (! ok || i == sl || prefix == 0) return QString ("Bad formula."); // ERROR
86 ElementList* elstemp = new ElementList;
87 while (i < sl) {
88 int j = i;
89 if (s[i] == '(') {
90 ElementList* inner = new ElementList;
91 int level = 1; // count levels of nested ( ).
92 while (1) {
93 if (i++ == sl) {
94 delete inner;
95 delete elstemp;
96 return QString ("Bad formula."); //ERROR
97 }
98 if (s[i] == '(') level++;
99 if (s[i] == ')') level--;
100 if (level == 0) break;
101 }
102 errors = KMolCalc::readGroup(s.mid(j+1, i-j-1), inner);
103 j = ++i;
104 while (i < sl && ((s[i] <= '9' && s[i] >= '0') || s[i] == '.')) i++;
105 double suffix = (i == j ? 1 : s.mid(j, i-j).toDouble(&ok));
106 if (! ok || suffix == 0) {
107 delete inner;
108 delete elstemp;
109 return QString ("Bad formula."); // ERROR
110 }
111 inner->addTo(*elstemp, suffix);
112 delete inner;
113 inner = NULL;
114 } else if ((s[i] >= 'A' && s[i] <= 'Z') || (s[i] >= 'a' && s[i] <= 'z')) {
115 while (++i < sl && ((s[i] >= 'a' && s[i] <= 'z') || s[i] == '*' ||
116 s[i] == '\''));
117 QString elname = s.mid(j, i-j);
118 j = i;
119 while (i < sl && ((s[i] <= '9' && s[i] >= '0') || s[i] == '.')) i++;
120 double suffix = (i == j ? 1 : s.mid(j, i-j).toDouble(&ok));
121 if (! ok || suffix == 0) {
122 delete elstemp;
123 return QString ("Bad formula."); // ERROR
124 }
125 SubUnit* group = elstable->find(elname);
126 if (group == 0) {
127 delete elstemp;
128 return QString ("Undefined symbol: ") + elname; //ERROR
129 }
130 group->addTo(*elstemp, suffix);
131 } else if (s[i] == '+') {
132 if (elstemp->isEmpty()) {
133 delete elstemp;
134 return QString ("Bad formula."); //ERROR
135 }
136 elstemp->addTo(*els, prefix);
137 delete elstemp;
138 errors = KMolCalc::readGroup(s.mid(i+1, sl-i-1), els);
139 return errors;
140 } else {
141 delete elstemp;
142 return QString ("Bad formula."); //ERROR
143 }
144 }
145 elstemp->addTo(*els, prefix);
146 delete elstemp;
147 return errors;
148}
149
150/**
151 * Calculate and return the molecular weight of the current chemical formula.
152 */
153double KMolCalc::getWeight() {
154 if (weight == -1) weight = elements->getWeight(elstable);
155 return weight;
156}
157
158/**
159 * Return the elemental composition of the current formula, as a string of tab-separated
160 * element - percentage pairs, separated by newlines.
161 */
162QString KMolCalc::getEA() {
163 if (weight == -1) weight = elements->getWeight(elstable);
164 if (weight == -1) return QString("ERROR: Couldn't get Mw..."); // ERROR
165 return elements->getEA(elstable, weight);
166}
167
168/**
169 * Return the empirical formula of the current compound as a QString.
170 */
171QString KMolCalc::getEmpFormula() {
172 return elements->getEmpFormula();
173}
174
175// Read the element definition file.
176void KMolCalc::readMwfile(QFile& f) {
177 if (! f.open(IO_ReadOnly)) return; //ERROR
178 QTextStream fs (&f);
179 QString line;
180 while (! fs.eof()) {
181 line = fs.readLine();
182 SubUnit* s = SubUnit::makeSubUnit(line);
183 elstable->replace(s->getName(), s);
184 }
185 f.close();
186}
187
188/**
189 * Save the element definitions file.
190 */
191void KMolCalc::writeElstable() {
192 QFile f(mwfile);
193 if (! f.open(IO_WriteOnly)) return; //ERROR
194 QTextStream fs (&f);
195 QString line;
196 QDictIterator<SubUnit> it(*elstable);
197 while (it.current()) {
198 it.current()->writeOut(line);
199 fs << line << endl;
200 ++it;
201 }
202 f.close();
203}
204
205/**
206 * Remove a group or element definition from ELSTABLE.
207 */
208void KMolCalc::undefineGroup (const QString& name) {
209 elstable->remove (name);
210}
211
212/**
213 * Add a new element name - atomic weight record to the ELSTABLE hashtable. Assumes
214 * NAME has valid syntax.
215
216 */
217void KMolCalc::defineElement (const QString& name, double weight) {
218 Element* el = new Element(name, weight);
219 elstable->replace(name, el);
220}
221
222/**
223 * Add a new group definition to the ELSTABLE. Returns 0 if OK, -1 if parsing FORMULA
224 * fails. Assumes the syntax of grpname is correct.
225 */
226QString KMolCalc::defineGroup (const QString& grpname, const QString& formula) {
227 ElementList* els = new ElementList(grpname);
228 QString error = readGroup(formula, els);
229 if (error != "OK") return error;
230 if (els->contains(grpname)) return QString("Can't define a group recursively!\n");
231 elstable->replace(grpname, els);
232 return QString("OK");
233}