USGS

Isis 3.0 Object Programmers' Reference

Home

PvlKeyword.cpp
Go to the documentation of this file.
1 
23 #include "IsisDebug.h"
24 
25 #include <QDebug>
26 #include <QString>
27 
28 #include "PvlKeyword.h"
29 #include "IException.h"
30 #include "Message.h"
31 #include "IString.h"
32 #include "PvlFormat.h"
33 #include "PvlSequence.h"
34 
35 using namespace std;
36 namespace Isis {
38  PvlKeyword::PvlKeyword() {
39  init();
40  }
41 
42 
48  PvlKeyword::PvlKeyword(QString name) {
49  init();
50  setName(name);
51  }
52 
53 
62  PvlKeyword::PvlKeyword(QString name, QString value,
63  QString unit) {
64  init();
65  setName(name);
66  addValue(value, unit);
67  }
68 
69 
71  PvlKeyword::PvlKeyword(const PvlKeyword &other) {
72  init();
73  *this = other;
74  }
75 
76 
80  PvlKeyword::~PvlKeyword() {
81  if (m_units) {
82  delete m_units;
83  m_units = NULL;
84  }
85 
86  if (m_comments) {
87  delete m_comments;
88  m_comments = NULL;
89  }
90 
91  if (m_name) {
92  delete [] m_name;
93  m_name = NULL;
94  }
95  }
96 
97 
99  void PvlKeyword::init() {
100  m_name = NULL;
101  m_units = NULL;
102  m_comments = NULL;
103  m_width = 0;
104  m_indent = 0;
105  m_formatter = NULL;
106 
107  clear();
108  }
109 
118  bool PvlKeyword::isNull(int index) const {
119  if (size() == 0) return true;
120  if (index < 0 || index >= (int)m_values.size()) {
121  QString msg = Message::ArraySubscriptNotInRange(index);
122  throw IException(IException::Programmer, msg, _FILEINFO_);
123  }
124  if (stringEqual("NULL", m_values[index])) return true;
125  if (stringEqual("", m_values[index])) return true;
126  if (stringEqual("\"\"", m_values[index])) return true;
127  if (stringEqual("''", m_values[index])) return true;
128  return false;
129  }
130 
136  void PvlKeyword::setName(QString name) {
137  QString final = name.trimmed();
138  if (final.contains(QRegExp("\\s"))) {
139  QString msg = "[" + name + "] is invalid. Keyword name cannot ";
140  msg += "contain whitespace.";
141  throw IException(IException::User, msg, _FILEINFO_);
142  }
143 
144  if (m_name) {
145  delete [] m_name;
146  m_name = NULL;
147  }
148 
149  if (final != "") {
150  QByteArray finalAscii = final.toAscii();
151  m_name = new char[finalAscii.size() + 1];
152  strncpy(m_name, finalAscii.data(), final.size() + 1);
153  }
154  }
155 
171  void PvlKeyword::setValue(QString value, QString unit) {
172  clear();
173  addValue(value, unit);
174  }
175 
176 
182  void PvlKeyword::setUnits(QString units) {
183  if (!m_units) {
184  m_units = new std::vector<QString>();
185  }
186 
187  m_units->clear();
188 
189  for (int i = 0; i < m_values.size(); i++) {
190  m_units->push_back(units);
191  }
192  }
193 
194 
203  void PvlKeyword::setUnits(QString value, QString units) {
204 
205  bool found = false;
206  int i = -1;
207  while(!found && ++i < (int) m_values.size()) {
208  if (value == m_values[i]) {
209  found = true;
210  }
211  }
212 
213  if (found) {
214  if (!m_units) {
215  m_units = new std::vector<QString>(m_values.size());
216  }
217  else {
218  m_units->resize(m_values.size());
219  }
220 
221  ASSERT(i < (int) m_units->size());
222 
223  (*m_units)[i] = units;
224  }
225  else {
226  IString msg = "PvlKeyword::setUnits called with value [" + value +
227  "] which does not exist in this Keyword";
228  throw IException(IException::Programmer, msg, _FILEINFO_);
229  }
230  }
231 
247  PvlKeyword &PvlKeyword::operator=(QString value) {
248  clear();
249  addValue(value);
250  return *this;
251  }
252 
268  void PvlKeyword::addValue(QString value, QString unit) {
269  m_values.append(value);
270 
271  if (unit != "") {
272  if (!m_units) {
273  m_units = new std::vector<QString>(m_values.size());
274  }
275  else {
276  m_units->resize(m_values.size());
277  }
278 
279  (*m_units)[m_units->size() - 1] = unit;
280  }
281  else if (m_units) {
282  m_units->push_back("");
283  }
284  }
285 
301  PvlKeyword &PvlKeyword::operator+=(QString value) {
302  addValue(value);
303  return *this;
304  }
305 
307  void PvlKeyword::clear() {
308  m_values.clear();
309 
310  if (m_units) {
311  delete m_units;
312  m_units = NULL;
313  }
314  }
315 
316 
317  PvlKeyword::operator QString() const {
318  return operator[](0);
319  }
320 
321 
334  QString &PvlKeyword::operator[](int index) {
335  if (index < 0 || index >= (int)m_values.size()) {
336  QString msg = (Message::ArraySubscriptNotInRange(index)) +
337  "for Keyword [" + QString(m_name) + "]";
338  throw IException(IException::Programmer, msg, _FILEINFO_);
339  }
340 
341  return m_values[index];
342  }
343 
356  const QString &PvlKeyword::operator[](int index) const {
357  if (index < 0 || index >= (int)m_values.size()) {
358  QString msg = Message::ArraySubscriptNotInRange(index);
359  throw IException(IException::Programmer, msg, _FILEINFO_);
360  }
361  return m_values[index];
362  }
363 
373  QString PvlKeyword::unit(int index) const {
374  if (!m_units) return "";
375 
376  if (index < 0 || index >= (int)m_units->size()) {
377  QString msg = Message::ArraySubscriptNotInRange(index);
378  throw IException(IException::Programmer, msg, _FILEINFO_);
379  }
380  return (*m_units)[index];
381  }
382 
392  void PvlKeyword::addComment(QString comment) {
393  if (!m_comments) {
394  m_comments = new std::vector<QString>();
395  }
396 
397  if (comment.size() == 0) {
398  m_comments->push_back("#");
399  }
400  if (comment[0] == '#') {
401  m_comments->push_back(comment);
402  }
403  else if (comment.size() == 1) {
404  m_comments->push_back("# " + comment);
405  }
406  else if ((comment[0] == '/') && (comment[1] == '*')) {
407  m_comments->push_back(comment);
408  }
409  else if ((comment[0] == '/') && (comment[1] == '/')) {
410  m_comments->push_back(comment);
411  }
412  else {
413  m_comments->push_back("# " + comment);
414  }
415  }
416 
426  void PvlKeyword::addCommentWrapped(QString comment) {
427  IString cmt = comment;
428  IString token = cmt.Token(" ");
429  while(cmt != "") {
430  IString temp = token;
431  token = cmt.Token(" ");
432  int length = temp.size() + token.size() + 1;
433  while((length < 72) && (token.size() > 0)) {
434  temp += " " + token;
435  token = cmt.Token(" ");
436  length = temp.size() + token.size() + 1;
437  }
438  addComment(temp.c_str());
439  }
440  if (token.size() != 0) addComment(token.c_str());
441  }
442 
444  void PvlKeyword::clearComment() {
445  if (m_comments) {
446  delete m_comments;
447  m_comments = NULL;
448  }
449  }
450 
457  QString PvlKeyword::comment(int index) const {
458  if (!m_comments) return "";
459 
460  if (index < 0 || index >= (int)m_comments->size()) {
461  QString msg = Message::ArraySubscriptNotInRange(index);
462  throw IException(IException::Programmer, msg, _FILEINFO_);
463  }
464  return (*m_comments)[index];
465  };
466 
474  QString PvlKeyword::reform(const QString &value) const {
475 #if 0
476  static bool firstTime = true;
477  static bool iPVL = true;
478  if (firstTime) {
479  firstTime = false;
480  Isis::PvlGroup &g = Isis::Preference::Preferences().findGroup(
481  "UserInterface", Isis::Pvl::Traverse);
482 
483  Isis::IString s = (QString) g["PvlFormat"];
484  s.UpCase();
485  if (s == "PVL") iPVL = false;
486  }
487 
488  if (iPVL) return toIPvl(value);
489 #endif
490  return toPvl(value);
491  }
492 
498  QString PvlKeyword::toIPvl(const QString &value) const {
499  QString out;
500  bool upcase = true;
501  bool lastlower = true;
502  for (int i = 0; i < value.size(); i++) {
503  if ((lastlower) && (value[i].isUpper())) upcase = true;
504  if (value[i] == '_') {
505  upcase = true;
506  }
507  else if (upcase) {
508  out += value[i].toUpper();
509  lastlower = false;
510  upcase = false;
511  }
512  else {
513  out += value[i].toLower();
514  if (value[i].isLower()) lastlower = true;
515  upcase = false;
516  }
517  }
518  return out;
519  }
520 
526  QString PvlKeyword::toPvl(const QString &value) const {
527  QString out;
528  bool lastlower = false;
529  for (int i = 0; i < value.size(); i++) {
530  if ((lastlower) && (value[i].isUpper())) out += "_";
531  if (value[i] == '_') {
532  out += "_";
533  lastlower = false;
534  }
535  else {
536  out += value[i].toUpper();
537  if (value[i].isLower()) lastlower = true;
538  }
539  }
540  return out;
541  }
542 
551  bool PvlKeyword::stringEqual(const QString &QString1,
552  const QString &QString2) {
553  Isis::IString s1(QString1);
554  Isis::IString s2(QString2);
555 
556  s1.ConvertWhiteSpace();
557  s2.ConvertWhiteSpace();
558 
559  s1.Remove(" _");
560  s2.Remove(" _");
561 
562  s1.UpCase();
563  s2.UpCase();
564 
565  if (s1 == s2) return true;
566  return false;
567  }
568 
578  bool PvlKeyword::isEquivalent(QString QString1, int index) const {
579  if (index < 0 || index >= (int)m_values.size()) {
580  QString msg = Message::ArraySubscriptNotInRange(index);
581  throw IException(IException::Programmer, msg, _FILEINFO_);
582  }
583 
584  return stringEqual(m_values[index], QString1);
585  }
586 
592  PvlKeyword &PvlKeyword::operator=(Isis::PvlSequence &seq) {
593  clear();
594  for (int i = 0; i < seq.Size(); i++) {
595  QString temp = "(";
596  for (int j = 0; j < (int)seq[i].size(); j++) {
597  QString val = seq[i][j];
598  if (val.contains(" ")) {
599  temp += "\"" + val + "\"";
600  }
601  else {
602  temp += val;
603  }
604  if (j < (int) seq[i].size() - 1) temp += ", ";
605  }
606  temp += ")";
607  this->operator+=(temp);
608  }
609 
610  return *this;
611  }
612 
627  ostream &PvlKeyword::writeWithWrap(std::ostream &os,
628  const QString &textToWrite,
629  int startColumn,
630  PvlFormat &format) const {
631 
632  /*
633  http://pds.jpl.nasa.gov/tools/standards-reference.shtml
634 
635  pds.jpl.nasa.gov/documents/sr/Chapter12.pdf
636 
637  Object Description Language Specification and Usage
638  The following provides a complete specification for Object Description Language
639  (ODL), the language used to encode data labels for the Planetary Data System
640  (PDS) and other NASA data systems. This standard contains a formal definition of
641  the grammar semantics of the language. PDS specific implementation notes and
642  standards are referenced in separate sections.
643 
644  12.5.3.1 Implementation of String Values
645  A text QString read in from a label is reassembled into a QString of characters.
646  The way in which the QString is broken into lines in a label does not affect the
647  format of the QString after it has been reassembled. The following rules are used
648  when reading text QStrings: If a format effector or a sequence of
649  format effectors is encountered within a text QString,
650  the effector (or sequence of effectors) is replaced by a single space
651  character, unless the last character is a hyphen (dash) character. Any
652  spacing characters at the end of the line are removed and any spacing
653  characters at the beginning of the following line are removed. This
654  allows a text QString in a label to appear with the left and right
655  margins set at arbitrary points without changing the QString value. For
656  example, the following two QStrings are the same: "To be or
657  not to be" and
658  "To be or
659  not to be"
660  If the last character on a line prior to a format effector is a hyphen
661  (dash) character, the hyphen is removed with any spacing characters at
662  the beginning of the following line. This follows the standard
663  convention in English of using a hyphen to break a word across lines.
664  For example, the following two QStrings are the same:
665  "The planet Jupiter is very big" and
666  "The planet Jupi-
667  ter is very big"
668  Control codes, other than the horizontal tabulation character and
669  format effectors, appearing within a text QString are removed.
670  */
671 
672  /*
673  We will be adding a condition for human-readable purposes:
674  If a quoted QString of text does not fit on the current line,
675  but will fit on the next line, use the next line.
676  */
677 
678  // Position set
679  QString remainingText = textToWrite;
680  int spaceForText = format.charLimit() - 1 - format.formatEOL().length() - startColumn;
681 
682  // indexOf quote positions to better determine which line to put the
683  // QString on. Data structure: vector< startPos, endPos > where
684  // remainingText[startPos] and remainingText[endPos] must both be quotes.
685  vector< pair<int, int> > quotedAreas;
686  int quoteStart = -1;
687 
688  // if its an array, indent subsequent lines 1 more
689  if (textToWrite.count() > 0 && (textToWrite[0] == '(' || textToWrite[0] == '"')) {
690  startColumn ++;
691  }
692 
693  /* Standard 12.3.3.1 ->
694  A quoted text QString may not contain the quotation mark, which is reserved
695  to be the text QString delimiter.
696 
697  So we don't have to worry about escaped quotes.
698  */
699 
700  vector< pair<char, char> > quoteStartEnds;
701  quoteStartEnds.push_back(pair<char, char>('"', '"'));
702  quoteStartEnds.push_back(pair<char, char>('\'', '\''));
703  quoteStartEnds.push_back(pair<char, char>('<', '>'));
704 
705  // clean up any EOL characters, they mustn't interfere, remove sections of
706  // multiple spaces (make them into one), and indexOf quoted areas
707  for (int pos = 0; pos < remainingText.size(); pos++) {
708  // remove \r and \n from QString
709  if (remainingText[pos] == '\n' || remainingText[pos] == '\r') {
710  if (pos != remainingText.size() - 1) {
711  remainingText = remainingText.mid(0, pos) +
712  remainingText.mid(pos + 1);
713  }
714  else {
715  remainingText = remainingText.mid(0, pos);
716  }
717  }
718 
719  // convert " " to " " if not quoted
720  if (quoteStart == -1) {
721  while(pos > 0 &&
722  remainingText[pos-1] == ' ' &&
723  remainingText[pos] == ' ') {
724  remainingText = remainingText.mid(0, pos) +
725  remainingText.mid(pos + 1);
726  }
727  }
728 
729  // Find quotes
730  for (unsigned int i = 0;
731  (quoteStart < 0) && i < quoteStartEnds.size();
732  i++) {
733  if (quoteStartEnds[i].first == remainingText[pos]) {
734  quoteStart = pos;
735  }
736  }
737 
738 
739  //bool mismatchQuote = false;
740 
741  // Check to see if we're ending a quote if we didn't just
742  // start the quote and we are inside a quote
743  if (quoteStart != (int)pos && quoteStart != -1) {
744  for (unsigned int i = 0; i < quoteStartEnds.size(); i++) {
745  if (quoteStartEnds[i].second == remainingText[pos]) {
746  if (quoteStartEnds[i].first != remainingText[quoteStart]) {
747  continue;
748  // mismatchQuote = true;
749  }
750 
751  quotedAreas.push_back(pair<int, int>(quoteStart, pos));
752 
753  quoteStart = -1;
754  }
755  }
756  }
757 
758  //if (mismatchQuote) {
759  // IString msg = "Pvl keyword values [" + textToWrite +
760  // "] can not have embedded quotes";
761  // throw iException::Message(iException::Programmer, msg, _FILEINFO_);
762  //}
763  }
764 
765  int charsLeft = spaceForText;
766  int printedSoFar = 0;
767 
768  // while we have something to write, keep going
769  while(!remainingText.isEmpty()) {
770  // search backwards for the last space or comma *in the limit* (80 chars)
771  int lastSpacePosition = charsLeft;
772 
773  // if everything fits into our remaining space, consider the last
774  // spot in the QString to be printed still the split position.
775  if (lastSpacePosition >= (int)remainingText.length()) {
776  lastSpacePosition = remainingText.length();
777  }
778  else {
779  // Everything does not fit; use good space for mediocre splits (inside
780  // quoted QStrings), and excellent space for good splits (between array
781  // values for example)
782  int goodSpace = -1;
783  int excellentSpace = -1;
784  int searchPosition = lastSpacePosition;
785  bool doneSearching = false;
786 
787  while(!doneSearching) {
788  bool currentPosQuoted = false;
789 
790  for (unsigned int i = 0; i < quotedAreas.size(); i++) {
791  if (searchPosition + printedSoFar >= quotedAreas[i].first &&
792  searchPosition + printedSoFar <= quotedAreas[i].second) {
793  currentPosQuoted = true;
794  }
795  }
796 
797  if (remainingText[searchPosition] == ' ') {
798  bool validSpace = true;
799 
800  // this really isn't a good space if the previous character is a
801  // '-' though - then it would be read wrong when re-imported.
802  if (searchPosition > 0 && remainingText[searchPosition - 1] == '-') {
803  validSpace = false;
804  }
805 
806  if (validSpace && goodSpace < 0) {
807  goodSpace = searchPosition;
808  }
809 
810  // An excellent space is the prefential break - not quoted and
811  // not units next.
812  // we were already done if we had an excellent space
813  if (validSpace && !currentPosQuoted) {
814  if ((int)searchPosition < (int)(remainingText.size() - 1) &&
815  remainingText[searchPosition+1] != '<') {
816  excellentSpace = searchPosition;
817  }
818  }
819  }
820 
821  doneSearching = (excellentSpace >= 0 || searchPosition <= 1);
822  searchPosition --;
823  }
824 
825  // Use the best breaking point we have
826  if (excellentSpace > 0) {
827  lastSpacePosition = excellentSpace;
828  }
829  else if (goodSpace > 0) {
830  lastSpacePosition = goodSpace;
831  }
832  else {
833  lastSpacePosition = -1;
834  }
835  }
836 
837  // we found a space or comma in our limit, write to that chatacter
838  // and repeat the loop
839  if (lastSpacePosition >= 0) {
840  os << remainingText.mid(0, lastSpacePosition);
841 
842  remainingText = remainingText.mid(lastSpacePosition);
843  printedSoFar += lastSpacePosition;
844  }
845  // we failed to indexOf a space or a comma in our limit,
846  // use a hyphen (-)
847  else {
848  // Make sure we don't break on "//" since Isis thinks that is a comment
849  if (remainingText.mid(charsLeft-1, 2) == "//") {
850  os << remainingText.mid(0, charsLeft - 2);
851  os << "-";
852  remainingText = remainingText.mid(charsLeft - 2);
853  printedSoFar += charsLeft - 2;
854  }
855  else {
856  os << remainingText.mid(0, charsLeft - 1);
857  os << "-";
858  remainingText = remainingText.mid(charsLeft - 1);
859  printedSoFar += charsLeft - 1;
860  }
861  }
862 
863  // we wrote as much as possible, do a newline and repeat
864  if (!remainingText.isEmpty()) {
865  os << format.formatEOL();
866  writeSpaces(os, startColumn);
867 
868  // dont allow spaces to begin the next line inside what we're printing
869  if (remainingText[0] == ' ') {
870  remainingText = remainingText.mid(1);
871  printedSoFar += 1;
872  }
873  }
874 
875  charsLeft = spaceForText;
876  }
877 
878  return os;
879  }
880 
881 
888  void PvlKeyword::writeSpaces(std::ostream &os, int numSpaces) const {
889  for (int space = 0; space < numSpaces; space ++) {
890  os << " ";
891  }
892  }
893 
894 
901  void PvlKeyword::setFormat(PvlFormat *formatter) {
902  m_formatter = formatter;
903  }
904 
905 
912  PvlFormat *PvlKeyword::format() {
913  return m_formatter;
914  };
915 
925  std::istream &operator>>(std::istream &is, PvlKeyword &result) {
926  result = PvlKeyword();
927  QString line;
928  QString keywordString;
929 
930  bool keywordDone = false;
931  bool multiLineComment = false;
932  bool error = !is.good();
933 
934  while(!error && !keywordDone) {
935  istream::pos_type beforeLine = is.tellg();
936 
937  line = PvlKeyword::readLine(is, multiLineComment);
938 
939  // We read an empty line (failed to read next non-empty line)
940  // and didnt complete our keyword, essentially we hit the implicit
941  // keyword named "End"
942  if (line.isEmpty() && !is.good()) {
943  if (keywordString.isEmpty() ||
944  keywordString[keywordString.size()-1] == '\n') {
945  line = "End";
946 
947  if (multiLineComment) {
948  error = true;
949  }
950  }
951  else {
952  error = true;
953  }
954  }
955 
956  bool comment = false;
957 
958  if (!multiLineComment) {
959  if (line.size() > 0 && line[0] == '#') {
960  comment = true;
961  }
962 
963  if (line.size() > 1 && line[0] == '/' &&
964  (line[1] == '*' || line[1] == '/')) {
965  comment = true;
966 
967  if (line[1] == '*') {
968  multiLineComment = true;
969  keywordString += line.mid(0, 2);
970  line = line.mid(2).trimmed();
971  }
972  }
973  }
974 
975  if (multiLineComment) {
976  comment = true;
977 
978  if (line.contains("/*")) {
979  IString msg = "Error when reading a pvl: Cannot have ['/*'] inside a "
980  "multi-line comment";
981  throw IException(IException::Unknown, msg, _FILEINFO_);
982  }
983 
984  if (line.contains("*/")) {
985  multiLineComment = false;
986 
987  line = line.mid(0, line.indexOf("*/")).trimmed() + " */";
988  }
989  }
990 
991  if (line.isEmpty()) {
992  continue;
993  }
994  // comment line
995  else if (comment) {
996  keywordString += line + '\n';
997  continue;
998  }
999  // first line of keyword data
1000  else if (keywordString.isEmpty()) {
1001  keywordString = line;
1002  }
1003  // concatenation
1004  else if (!comment && keywordString[keywordString.size()-1] == '-') {
1005  keywordString = keywordString.mid(0, keywordString.size() - 1) + line;
1006  }
1007  // Non-commented and non-concatenation -> put in the space
1008  else {
1009  keywordString += " " + line;
1010  }
1011  // if this line concatenates with the next, read the next
1012  if (line[line.size()-1] == '-') {
1013  continue;
1014  }
1015 
1016  std::vector<QString> keywordComments;
1017  QString keywordName;
1018  std::vector< std::pair<QString, QString> > keywordValues;
1019 
1020  bool attemptedRead = false;
1021 
1022  try {
1023  attemptedRead = PvlKeyword::readCleanKeyword(keywordString,
1024  keywordComments,
1025  keywordName,
1026  keywordValues);
1027  }
1028  catch (IException &e) {
1029  if (is.eof() && !is.bad()) {
1030  is.clear();
1031  is.unget();
1032  }
1033 
1034  is.seekg(beforeLine, ios::beg);
1035 
1036  QString msg = "Unable to read PVL keyword [";
1037  msg += keywordString;
1038  msg += "]";
1039 
1040  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1041  }
1042 
1043  // Result valid?
1044  if (attemptedRead) {
1045  // if the next line starts with '<' then it should be read too...
1046  // it should be units
1047  // however, you can't have units if there is no value
1048  if (is.good() && is.peek() == '<' && !keywordValues.empty()) {
1049  continue;
1050  }
1051 
1052  result.setName(keywordName);
1053  result.addComments(keywordComments);
1054 
1055  for (unsigned int value = 0; value < keywordValues.size(); value++) {
1056  result.addValue(keywordValues[value].first,
1057  keywordValues[value].second);
1058  }
1059 
1060  keywordDone = true;
1061  }
1062 
1063  if (!attemptedRead) {
1064  error = error || !is.good();
1065  }
1066  // else we need to keep reading
1067  }
1068 
1069  if (error) {
1070  // skip comments
1071  while(keywordString.contains('\n')) {
1072  keywordString = keywordString.mid(keywordString.indexOf('\n') + 1);
1073  }
1074 
1075  QString msg;
1076 
1077  if (keywordString.isEmpty() && !multiLineComment) {
1078  msg = "PVL input contains no Pvl Keywords";
1079  }
1080  else if (multiLineComment) {
1081  msg = "PVL input ends while still in a multi-line comment";
1082  }
1083  else {
1084  msg = "The PVL keyword [" + keywordString + "] does not appear to be";
1085  msg += " a valid Pvl Keyword";
1086  }
1087 
1088  throw IException(IException::Unknown, msg, _FILEINFO_);
1089  }
1090 
1091  if (!keywordDone) {
1092  // skip comments
1093  while(keywordString.contains('\n'))
1094  keywordString = keywordString.mid(keywordString.indexOf('\n') + 1);
1095 
1096  QString msg;
1097 
1098  if (keywordString.isEmpty()) {
1099  msg = "Error reading PVL keyword";
1100  }
1101  else {
1102  msg = "The PVL keyword [" + keywordString + "] does not appear to be";
1103  msg += " complete";
1104  }
1105 
1106  throw IException(IException::Unknown, msg, _FILEINFO_);
1107  }
1108 
1109  return is;
1110  }
1111 
1118  void PvlKeyword::addComments(const std::vector<QString> &comments) {
1119  for (unsigned int i = 0; i < comments.size(); i++) {
1120  addComment(comments[i]);
1121  }
1122  }
1123 
1139  bool PvlKeyword::readCleanKeyword(QString keyword,
1140  std::vector<QString> &keywordComments,
1141  QString &keywordName,
1142  std::vector< std::pair<QString, QString> > &keywordValues) {
1143  // Reset outputs
1144  keywordComments.clear();
1145  keywordName = "";
1146  keywordValues.clear();
1147 
1148  // This is in case a close quote doesn't exist
1149  bool explicitIncomplete = false;
1150 
1151  // Possible (known) comment starts in pvl
1152  QString comments[] = {
1153  "#",
1154  "//"
1155  };
1156 
1157  // Need more data if nothing is here!
1158  if (keyword.isEmpty()) return 0;
1159 
1160  /*
1161  Step 1: Read Comments
1162 
1163  Theoretically, we should have an input that looks like this:
1164  #Comment
1165  //Comment
1166  / * Comment
1167  Comment * /
1168  Keyword = Data
1169 
1170  So we could just grab all of the first lines; however, this method
1171  needs to be as error-proof as possible (it is the basis of reading
1172  all PVLs after all), so verify we have things that look like comments
1173  first, strip them & store them.
1174  */
1175 
1176  // While we have newlines, we have comments
1177  while(keyword.contains("\n")) {
1178  // Make sure we strip data every loop of comment types; otherwise we
1179  // have no comment and need to error out.
1180  bool noneStripped = true;
1181 
1182  // Check every comment type now and make sure this line (it isn't the last
1183  // line since a newline exists) starts in a comment
1184  QString keywordStart = keyword.mid(0, 2);
1185 
1186  // Handle multi-line comments
1187  if (keywordStart == "/*") {
1188  noneStripped = false;
1189  bool inComment = true;
1190 
1191  while(inComment && keyword.contains("*/")) {
1192  // Correct the */ to make sure it has a \n after it,
1193  // without this we risk an infinite loop
1194  int closePos = keyword.indexOf("*/\n");
1195 
1196  if (closePos == -1) {
1197  closePos = keyword.indexOf("*/") + 2;
1198  keyword = keyword.mid(0, closePos) + "\n" +
1199  keyword.mid(closePos);
1200  }
1201 
1202  QString comment = keyword.mid(0, keyword.indexOf("\n")).trimmed();
1203 
1204  // Set these to true if too small, false if not (they need if
1205  // cant currently fit).
1206  bool needsStart = (comment.size() < 2);
1207  bool needsStartSpace = comment.size() < 3;
1208 
1209  int commentEndPos = comment.size() - 2;
1210  bool needsEnd = (commentEndPos < 0);
1211  bool needsEndSpace = comment.size() < 3;
1212 
1213  // Needs are currently set based on QString size, apply real logic
1214  // to test for character sequences (try to convert them from false
1215  // to true).
1216  if (!needsStart) {
1217  needsStart = (comment.mid(0, 2) != "/*");
1218  }
1219 
1220  if (!needsEnd) {
1221  needsEnd = (comment.mid(commentEndPos, 2) != "*/");
1222  }
1223 
1224  if (!needsStartSpace) {
1225  needsStartSpace = (comment.mid(0, 3) != "/* ");
1226  }
1227 
1228  if (!needsEndSpace) {
1229  needsEndSpace = (comment.mid(commentEndPos - 1, 3) != " */");
1230  }
1231 
1232  if (needsStart) {
1233  comment = "/* " + comment;
1234  }
1235  else if (needsStartSpace) {
1236  comment = "/* " + comment.mid(2);
1237  }
1238 
1239  if (needsEnd) {
1240  comment = comment + " */";
1241  }
1242  else if (needsEndSpace) {
1243  comment = comment.mid(0, comment.size() - 2) + " */";;
1244  }
1245 
1246  inComment = needsEnd;
1247 
1248  keywordComments.push_back(comment);
1249 
1250  if (keyword.contains("\n")) {
1251  keyword = keyword.mid(keyword.indexOf("\n") + 1).trimmed();
1252  }
1253 
1254  // Check for another comment start
1255  if (!inComment) {
1256  if (keyword.size() >= 2)
1257  keywordStart = keyword.mid(0, 2);
1258 
1259  inComment = (keywordStart == "/*");
1260  }
1261  }
1262 
1263  // So we have a bunch of multi-line commands... make them the same size
1264  // Find longest
1265  int longest = 0;
1266  for (unsigned int index = 0; index < keywordComments.size(); index++) {
1267  QString comment = keywordComments[index];
1268 
1269  if (comment.size() > longest)
1270  longest = comment.size();
1271  }
1272 
1273  // Now make all the sizes match longest
1274  for (unsigned int index = 0; index < keywordComments.size(); index++) {
1275  QString comment = keywordComments[index];
1276 
1277  while(comment.size() < longest) {
1278  // This adds a space to the end of the comment
1279  comment = comment.mid(0, comment.size() - 2) + " */";
1280  }
1281 
1282  keywordComments[index] = comment;
1283  }
1284  // They should all be the same length now
1285  }
1286 
1287  // Search for single line comments
1288  for (unsigned int commentType = 0;
1289  commentType < sizeof(comments) / sizeof(QString);
1290  commentType++) {
1291 
1292  if (keywordStart.startsWith(comments[commentType])) {
1293  // Found a comment start; strip this line out and store it as a
1294  // comment!
1295  QString comment = keyword.mid(0, keyword.indexOf("\n"));
1296  keywordComments.push_back(comment.trimmed());
1297 
1298  noneStripped = false;
1299 
1300  if (keyword.contains("\n")) {
1301  keyword = keyword.mid(keyword.indexOf("\n") + 1).trimmed();
1302  }
1303  }
1304  }
1305 
1306  // Does it look like Name=Value/*comm
1307  // mment*/ ?
1308  if (noneStripped && keyword.contains("/*") &&
1309  keyword.contains("*/")) {
1310  QString firstPart = keyword.mid(0, keyword.indexOf("\n"));
1311  QString lastPart = keyword.mid(keyword.indexOf("\n") + 1);
1312 
1313  keyword = firstPart.trimmed() + " " + lastPart.trimmed();
1314  noneStripped = false;
1315  }
1316 
1317  if (noneStripped) {
1318  QString msg = "Expected a comment in PVL but found [";
1319  msg += keyword;
1320  msg += "]";
1321  throw IException(IException::Unknown, msg, _FILEINFO_);
1322  }
1323  }
1324 
1325  // Do we have a keyword at all?
1326  if (keyword.isEmpty()) {
1327  return false; // need more data
1328  }
1329 
1330  /*
1331  Step 2: Determine Keyword Format
1332 
1333  Make sure we have a keyword after the comments first. We expect
1334  one of three formats:
1335  KEYWORD PROCESSED IN STEP 3.1
1336  KEYWORD = (VALUE,VALUE,...) PROCESSED IN STEP 3.2
1337  KEYWORD = VALUE PROCESSED IN STEP 3.3
1338  */
1339 
1340  // Get the keyword name
1341  keywordName = readValue(keyword, explicitIncomplete);
1342 
1343  // we have taken the name; if nothing remains then it is value-less
1344  // and we are done.
1345  if (keyword.isEmpty()) {
1346  /*
1347  Step 3.1
1348 
1349  Format is determined to be:
1350  KEYWORD
1351 
1352  Well, no value/units may exist so we're done processing this keyword.
1353  */
1354  return 1; // Valid & Successful
1355  }
1356 
1357  // if we don't have an equal, then we have a problem - an invalid symbol.
1358  // Our possible remaining formats both are KEYWORD = ...
1359  if (keyword[0] != '=') {
1360  QString msg = "Expected an assignment [=] when reading PVL, but found [";
1361  msg += keyword[0];
1362  msg += "]";
1363 
1364  throw IException(IException::Unknown, msg, _FILEINFO_);
1365  }
1366 
1367  keyword = keyword.mid(1).trimmed();
1368 
1369  if (keyword.isEmpty()) {
1370  return false;
1371  }
1372 
1373  // now we need to split into two possibilities: array or non-array
1374  if (keyword[0] == '(' || keyword[0] == '{') {
1375  /*
1376  Step 3.2
1377 
1378  Our format is confirmed as:
1379  KEYWORD = (...)
1380 
1381  We need to read each value/unit in the array.
1382  */
1383 
1384  char closingParen = ((keyword[0] == '(') ? ')' : '}');
1385  char wrongClosingParen = ((keyword[0] == '(') ? '}' : ')');
1386  bool closedProperly = false;
1387 
1388  vector< pair<char, char> > extraDelims;
1389  extraDelims.push_back(pair<char, char>('(', ')'));
1390  extraDelims.push_back(pair<char, char>('{', '}'));
1391 
1392  // strip '(' - onetime, this makes every element in the array the same
1393  // (except the last)
1394  keyword = keyword.mid(1).trimmed();
1395 
1396  // handle empty arrays: KEYWORD = ()
1397  if (!keyword.isEmpty() && keyword[0] == closingParen) {
1398  closedProperly = true;
1399  }
1400 
1401  // Each iteration of this loop should consume 1 value in the array,
1402  // including the comma, i.e. we should start out with:
1403  // 'VALUE,VALUE,...)' until we hit ')' (our exit condition)
1404  while(!keyword.isEmpty() && keyword[0] != closingParen) {
1405  // foundComma delimits the end of this element in the array (remains
1406  // false for last value which has no comma at the end)
1407  bool foundComma = false;
1408  // keyword should be of the format: VALUE <UNIT>,....)
1409  // Read VALUE from it
1410  QString nextItem = readValue(keyword, explicitIncomplete, extraDelims);
1411 
1412  if (!keyword.isEmpty() && keyword[0] == wrongClosingParen) {
1413 
1414  QString msg = "Incorrect array close when reading PVL; expected [";
1415  msg += closingParen;
1416  msg += "] but found [";
1417  msg += wrongClosingParen;
1418  msg += "] in keyword named [";
1419  msg += keywordName;
1420  msg += "]";
1421  throw IException(IException::Unknown, msg, _FILEINFO_);
1422  }
1423 
1424  // This contains <VALUE, UNIT>
1425  pair<QString, QString> keywordValue;
1426 
1427  // Store VALUE
1428  keywordValue.first = nextItem;
1429 
1430  // Now there's 2 possibilities: units or no units ->
1431  // if we have units, read them
1432  if (!keyword.isEmpty() && keyword[0] == '<') {
1433  QString unitsValue = readValue(keyword, explicitIncomplete);
1434  keywordValue.second = unitsValue;
1435  }
1436 
1437  // Now we should* have a comma, strip it
1438  if (!keyword.isEmpty() && keyword[0] == ',') {
1439  foundComma = true;
1440  keyword = keyword.mid(1).trimmed();
1441  }
1442 
1443  // No comma and nothing more in QString - we found
1444  // KEYWORD = (VALUE,VALUE\0
1445  // we need more information to finish this keyword
1446  if (!foundComma && keyword.isEmpty()) {
1447  return false; // could become valid later
1448  }
1449 
1450  bool foundCloseParen = (!keyword.isEmpty() && keyword[0] == closingParen);
1451 
1452  if (foundCloseParen) {
1453  closedProperly = true;
1454  }
1455 
1456  // Check for the condition of:
1457  // keyword = (VALUE,VALUE,)
1458  // which is unrecoverable
1459  if (foundComma && foundCloseParen) {
1460  QString msg = "Unexpected close of keyword-value array when reading "
1461  "PVL";
1462  throw IException(IException::Unknown, msg, _FILEINFO_);
1463  }
1464 
1465  // Check for (VALUE VALUE
1466  if (!foundComma && !foundCloseParen) {
1467  // We have ("VALUE VALUE
1468  if (explicitIncomplete) return false;
1469 
1470  // We have (VALUE VALUE
1471  QString msg = "Found extra data after [";
1472  msg += nextItem;
1473  msg += "] in array when reading PVL";
1474  throw IException(IException::Unknown, msg, _FILEINFO_);
1475  }
1476 
1477  // we're good with this element of the array, remember it
1478  keywordValues.push_back(keywordValue);
1479  }
1480 
1481  if (!closedProperly) {
1482  return false;
1483  }
1484 
1485  // Trim off the closing paren
1486  if (!keyword.isEmpty()) {
1487  keyword = keyword.mid(1).trimmed();
1488  }
1489 
1490  // Read units here if they exist and apply them
1491  // case: (A,B,C) <unit>
1492  if (!keyword.isEmpty() && keyword[0] == '<') {
1493  QString units = readValue(keyword, explicitIncomplete);
1494  for (unsigned int val = 0; val < keywordValues.size(); val++) {
1495  if (keywordValues[val].second.isEmpty()) {
1496  keywordValues[val].second = units;
1497  }
1498  }
1499  }
1500  }
1501  else {
1502  /*
1503  Step 3.3
1504 
1505  Our format is confirmed as:
1506  "KEYWORD = VALUE <UNIT>"
1507 
1508  We need to read the single value/unit in the keywordValues array.
1509  */
1510  pair<QString, QString> keywordValue;
1511  keywordValue.first = readValue(keyword, explicitIncomplete);
1512 
1513  if (!keyword.isEmpty() && keyword[0] == '<') {
1514  keywordValue.second = readValue(keyword, explicitIncomplete);
1515  }
1516 
1517  keywordValues.push_back(keywordValue);
1518  }
1519 
1520  /*
1521  This is set when a quote is opened somewhere and never closed, it means
1522  we need more information
1523  */
1524  if (explicitIncomplete) {
1525  return false; // unclosed quote at end... need more information
1526  }
1527 
1528  /*
1529  See if we have a comment at the end of the keyword...
1530  */
1531  if (!keyword.isEmpty()) {
1532  // if the data left is a comment, we can handle it probably
1533  if (keyword[0] == '#' ||
1534  ((keyword.size() > 1 && keyword[0] == '/') &&
1535  (keyword[1] == '/' || keyword[1] == '*'))) {
1536  keywordComments.push_back(keyword);
1537 
1538  if (keyword.size() > 1 && keyword.mid(0, 2) == "/*") {
1539  if (keyword.mid(keyword.size() - 2, 2) != "*/")
1540  return false; // need more comment data
1541  }
1542 
1543  keyword = "";
1544  }
1545  }
1546 
1547  /*
1548  If more data remains, it is unrecognized.
1549  */
1550  if (!keyword.isEmpty()) {
1551  QString msg = "Keyword has extraneous data [";
1552  msg += keyword;
1553  msg += "] at the end";
1554  throw IException(IException::Unknown, msg, _FILEINFO_);
1555  }
1556 
1557  // We've parsed this keyword! :)
1558  return true;
1559  }
1560 
1561 
1562  QString PvlKeyword::readValue(QString &keyword, bool &quoteProblem) {
1563  std::vector< std::pair<char, char> > otherDelims;
1564 
1565  return readValue(keyword, quoteProblem, otherDelims);
1566  }
1567 
1584  QString PvlKeyword::readValue(QString &keyword, bool &quoteProblem,
1585  const std::vector< std::pair<char, char> > &
1586  otherDelimiters) {
1587  QString value = "";
1588 
1589  // This method ignores spaces except as delimiters; let's trim the QString
1590  // to start
1591  keyword = keyword.trimmed();
1592 
1593  if (keyword.isEmpty()) {
1594  return "";
1595  }
1596 
1597  // An implied quote is one that is started without a special character, for
1598  // example HELLO WORLD has HELLO and WORLD separately as implied quotes for
1599  // PVLs. However, "HELLO WORLD" has an explicit (not implied) double quote.
1600  // We do consider <> as quotes.
1601  bool impliedQuote = true;
1602  QChar quoteEnd = ' ';
1603  bool keepQuotes = false;
1604 
1605  if (keyword[0] == '\'' || keyword[0] == '"') {
1606  quoteEnd = keyword[0];
1607  impliedQuote = false;
1608  }
1609  else if (keyword[0] == '<') {
1610  quoteEnd = '>';
1611  impliedQuote = false;
1612  }
1613  else {
1614  // we're not quoted, look for alternative delimiters.
1615  char implicitQuotes[] = {
1616  ')',
1617  '}',
1618  ',',
1619  ' ',
1620  '\t',
1621  '<',
1622  '='
1623  };
1624 
1625  bool foundImplicitQuote = false;
1626 
1627  int currentPos = 0;
1628  while(!foundImplicitQuote && currentPos != keyword.size()) {
1629  for (unsigned int quote = 0;
1630  quote < sizeof(implicitQuotes) / sizeof(char);
1631  quote ++) {
1632  if (keyword[currentPos] == implicitQuotes[quote]) {
1633  quoteEnd = implicitQuotes[quote];
1634  foundImplicitQuote = true;
1635  }
1636  }
1637 
1638  if (!foundImplicitQuote) {
1639  currentPos ++;
1640  }
1641  }
1642  }
1643 
1644  for (unsigned int delim = 0; delim < otherDelimiters.size(); delim ++) {
1645  if (keyword[0] == otherDelimiters[delim].first) {
1646  quoteEnd = otherDelimiters[delim].second;
1647  keepQuotes = true;
1648  impliedQuote = false;
1649  }
1650  }
1651 
1652  QString startQuote;
1653  // non-implied delimeters need the opening delimiter ignored. Remember
1654  // startQuote in case of error later on we can reconstruct the original
1655  // QString.
1656  if (!impliedQuote) {
1657  startQuote += keyword[0];
1658  keyword = keyword.mid(1);
1659  }
1660 
1661  // Do we have a known quote end?
1662  int quoteEndPos = keyword.indexOf(quoteEnd);
1663 
1664  if (quoteEndPos != -1) {
1665  value = keyword.mid(0, quoteEndPos);
1666 
1667  // Trim keyword 1 past end delimiter (if end delimiter is last, this
1668  // results in empty QString). If the close delimiter is ')' or ',' then
1669  // leave it be however, since we need to preserve that to denote this was
1670  // an end of a valuein array keyword.
1671  if (!impliedQuote) {
1672  keyword = keyword.mid(quoteEndPos + 1);
1673  }
1674  else {
1675  keyword = keyword.mid(quoteEndPos);
1676  }
1677 
1678  // Make sure we dont have padding
1679  keyword = keyword.trimmed();
1680 
1681  if (keepQuotes) {
1682  value = startQuote + value + quoteEnd;
1683  }
1684 
1685  return value;
1686  }
1687  // implied quotes terminate at end of keyword; otherwise we have a problem
1688  // (which is this condition)
1689  else if (!impliedQuote) {
1690  // restore the original QString
1691  keyword = startQuote + keyword;
1692  quoteProblem = true;
1693 
1694  return "";
1695  }
1696 
1697  // we have an implied quote but no quote character, the rest must be the
1698  // value.
1699  value = keyword;
1700  keyword = "";
1701 
1702  return value;
1703  }
1704 
1705 
1719  QString PvlKeyword::readLine(std::istream &is, bool insideComment) {
1720  QString lineOfData;
1721 
1722  while(is.good() && lineOfData.isEmpty()) {
1723 
1724  // read until \n (works for both \r\n and \n) or */
1725  while(is.good() &&
1726  (!lineOfData.size() || lineOfData[lineOfData.size() - 1] != '\n')) {
1727  char next = is.get();
1728 
1729  // if non-ascii found then we're done... immediately
1730  if (next <= 0) {
1731  is.seekg(0, ios::end);
1732  is.get();
1733  return lineOfData;
1734  }
1735 
1736  // if any errors (i.e. eof) happen in the get operation then don't
1737  // store this data
1738  if (is.good()) {
1739  lineOfData += next;
1740  }
1741 
1742  if (insideComment &&
1743  lineOfData.size() >= 2 && lineOfData[lineOfData.size() - 2] == '*' &&
1744  lineOfData[lineOfData.size() - 1] == '/') {
1745  // End of multi-line comment = end of line!
1746  break;
1747  }
1748  else if (lineOfData.size() >= 2 &&
1749  lineOfData[lineOfData.size() - 2] == '/' &&
1750  lineOfData[lineOfData.size() - 1] == '*') {
1751  insideComment = true;
1752  }
1753  }
1754 
1755  // Trim off non-visible characters from this line of data
1756  lineOfData = lineOfData.trimmed();
1757 
1758  // read up to next non-whitespace in input stream
1759  while(is.good() &&
1760  (is.peek() == ' ' ||
1761  is.peek() == '\r' ||
1762  is.peek() == '\n')) {
1763  is.get();
1764  }
1765 
1766  // if lineOfData is empty (line was empty), we repeat
1767  }
1768 
1769  return lineOfData;
1770  }
1771 
1772 
1781  ostream &operator<<(std::ostream &os, const Isis::PvlKeyword &keyword) {
1782  // Set up a Formatter
1783  PvlFormat *tempFormat = keyword.m_formatter;
1784  bool removeFormatter = false;
1785  if (tempFormat == NULL) {
1786  tempFormat = new PvlFormat();
1787  removeFormatter = true;
1788  }
1789 
1790  // Write out the comments
1791  for (int i = 0; i < keyword.comments(); i++) {
1792  for (int j = 0; j < keyword.indent(); j++) os << " ";
1793  os << keyword.comment(i) << tempFormat->formatEOL();
1794  }
1795 
1796  // Write the keyword name & add length to startColumn.
1797  int startColumn = 0;
1798  for (int i = 0; i < keyword.indent(); i++) {
1799  os << " ";
1800  ++startColumn;
1801  }
1802  QString keyname = tempFormat->formatName(keyword);
1803  os << keyname;
1804  startColumn += keyname.length();
1805 
1806  // Add padding and then write equal sign.
1807  for (int i = 0; i < keyword.width() - (int)keyname.size(); ++i) {
1808  os << " ";
1809  ++startColumn;
1810  }
1811  os << " = ";
1812  startColumn += 3;
1813 
1814  // If it has no value then write a NULL
1815  if (keyword.size() == 0) {
1816  os << tempFormat->formatValue(keyword);
1817  }
1818 
1819  // Loop and write each array value
1820  QString stringToWrite;
1821  for (int i = 0; i < keyword.size(); i ++) {
1822  stringToWrite += tempFormat->formatValue(keyword, i);
1823  }
1824 
1825  keyword.writeWithWrap(os,
1826  stringToWrite,
1827  startColumn,
1828  *tempFormat);
1829 
1830  if (removeFormatter) delete tempFormat;
1831 
1832  return os;
1833  }
1834 
1835 
1837  const PvlKeyword &PvlKeyword::operator=(const PvlKeyword &other) {
1838  if (this != &other) {
1839  m_formatter = other.m_formatter;
1840 
1841  if (m_name) {
1842  delete [] m_name;
1843  m_name = NULL;
1844  }
1845 
1846  if (other.m_name) {
1847  setName(other.m_name);
1848  }
1849 
1850  m_values = other.m_values;
1851 
1852  if (m_units) {
1853  delete m_units;
1854  m_units = NULL;
1855  }
1856 
1857  if (other.m_units) {
1858  m_units = new std::vector<QString>(*other.m_units);
1859  }
1860 
1861  if (m_comments) {
1862  delete m_comments;
1863  m_comments = NULL;
1864  }
1865 
1866  if (other.m_comments) {
1867  m_comments = new std::vector<QString>(*other.m_comments);
1868  }
1869 
1870  m_width = other.m_width;
1871  m_indent = other.m_indent;
1872  }
1873 
1874  return *this;
1875  }
1876 
1889  void PvlKeyword::validateKeyword(PvlKeyword & pvlKwrd, QString psValueType, PvlKeyword* pvlKwrdValue)
1890  {
1891  int iSize = pvlKwrd.size();
1892 
1893  QString sType = m_values[0].toLower();
1894 
1895  // Value type
1896  if (psValueType.length()) {
1897  psValueType = psValueType.toLower();
1898  }
1899 
1900  double dRangeMin=0, dRangeMax=0;
1901  bool bRange = false;
1902  bool bValue = false;
1903  if (pvlKwrdValue != NULL) {
1904  QString sValueName = pvlKwrdValue->name();
1905 
1906  // Check for Range
1907  if (sValueName.contains("__Range")) {
1908  dRangeMin = toDouble((*pvlKwrdValue)[0]);
1909  dRangeMax = toDouble((*pvlKwrdValue)[1]);
1910  bRange = true;
1911  }
1912  else if (sValueName.contains("__Value")) {
1913  bValue = true;
1914  }
1915  }
1916 
1917  // Type integer
1918  if (sType == "integer") {
1919  for (int i=0; i<iSize; i++) {
1920  QString sValue = pvlKwrd[i].toLower();
1921  if (sValue != "null"){
1922  int iValue=0;
1923  try {
1924  iValue = toInt(sValue);
1925  } catch (IException & e) {
1926  QString sErrMsg = "\"" +pvlKwrd.name() +"\" expects an Integer value";
1927  throw IException(e, IException::User, sErrMsg, _FILEINFO_);
1928  }
1929  if (bRange && (iValue < dRangeMin || iValue > dRangeMax)) {
1930  QString sErrMsg = "\"" +pvlKwrd.name() +"\" is not in the specified Range";
1931  throw IException(IException::User, sErrMsg, _FILEINFO_);
1932  }
1933  if (bValue) {
1934  bool bFound = false;
1935  for (int j=0; j<pvlKwrdValue->size(); j++) {
1936  if (iValue == toInt((*pvlKwrdValue)[j])) {
1937  bFound = true;
1938  break;
1939  }
1940  }
1941  if (!bFound) {
1942  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has value not in the accepted list";
1943  throw IException(IException::User, sErrMsg, _FILEINFO_);
1944  }
1945  }
1946  // Type is specified (positive / negative)
1947  if (psValueType.length()) {
1948  if ((psValueType == "positive" && iValue < 0) || (psValueType == "negative" && iValue >= 0) ) {
1949  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has invalid value";
1950  throw IException(IException::User, sErrMsg, _FILEINFO_);
1951  }
1952  }
1953  }
1954  }
1955  return;
1956  }
1957 
1958  // Type double
1959  if (sType == "double") {
1960  for (int i=0; i<iSize; i++) {
1961  QString sValue = pvlKwrd[i].toLower();
1962  if (sValue != "null"){
1963  double dValue = toDouble(sValue);
1964  if (bRange && (dValue < dRangeMin || dValue > dRangeMax)) {
1965  QString sErrMsg = "\"" +pvlKwrd.name() +"\" is not in the specified Range";
1966  throw IException(IException::User, sErrMsg, _FILEINFO_);
1967  }
1968  if (bValue) {
1969  bool bFound = false;
1970  for (int j=0; j<pvlKwrdValue->size(); j++) {
1971  if (dValue == toDouble((*pvlKwrdValue)[j])) {
1972  bFound = true;
1973  break;
1974  }
1975  }
1976  if (!bFound) {
1977  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has value not in the accepted list";
1978  throw IException(IException::User, sErrMsg, _FILEINFO_);
1979  }
1980  }
1981  // Type is specified (positive / negative)
1982  if (psValueType.length()) {
1983  if ((psValueType == "positive" && dValue < 0) || (psValueType == "negative" && dValue >= 0) ) {
1984  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has invalid value";
1985  throw IException(IException::User, sErrMsg, _FILEINFO_);
1986  }
1987  }
1988  }
1989  }
1990  return;
1991  }
1992 
1993  // Type boolean
1994  if (sType == "boolean") {
1995  for (int i=0; i<iSize; i++) {
1996  QString sValue = pvlKwrd[i].toLower();
1997  if (sValue != "null" && sValue != "true" && sValue != "false"){
1998  QString sErrMsg = "Wrong Type of value in the Keyword \"" + name() + "\" \n";
1999  throw IException(IException::User, sErrMsg, _FILEINFO_);
2000  }
2001  }
2002  return;
2003  }
2004 
2005  // Type String
2006  if (sType == "string") {
2007  for (int i=0; i<iSize; i++) {
2008  QString sValue = pvlKwrd[i].toLower();
2009  if (bValue) {
2010  bool bValFound = false;
2011  for (int i=0; i<pvlKwrdValue->size(); i++) {
2012  if (sValue == (*pvlKwrdValue)[i].toLower()) {
2013  bValFound = true;
2014  break;
2015  }
2016  }
2017  if (!bValFound) {
2018  QString sErrMsg = "Wrong Type of value in the Keyword \"" + name() + "\" \n";
2019  throw IException(IException::User, sErrMsg, _FILEINFO_);
2020  }
2021  }
2022  }
2023  }
2024  }
2025 }