Isis 3 Programmer Reference
Pvl.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7#include "Pvl.h"
8#include "PvlGroup.h"
9#include "PvlKeyword.h"
10
11#include <locale>
12#include <fstream>
13#include <sstream>
14
15#include "FileName.h"
16#include "IException.h"
17#include "Message.h"
18#include "PvlTokenizer.h"
19#include "PvlFormat.h"
20
21using namespace std;
22namespace Isis {
24 Pvl::Pvl() : Isis::PvlObject("Root") {
25 init();
26 }
27
28
34 Pvl::Pvl(const QString &file) : Isis::PvlObject("Root") {
35 init();
36 read(file);
37 }
38
39
41 Pvl::Pvl(const Pvl &other) : PvlObject::PvlObject(other) {
42 m_internalTemplate = false;
43 m_terminator = other.m_terminator;
44 }
45
46
48 void Pvl::init() {
49 m_filename = "";
50 m_terminator = "End";
51 m_internalTemplate = false;
52 }
53
54
60 void Pvl::fromString(const std::string &str) {
61 stringstream stm;
62 stm << str;
63 if(!stm) {
64 QString message = "Failed to use string: " + QString::fromStdString(str) + "to create Pvl";
65 throw IException(IException::Io, message, _FILEINFO_);
66 }
67
68 // Read it
69 try {
70 stm >> *this;
71 }
72 catch(IException &e) {
73 QString message = "Unable to create PVL from string: " + QString::fromStdString(str);
74 throw IException(e, IException::Unknown, message, _FILEINFO_);
75 }
76 catch(...) {
77 QString message = "Unable to create PVL from string: " + QString::fromStdString(str);
78 throw IException(IException::Unknown, message, _FILEINFO_);
79 }
80 }
81
82
90 void Pvl::read(const QString &file) {
91 // Expand the filename
92 Isis::FileName temp(file);
93 m_filename = temp.expanded();
94
95 // Open the file
96 ifstream istm;
97 istm.open(m_filename.toLatin1().data(), std::ios::in);
98 if(!istm) {
99 QString message = Message::FileOpen(temp.expanded());
100 throw IException(IException::Io, message, _FILEINFO_);
101 }
102
103 // Read it
104 try {
105 istm >> *this;
106 }
107 catch(IException &e) {
108 istm.close();
109 QString message = "Unable to read PVL file [" + temp.expanded() + "]";
110 throw IException(e, IException::Unknown, message, _FILEINFO_);
111 }
112 catch(...) {
113 istm.close();
114 QString message = "Unable to read PVL file [" + temp.expanded() + "]";
115 throw IException(IException::Unknown, message, _FILEINFO_);
116 }
117 istm.close();
118 }
119
120
130 void Pvl::write(const QString &file) {
131 // Expand the filename
132 Isis::FileName temp(file);
133
134 // Set up a Formatter
135 bool removeFormatter = false;
136 if(format() == NULL) {
137 setFormat(new PvlFormat());
138 removeFormatter = true;
139 }
140
141 // Open the file
142 ofstream ostm;
143 QString tempName(temp.expanded());
144 ostm.open(tempName.toLatin1().data(), std::ios::out);
145 ostm.seekp(0, std::ios::beg);
146 if(!ostm) {
147 QString message = Isis::Message::FileCreate(temp.expanded());
148 throw IException(IException::Io, message, _FILEINFO_);
149 }
150
151 // Write the labels
152 try {
153 ostm << *this;
154 if(terminator() != "") ostm << format()->formatEOL();
155 }
156 catch(IException &e) {
157 ostm.close();
158 QString message = "Unable to write PVL to file [" + temp.expanded() + "]";
159 throw IException(e, IException::Io, message, _FILEINFO_);
160 }
161 catch(...) {
162 ostm.close();
163 QString message = "Unable to write PVL to file [" + temp.expanded() + "]";
164 throw IException(IException::Io, message, _FILEINFO_);
165 }
166
167 if(removeFormatter) {
168 delete format();
169 setFormat(NULL);
170 }
171
172 // Close the file
173 ostm.close();
174 }
175
176
184 void Pvl::append(const QString &file) {
185 // Set up for opening and writing
186 Isis::FileName temp(file);
187
188 // Set up a Formatter
189 bool removeFormatter = false;
190 if(format() == NULL) {
191 setFormat(new PvlFormat());
192 removeFormatter = true;
193 }
194
195 // Open the file
196 ofstream ostm;
197 QString tempName(temp.expanded());
198 ostm.open(tempName.toLatin1().data(), std::ios::app);
199 ostm.seekp(0, std::ios::end);
200 if(!ostm) {
201 QString message = Message::FileOpen(temp.expanded());
202 throw IException(IException::Io, message, _FILEINFO_);
203 }
204
205 // Write the labels
206 try {
207 ostm << *this;
208 if(terminator() != "") ostm << format()->formatEOL();
209 }
210 catch(...) {
211 ostm.close();
212 QString message = "Unable to append PVL infomation to file [" +
213 temp.expanded() + "]";
214 throw IException(IException::Io, message, _FILEINFO_);
215 }
216
217 if(removeFormatter) {
218 delete format();
219 setFormat(NULL);
220 }
221
222 // Close the file
223 ostm.close();
224 }
225
226
227 void Pvl::setFormatTemplate(Isis::Pvl &temp) {
228 if(m_internalTemplate) delete m_formatTemplate;
229 m_internalTemplate = false;
230 Isis::PvlObject::setFormatTemplate(temp);
231 }
232
233
234 void Pvl::setFormatTemplate(const QString &file) {
235 if(m_internalTemplate) delete m_formatTemplate;
236 m_internalTemplate = true;
237 m_formatTemplate = new Isis::Pvl(file);
238 }
239
240
249 ostream &operator<<(std::ostream &os, Pvl &pvl) {
250 // Set up a Formatter
251 bool removeFormatter = false;
252 if(pvl.format() == NULL) {
253 pvl.setFormat(new PvlFormat());
254 removeFormatter = true;
255 }
256
257 Isis::Pvl outTemplate;
258 if(pvl.hasFormatTemplate()) outTemplate = *(Isis::Pvl *)pvl.formatTemplate();
259
260 // Look for and process all include files and remove duplicates from the
261 // format template. Include files take precedence over all other objects and
262 // groups
263 Isis::Pvl newTemp;
264 for(int i = 0; i < outTemplate.keywords(); i++) {
265 if(outTemplate[i].isNamed("Isis:PvlTemplate:File")) {
266 QString filename = outTemplate[i];
267 Isis::FileName file(filename);
268 if(!file.fileExists()) {
269 QString message = "Could not open the template file [" + filename + "]";
270 throw IException(IException::Io, message, _FILEINFO_);
271 }
272 Isis::Pvl include(file.expanded());
273
274 for(int j = 0; j < include.keywords(); j++) {
275 if(!newTemp.hasKeyword(include[j].name()))
276 newTemp.addKeyword(include[j]);
277 }
278
279 for(int j = 0; j < include.objects(); j++) {
280 if(!newTemp.hasObject(include.object(j).name()))
281 newTemp.addObject(include.object(j));
282 }
283
284 for(int j = 0; j < include.groups(); j++) {
285 if(!newTemp.hasGroup(include.group(j).name()))
286 newTemp.addGroup(include.group(j));
287 }
288 }
289 // If it is not an include file add it in place
290 else if(!newTemp.hasKeyword(outTemplate[i].name()))
291 newTemp.addKeyword(outTemplate[i]);
292 }
293
294 // copy over the objects
295 for(int i = 0; i < outTemplate.objects(); i++) {
296 if(!newTemp.hasObject(outTemplate.object(i).name()))
297 newTemp.addObject(outTemplate.object(i));
298 }
299
300 // copy over the groups
301 for(int i = 0; i < outTemplate.groups(); i++) {
302 if(!newTemp.hasGroup(outTemplate.group(i).name()))
303 newTemp.addGroup(outTemplate.group(i));
304 }
305
306 outTemplate = newTemp;
307
308 // Output the pvl's comments
309 for(int i = 0; i < pvl.comments(); i++) {
310 os << pvl.comment(i) << pvl.format()->formatEOL();
311 if(i == (pvl.comments() - 1)) os << pvl.format()->formatEOL();
312 }
313
314 // Output the keywords
315 if(pvl.keywords() > 0) {
316 os << (Isis::PvlContainer &) pvl << pvl.format()->formatEOL();
317 }
318
319 // this number keeps track of the number of objects that have been written
320 int numObjects = 0;
321
322 // Output the objects using the format template
323 for(int i = 0; i < outTemplate.objects(); i++) {
324 for(int j = 0; j < pvl.objects(); j++) {
325 if(outTemplate.object(i).name() != pvl.object(j).name()) continue;
326 if(numObjects == 0 && pvl.keywords() > 0) os << pvl.format()->formatEOL();
327 pvl.object(j).setIndent(pvl.indent());
328 pvl.object(j).setFormatTemplate(outTemplate.object(i));
329 pvl.object(j).setFormat(pvl.format());
330 os << pvl.object(j) << pvl.format()->formatEOL();
331 pvl.object(j).setFormat(NULL);
332 pvl.object(j).setIndent(0);
333 if(++numObjects < pvl.objects()) os << pvl.format()->formatEOL();
334 }
335 }
336
337 // Output the objects that were not included in the format template pvl
338 for(int i = 0; i < pvl.objects(); i++) {
339 if(outTemplate.hasObject(pvl.object(i).name())) continue;
340 if(numObjects == 0 && pvl.keywords() > 0) os << pvl.format()->formatEOL();
341 pvl.object(i).setIndent(pvl.indent());
342 pvl.object(i).setFormat(pvl.format());
343 os << pvl.object(i) << pvl.format()->formatEOL();
344 pvl.object(i).setFormat(NULL);
345 pvl.object(i).setIndent(0);
346 if(++numObjects < pvl.objects()) os << pvl.format()->formatEOL();
347 }
348
349 // this number keeps track of the number of groups that have been written
350 int numGroups = 0;
351
352 // Output the groups using the format template
353 for(int i = 0; i < outTemplate.groups(); i++) {
354 for(int j = 0; j < pvl.groups(); j++) {
355 if(outTemplate.group(i).name() != pvl.group(j).name()) continue;
356 if((numGroups == 0) &&
357 (pvl.objects() > 0 || pvl.keywords() > 0)) os << pvl.format()->formatEOL();
358 pvl.group(j).setIndent(pvl.indent());
359 pvl.group(j).setFormatTemplate(outTemplate.group(i));
360 pvl.group(j).setFormat(pvl.format());
361 os << pvl.group(j) << pvl.format()->formatEOL();
362 pvl.group(j).setFormat(NULL);
363 pvl.group(j).setIndent(0);
364 if(++numGroups < pvl.groups()) os << pvl.format()->formatEOL();
365 }
366 }
367
368 // Output the groups that were not in the format template
369 for(int i = 0; i < pvl.groups(); i++) {
370 if(outTemplate.hasGroup(pvl.group(i).name())) continue;
371 if((numGroups == 0) &&
372 (pvl.objects() > 0 || pvl.keywords() > 0)) os << pvl.format()->formatEOL();
373 pvl.group(i).setIndent(pvl.indent());
374 pvl.group(i).setFormat(pvl.format());
375 os << pvl.group(i) << pvl.format()->formatEOL();
376 pvl.group(i).setFormat(NULL);
377 pvl.group(i).setIndent(0);
378 if(++numGroups < pvl.groups()) os << pvl.format()->formatEOL();
379 }
380
381 // Output the terminator
382 if(pvl.terminator() != "") {
383 os << pvl.terminator();
384 }
385
386 if(removeFormatter) {
387 delete pvl.format();
388 pvl.setFormat(NULL);
389 }
390
391 return os;
392 }
393
394
403 istream &operator>>(std::istream &is, Pvl &pvl) {
404 if(!is.good()) {
405 string msg = "Tried to read input stream with an error state into a Pvl";
406 throw IException(IException::Programmer, msg, _FILEINFO_);
407 }
408
409 try {
410 PvlKeyword termination("End");
411
412 PvlKeyword errorKeywords[] = {
413 PvlKeyword("EndGroup"),
414 PvlKeyword("EndObject")
415 };
416
417 PvlKeyword readKeyword;
418 istream::pos_type beforeKeywordPos = is.tellg();
419
420 is >> readKeyword;
421
422 while(readKeyword != termination) {
423 for(unsigned int errorKey = 0;
424 errorKey < sizeof(errorKeywords) / sizeof(PvlKeyword);
425 errorKey++) {
426 if(readKeyword == errorKeywords[errorKey]) {
427 is.seekg(beforeKeywordPos, ios::beg);
428
429 QString msg = "Unexpected [";
430 msg += readKeyword.name();
431 msg += "] in PVL Object [ROOT]";
432 throw IException(IException::Unknown, msg, _FILEINFO_);
433 }
434 }
435
436 if(readKeyword == PvlKeyword("Group")) {
437 is.seekg(beforeKeywordPos);
438 PvlGroup newGroup;
439 is >> newGroup;
440 pvl.addGroup(newGroup);
441 }
442 else if(readKeyword == PvlKeyword("Object")) {
443 is.seekg(beforeKeywordPos);
444 PvlObject newObject;
445 is >> newObject;
446 pvl.addObject(newObject);
447 }
448 else {
449 pvl.addKeyword(readKeyword);
450 }
451
452 readKeyword = PvlKeyword();
453 beforeKeywordPos = is.tellg();
454
455 // non-whitespace non-ascii says we're done
456 if(is.good() && (is.peek() < 32 || is.peek() > 126)) {
457 // fake eof (binary data)
458 break;
459 }
460
461 if(is.good()) {
462 is >> readKeyword;
463 }
464 else {
465 // eof
466 break;
467 }
468 }
469
470 return is;
471 }
472 catch(IException &e) {
473 if(is.eof() && !is.bad()) {
474 is.clear();
475 is.unget();
476 }
477
478 istream::pos_type errorPos = is.tellg();
479 if((int)errorPos == -1) throw;
480
481 is.seekg(0, ios::beg);
482 long lineNumber = 1;
483
484 if((int)is.tellg() == -1) throw;
485
486 while(is.good() && is.tellg() < errorPos) {
487 char next = is.get();
488
489 if(!isprint(next) && !isspace(next)) {
490 is.seekg(errorPos, ios::beg);
491 }
492 else if(next == '\n') {
493 lineNumber ++;
494 }
495 }
496
497 string msg;
498 if(lineNumber > 0) {
499 msg = "Error in PVL file on line [";
500 msg += IString((Isis::BigInt)lineNumber);
501 msg += "]";
502 }
503
504 throw IException(e, IException::Unknown, msg, _FILEINFO_);
505 }
506 }
507
508
510 const Pvl &Pvl::operator=(const Pvl &other) {
511 this->PvlObject::operator=(other);
512
513 m_internalTemplate = other.m_internalTemplate;
514 m_terminator = other.m_terminator;
515
516 return *this;
517 }
518
519
529 void Pvl::validatePvl(const Pvl & pPvl, Pvl & pPvlResults)
530 {
531 pPvlResults=Pvl(pPvl);
532
533 // Validate Objects
534 int iTmplObjSize = objects();
535
536 for(int i=0; i<iTmplObjSize; i++) {
537 PvlObject & pvlTmplObj = object(i);
538
539 QString sObjName = pvlTmplObj.name();
540 bool bObjFound = false;
541
542 // Pvl contains the Object Name
543 if(pPvl.hasObject(sObjName)) {
544 PvlObject & pvlObj = pPvlResults.findObject(sObjName);
545 pvlTmplObj.validateObject(pvlObj);
546 if(pvlObj.objects()==0 && pvlObj.groups()==0 && pvlObj.keywords()==0) {
547 pPvlResults.deleteObject(sObjName);
548 }
549 bObjFound = true;
550 }
551 else {
552 QString sOption = sObjName + "__Required";
553 bObjFound = true; // optional is the default
554 if(pvlTmplObj.hasKeyword(sOption)) {
555 PvlKeyword pvlKeyOption = pvlTmplObj.findKeyword(sOption);
556 if(pvlKeyOption[0] == "true") { // Required is true
557 bObjFound = false;
558 }
559 }
560 }
561 if (bObjFound == false) {
562 QString sErrMsg = "Object \"" + sObjName + "\" Not Found in the Template File\n";
563 throw IException(IException::User, sErrMsg, _FILEINFO_);
564 }
565 }
566
567 // Validate Groups
568 int iTmplGrpSize = groups();
569 for(int i=0; i<iTmplGrpSize; i++) {
570 PvlGroup & pvlTmplGrp = group(i);
571
572 QString sGrpName = pvlTmplGrp.name();
573 bool bGrpFound = false;
574
575 // Pvl contains the Object Name
576 if(pPvl.hasGroup(sGrpName)) {
577 PvlGroup & pvlGrp = pPvlResults.findGroup(sGrpName);
578 pvlTmplGrp.validateGroup(pvlGrp);
579 if(pvlGrp.keywords()==0) {
580 pPvlResults.deleteGroup(sGrpName);
581 }
582 bGrpFound = true;
583 }
584 else {
585 bGrpFound = true;
586 QString sOption = sGrpName + "__Required";
587 if(pvlTmplGrp.hasKeyword(sOption)) {
588 PvlKeyword pvlKeyOption = pvlTmplGrp.findKeyword(sOption);
589 if(pvlKeyOption[0] == "true") { // Required is true
590 bGrpFound = false;
591 }
592 }
593 }
594 if (bGrpFound == false) {
595 QString sErrMsg = "Group \"" + sGrpName + "\" Not Found in the Template File\n";
596 throw IException(IException::User, sErrMsg, _FILEINFO_);
597 }
598 }
599
600 // Validate all the Keywords
601 validateAllKeywords((PvlContainer &)pPvlResults);
602 }
603
604} //end namespace isis
File name manipulation and expansion.
Definition FileName.h:100
Isis exception class.
Definition IException.h:91
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition IException.h:118
@ User
A type of error that could only have occurred due to a mistake on the user's part (e....
Definition IException.h:126
@ Programmer
This error is for when a programmer made an API call that was illegal.
Definition IException.h:146
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition IException.h:155
Adds specific functionality to C++ strings.
Definition IString.h:165
Contains more than one keyword-value pair.
QString m_filename
This contains the filename used to initialize the pvl object.
int keywords() const
Returns the number of keywords contained in the PvlContainer.
QString name() const
Returns the container name.
void validateAllKeywords(PvlContainer &pPvlCont)
Validate All the Keywords in a Container comparing with the Template.
Formats a Pvl name value pair to Isis standards.
Definition PvlFormat.h:108
Contains multiple PvlContainers.
Definition PvlGroup.h:41
void validateGroup(PvlGroup &pPvlGrp)
Validate a Group comparing with the Template Group.
Definition PvlGroup.cpp:207
Container for cube-like labels.
Definition Pvl.h:119
void fromString(const std::string &str)
Load PVL information from a string.
Definition Pvl.cpp:60
void init()
initializes the class
Definition Pvl.cpp:48
void validatePvl(const Pvl &pPvl, Pvl &pPvlResults)
Validate a Pvl with the Template Pvl.
Definition Pvl.cpp:529
Pvl()
Constructs an empty Pvl object.
Definition Pvl.cpp:24
void write(const QString &file)
Opens and writes PVL information to a file and handles the end of line sequence.
Definition Pvl.cpp:130
const Pvl & operator=(const Pvl &other)
This is an assignment operator.
Definition Pvl.cpp:510
void append(const QString &file)
Appends PVL information to a file and handles the end of line sequence.
Definition Pvl.cpp:184
void read(const QString &file)
Loads PVL information from a stream.
Definition Pvl.cpp:90
QString terminator() const
Returns the terminator used to signify the end of the PVL informationDefaults to "END".
Definition Pvl.h:153
QString m_terminator
Terminator used to signify the end of the PVL informationDefaults to "END".
Definition Pvl.h:168
A single keyword-value pair.
Definition PvlKeyword.h:87
QString name() const
Returns the keyword name.
Definition PvlKeyword.h:103
Contains Pvl Groups and Pvl Objects.
Definition PvlObject.h:61
const PvlObject & operator=(const PvlObject &other)
This is an assignment operator.
PvlGroup & group(const int index)
Return the group at the specified index.
int groups() const
Returns the number of groups contained.
Definition PvlObject.h:75
PvlObjectIterator findObject(const QString &name, PvlObjectIterator beg, PvlObjectIterator end)
Find the index of object with a specified name, between two indexes.
Definition PvlObject.h:276
int objects() const
Returns the number of objects.
Definition PvlObject.h:221
PvlObject & object(const int index)
Return the object at the specified index.
void addObject(const PvlObject &object)
Add a PvlObject.
Definition PvlObject.h:309
QString FileOpen(const QString &filename)
This error should be used when a file could not be opened.
Definition FileOpen.cpp:11
QString FileCreate(const QString &filename)
This error should be used when a file could not be created.
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
long long int BigInt
Big int.
Definition Constants.h:49
std::istream & operator>>(std::istream &is, CSVReader &csv)
Input read operator for input stream sources.
QDebug operator<<(QDebug debug, const Hillshade &hillshade)
Print this class out to a QDebug object.
Namespace for the standard library.