Isis 3 Programmer Reference
FileName.cpp
1
6/* SPDX-License-Identifier: CC0-1.0 */
7
8#include "FileName.h"
9
10#include <cmath>
11
12#include <QDate>
13#include <QDebug>
14#include <QDir>
15#include <QLocale>
16#include <QPair>
17#include <QString>
18#include <QTemporaryFile>
19
20#include "Preference.h"
21#include "IException.h"
22#include "IString.h"
23
24using namespace std;
25
26namespace Isis {
27
32 m_d = new Data;
33 }
34
40 FileName::FileName(const char *file) {
41 m_d = new Data;
42 m_d->setOriginal(file);
43 }
44
50 FileName::FileName(const QString &file) {
51 m_d = new Data;
52 m_d->setOriginal(file);
53 }
54
60 FileName::FileName(const FileName &other) : m_d(other.m_d) {
61 }
62
68
84 QString FileName::originalPath() const {
85 return QFileInfo(m_d->original(false)).path();
86 }
87
103 QString FileName::path() const {
104 return QFileInfo(expanded()).path();
105 }
106
121 QString FileName::attributes() const {
122 QString result;
123 QString fileNameWithAttribs = QFileInfo(m_d->original(true)).fileName();
124
125 int attribStartPos = fileNameWithAttribs.indexOf("+");
126
127 if (attribStartPos != -1)
128 result = fileNameWithAttribs.mid(attribStartPos + 1);
129
130 return result;
131 }
132
145 QString FileName::baseName() const {
146 return QFileInfo(m_d->original(false)).completeBaseName();
147 }
148
162 QString FileName::name() const {
163 return QFileInfo(m_d->original(false)).fileName();
164 }
165
178 QString FileName::extension() const {
179 return QFileInfo(m_d->original(false)).suffix();
180 }
181
196 QString FileName::expanded() const {
197 return m_d->expanded(false);
198 }
199
212 QString FileName::original() const {
213 return m_d->original(true);
214 }
215
225 FileName FileName::addExtension(const QString &newExtension) const {
226 FileName result = *this;
227
228 if (result.extension() != newExtension) {
229 QString attributesStr = result.attributes();
230
231 if (attributesStr == "")
232 result = FileName(result.originalPath() + "/" + result.name() + "." + newExtension);
233 else
234 result = FileName(result.originalPath() + "/" + result.name() + "." + newExtension
235 + "+" + attributesStr);
236 }
237
238 return result;
239 }
240
247 QString attributesStr = attributes();
248
249 FileName result;
250 if (attributesStr == "")
251 result = FileName(originalPath() + "/" + baseName());
252 else
253 result = FileName(originalPath() + "/" + baseName() + "+" + attributesStr);
254
255 return result;
256 }
257
265 FileName FileName::setExtension(const QString &newExtension) const {
266 FileName result = *this;
267
268 if (extension() != newExtension) {
269 result = result.removeExtension().addExtension(newExtension);
270 }
271
272 return result;
273 }
274
286
294 return FileName(expanded()).name().contains("?");
295 }
296
304 return FileName(expanded()).name().contains(QRegExp("\\{.*\\}"));
305 }
306
315
316 FileName result = *this;
317
318 if (!isVersioned()) {
320 QObject::tr("Asked for highest version of file named [%1] in [%2] but there "
321 "are no version sequences in the name")
322 .arg(name()).arg(originalPath()),
323 _FILEINFO_);
324 }
325
326 // Look for dates
327 if (isDateVersioned()) {
328 result = result.version(result.highestVersionDate());
329 }
330
331 // Look for number's
333 result = result.version(result.highestVersionNum());
334 }
335
336 return result;
337 }
338
350
351 FileName result = *this;
352
353 if (!isVersioned()) {
355 QObject::tr("Asked for new version of file named [%1] in [%2] but there "
356 "are no version sequences in the name")
357 .arg(name()).arg(originalPath()),
358 _FILEINFO_);
359 }
360
361 // Look for date
362 if (isDateVersioned()) {
363 result = result.version(QDate::currentDate());
364 }
365
366 // Look for #'s
368 try {
369 result = result.version(result.highestVersionNum() + 1);
370 }
371 catch (IException &) {
372 result = result.version(1);
373 }
374 }
375
376 if (result.fileExists()) {
378 QObject::tr("Could not generate unique new version of file named [%1] in "
379 "[%2] because the file [%3] exists")
380 .arg(name()).arg(originalPath()).arg(result.name()),
381 _FILEINFO_);
382
383 }
384
385 return result;
386 }
387
398 FileName FileName::version(long versionNumber) const {
399 QString file = FileName(expanded()).name();
400
401 int width = file.count("?");
402
403 if (versionNumber < 0) {
405 QObject::tr("FileName does not support negative version numbers in the file name, "
406 "tried to get version [%1] in file named [%2]")
407 .arg(versionNumber).arg(originalPath() + "/" + file),
408 _FILEINFO_);
409 }
410
411 if (versionNumber >= pow(10.0, width)) {
413 QObject::tr("FileName does not support version numbers greater than what would fit in "
414 "the file name, tried to get version [%1] in file named [%2]")
415 .arg(versionNumber).arg(originalPath() + "/" + file),
416 _FILEINFO_);
417 }
418
419 QPair<QString, QString> splitName = splitNameAroundVersionNum();
420 QString &before = splitName.first;
421 QString &after = splitName.second;
422
423 file = before + QString("%1").arg(QString::number(versionNumber), width, '0') + after;
424
425 return FileName(originalPath() + "/" + file);
426 }
427
437 FileName FileName::version(QDate versionDate) const {
438 QString newName = versionDate.toString(fileNameQDatePattern());
439
440 return FileName(originalPath() + "/" + newName);
441 }
442
449 bool FileName::fileExists() const {
450 return QFileInfo(expanded()).exists();
451 }
452
465 QDir FileName::dir() const {
466 return QFileInfo(expanded()).dir();
467 }
468
479 QString preppedFileName = QString("%1/%2XXXXXX.%3").arg(templateFileName.path())
480 .arg(templateFileName.baseName()).arg(templateFileName.extension());
481 QTemporaryFile tempFile(preppedFileName);
482 tempFile.setAutoRemove(false);
483
484 if (!tempFile.open()) {
486 QObject::tr("Could not create a unique temporary file name based on [%1]")
487 .arg(templateFileName.original()),
488 _FILEINFO_);
489 }
490
491 // We want to set the 'original' path as correctly as possible. So let's use the input original
492 // path with the output temp file's file name in our result.
493 FileName result;
494 QString newTempFileNameStr = templateFileName.originalPath() + "/" +
495 QFileInfo(tempFile.fileName()).fileName();
496 result = FileName(newTempFileNameStr);
497
498 return result;
499 }
500
515 QString FileName::toString() const {
516 return expanded();
517 }
518
528 m_d = rhs.m_d;
529 return *this;
530 }
531
541 QString expandedOfThis = expanded();
542 QString canonicalOfThis = QFileInfo(expandedOfThis).canonicalFilePath();
543
544 QString expandedOfRhs = rhs.expanded();
545 QString canonicalOfRhs = QFileInfo(expandedOfRhs).canonicalFilePath();
546
547 // Cononical file paths return empty strings if the file does not exist. Either both canonicals
548 // are valid and the same (equal is initialized to true), or neither canonical is valid but
549 // the expandeds are the same (equal is set to true when it isn't initialized to true).
550 bool equal = (!canonicalOfThis.isEmpty() && canonicalOfThis == canonicalOfRhs);
551
552 if (!equal) {
553 equal = (canonicalOfThis.isEmpty() && canonicalOfRhs.isEmpty() &&
554 expandedOfThis == expandedOfRhs);
555 }
556
557 return equal;
558 }
559
569 return !(*this == rhs);
570 }
571
579 QString fileQDatePattern = fileNameQDatePattern();
580
581 QPair<int, int> truncateRange(-1, -1);
582 if (fileQDatePattern.contains("?")) {
583 QString trueLengthName = name().replace(QRegExp("[{}]"), "");
584 truncateRange.first = trueLengthName.indexOf("?");
585 truncateRange.second = trueLengthName.lastIndexOf("?");
586 fileQDatePattern = fileQDatePattern.replace("?", "");
587 }
588
589 QString file = name();
590
591 QDate result;
592 QDate sputnikLaunch(1957, 10, 4);
593
594 QString before = file.mid(0, file.indexOf("{"));
595 QString after = file.mid(file.lastIndexOf("}") + 1);
596
597 QStringList nameFilters;
598
599 nameFilters.append(before + "*" + after);
600 QStringList files = dir().entryList(nameFilters);
601
602 // We can't sort the files to get our answer, so we need to go through every possible file.
603 foreach (QString foundFile, files) {
604 // Toss any numerical versioning sequence
605 if (truncateRange.first >= 0 && truncateRange.second > truncateRange.first) {
606 foundFile = foundFile.mid(0, truncateRange.first) +
607 foundFile.mid(truncateRange.second + 1);
608
609 }
610 QDate fileDate = QLocale(QLocale::English, QLocale::UnitedStates).toDate(foundFile, fileQDatePattern);
611
612 if (fileDate.isValid()) {
613 // No missions before Sputnik 1, so we must be in the new millenium
614 if (fileDate < sputnikLaunch)
615 fileDate = fileDate.addYears(100);
616
617 if (!result.isValid() || fileDate > result) {
618 result = fileDate;
619 }
620 }
621 }
622
623 if (!result.isValid()) {
625 QObject::tr("No existing files found with a date version matching [%1] in "
626 "[%2]")
627 .arg(FileName(expanded()).name()).arg(path()),
628 _FILEINFO_);
629 }
630
631 return result;
632 }
633
641 QString file = FileName(expanded()).name();
642 int result = 0;
643
644 int width = file.count("?");
645
646 QPair<QString, QString> splitName = splitNameAroundVersionNum();
647 QString &before = splitName.first;
648 QString &after = splitName.second;
649
650 QStringList nameFilters;
651 nameFilters.append(before + QString("%1").arg("", width, '?') + after);
652 QStringList files = dir().entryList(nameFilters, QDir::NoFilter, QDir::Name);
653
654 long foundValue = -1;
655 bool success = false;
656
657 for (int i = files.count() - 1; !success && i >= 0; i--) {
658 foundValue = files[i].mid(before.count(), width).toLong(&success);
659 }
660
661 if (success) {
662 result = foundValue;
663 }
664 else {
666 QObject::tr("No existing files found with a numerical version matching [%1] "
667 "in [%2]")
668 .arg(FileName(expanded()).name()).arg(path()),
669 _FILEINFO_);
670 }
671
672 return result;
673 }
674
680 QString file = QFileInfo(expanded()).fileName();
681
682 if (file.contains(QRegExp("\\?\\?*[^?][^?]*\\?"))) {
684 QObject::tr("Only one numerical version sequence is allowed in a filename; "
685 "there are multiple in [%1]").arg(file),
686 _FILEINFO_);
687 }
688
689 if (isDateVersioned()) {
690 QString fileDatePattern = FileName(expanded()).name();
691
692 // {} needs to be removed from the fileDatePattern (i.e. an empty date version sequence).
693 // This prevents the replacement of {} with '' in the pattern, since
694 // Qt5's QDate.toString(pattern) handles these two adjacent single quotes in the pattern
695 // differently than Qt4 did.
696 fileDatePattern.replace(QRegExp("\\{\\}"), "");
697
698 fileDatePattern = "'" + fileDatePattern.replace(QRegExp("[{}]"), "'") + "'";
699
700 QString dated = QDate::currentDate().toString(fileDatePattern);
701 if (file.contains("'")) {
703 QObject::tr("Date version sequenced file names cannot have single quotes in them; "
704 "the file named [%1] is not usable").arg(file),
705 _FILEINFO_);
706 }
707 else if (dated == "") {
709 QObject::tr("The date version sequence is not usable in the file named [%1]").arg(file),
710 _FILEINFO_);
711 }
712 else if (dated == fileDatePattern.replace(QRegExp("'"), "")) {
714 QObject::tr("The date version sequences are not recognized in the file named [%1]")
715 .arg(file),
716 _FILEINFO_);
717 }
718 }
719 }
720
728 // We need to quote everything not in {} with single quotes.
729 QString file = FileName(expanded()).name();
730
731 // Current Text: {VAR}XXX{VAR}XXX{VAR} or XXX{VAR}XXX{VAR} or XXX{VAR}XXX or {VAR}XXX
732 file = file.replace(QRegExp("[{}]"), "'");
733
734 // Current Text: 'VAR'XXX'VAR'XXX'VAR' or XXX'VAR'XXX'VAR' or XXX'VAR'XXX or 'VAR'XXX
735 if (file.startsWith("'"))
736 file = file.mid(1);
737 else
738 file = "'" + file;
739
740 // Current Text: VAR'XXX'VAR'XXX'VAR' or 'XXX'VAR'XXX'VAR' or 'XXX'VAR'XXX or VAR'XXX
741 if (file.endsWith("'"))
742 file = file.mid(0, file.length() - 1);
743 else
744 file = file + "'";
745
746 // Current Text: VAR'XXX'VAR'XXX'VAR or 'XXX'VAR'XXX'VAR or 'XXX'VAR'XXX' or VAR'XXX' -- VAR's
747 // are the only text not quoted; this is success.
748 return file;
749 }
750
757 QPair<QString, QString> FileName::splitNameAroundVersionNum() const {
758 QString file = FileName(expanded()).name();
759 QString before;
760 QString after;
761
762 if (!isNumericallyVersioned()) {
763 before = file;
764 }
765 else {
766 before = file.mid(0, file.indexOf("?"));
767 after = file.mid(file.lastIndexOf("?") + 1);
768 }
769
770 return QPair<QString, QString>(before, after);
771 }
772
777 m_originalFileNameString = NULL;
778 m_expandedFileNameString = NULL;
779
780 m_originalFileNameString = new QString;
781 m_expandedFileNameString = new QString;
782 }
783
789 FileName::Data::Data(const Data &other) : QSharedData(other) {
790 m_originalFileNameString = NULL;
791 m_expandedFileNameString = NULL;
792
793 m_originalFileNameString = new QString(*other.m_originalFileNameString);
794 m_expandedFileNameString = new QString(*other.m_expandedFileNameString);
795 }
796
801 delete m_originalFileNameString;
802 m_originalFileNameString = NULL;
803
804 delete m_expandedFileNameString;
805 m_expandedFileNameString = NULL;
806 }
807
817 QString FileName::Data::original(bool includeAttributes) const {
818 QString result = *m_originalFileNameString;
819
820
821 if (!includeAttributes) {
822 int attributesPos = result.indexOf("+");
823
824 if (attributesPos != -1)
825 result = result.left(attributesPos);
826 }
827
828 return result;
829 }
830
838 void FileName::Data::setOriginal(const QString &originalStr) {
839 *m_originalFileNameString = originalStr;
840
841 // Expand the file name and store that too.
842 QString expandedStr = original(true);
843
844 int varSearchStartPos = 0;
845 int varStartPos = -1;
846 // Loop while there are any "$" at the current position or after
847 // Some "$" might be skipped if no translation can be found
848 while((varStartPos = expandedStr.indexOf("$", varSearchStartPos)) != -1) {
849 int varEndPos = expandedStr.indexOf(QRegExp("[^a-zA-Z{}0-9_]"), varStartPos + 1);
850 if (varEndPos == -1)
851 varEndPos = expandedStr.length();
852
853 bool variableValid = false;
854 int varNameLength = varEndPos - varStartPos;
855
856 if (varNameLength > 0) {
857 QString varName = expandedStr.mid(varStartPos + 1, varEndPos - varStartPos - 1);
858
859 if (varName.length()) {
860 if (varName[0] =='{' && varName[varName.length() - 1] == '}')
861 varName = varName.mid(1, varName.length() - 2);
862
863 QString varValue;
864
865 // Find the corresponding Isis Preference if one exists
866 if(Preference::Preferences().hasGroup("DataDirectory")) {
867 PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory");
868 if(dataDir.hasKeyword(varName)) {
869 varValue = ((QString)dataDir[varName.toStdString().c_str()][0]);
870 }
871 }
872
873 // Find the corresponding environment variable if one exists
874 if (varValue.isEmpty()) {
875 char *val;
876 val = getenv(varName.toStdString().c_str());
877 if(val != NULL) varValue = val;
878 }
879
880 // Replace the $xxxx with the pref/env, but don't move
881 // the pointer. We may have replaced one $ for another.
882 // Note: May need to put a test for circular replaces in here
883 if (!varValue.isEmpty()) {
884 expandedStr = expandedStr.replace(varStartPos, varNameLength, varValue);
885 variableValid = true;
886 }
887 }
888 }
889
890 if (variableValid) {
891 // We could expand multiple times...
892 varSearchStartPos = varStartPos;
893 }
894 else {
895 // We failed to understand this variable, move along
896 varSearchStartPos = varStartPos + 1;
897 }
898 }
899
900 *m_expandedFileNameString = expandedStr;
901 }
902
912 QString FileName::Data::expanded(bool includeAttributes) const {
913 QString result = *m_expandedFileNameString;
914
915 if (!includeAttributes) {
916 int attributesPos = result.indexOf("+");
917
918 if (attributesPos != -1)
919 result = result.left(attributesPos);
920 }
921
922 return result;
923 }
924}
This is the reference-counted data for FileName.
Definition FileName.h:203
QString original(bool includeAttributes) const
Returns the original file name, stored in m_originalFileNameString.
Definition FileName.cpp:817
Data()
Data constructor, creates a new Data object.
Definition FileName.cpp:776
QString expanded(bool includeAttributes) const
Returns the expanded file name, stored in m_expandedFileNameString.
Definition FileName.cpp:912
~Data()
Destroys the Data object.
Definition FileName.cpp:800
void setOriginal(const QString &originalStr)
Sets the original file name, stored in m_originalFileNameString.
Definition FileName.cpp:838
File name manipulation and expansion.
Definition FileName.h:100
QString path() const
Returns the path of the file name.
Definition FileName.cpp:103
bool isDateVersioned() const
Checks if the file name is versioned by date.
Definition FileName.cpp:303
~FileName()
Destroys the FileName object.
Definition FileName.cpp:66
QPair< QString, QString > splitNameAroundVersionNum() const
This returns a QPair of the text (before, after) a version number in a file.
Definition FileName.cpp:757
bool isNumericallyVersioned() const
Checks if the file name is versioned numerically.
Definition FileName.cpp:293
QDir dir() const
Returns the path of the file's parent directory as a QDir object.
Definition FileName.cpp:465
FileName()
Constructs an empty FileName object.
Definition FileName.cpp:31
bool fileExists() const
Returns true if the file exists; false otherwise.
Definition FileName.cpp:449
FileName setExtension(const QString &extension) const
Sets all current file extensions to a new extension in the file name.
Definition FileName.cpp:265
QString baseName() const
Returns the name of the file without the path and without extensions.
Definition FileName.cpp:145
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
Definition FileName.cpp:162
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition FileName.cpp:196
QDate highestVersionDate() const
This looks through the directory of the file and checks for the highest version date of the file that...
Definition FileName.cpp:578
static FileName createTempFile(FileName templateFileName="$TEMPORARY/temp")
Creates a temporary file and returns a FileName object created using the temporary file.
Definition FileName.cpp:478
FileName highestVersion() const
Searches the directory specified in the file name for the highest version of the file name.
Definition FileName.cpp:313
QString original() const
Returns the full file name including the file path.
Definition FileName.cpp:212
FileName newVersion() const
Updates the file name to be the latest version.
Definition FileName.cpp:348
FileName & operator=(const FileName &rhs)
Clears the current contents of the FileName object and reinitializes it with the argument.
Definition FileName.cpp:527
long highestVersionNum() const
This looks through the directory of the file and checks for the highest version number of the file th...
Definition FileName.cpp:640
QString fileNameQDatePattern() const
This changes the files format.
Definition FileName.cpp:727
QString extension() const
Returns the last extension of the file name.
Definition FileName.cpp:178
bool operator!=(const FileName &rhs)
Compares equality of two FileName objects.
Definition FileName.cpp:568
QString originalPath() const
Returns the path of the original file name.
Definition FileName.cpp:84
void validateVersioningState() const
This verifies the class invariant when using versioning - that the FileName is in an acceptable state...
Definition FileName.cpp:679
FileName removeExtension() const
Removes all extensions in the file name.
Definition FileName.cpp:246
QString attributes() const
Returns a QString of the attributes in a filename, attributes are expected to be of type CubeAttribut...
Definition FileName.cpp:121
bool operator==(const FileName &rhs)
Compares equality of two FileName objects.
Definition FileName.cpp:540
FileName addExtension(const QString &extension) const
Adds a new extension to the file name.
Definition FileName.cpp:225
bool isVersioned() const
Checks to see if a file name is versioned by date or numerically.
Definition FileName.cpp:281
FileName version(long versionNumber) const
Returns a FileName object of the same file name but versioned numerically by the number passed in as ...
Definition FileName.cpp:398
QString toString() const
Returns a QString of the full file name including the file path, excluding the attributes with any Is...
Definition FileName.cpp:515
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
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition IException.h:155
Contains multiple PvlContainers.
Definition PvlGroup.h:41
This is free and unencumbered software released into the public domain.
Definition Apollo.h:16
Namespace for the standard library.