Isis 3 Programmer Reference
PvlKeyword.cpp
1 
6 /* SPDX-License-Identifier: CC0-1.0 */
7 #include "IsisDebug.h"
8 
9 #include <QDebug>
10 #include <QString>
11 
12 #include "PvlKeyword.h"
13 #include "IException.h"
14 #include "Message.h"
15 #include "IString.h"
16 #include "PvlFormat.h"
17 #include "PvlSequence.h"
18 
19 using namespace std;
20 namespace Isis {
22  PvlKeyword::PvlKeyword() {
23  init();
24  }
25 
26 
32  PvlKeyword::PvlKeyword(QString name) {
33  init();
34  setName(name);
35  }
36 
37 
46  PvlKeyword::PvlKeyword(QString name, QString value,
47  QString unit) {
48  init();
49  setName(name);
50  addValue(value, unit);
51  }
52 
53 
55  PvlKeyword::PvlKeyword(const PvlKeyword &other) {
56  init();
57  *this = other;
58  }
59 
60 
64  PvlKeyword::~PvlKeyword() {
65  if (m_units) {
66  delete m_units;
67  m_units = NULL;
68  }
69 
70  if (m_comments) {
71  delete m_comments;
72  m_comments = NULL;
73  }
74 
75  if (m_name) {
76  delete [] m_name;
77  m_name = NULL;
78  }
79  }
80 
81 
83  void PvlKeyword::init() {
84  m_name = NULL;
85  m_units = NULL;
86  m_comments = NULL;
87  m_width = 0;
88  m_indent = 0;
89  m_formatter = NULL;
90 
91  clear();
92  }
93 
102  bool PvlKeyword::isNull(int index) const {
103  if (size() == 0) return true;
104  if (index < 0 || index >= (int)m_values.size()) {
105  QString msg = Message::ArraySubscriptNotInRange(index);
106  throw IException(IException::Programmer, msg, _FILEINFO_);
107  }
108  if (stringEqual("NULL", m_values[index])) return true;
109  if (stringEqual("", m_values[index])) return true;
110  if (stringEqual("\"\"", m_values[index])) return true;
111  if (stringEqual("''", m_values[index])) return true;
112  return false;
113  }
114 
120  void PvlKeyword::setName(QString name) {
121  QString final = name.trimmed();
122  if (final.contains(QRegExp("\\s"))) {
123  QString msg = "[" + name + "] is invalid. Keyword name cannot ";
124  msg += "contain whitespace.";
125  throw IException(IException::User, msg, _FILEINFO_);
126  }
127 
128  if (m_name) {
129  delete [] m_name;
130  m_name = NULL;
131  }
132 
133  if (final != "") {
134  QByteArray finalAscii = final.toLatin1();
135  m_name = new char[finalAscii.size() + 1];
136  strncpy(m_name, finalAscii.data(), final.size() + 1);
137  }
138  }
139 
155  void PvlKeyword::setValue(QString value, QString unit) {
156  clear();
157  addValue(value, unit);
158  }
159 
160 
166  void PvlKeyword::setUnits(QString units) {
167  if (!m_units) {
168  m_units = new std::vector<QString>();
169  }
170 
171  m_units->clear();
172 
173  for (int i = 0; i < m_values.size(); i++) {
174  m_units->push_back(units);
175  }
176  }
177 
178 
187  void PvlKeyword::setUnits(QString value, QString units) {
188 
189  bool found = false;
190  int i = -1;
191  while(!found && ++i < (int) m_values.size()) {
192  if (value == m_values[i]) {
193  found = true;
194  }
195  }
196 
197  if (found) {
198  if (!m_units) {
199  m_units = new std::vector<QString>(m_values.size());
200  }
201  else {
202  m_units->resize(m_values.size());
203  }
204 
205  ASSERT(i < (int) m_units->size());
206 
207  (*m_units)[i] = units;
208  }
209  else {
210  IString msg = "PvlKeyword::setUnits called with value [" + value +
211  "] which does not exist in this Keyword";
212  throw IException(IException::Programmer, msg, _FILEINFO_);
213  }
214  }
215 
231  PvlKeyword &PvlKeyword::operator=(QString value) {
232  clear();
233  addValue(value);
234  return *this;
235  }
236 
252  void PvlKeyword::addValue(QString value, QString unit) {
253  m_values.append(value);
254 
255  if (unit != "") {
256  if (!m_units) {
257  m_units = new std::vector<QString>(m_values.size());
258  }
259  else {
260  m_units->resize(m_values.size());
261  }
262 
263  (*m_units)[m_units->size() - 1] = unit;
264  }
265  else if (m_units) {
266  m_units->push_back("");
267  }
268  }
269 
285  PvlKeyword &PvlKeyword::operator+=(QString value) {
286  addValue(value);
287  return *this;
288  }
289 
291  void PvlKeyword::clear() {
292  m_values.clear();
293 
294  if (m_units) {
295  delete m_units;
296  m_units = NULL;
297  }
298  }
299 
300 
301  PvlKeyword::operator QString() const {
302  return operator[](0);
303  }
304 
305 
318  QString &PvlKeyword::operator[](int index) {
319  if (index < 0 || index >= (int)m_values.size()) {
320  QString msg = (Message::ArraySubscriptNotInRange(index)) +
321  "for Keyword [" + QString(m_name) + "]";
322  throw IException(IException::Programmer, msg, _FILEINFO_);
323  }
324 
325  return m_values[index];
326  }
327 
340  const QString &PvlKeyword::operator[](int index) const {
341  if (index < 0 || index >= (int)m_values.size()) {
342  QString msg = Message::ArraySubscriptNotInRange(index);
343  throw IException(IException::Programmer, msg, _FILEINFO_);
344  }
345  return m_values[index];
346  }
347 
357  QString PvlKeyword::unit(int index) const {
358  if (!m_units) return "";
359 
360  if (index < 0 || index >= (int)m_units->size()) {
361  QString msg = Message::ArraySubscriptNotInRange(index);
362  throw IException(IException::Programmer, msg, _FILEINFO_);
363  }
364  return (*m_units)[index];
365  }
366 
376  void PvlKeyword::addComment(QString comment) {
377  if (!m_comments) {
378  m_comments = new std::vector<QString>();
379  }
380 
381  if (comment.size() == 0) {
382  m_comments->push_back("#");
383  }
384  if (comment[0] == '#') {
385  m_comments->push_back(comment);
386  }
387  else if (comment.size() == 1) {
388  m_comments->push_back("# " + comment);
389  }
390  else if ((comment[0] == '/') && (comment[1] == '*')) {
391  m_comments->push_back(comment);
392  }
393  else if ((comment[0] == '/') && (comment[1] == '/')) {
394  m_comments->push_back(comment);
395  }
396  else {
397  m_comments->push_back("# " + comment);
398  }
399  }
400 
410  void PvlKeyword::addCommentWrapped(QString comment) {
411  IString cmt = comment;
412  IString token = cmt.Token(" ");
413  while(cmt != "") {
414  IString temp = token;
415  token = cmt.Token(" ");
416  int length = temp.size() + token.size() + 1;
417  while((length < 72) && (token.size() > 0)) {
418  temp += " " + token;
419  token = cmt.Token(" ");
420  length = temp.size() + token.size() + 1;
421  }
422  addComment(temp.c_str());
423  }
424  if (token.size() != 0) addComment(token.c_str());
425  }
426 
428  void PvlKeyword::clearComment() {
429  if (m_comments) {
430  delete m_comments;
431  m_comments = NULL;
432  }
433  }
434 
441  QString PvlKeyword::comment(int index) const {
442  if (!m_comments) return "";
443 
444  if (index < 0 || index >= (int)m_comments->size()) {
445  QString msg = Message::ArraySubscriptNotInRange(index);
446  throw IException(IException::Programmer, msg, _FILEINFO_);
447  }
448  return (*m_comments)[index];
449  };
450 
458  QString PvlKeyword::reform(const QString &value) const {
459 #if 0
460  static bool firstTime = true;
461  static bool iPVL = true;
462  if (firstTime) {
463  firstTime = false;
464  Isis::PvlGroup &g = Isis::Preference::Preferences().findGroup(
465  "UserInterface", Isis::Pvl::Traverse);
466 
467  Isis::IString s = (QString) g["PvlFormat"];
468  s.UpCase();
469  if (s == "PVL") iPVL = false;
470  }
471 
472  if (iPVL) return toIPvl(value);
473 #endif
474  return toPvl(value);
475  }
476 
482  QString PvlKeyword::toIPvl(const QString &value) const {
483  QString out;
484  bool upcase = true;
485  bool lastlower = true;
486  for (int i = 0; i < value.size(); i++) {
487  if ((lastlower) && (value[i].isUpper())) upcase = true;
488  if (value[i] == '_') {
489  upcase = true;
490  }
491  else if (upcase) {
492  out += value[i].toUpper();
493  lastlower = false;
494  upcase = false;
495  }
496  else {
497  out += value[i].toLower();
498  if (value[i].isLower()) lastlower = true;
499  upcase = false;
500  }
501  }
502  return out;
503  }
504 
510  QString PvlKeyword::toPvl(const QString &value) const {
511  QString out;
512  bool lastlower = false;
513  for (int i = 0; i < value.size(); i++) {
514  if ((lastlower) && (value[i].isUpper())) out += "_";
515  if (value[i] == '_') {
516  out += "_";
517  lastlower = false;
518  }
519  else {
520  out += value[i].toUpper();
521  if (value[i].isLower()) lastlower = true;
522  }
523  }
524  return out;
525  }
526 
535  bool PvlKeyword::stringEqual(const QString &QString1,
536  const QString &QString2) {
537  Isis::IString s1(QString1);
538  Isis::IString s2(QString2);
539 
540  s1.ConvertWhiteSpace();
541  s2.ConvertWhiteSpace();
542 
543  s1.Remove(" _");
544  s2.Remove(" _");
545 
546  s1.UpCase();
547  s2.UpCase();
548 
549  if (s1 == s2) return true;
550  return false;
551  }
552 
562  bool PvlKeyword::isEquivalent(QString QString1, int index) const {
563  if (index < 0 || index >= (int)m_values.size()) {
564  QString msg = Message::ArraySubscriptNotInRange(index);
565  throw IException(IException::Programmer, msg, _FILEINFO_);
566  }
567 
568  return stringEqual(m_values[index], QString1);
569  }
570 
576  PvlKeyword &PvlKeyword::operator=(Isis::PvlSequence &seq) {
577  clear();
578  for (int i = 0; i < seq.Size(); i++) {
579  QString temp = "(";
580  for (int j = 0; j < (int)seq[i].size(); j++) {
581  QString val = seq[i][j];
582  if (val.contains(" ")) {
583  temp += "\"" + val + "\"";
584  }
585  else {
586  temp += val;
587  }
588  if (j < (int) seq[i].size() - 1) temp += ", ";
589  }
590  temp += ")";
591  this->operator+=(temp);
592  }
593 
594  return *this;
595  }
596 
611  ostream &PvlKeyword::writeWithWrap(std::ostream &os,
612  const QString &textToWrite,
613  int startColumn,
614  PvlFormat &format) const {
615 
616  /*
617  http://pds.jpl.nasa.gov/tools/standards-reference.shtml
618 
619  pds.jpl.nasa.gov/documents/sr/Chapter12.pdf
620 
621  Object Description Language Specification and Usage
622  The following provides a complete specification for Object Description Language
623  (ODL), the language used to encode data labels for the Planetary Data System
624  (PDS) and other NASA data systems. This standard contains a formal definition of
625  the grammar semantics of the language. PDS specific implementation notes and
626  standards are referenced in separate sections.
627 
628  12.5.3.1 Implementation of String Values
629  A text QString read in from a label is reassembled into a QString of characters.
630  The way in which the QString is broken into lines in a label does not affect the
631  format of the QString after it has been reassembled. The following rules are used
632  when reading text QStrings: If a format effector or a sequence of
633  format effectors is encountered within a text QString,
634  the effector (or sequence of effectors) is replaced by a single space
635  character, unless the last character is a hyphen (dash) character. Any
636  spacing characters at the end of the line are removed and any spacing
637  characters at the beginning of the following line are removed. This
638  allows a text QString in a label to appear with the left and right
639  margins set at arbitrary points without changing the QString value. For
640  example, the following two QStrings are the same: "To be or
641  not to be" and
642  "To be or
643  not to be"
644  If the last character on a line prior to a format effector is a hyphen
645  (dash) character, the hyphen is removed with any spacing characters at
646  the beginning of the following line. This follows the standard
647  convention in English of using a hyphen to break a word across lines.
648  For example, the following two QStrings are the same:
649  "The planet Jupiter is very big" and
650  "The planet Jupi-
651  ter is very big"
652  Control codes, other than the horizontal tabulation character and
653  format effectors, appearing within a text QString are removed.
654  */
655 
656  /*
657  We will be adding a condition for human-readable purposes:
658  If a quoted QString of text does not fit on the current line,
659  but will fit on the next line, use the next line.
660  */
661 
662  // Position set
663  QString remainingText = textToWrite;
664  int spaceForText = format.charLimit() - 1 - format.formatEOL().length() - startColumn;
665 
666  // indexOf quote positions to better determine which line to put the
667  // QString on. Data structure: vector< startPos, endPos > where
668  // remainingText[startPos] and remainingText[endPos] must both be quotes.
669  vector< pair<int, int> > quotedAreas;
670  int quoteStart = -1;
671 
672  // if its an array, indent subsequent lines 1 more
673  if (textToWrite.count() > 0 && (textToWrite[0] == '(' || textToWrite[0] == '"')) {
674  startColumn ++;
675  }
676 
677  /* Standard 12.3.3.1 ->
678  A quoted text QString may not contain the quotation mark, which is reserved
679  to be the text QString delimiter.
680 
681  So we don't have to worry about escaped quotes.
682  */
683 
684  vector< pair<char, char> > quoteStartEnds;
685  quoteStartEnds.push_back(pair<char, char>('"', '"'));
686  quoteStartEnds.push_back(pair<char, char>('\'', '\''));
687  quoteStartEnds.push_back(pair<char, char>('<', '>'));
688 
689  // clean up any EOL characters, they mustn't interfere, remove sections of
690  // multiple spaces (make them into one), and indexOf quoted areas
691  for (int pos = 0; pos < remainingText.size(); pos++) {
692  // remove \r and \n from QString
693  if (remainingText[pos] == '\n' || remainingText[pos] == '\r') {
694  if (pos != remainingText.size() - 1) {
695  remainingText = remainingText.mid(0, pos) +
696  remainingText.mid(pos + 1);
697  }
698  else {
699  remainingText = remainingText.mid(0, pos);
700  }
701  }
702 
703  // convert " " to " " if not quoted
704  if (quoteStart == -1) {
705  while(pos > 0 &&
706  remainingText[pos-1] == ' ' &&
707  remainingText[pos] == ' ') {
708  remainingText = remainingText.mid(0, pos) +
709  remainingText.mid(pos + 1);
710  }
711  }
712 
713  // Find quotes
714  for (unsigned int i = 0;
715  (quoteStart < 0) && i < quoteStartEnds.size();
716  i++) {
717  if (quoteStartEnds[i].first == remainingText[pos]) {
718  quoteStart = pos;
719  }
720  }
721 
722 
723  //bool mismatchQuote = false;
724 
725  // Check to see if we're ending a quote if we didn't just
726  // start the quote and we are inside a quote
727  if (quoteStart != (int)pos && quoteStart != -1) {
728  for (unsigned int i = 0; i < quoteStartEnds.size(); i++) {
729  if (quoteStartEnds[i].second == remainingText[pos]) {
730  if (quoteStartEnds[i].first != remainingText[quoteStart]) {
731  continue;
732  // mismatchQuote = true;
733  }
734 
735  quotedAreas.push_back(pair<int, int>(quoteStart, pos));
736 
737  quoteStart = -1;
738  }
739  }
740  }
741 
742  //if (mismatchQuote) {
743  // IString msg = "Pvl keyword values [" + textToWrite +
744  // "] can not have embedded quotes";
745  // throw iException::Message(iException::Programmer, msg, _FILEINFO_);
746  //}
747  }
748 
749  int charsLeft = spaceForText;
750  int printedSoFar = 0;
751 
752  // while we have something to write, keep going
753  while(!remainingText.isEmpty()) {
754  // search backwards for the last space or comma *in the limit* (80 chars)
755  int lastSpacePosition = charsLeft;
756 
757  // if everything fits into our remaining space, consider the last
758  // spot in the QString to be printed still the split position.
759  if (lastSpacePosition >= (int)remainingText.length()) {
760  lastSpacePosition = remainingText.length();
761  }
762  else {
763  // Everything does not fit; use good space for mediocre splits (inside
764  // quoted QStrings), and excellent space for good splits (between array
765  // values for example)
766  int goodSpace = -1;
767  int excellentSpace = -1;
768  int searchPosition = lastSpacePosition;
769  bool doneSearching = false;
770 
771  while(!doneSearching) {
772  bool currentPosQuoted = false;
773 
774  for (unsigned int i = 0; i < quotedAreas.size(); i++) {
775  if (searchPosition + printedSoFar >= quotedAreas[i].first &&
776  searchPosition + printedSoFar <= quotedAreas[i].second) {
777  currentPosQuoted = true;
778  }
779  }
780 
781  if (remainingText[searchPosition] == ' ') {
782  bool validSpace = true;
783 
784  // this really isn't a good space if the previous character is a
785  // '-' though - then it would be read wrong when re-imported.
786  if (searchPosition > 0 && remainingText[searchPosition - 1] == '-') {
787  validSpace = false;
788  }
789 
790  if (validSpace && goodSpace < 0) {
791  goodSpace = searchPosition;
792  }
793 
794  // An excellent space is the prefential break - not quoted and
795  // not units next.
796  // we were already done if we had an excellent space
797  if (validSpace && !currentPosQuoted) {
798  if ((int)searchPosition < (int)(remainingText.size() - 1) &&
799  remainingText[searchPosition+1] != '<') {
800  excellentSpace = searchPosition;
801  }
802  }
803  }
804 
805  doneSearching = (excellentSpace >= 0 || searchPosition <= 1);
806  searchPosition --;
807  }
808 
809  // Use the best breaking point we have
810  if (excellentSpace > 0) {
811  lastSpacePosition = excellentSpace;
812  }
813  else if (goodSpace > 0) {
814  lastSpacePosition = goodSpace;
815  }
816  else {
817  lastSpacePosition = -1;
818  }
819  }
820 
821  // we found a space or comma in our limit, write to that chatacter
822  // and repeat the loop
823  if (lastSpacePosition >= 0) {
824  os << remainingText.mid(0, lastSpacePosition);
825 
826  remainingText = remainingText.mid(lastSpacePosition);
827  printedSoFar += lastSpacePosition;
828  }
829  // we failed to indexOf a space or a comma in our limit,
830  // use a hyphen (-)
831  else {
832  // Make sure we don't break on "//" since Isis thinks that is a comment
833  if (remainingText.mid(charsLeft-1, 2) == "//") {
834  os << remainingText.mid(0, charsLeft - 2);
835  os << "-";
836  remainingText = remainingText.mid(charsLeft - 2);
837  printedSoFar += charsLeft - 2;
838  }
839  else {
840  os << remainingText.mid(0, charsLeft - 1);
841  os << "-";
842  remainingText = remainingText.mid(charsLeft - 1);
843  printedSoFar += charsLeft - 1;
844  }
845  }
846 
847  // we wrote as much as possible, do a newline and repeat
848  if (!remainingText.isEmpty()) {
849  os << format.formatEOL();
850  writeSpaces(os, startColumn);
851 
852  // dont allow spaces to begin the next line inside what we're printing
853  if (remainingText[0] == ' ') {
854  remainingText = remainingText.mid(1);
855  printedSoFar += 1;
856  }
857  }
858 
859  charsLeft = spaceForText;
860  }
861 
862  return os;
863  }
864 
865 
872  void PvlKeyword::writeSpaces(std::ostream &os, int numSpaces) const {
873  for (int space = 0; space < numSpaces; space ++) {
874  os << " ";
875  }
876  }
877 
878 
885  void PvlKeyword::setFormat(PvlFormat *formatter) {
886  m_formatter = formatter;
887  }
888 
889 
896  PvlFormat *PvlKeyword::format() {
897  return m_formatter;
898  };
899 
909  std::istream &operator>>(std::istream &is, PvlKeyword &result) {
910  result = PvlKeyword();
911  QString line;
912  QString keywordString;
913 
914  bool keywordDone = false;
915  bool multiLineComment = false;
916  bool error = !is.good();
917 
918  while(!error && !keywordDone) {
919  istream::pos_type beforeLine = is.tellg();
920 
921  line = PvlKeyword::readLine(is, multiLineComment);
922 
923  // We read an empty line (failed to read next non-empty line)
924  // and didnt complete our keyword, essentially we hit the implicit
925  // keyword named "End"
926  if (line.isEmpty() && !is.good()) {
927  if (keywordString.isEmpty() ||
928  keywordString[keywordString.size()-1] == '\n') {
929  line = "End";
930 
931  if (multiLineComment) {
932  error = true;
933  }
934  }
935  else {
936  error = true;
937  }
938  }
939 
940  bool comment = false;
941 
942  if (!multiLineComment) {
943  if (line.size() > 0 && line[0] == '#') {
944  comment = true;
945  }
946 
947  if (line.size() > 1 && line[0] == '/' &&
948  (line[1] == '*' || line[1] == '/')) {
949  comment = true;
950 
951  if (line[1] == '*') {
952  multiLineComment = true;
953  keywordString += line.mid(0, 2);
954  line = line.mid(2).trimmed();
955  }
956  }
957  }
958 
959  if (multiLineComment) {
960  comment = true;
961 
962  if (line.contains("/*")) {
963  IString msg = "Error when reading a pvl: Cannot have ['/*'] inside a "
964  "multi-line comment";
965  throw IException(IException::Unknown, msg, _FILEINFO_);
966  }
967 
968  if (line.contains("*/")) {
969  multiLineComment = false;
970 
971  line = line.mid(0, line.indexOf("*/")).trimmed() + " */";
972  }
973  }
974 
975  if (line.isEmpty()) {
976  continue;
977  }
978  // comment line
979  else if (comment) {
980  keywordString += line + '\n';
981  continue;
982  }
983  // first line of keyword data
984  else if (keywordString.isEmpty()) {
985  keywordString = line;
986  }
987  // concatenation
988  else if (!comment && keywordString[keywordString.size()-1] == '-') {
989  keywordString = keywordString.mid(0, keywordString.size() - 1) + line;
990  }
991  // Non-commented and non-concatenation -> put in the space
992  else {
993  keywordString += " " + line;
994  }
995  // if this line concatenates with the next, read the next
996  if (line[line.size()-1] == '-') {
997  continue;
998  }
999 
1000  std::vector<QString> keywordComments;
1001  QString keywordName;
1002  std::vector< std::pair<QString, QString> > keywordValues;
1003 
1004  bool attemptedRead = false;
1005 
1006  try {
1007  attemptedRead = PvlKeyword::readCleanKeyword(keywordString,
1008  keywordComments,
1009  keywordName,
1010  keywordValues);
1011  }
1012  catch (IException &e) {
1013  if (is.eof() && !is.bad()) {
1014  is.clear();
1015  is.unget();
1016  }
1017 
1018  is.seekg(beforeLine, ios::beg);
1019 
1020  QString msg = "Unable to read PVL keyword [";
1021  msg += keywordString;
1022  msg += "]";
1023 
1024  throw IException(e, IException::Unknown, msg, _FILEINFO_);
1025  }
1026 
1027  // Result valid?
1028  if (attemptedRead) {
1029  // if the next line starts with '<' then it should be read too...
1030  // it should be units
1031  // however, you can't have units if there is no value
1032  if (is.good() && is.peek() == '<' && !keywordValues.empty()) {
1033  continue;
1034  }
1035 
1036  result.setName(keywordName);
1037  result.addComments(keywordComments);
1038 
1039  for (unsigned int value = 0; value < keywordValues.size(); value++) {
1040  result.addValue(keywordValues[value].first,
1041  keywordValues[value].second);
1042  }
1043 
1044  keywordDone = true;
1045  }
1046 
1047  if (!attemptedRead) {
1048  error = error || !is.good();
1049  }
1050  // else we need to keep reading
1051  }
1052 
1053  if (error) {
1054  // skip comments
1055  while(keywordString.contains('\n')) {
1056  keywordString = keywordString.mid(keywordString.indexOf('\n') + 1);
1057  }
1058 
1059  QString msg;
1060 
1061  if (keywordString.isEmpty() && !multiLineComment) {
1062  msg = "PVL input contains no Pvl Keywords";
1063  }
1064  else if (multiLineComment) {
1065  msg = "PVL input ends while still in a multi-line comment";
1066  }
1067  else {
1068  msg = "The PVL keyword [" + keywordString + "] does not appear to be";
1069  msg += " a valid Pvl Keyword";
1070  }
1071 
1072  throw IException(IException::Unknown, msg, _FILEINFO_);
1073  }
1074 
1075  if (!keywordDone) {
1076  // skip comments
1077  while(keywordString.contains('\n'))
1078  keywordString = keywordString.mid(keywordString.indexOf('\n') + 1);
1079 
1080  QString msg;
1081 
1082  if (keywordString.isEmpty()) {
1083  msg = "Error reading PVL keyword";
1084  }
1085  else {
1086  msg = "The PVL keyword [" + keywordString + "] does not appear to be";
1087  msg += " complete";
1088  }
1089 
1090  throw IException(IException::Unknown, msg, _FILEINFO_);
1091  }
1092 
1093  return is;
1094  }
1095 
1102  void PvlKeyword::addComments(const std::vector<QString> &comments) {
1103  for (unsigned int i = 0; i < comments.size(); i++) {
1104  addComment(comments[i]);
1105  }
1106  }
1107 
1123  bool PvlKeyword::readCleanKeyword(QString keyword,
1124  std::vector<QString> &keywordComments,
1125  QString &keywordName,
1126  std::vector< std::pair<QString, QString> > &keywordValues) {
1127  // Reset outputs
1128  keywordComments.clear();
1129  keywordName = "";
1130  keywordValues.clear();
1131 
1132  // This is in case a close quote doesn't exist
1133  bool explicitIncomplete = false;
1134 
1135  // Possible (known) comment starts in pvl
1136  QString comments[] = {
1137  "#",
1138  "//"
1139  };
1140 
1141  // Need more data if nothing is here!
1142  if (keyword.isEmpty()) return 0;
1143 
1144  /*
1145  Step 1: Read Comments
1146 
1147  Theoretically, we should have an input that looks like this:
1148  #Comment
1149  //Comment
1150  / * Comment
1151  Comment * /
1152  Keyword = Data
1153 
1154  So we could just grab all of the first lines; however, this method
1155  needs to be as error-proof as possible (it is the basis of reading
1156  all PVLs after all), so verify we have things that look like comments
1157  first, strip them & store them.
1158  */
1159 
1160  // While we have newlines, we have comments
1161  while(keyword.contains("\n")) {
1162  // Make sure we strip data every loop of comment types; otherwise we
1163  // have no comment and need to error out.
1164  bool noneStripped = true;
1165 
1166  // Check every comment type now and make sure this line (it isn't the last
1167  // line since a newline exists) starts in a comment
1168  QString keywordStart = keyword.mid(0, 2);
1169 
1170  // Handle multi-line comments
1171  if (keywordStart == "/*") {
1172  noneStripped = false;
1173  bool inComment = true;
1174 
1175  while(inComment && keyword.contains("*/")) {
1176  // Correct the */ to make sure it has a \n after it,
1177  // without this we risk an infinite loop
1178  int closePos = keyword.indexOf("*/\n");
1179 
1180  if (closePos == -1) {
1181  closePos = keyword.indexOf("*/") + 2;
1182  keyword = keyword.mid(0, closePos) + "\n" +
1183  keyword.mid(closePos);
1184  }
1185 
1186  QString comment = keyword.mid(0, keyword.indexOf("\n")).trimmed();
1187 
1188  // Set these to true if too small, false if not (they need if
1189  // cant currently fit).
1190  bool needsStart = (comment.size() < 2);
1191  bool needsStartSpace = comment.size() < 3;
1192 
1193  int commentEndPos = comment.size() - 2;
1194  bool needsEnd = (commentEndPos < 0);
1195  bool needsEndSpace = comment.size() < 3;
1196 
1197  // Needs are currently set based on QString size, apply real logic
1198  // to test for character sequences (try to convert them from false
1199  // to true).
1200  if (!needsStart) {
1201  needsStart = (comment.mid(0, 2) != "/*");
1202  }
1203 
1204  if (!needsEnd) {
1205  needsEnd = (comment.mid(commentEndPos, 2) != "*/");
1206  }
1207 
1208  if (!needsStartSpace) {
1209  needsStartSpace = (comment.mid(0, 3) != "/* ");
1210  }
1211 
1212  if (!needsEndSpace) {
1213  needsEndSpace = (comment.mid(commentEndPos - 1, 3) != " */");
1214  }
1215 
1216  if (needsStart) {
1217  comment = "/* " + comment;
1218  }
1219  else if (needsStartSpace) {
1220  comment = "/* " + comment.mid(2);
1221  }
1222 
1223  if (needsEnd) {
1224  comment = comment + " */";
1225  }
1226  else if (needsEndSpace) {
1227  comment = comment.mid(0, comment.size() - 2) + " */";;
1228  }
1229 
1230  inComment = needsEnd;
1231 
1232  keywordComments.push_back(comment);
1233 
1234  if (keyword.contains("\n")) {
1235  keyword = keyword.mid(keyword.indexOf("\n") + 1).trimmed();
1236  }
1237 
1238  // Check for another comment start
1239  if (!inComment) {
1240  if (keyword.size() >= 2)
1241  keywordStart = keyword.mid(0, 2);
1242 
1243  inComment = (keywordStart == "/*");
1244  }
1245  }
1246 
1247  // So we have a bunch of multi-line commands... make them the same size
1248  // Find longest
1249  int longest = 0;
1250  for (unsigned int index = 0; index < keywordComments.size(); index++) {
1251  QString comment = keywordComments[index];
1252 
1253  if (comment.size() > longest)
1254  longest = comment.size();
1255  }
1256 
1257  // Now make all the sizes match longest
1258  for (unsigned int index = 0; index < keywordComments.size(); index++) {
1259  QString comment = keywordComments[index];
1260 
1261  while(comment.size() < longest) {
1262  // This adds a space to the end of the comment
1263  comment = comment.mid(0, comment.size() - 2) + " */";
1264  }
1265 
1266  keywordComments[index] = comment;
1267  }
1268  // They should all be the same length now
1269  }
1270 
1271  // Search for single line comments
1272  for (unsigned int commentType = 0;
1273  commentType < sizeof(comments) / sizeof(QString);
1274  commentType++) {
1275 
1276  if (keywordStart.startsWith(comments[commentType])) {
1277  // Found a comment start; strip this line out and store it as a
1278  // comment!
1279  QString comment = keyword.mid(0, keyword.indexOf("\n"));
1280  keywordComments.push_back(comment.trimmed());
1281 
1282  noneStripped = false;
1283 
1284  if (keyword.contains("\n")) {
1285  keyword = keyword.mid(keyword.indexOf("\n") + 1).trimmed();
1286  }
1287  }
1288  }
1289 
1290  // Does it look like Name=Value/*comm
1291  // mment*/ ?
1292  if (noneStripped && keyword.contains("/*") &&
1293  keyword.contains("*/")) {
1294  QString firstPart = keyword.mid(0, keyword.indexOf("\n"));
1295  QString lastPart = keyword.mid(keyword.indexOf("\n") + 1);
1296 
1297  keyword = firstPart.trimmed() + " " + lastPart.trimmed();
1298  noneStripped = false;
1299  }
1300 
1301  if (noneStripped) {
1302  QString msg = "Expected a comment in PVL but found [";
1303  msg += keyword;
1304  msg += "]";
1305  throw IException(IException::Unknown, msg, _FILEINFO_);
1306  }
1307  }
1308 
1309  // Do we have a keyword at all?
1310  if (keyword.isEmpty()) {
1311  return false; // need more data
1312  }
1313 
1314  /*
1315  Step 2: Determine Keyword Format
1316 
1317  Make sure we have a keyword after the comments first. We expect
1318  one of three formats:
1319  KEYWORD PROCESSED IN STEP 3.1
1320  KEYWORD = (VALUE,VALUE,...) PROCESSED IN STEP 3.2
1321  KEYWORD = VALUE PROCESSED IN STEP 3.3
1322  */
1323 
1324  // Get the keyword name
1325  keywordName = readValue(keyword, explicitIncomplete);
1326 
1327  // we have taken the name; if nothing remains then it is value-less
1328  // and we are done.
1329  if (keyword.isEmpty()) {
1330  /*
1331  Step 3.1
1332 
1333  Format is determined to be:
1334  KEYWORD
1335 
1336  Well, no value/units may exist so we're done processing this keyword.
1337  */
1338  return 1; // Valid & Successful
1339  }
1340 
1341  // if we don't have an equal, then we have a problem - an invalid symbol.
1342  // Our possible remaining formats both are KEYWORD = ...
1343  if (keyword[0] != '=') {
1344  QString msg = "Expected an assignment [=] when reading PVL, but found [";
1345  msg += keyword[0];
1346  msg += "]";
1347 
1348  throw IException(IException::Unknown, msg, _FILEINFO_);
1349  }
1350 
1351  keyword = keyword.mid(1).trimmed();
1352 
1353  if (keyword.isEmpty()) {
1354  return false;
1355  }
1356 
1357  // now we need to split into two possibilities: array or non-array
1358  if (keyword[0] == '(' || keyword[0] == '{') {
1359  /*
1360  Step 3.2
1361 
1362  Our format is confirmed as:
1363  KEYWORD = (...)
1364 
1365  We need to read each value/unit in the array.
1366  */
1367 
1368  char closingParen = ((keyword[0] == '(') ? ')' : '}');
1369  char wrongClosingParen = ((keyword[0] == '(') ? '}' : ')');
1370  bool closedProperly = false;
1371 
1372  vector< pair<char, char> > extraDelims;
1373  extraDelims.push_back(pair<char, char>('(', ')'));
1374  extraDelims.push_back(pair<char, char>('{', '}'));
1375 
1376  // strip '(' - onetime, this makes every element in the array the same
1377  // (except the last)
1378  keyword = keyword.mid(1).trimmed();
1379 
1380  // handle empty arrays: KEYWORD = ()
1381  if (!keyword.isEmpty() && keyword[0] == closingParen) {
1382  closedProperly = true;
1383  }
1384 
1385  // Each iteration of this loop should consume 1 value in the array,
1386  // including the comma, i.e. we should start out with:
1387  // 'VALUE,VALUE,...)' until we hit ')' (our exit condition)
1388  while(!keyword.isEmpty() && keyword[0] != closingParen) {
1389  // foundComma delimits the end of this element in the array (remains
1390  // false for last value which has no comma at the end)
1391  bool foundComma = false;
1392  // keyword should be of the format: VALUE <UNIT>,....)
1393  // Read VALUE from it
1394  QString nextItem = readValue(keyword, explicitIncomplete, extraDelims);
1395 
1396  if (!keyword.isEmpty() && keyword[0] == wrongClosingParen) {
1397 
1398  QString msg = "Incorrect array close when reading PVL; expected [";
1399  msg += closingParen;
1400  msg += "] but found [";
1401  msg += wrongClosingParen;
1402  msg += "] in keyword named [";
1403  msg += keywordName;
1404  msg += "]";
1405  throw IException(IException::Unknown, msg, _FILEINFO_);
1406  }
1407 
1408  // This contains <VALUE, UNIT>
1409  pair<QString, QString> keywordValue;
1410 
1411  // Store VALUE
1412  keywordValue.first = nextItem;
1413 
1414  // Now there's 2 possibilities: units or no units ->
1415  // if we have units, read them
1416  if (!keyword.isEmpty() && keyword[0] == '<') {
1417  QString unitsValue = readValue(keyword, explicitIncomplete);
1418  keywordValue.second = unitsValue;
1419  }
1420 
1421  // Now we should* have a comma, strip it
1422  if (!keyword.isEmpty() && keyword[0] == ',') {
1423  foundComma = true;
1424  keyword = keyword.mid(1).trimmed();
1425  }
1426 
1427  // No comma and nothing more in QString - we found
1428  // KEYWORD = (VALUE,VALUE\0
1429  // we need more information to finish this keyword
1430  if (!foundComma && keyword.isEmpty()) {
1431  return false; // could become valid later
1432  }
1433 
1434  bool foundCloseParen = (!keyword.isEmpty() && keyword[0] == closingParen);
1435 
1436  if (foundCloseParen) {
1437  closedProperly = true;
1438  }
1439 
1440  // Check for the condition of:
1441  // keyword = (VALUE,VALUE,)
1442  // which is unrecoverable
1443  if (foundComma && foundCloseParen) {
1444  QString msg = "Unexpected close of keyword-value array when reading "
1445  "PVL";
1446  throw IException(IException::Unknown, msg, _FILEINFO_);
1447  }
1448 
1449  // Check for (VALUE VALUE
1450  if (!foundComma && !foundCloseParen) {
1451  // We have ("VALUE VALUE
1452  if (explicitIncomplete) return false;
1453 
1454  // We have (VALUE VALUE
1455  QString msg = "Found extra data after [";
1456  msg += nextItem;
1457  msg += "] in array when reading PVL";
1458  throw IException(IException::Unknown, msg, _FILEINFO_);
1459  }
1460 
1461  // we're good with this element of the array, remember it
1462  keywordValues.push_back(keywordValue);
1463  }
1464 
1465  if (!closedProperly) {
1466  return false;
1467  }
1468 
1469  // Trim off the closing paren
1470  if (!keyword.isEmpty()) {
1471  keyword = keyword.mid(1).trimmed();
1472  }
1473 
1474  // Read units here if they exist and apply them
1475  // case: (A,B,C) <unit>
1476  if (!keyword.isEmpty() && keyword[0] == '<') {
1477  QString units = readValue(keyword, explicitIncomplete);
1478  for (unsigned int val = 0; val < keywordValues.size(); val++) {
1479  if (keywordValues[val].second.isEmpty()) {
1480  keywordValues[val].second = units;
1481  }
1482  }
1483  }
1484  }
1485  else {
1486  /*
1487  Step 3.3
1488 
1489  Our format is confirmed as:
1490  "KEYWORD = VALUE <UNIT>"
1491 
1492  We need to read the single value/unit in the keywordValues array.
1493  */
1494  pair<QString, QString> keywordValue;
1495  keywordValue.first = readValue(keyword, explicitIncomplete);
1496 
1497  if (!keyword.isEmpty() && keyword[0] == '<') {
1498  keywordValue.second = readValue(keyword, explicitIncomplete);
1499  }
1500 
1501  keywordValues.push_back(keywordValue);
1502  }
1503 
1504  /*
1505  This is set when a quote is opened somewhere and never closed, it means
1506  we need more information
1507  */
1508  if (explicitIncomplete) {
1509  return false; // unclosed quote at end... need more information
1510  }
1511 
1512  /*
1513  See if we have a comment at the end of the keyword...
1514  */
1515  if (!keyword.isEmpty()) {
1516  // if the data left is a comment, we can handle it probably
1517  if (keyword[0] == '#' ||
1518  ((keyword.size() > 1 && keyword[0] == '/') &&
1519  (keyword[1] == '/' || keyword[1] == '*'))) {
1520  keywordComments.push_back(keyword);
1521 
1522  if (keyword.size() > 1 && keyword.mid(0, 2) == "/*") {
1523  if (keyword.mid(keyword.size() - 2, 2) != "*/")
1524  return false; // need more comment data
1525  }
1526 
1527  keyword = "";
1528  }
1529  }
1530 
1531  /*
1532  If more data remains, it is unrecognized.
1533  */
1534  if (!keyword.isEmpty()) {
1535  QString msg = "Keyword has extraneous data [";
1536  msg += keyword;
1537  msg += "] at the end";
1538  throw IException(IException::Unknown, msg, _FILEINFO_);
1539  }
1540 
1541  // We've parsed this keyword! :)
1542  return true;
1543  }
1544 
1545 
1546  QString PvlKeyword::readValue(QString &keyword, bool &quoteProblem) {
1547  std::vector< std::pair<char, char> > otherDelims;
1548 
1549  return readValue(keyword, quoteProblem, otherDelims);
1550  }
1551 
1568  QString PvlKeyword::readValue(QString &keyword, bool &quoteProblem,
1569  const std::vector< std::pair<char, char> > &
1570  otherDelimiters) {
1571  QString value = "";
1572 
1573  // This method ignores spaces except as delimiters; let's trim the QString
1574  // to start
1575  keyword = keyword.trimmed();
1576 
1577  if (keyword.isEmpty()) {
1578  return "";
1579  }
1580 
1581  // An implied quote is one that is started without a special character, for
1582  // example HELLO WORLD has HELLO and WORLD separately as implied quotes for
1583  // PVLs. However, "HELLO WORLD" has an explicit (not implied) double quote.
1584  // We do consider <> as quotes.
1585  bool impliedQuote = true;
1586  QChar quoteEnd = ' ';
1587  bool keepQuotes = false;
1588 
1589  if (keyword[0] == '\'' || keyword[0] == '"') {
1590  quoteEnd = keyword[0];
1591  impliedQuote = false;
1592  }
1593  else if (keyword[0] == '<') {
1594  quoteEnd = '>';
1595  impliedQuote = false;
1596  }
1597  else {
1598  // we're not quoted, look for alternative delimiters.
1599  char implicitQuotes[] = {
1600  ')',
1601  '}',
1602  ',',
1603  ' ',
1604  '\t',
1605  '<',
1606  '='
1607  };
1608 
1609  bool foundImplicitQuote = false;
1610 
1611  int currentPos = 0;
1612  while(!foundImplicitQuote && currentPos != keyword.size()) {
1613  for (unsigned int quote = 0;
1614  quote < sizeof(implicitQuotes) / sizeof(char);
1615  quote ++) {
1616  if (keyword[currentPos] == implicitQuotes[quote]) {
1617  quoteEnd = implicitQuotes[quote];
1618  foundImplicitQuote = true;
1619  }
1620  }
1621 
1622  if (!foundImplicitQuote) {
1623  currentPos ++;
1624  }
1625  }
1626  }
1627 
1628  for (unsigned int delim = 0; delim < otherDelimiters.size(); delim ++) {
1629  if (keyword[0] == otherDelimiters[delim].first) {
1630  quoteEnd = otherDelimiters[delim].second;
1631  keepQuotes = true;
1632  impliedQuote = false;
1633  }
1634  }
1635 
1636  QString startQuote;
1637  // non-implied delimeters need the opening delimiter ignored. Remember
1638  // startQuote in case of error later on we can reconstruct the original
1639  // QString.
1640  if (!impliedQuote) {
1641  startQuote += keyword[0];
1642  keyword = keyword.mid(1);
1643  }
1644 
1645  // Do we have a known quote end?
1646  int quoteEndPos = keyword.indexOf(quoteEnd);
1647 
1648  if (quoteEndPos != -1) {
1649  value = keyword.mid(0, quoteEndPos);
1650 
1651  // Trim keyword 1 past end delimiter (if end delimiter is last, this
1652  // results in empty QString). If the close delimiter is ')' or ',' then
1653  // leave it be however, since we need to preserve that to denote this was
1654  // an end of a valuein array keyword.
1655  if (!impliedQuote) {
1656  keyword = keyword.mid(quoteEndPos + 1);
1657  }
1658  else {
1659  keyword = keyword.mid(quoteEndPos);
1660  }
1661 
1662  // Make sure we dont have padding
1663  keyword = keyword.trimmed();
1664 
1665  if (keepQuotes) {
1666  value = startQuote + value + quoteEnd;
1667  }
1668 
1669  return value;
1670  }
1671  // implied quotes terminate at end of keyword; otherwise we have a problem
1672  // (which is this condition)
1673  else if (!impliedQuote) {
1674  // restore the original QString
1675  keyword = startQuote + keyword;
1676  quoteProblem = true;
1677 
1678  return "";
1679  }
1680 
1681  // we have an implied quote but no quote character, the rest must be the
1682  // value.
1683  value = keyword;
1684  keyword = "";
1685 
1686  return value;
1687  }
1688 
1689 
1703  QString PvlKeyword::readLine(std::istream &is, bool insideComment) {
1704  QString lineOfData;
1705 
1706  while(is.good() && lineOfData.isEmpty()) {
1707 
1708  // read until \n (works for both \r\n and \n) or */
1709  while(is.good() &&
1710  (!lineOfData.size() || lineOfData[lineOfData.size() - 1] != '\n')) {
1711  char next = is.get();
1712 
1713  // if non-ascii found then we're done... immediately
1714  if (next <= 0) {
1715  is.seekg(0, ios::end);
1716  is.get();
1717  return lineOfData;
1718  }
1719 
1720  // if any errors (i.e. eof) happen in the get operation then don't
1721  // store this data
1722  if (is.good()) {
1723  lineOfData += next;
1724  }
1725 
1726  if (insideComment &&
1727  lineOfData.size() >= 2 && lineOfData[lineOfData.size() - 2] == '*' &&
1728  lineOfData[lineOfData.size() - 1] == '/') {
1729  // End of multi-line comment = end of line!
1730  break;
1731  }
1732  else if (lineOfData.size() >= 2 &&
1733  lineOfData[lineOfData.size() - 2] == '/' &&
1734  lineOfData[lineOfData.size() - 1] == '*') {
1735  insideComment = true;
1736  }
1737  }
1738 
1739  // Trim off non-visible characters from this line of data
1740  lineOfData = lineOfData.trimmed();
1741 
1742  // read up to next non-whitespace in input stream
1743  while(is.good() &&
1744  (is.peek() == ' ' ||
1745  is.peek() == '\r' ||
1746  is.peek() == '\n')) {
1747  is.get();
1748  }
1749 
1750  // if lineOfData is empty (line was empty), we repeat
1751  }
1752 
1753  return lineOfData;
1754  }
1755 
1756 
1765  ostream &operator<<(std::ostream &os, const Isis::PvlKeyword &keyword) {
1766  // Set up a Formatter
1767  PvlFormat *tempFormat = keyword.m_formatter;
1768  bool removeFormatter = false;
1769  if (tempFormat == NULL) {
1770  tempFormat = new PvlFormat();
1771  removeFormatter = true;
1772  }
1773 
1774  // Write out the comments
1775  for (int i = 0; i < keyword.comments(); i++) {
1776  for (int j = 0; j < keyword.indent(); j++) os << " ";
1777  os << keyword.comment(i) << tempFormat->formatEOL();
1778  }
1779 
1780  // Write the keyword name & add length to startColumn.
1781  int startColumn = 0;
1782  for (int i = 0; i < keyword.indent(); i++) {
1783  os << " ";
1784  ++startColumn;
1785  }
1786  QString keyname = tempFormat->formatName(keyword);
1787  os << keyname;
1788  startColumn += keyname.length();
1789 
1790  // Add padding and then write equal sign.
1791  for (int i = 0; i < keyword.width() - (int)keyname.size(); ++i) {
1792  os << " ";
1793  ++startColumn;
1794  }
1795  os << " = ";
1796  startColumn += 3;
1797 
1798  // If it has no value then write a NULL
1799  if (keyword.size() == 0) {
1800  os << tempFormat->formatValue(keyword);
1801  }
1802 
1803  // Loop and write each array value
1804  QString stringToWrite;
1805  for (int i = 0; i < keyword.size(); i ++) {
1806  stringToWrite += tempFormat->formatValue(keyword, i);
1807  }
1808 
1809  keyword.writeWithWrap(os,
1810  stringToWrite,
1811  startColumn,
1812  *tempFormat);
1813 
1814  if (removeFormatter) delete tempFormat;
1815 
1816  return os;
1817  }
1818 
1819 
1821  const PvlKeyword &PvlKeyword::operator=(const PvlKeyword &other) {
1822  if (this != &other) {
1823  m_formatter = other.m_formatter;
1824 
1825  if (m_name) {
1826  delete [] m_name;
1827  m_name = NULL;
1828  }
1829 
1830  if (other.m_name) {
1831  setName(other.m_name);
1832  }
1833 
1834  m_values = other.m_values;
1835 
1836  if (m_units) {
1837  delete m_units;
1838  m_units = NULL;
1839  }
1840 
1841  if (other.m_units) {
1842  m_units = new std::vector<QString>(*other.m_units);
1843  }
1844 
1845  if (m_comments) {
1846  delete m_comments;
1847  m_comments = NULL;
1848  }
1849 
1850  if (other.m_comments) {
1851  m_comments = new std::vector<QString>(*other.m_comments);
1852  }
1853 
1854  m_width = other.m_width;
1855  m_indent = other.m_indent;
1856  }
1857 
1858  return *this;
1859  }
1860 
1873  void PvlKeyword::validateKeyword(PvlKeyword & pvlKwrd, QString psValueType, PvlKeyword* pvlKwrdValue)
1874  {
1875  int iSize = pvlKwrd.size();
1876 
1877  QString sType = m_values[0].toLower();
1878 
1879  // Value type
1880  if (psValueType.length()) {
1881  psValueType = psValueType.toLower();
1882  }
1883 
1884  double dRangeMin=0, dRangeMax=0;
1885  bool bRange = false;
1886  bool bValue = false;
1887  if (pvlKwrdValue != NULL) {
1888  QString sValueName = pvlKwrdValue->name();
1889 
1890  // Check for Range
1891  if (sValueName.contains("__Range")) {
1892  dRangeMin = toDouble((*pvlKwrdValue)[0]);
1893  dRangeMax = toDouble((*pvlKwrdValue)[1]);
1894  bRange = true;
1895  }
1896  else if (sValueName.contains("__Value")) {
1897  bValue = true;
1898  }
1899  }
1900 
1901  // Type integer
1902  if (sType == "integer") {
1903  for (int i=0; i<iSize; i++) {
1904  QString sValue = pvlKwrd[i].toLower();
1905  if (sValue != "null"){
1906  int iValue=0;
1907  try {
1908  iValue = toInt(sValue);
1909  } catch (IException & e) {
1910  QString sErrMsg = "\"" +pvlKwrd.name() +"\" expects an Integer value";
1911  throw IException(e, IException::User, sErrMsg, _FILEINFO_);
1912  }
1913  if (bRange && (iValue < dRangeMin || iValue > dRangeMax)) {
1914  QString sErrMsg = "\"" +pvlKwrd.name() +"\" is not in the specified Range";
1915  throw IException(IException::User, sErrMsg, _FILEINFO_);
1916  }
1917  if (bValue) {
1918  bool bFound = false;
1919  for (int j=0; j<pvlKwrdValue->size(); j++) {
1920  if (iValue == toInt((*pvlKwrdValue)[j])) {
1921  bFound = true;
1922  break;
1923  }
1924  }
1925  if (!bFound) {
1926  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has value not in the accepted list";
1927  throw IException(IException::User, sErrMsg, _FILEINFO_);
1928  }
1929  }
1930  // Type is specified (positive / negative)
1931  if (psValueType.length()) {
1932  if ((psValueType == "positive" && iValue < 0) || (psValueType == "negative" && iValue >= 0) ) {
1933  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has invalid value";
1934  throw IException(IException::User, sErrMsg, _FILEINFO_);
1935  }
1936  }
1937  }
1938  }
1939  return;
1940  }
1941 
1942  // Type double
1943  if (sType == "double") {
1944  for (int i=0; i<iSize; i++) {
1945  QString sValue = pvlKwrd[i].toLower();
1946  if (sValue != "null"){
1947  double dValue = toDouble(sValue);
1948  if (bRange && (dValue < dRangeMin || dValue > dRangeMax)) {
1949  QString sErrMsg = "\"" +pvlKwrd.name() +"\" is not in the specified Range";
1950  throw IException(IException::User, sErrMsg, _FILEINFO_);
1951  }
1952  if (bValue) {
1953  bool bFound = false;
1954  for (int j=0; j<pvlKwrdValue->size(); j++) {
1955  if (dValue == toDouble((*pvlKwrdValue)[j])) {
1956  bFound = true;
1957  break;
1958  }
1959  }
1960  if (!bFound) {
1961  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has value not in the accepted list";
1962  throw IException(IException::User, sErrMsg, _FILEINFO_);
1963  }
1964  }
1965  // Type is specified (positive / negative)
1966  if (psValueType.length()) {
1967  if ((psValueType == "positive" && dValue < 0) || (psValueType == "negative" && dValue >= 0) ) {
1968  QString sErrMsg = "\"" +pvlKwrd.name() +"\" has invalid value";
1969  throw IException(IException::User, sErrMsg, _FILEINFO_);
1970  }
1971  }
1972  }
1973  }
1974  return;
1975  }
1976 
1977  // Type boolean
1978  if (sType == "boolean") {
1979  for (int i=0; i<iSize; i++) {
1980  QString sValue = pvlKwrd[i].toLower();
1981  if (sValue != "null" && sValue != "true" && sValue != "false"){
1982  QString sErrMsg = "Wrong Type of value in the Keyword \"" + name() + "\" \n";
1983  throw IException(IException::User, sErrMsg, _FILEINFO_);
1984  }
1985  }
1986  return;
1987  }
1988 
1989  // Type String
1990  if (sType == "string") {
1991  for (int i=0; i<iSize; i++) {
1992  QString sValue = pvlKwrd[i].toLower();
1993  if (bValue) {
1994  bool bValFound = false;
1995  for (int i=0; i<pvlKwrdValue->size(); i++) {
1996  if (sValue == (*pvlKwrdValue)[i].toLower()) {
1997  bValFound = true;
1998  break;
1999  }
2000  }
2001  if (!bValFound) {
2002  QString sErrMsg = "Wrong Type of value in the Keyword \"" + name() + "\" \n";
2003  throw IException(IException::User, sErrMsg, _FILEINFO_);
2004  }
2005  }
2006  }
2007  }
2008  }
2009 }
Isis::PvlKeyword::name
QString name() const
Returns the keyword name.
Definition: PvlKeyword.h:98
Isis::PvlObject::findGroup
PvlGroupIterator findGroup(const QString &name, PvlGroupIterator beg, PvlGroupIterator end)
Find a group with the specified name, within these indexes.
Definition: PvlObject.h:129
Isis::PvlKeyword::m_indent
int m_indent
The number of indentations to make.
Definition: PvlKeyword.h:289
Isis::PvlKeyword
A single keyword-value pair.
Definition: PvlKeyword.h:82
Isis::PvlFormat
Formats a Pvl name value pair to Isis standards.
Definition: PvlFormat.h:108
Isis::PvlKeyword::m_units
std::vector< QString > * m_units
The units for the values.
Definition: PvlKeyword.h:271
Isis::PvlKeyword::addComments
void addComments(const std::vector< QString > &comments)
This method adds multiple comments at once by calling AddComments on each element in the vector.
Definition: PvlKeyword.cpp:1102
Isis::PvlKeyword::indent
int indent() const
Returns the current indent level.
Definition: PvlKeyword.h:213
Isis::PvlKeyword::addValue
void addValue(QString value, QString unit="")
Adds a value with units.
Definition: PvlKeyword.cpp:252
Isis::IString::ConvertWhiteSpace
IString ConvertWhiteSpace()
Returns the string with all "new lines", "carriage returns", "tabs", "form feeds",...
Definition: IString.cpp:1238
Isis::PvlKeyword::m_comments
std::vector< QString > * m_comments
The comments for the keyword.
Definition: PvlKeyword.h:274
Isis::PvlKeyword::width
int width() const
Returns the current set longest keyword name.
Definition: PvlKeyword.h:208
Isis::PvlObject::Traverse
@ Traverse
Search child objects.
Definition: PvlObject.h:158
Isis::PvlKeyword::comments
int comments() const
Returns the number of lines of comments associated with this keyword.
Definition: PvlKeyword.h:160
Isis::IString::UpCase
IString UpCase()
Converst any lower case characters in the object IString with uppercase characters.
Definition: IString.cpp:617
Isis::PvlKeyword::setName
void setName(QString name)
Sets the keyword name.
Definition: PvlKeyword.cpp:120
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::PvlSequence::Size
int Size() const
Number of arrays in the sequence.
Definition: PvlSequence.h:70
Isis::toInt
int toInt(const QString &string)
Global function to convert from a string to an integer.
Definition: IString.cpp:93
Isis::PvlKeyword::m_name
char * m_name
The keyword's name... This is a c-string for memory efficiency.
Definition: PvlKeyword.h:258
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::PvlKeyword::m_formatter
PvlFormat * m_formatter
Formatter object.
Definition: PvlKeyword.h:254
Isis::PvlKeyword::writeWithWrap
std::ostream & writeWithWrap(std::ostream &os, const QString &textToWrite, int startColumn, PvlFormat &format) const
Wraps output so that length doesn't exceed the character limit.
Definition: PvlKeyword.cpp:611
Isis::PvlKeyword::m_values
QVarLengthArray< QString, 1 > m_values
The values in the keyword.
Definition: PvlKeyword.h:268
Isis::toDouble
double toDouble(const QString &string)
Global function to convert from a string to a double.
Definition: IString.cpp:149
std
Namespace for the standard library.
Isis::IString::Token
IString Token(const IString &separator)
Returns the first token in the IString.
Definition: IString.cpp:897
Isis::PvlKeyword::size
int size() const
Returns the number of values stored in this keyword.
Definition: PvlKeyword.h:125
Isis::IString
Adds specific functionality to C++ strings.
Definition: IString.h:165
Isis::PvlSequence
Parse and return elements of a Pvl sequence.
Definition: PvlSequence.h:46
Isis::PvlKeyword::comment
QString comment(const int index) const
Return a comment at the specified index.
Definition: PvlKeyword.cpp:441
Isis::PvlKeyword::m_width
int m_width
The width of the longest keyword.
Definition: PvlKeyword.h:284
Isis::operator>>
std::istream & operator>>(std::istream &is, CSVReader &csv)
Input read operator for input stream sources.
Definition: CSVReader.cpp:447
Isis::IString::Remove
IString Remove(const std::string &del)
Remove all instances of any character in the string from the IString.
Definition: IString.cpp:1266
Isis::PvlFormat::charLimit
unsigned int charLimit() const
Retrieves the maximum number of characters in a keyword value that can be printed to a line before it...
Definition: PvlFormat.h:138
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16