• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdeedu API Reference
  • KDE Home
  • Contact Us
 

kalzium/libscience

  • sources
  • kde-4.12
  • kdeedu
  • kalzium
  • libscience
moleculeparser.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  copyright : (C) 2005 by Inge Wallin
3  email : inge@lysator.liu.se
4  ***************************************************************************/
5 /***************************************************************************
6  * *
7  * This program is free software; you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation; either version 2 of the License, or *
10  * (at your option) any later version. *
11  * *
12  ***************************************************************************/
13 
14 #include "moleculeparser.h"
15 
16 #include <ctype.h>
17 
18 #include <kstandarddirs.h>
19 #include <kdebug.h>
20 #include <QFile>
21 
22 // ================================================================
23 // class ElementCountMap
24 
25 
26 
27 ElementCountMap::ElementCountMap()
28 {
29  m_map.clear();
30 }
31 
32 
33 ElementCountMap::~ElementCountMap()
34 {
35 }
36 
37 
38 ElementCount *
39 ElementCountMap::search(Element *_element)
40 {
41  foreach( ElementCount* c, m_map ){
42  if ( c->element() == _element )
43  return c;
44  }
45 
46  return 0;
47 }
48 
49 
50 void
51 ElementCountMap::add(ElementCountMap &_map)
52 {
53  foreach( ElementCount* c, _map.m_map ){
54  add( c->m_element, c->m_count );
55  }
56 
57 }
58 
59 QList<Element*>
60 ElementCountMap::elements()
61 {
62  QList<Element*> list;
63 
64  foreach( ElementCount* c, m_map ){
65  Element* e = c->m_element;
66  if ( !list.contains( e ) )
67  list << e;
68  }
69 
70  return list;
71 }
72 
73 
74 void
75 ElementCountMap::add(Element *_element, int _count)
76 {
77  ElementCount *elemCount;
78 
79  elemCount = search(_element);
80  if (elemCount)
81  elemCount->m_count += _count;
82  else
83  m_map.append(new ElementCount(_element, _count));
84 }
85 
86 
87 void
88 ElementCountMap::multiply(int _factor)
89 {
90  foreach (ElementCount * count, m_map) {
91  count->multiply(_factor);
92  }
93 }
94 
95 
96 // ================================================================
97 // class MoleculeParser
98 
99 
100 MoleculeParser::MoleculeParser( const QList<Element*>& list)
101  : Parser()
102 {
103  m_elementList = list;
104  m_aliasList = new QSet<QString>;
105 }
106 
107 
108 MoleculeParser::MoleculeParser(const QString& _str)
109  : Parser(_str)
110 {
111  m_aliasList = new QSet<QString>;
112 }
113 
114 
115 MoleculeParser::~MoleculeParser()
116 {
117  delete m_aliasList;
118 }
119 
120 
121 // ----------------------------------------------------------------
122 // public methods
123 
124 
125 // Try to parse the molecule and get the weight of it.
126 //
127 // This method also acts as the main loop.
128 
129 bool
130 MoleculeParser::weight(const QString& _shortMoleculeString,
131  double *_resultMass,
132  ElementCountMap *_resultMap)
133 {
134  if ( _shortMoleculeString.isEmpty() )
135  return false;
136  // Clear the list of aliases and start filling it again.
137 
138  m_aliasList -> clear();
139  QString _moleculeString;
140  // Clear the result variables and set m_error to false
141  _resultMap->clear();
142  m_error = false;
143  *_resultMass = 0.0;
144 
145  // Expand the molecule string
146  // Example : MeOH -> (CH3)OH
147  kDebug() << _shortMoleculeString << "is going to be expanded";
148  _moleculeString = expandFormula(_shortMoleculeString);
149  kDebug() << _moleculeString << "is the expanded string";
150 
151  // Now set the expanded string
152  // Initialize the parsing process, and parse te molecule.
153  start(_moleculeString);
154  parseSubmolecule(_resultMass, _resultMap);
155 
156  if (nextToken() != -1)
157  return false;
158 
159  if ( m_error )//there was an error in the input...
160  return false;
161 
162  return true;
163 }
164 
165 QSet<QString>
166 MoleculeParser::aliasList()
167 {
168  return *m_aliasList;
169 }
170 // ----------------------------------------------------------------
171 // helper methods for the public methods
172 
173 
174 // Parse a submolecule. This is a list of terms.
175 //
176 
177 bool
178 MoleculeParser::parseSubmolecule(double *_resultMass,
179  ElementCountMap *_resultMap)
180 {
181  double subMass = 0.0;
182  ElementCountMap subMap;
183 
184  *_resultMass = 0.0;
185  _resultMap->clear();
186  while (parseTerm(&subMass, &subMap)) {
187  //kDebug() << "Parsed a term, weight = " << subresult;
188 
189  // Add the mass and composition of the submolecule to the total.
190  *_resultMass += subMass;
191  _resultMap->add(subMap);
192  }
193 
194  return true;
195 }
196 
197 
198 // Parse a term within the molecule, i.e. a single atom or a
199 // submolecule within parenthesis followed by an optional number.
200 // Examples: Bk, Mn2, (COOH)2
201 //
202 // Return true if correct, otherwise return false.
203 
204 // If correct, the mass of the term is returned in *_resultMass, and
205 // the flattened composition of the molecule in *_resultMap.
206 //
207 
208 bool
209 MoleculeParser::parseTerm(double *_resultMass,
210  ElementCountMap *_resultMap)
211 {
212  *_resultMass = 0.0;
213  _resultMap->clear();
214 
215  if (nextToken() == ELEMENT_TOKEN) {
216  //kDebug() << "Parsed an element: " << m_elementVal->symbol();
217  *_resultMass = m_elementVal->dataAsVariant( ChemicalDataObject::mass ).toDouble();
218  _resultMap->add(m_elementVal, 1);
219 
220  getNextToken();
221  }
222 
223  else if (nextToken() == '(') {
224  // A submolecule.
225 
226  getNextToken();
227  parseSubmolecule(_resultMass, _resultMap);
228 
229  // Must end in a ")".
230  if (nextToken() == ')') {
231  //kDebug() << "Parsed a submolecule. weight = " << *_result;
232  getNextToken();
233  }
234  else
235  return false;
236  }
237  else
238  // Neither an element nor a list within ().
239  return false;
240 
241  // Optional number.
242  if (nextToken() == INT_TOKEN) {
243  //kDebug() << "Parsed a number: " << intVal();
244 
245  *_resultMass *= intVal();
246  _resultMap->multiply(intVal());
247 
248  getNextToken();
249  }
250 
251  kDebug() << "Weight of term = " << *_resultMass;
252  return true;
253 }
254 
255 
256 // ----------------------------------------------------------------
257 // protected methods
258 
259 
260 // Extend Parser::getNextToken with elements.
261 
262 int
263 MoleculeParser::getNextToken()
264 {
265  QString name;
266 
267 #if 0
268  kDebug() << "getNextToken(): Next character = "
269  << nextChar() << endl;
270 #endif
271 
272  // Check if the token is an element name.
273  if ('A' <= nextChar() && nextChar() <= 'Z') {
274  name = char(nextChar());
275  getNextChar();
276 
277  if ('a' <= nextChar() && nextChar() <= 'z') {
278  name.append(char(nextChar()));
279  getNextChar();
280  }
281 
282  // Look up the element from the name..
283  m_elementVal = lookupElement(name);
284  if (m_elementVal)
285  {
286  m_nextToken = ELEMENT_TOKEN;
287  }
288  else
289  m_nextToken = -1;
290  }
291  else
292  return Parser::getNextToken();
293 
294  return m_nextToken;
295 }
296 
297 
298 // ----------------------------------------------------------------
299 // private methods
300 
301 
302 Element *
303 MoleculeParser::lookupElement( const QString& _name )
304 {
305  kDebug() << "looking up " << _name;
306 
307  foreach( Element* e, m_elementList ){
308  if ( e->dataAsVariant(ChemicalDataObject::symbol) == _name ) {
309  kDebug() << "Found element " << _name;
310  return e;
311  }
312  }
313 
314  //if there is an error make m_error true.
315  m_error = true;
316 
317  kDebug() << "no such element!: " << _name;
318 
319  return NULL;
320 }
321 
322 QString
323 MoleculeParser::expandFormula( const QString& _shortString)
324 {
325  QString _fullString; // The expanded string that will be returned
326  QString::const_iterator i; // iterator
327  QString temp; // A temporary string that will contain a single element/group
328  QString expandedTerm; // expansion of a particular term.
329 
330  // Go through all letters in the string.
331  for(i = _shortString.constBegin(); i != _shortString.constEnd(); )
332  {
333  temp = "";
334 
335  // If a capital letter was found
336  if((*i).category() == QChar::Letter_Uppercase) {
337  temp += (*i);
338  i++;
339 
340  // A small letter following a capital letter
341  if(i != _shortString.end() && (*i).category() == QChar::Letter_Lowercase) {
342  temp += (*i);
343  i++;
344  }
345 
346  // If element is found, append it
347  if (lookupElement(temp)) {
348  _fullString += temp;
349  }
350 
351  // If an expansion was made, return the expansion
352  else if (!((expandedTerm = expandTerm(temp)).isEmpty())) {
353  kDebug() << "expanded" << temp << "to" << expandedTerm;
354  _fullString += '('+expandedTerm+')';
355  }
356  // invalid term, append it. ( Validation is done later anyway. )
357  else {
358  _fullString += temp;
359  }
360 
361  }
362 
363  // Return parenthesis as and when found
364  else if (*i == '(') {
365  _fullString += '(';
366  i++;
367  }
368  else if (*i == ')') {
369  _fullString += ')';
370  i++;
371  }
372 
373  // If # is found, we have a short-form eg #EDTA#
374  else if (*i == '#') {
375  i ++; // go to the next character
376  // Get the term between # and #
377  while (*i != '#' && i != _shortString.constEnd() )
378  {
379  temp += *i;
380  i ++;
381  }
382  // If the string ended, just add the part that comes after #
383  if ( i == _shortString.constEnd()) {
384  _fullString += temp;
385  break;
386  }
387  // else expand the term between # and #
388  else if (!temp.isEmpty())
389  {
390  // if alias is not found, just add without expanding the term
391  if((expandedTerm = expandTerm(temp)).isEmpty())
392  _fullString += temp;
393  // else add the expanded term
394  else
395  _fullString += expandedTerm;
396  }
397  i ++;
398  }
399  // If number was found, return it
400  else if ((*i).category() == QChar::Number_DecimalDigit) {
401  _fullString += *i;
402  i++;
403  }
404  else { // invalid character, return it, validation is done again later
405  _fullString += *i;
406  i++;
407  kDebug() << *i << "invalid character!";
408  }
409  }
410 
411  // Reset all "element not found" errors.
412  m_error = false;
413  return _fullString;
414 }
415 
416 QString
417 MoleculeParser::expandTerm (const QString& _group)
418 {
419 
420  QString shortForm, fullForm; // short form (symbol) and full form (expansion)
421  QString temp; // A temporary QString used in Regular expressions
422 
423  // Search in User defined aliases.
424  QString fileName = KStandardDirs::locate( "data", "libkdeedu/data/symbols2.csv");
425  QFile file(fileName);
426 
427  // Check file validity
428  if (!(!file.open(QIODevice::ReadOnly | QIODevice::Text)))
429  {
430  kDebug() << fileName << " opened";
431  QTextStream in(&file);
432 
433  // Get all shortForms and fullForms in the file.
434  while (!in.atEnd()) {
435  QString line = in.readLine();
436  shortForm = line.section(',', 0, 0);
437  shortForm.remove(QChar('\"'));
438  fullForm = line.section(',', 1, 1);
439  fullForm.remove(QChar('\"'));
440 
441  // If short term is found, return fullForm
442  if (shortForm == _group)
443  {
444  *m_aliasList << (_group + " : " + fullForm);
445  return (fullForm);
446  }
447  }
448  }
449  else
450  {
451  kDebug() << fileName << " could not be opened!";
452  }
453 
454  // Find the system defined aliases
455  // Open the file
456  fileName = KStandardDirs::locate( "data", "libkdeedu/data/symbols.csv");
457  QFile file2(fileName);
458 
459  // Check file validity
460  if (!(!file2.open(QIODevice::ReadOnly | QIODevice::Text)))
461  {
462  kDebug() << fileName << " opened";
463  QTextStream in(&file2);
464 
465  // Get all shortForms and fullForms in the file.
466  while (!in.atEnd()) {
467  QString line = in.readLine();
468  shortForm = line.section(',', 0, 0);
469  shortForm.remove(QChar('\"'));
470  fullForm = line.section(',', 1, 1);
471  fullForm.remove(QChar('\"'));
472 
473  if (shortForm == _group)
474  {
475  *m_aliasList << (_group + " : " + fullForm);
476  return (fullForm);
477  }
478  }
479  }
480  else
481  {
482  kDebug() << fileName << " could not be opened!";
483  }
484 
485  // Sample expansions, work even when file is not found, testing purposes
486  if (_group == "Me")
487  return ("CH3");
488  else if (_group == "Et")
489  return ("C2H5");
490 
491  // If not found return an empty string.
492  else
493  return ("");
494 }
ElementCountMap::clear
void clear()
Clear the map of ElementCount pointers.
Definition: moleculeparser.h:106
MoleculeParser::~MoleculeParser
virtual ~MoleculeParser()
Destructor.
Definition: moleculeparser.cpp:115
ElementCountMap::elements
QList< Element * > elements()
Returns the elements in the molecule.
Definition: moleculeparser.cpp:60
ElementCountMap::~ElementCountMap
~ElementCountMap()
Destructor.
Definition: moleculeparser.cpp:33
ElementCount::element
Element * element() const
Definition: moleculeparser.h:58
ElementCountMap::ElementCountMap
ElementCountMap()
Constructor.
Definition: moleculeparser.cpp:27
Parser::INT_TOKEN
static const int INT_TOKEN
All characters are their own token value per default.
Definition: parser.h:81
MoleculeParser::weight
bool weight(const QString &_moleculeString, double *_resultMass, ElementCountMap *_resultMap)
Try to parse the molecule molecule and get the weight of it.
Definition: moleculeparser.cpp:130
Parser::nextChar
int nextChar() const
Peek at the next character;.
Definition: parser.h:57
Parser::getNextChar
int getNextChar()
Make the next character the current one.
Definition: parser.cpp:139
ElementCount::m_element
Element * m_element
The Element of the object.
Definition: moleculeparser.h:75
ElementCount::m_count
int m_count
The number of occurrences.
Definition: moleculeparser.h:79
ElementCount
Definition: moleculeparser.h:30
ChemicalDataObject::symbol
the symbol of the element
Definition: chemicaldataobject.h:50
MoleculeParser::aliasList
QSet< QString > aliasList()
Definition: moleculeparser.cpp:166
ChemicalDataObject::mass
IUPAC Official Masses
Definition: chemicaldataobject.h:52
ElementCountMap::search
ElementCount * search(Element *_element)
Definition: moleculeparser.cpp:39
Element::dataAsVariant
QVariant dataAsVariant(ChemicalDataObject::BlueObelisk type) const
Definition: element.cpp:30
Parser::start
void start(const QString &_str)
Start a new parse.
Definition: parser.cpp:39
ElementCountMap::multiply
void multiply(int _factor)
Definition: moleculeparser.cpp:88
ElementCountMap::add
void add(ElementCountMap &_map)
Definition: moleculeparser.cpp:51
ElementCount::multiply
void multiply(int _factor)
Definition: moleculeparser.h:70
MoleculeParser::getNextToken
virtual int getNextToken()
Extends the standard tokenizer in Parser::getNextToken().
Definition: moleculeparser.cpp:263
Element
In this class all information about an element are stored.
Definition: element.h:41
moleculeparser.h
Parser::nextToken
int nextToken() const
Peek at the next token.
Definition: parser.h:62
Parser::m_nextToken
int m_nextToken
The next token to be used in the parser.
Definition: parser.h:118
MoleculeParser::MoleculeParser
MoleculeParser(const QList< Element * > &list)
Definition: moleculeparser.cpp:100
ElementCountMap
This class is used to count the elements in the molecule which is being calculated.
Definition: moleculeparser.h:90
Parser::intVal
int intVal() const
Get the value stored for different types of tokens.
Definition: parser.h:67
Parser::getNextToken
virtual int getNextToken()
Fetches the next token.
Definition: parser.cpp:184
Parser
This is a general purpose parser originally written by Inge Wallin.
Definition: parser.h:30
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 22:35:31 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

kalzium/libscience

Skip menu "kalzium/libscience"
  • Main Page
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members

kdeedu API Reference

Skip menu "kdeedu API Reference"
  • Analitza
  •     lib
  • kalgebra
  • kalzium
  •   libscience
  • kanagram
  • kig
  •   lib
  • klettres
  • kstars
  • libkdeedu
  •   keduvocdocument
  • marble
  • parley
  • rocs
  •   App
  •   RocsCore
  •   VisualEditor
  •   stepcore

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal