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 
24 using namespace std;
25 
26 namespace Isis {
27 
31  FileName::FileName() {
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 
67  }
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 
281  bool FileName::isVersioned() const {
283 
285  }
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
332  if (isNumericallyVersioned()) {
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
367  if (isNumericallyVersioned()) {
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 
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 
540  bool FileName::operator==(const FileName &rhs) {
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 
568  bool FileName::operator!=(const FileName &rhs) {
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 
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 numerial 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 
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 }
Isis::FileName::originalPath
QString originalPath() const
Returns the path of the original file name.
Definition: FileName.cpp:84
Isis::FileName::fileNameQDatePattern
QString fileNameQDatePattern() const
This changes the files format.
Definition: FileName.cpp:727
Isis::IException::Io
@ Io
A type of error that occurred when performing an actual I/O operation.
Definition: IException.h:155
QSharedData
Isis::FileName::name
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
Definition: FileName.cpp:162
Isis::FileName
File name manipulation and expansion.
Definition: FileName.h:100
Isis::IException::Unknown
@ Unknown
A type of error that cannot be classified as any of the other error types.
Definition: IException.h:118
Isis::FileName::fileExists
bool fileExists() const
Returns true if the file exists; false otherwise.
Definition: FileName.cpp:449
Isis::FileName::isDateVersioned
bool isDateVersioned() const
Checks if the file name is versioned by date.
Definition: FileName.cpp:303
Isis::FileName::splitNameAroundVersionNum
QPair< QString, QString > splitNameAroundVersionNum() const
This returns a QPair of the text (before, after) a version number in a file.
Definition: FileName.cpp:757
Isis::FileName::Data::~Data
~Data()
Destroys the Data object.
Definition: FileName.cpp:800
Isis::PvlContainer::hasKeyword
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
Definition: PvlContainer.cpp:159
Isis::FileName::Data::original
QString original(bool includeAttributes) const
Returns the original file name, stored in m_originalFileNameString.
Definition: FileName.cpp:817
QStringList
Isis::FileName::operator=
FileName & operator=(const FileName &rhs)
Clears the current contents of the FileName object and reinitializes it with the argument.
Definition: FileName.cpp:527
Isis::FileName::createTempFile
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
Isis::FileName::newVersion
FileName newVersion() const
Updates the file name to be the latest version.
Definition: FileName.cpp:348
Isis::FileName::baseName
QString baseName() const
Returns the name of the file without the path and without extensions.
Definition: FileName.cpp:145
Isis::FileName::expanded
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:196
Isis::PvlGroup
Contains multiple PvlContainers.
Definition: PvlGroup.h:41
Isis::FileName::Data::expanded
QString expanded(bool includeAttributes) const
Returns the expanded file name, stored in m_expandedFileNameString.
Definition: FileName.cpp:912
Isis::FileName::addExtension
FileName addExtension(const QString &extension) const
Adds a new extension to the file name.
Definition: FileName.cpp:225
Isis::FileName::dir
QDir dir() const
Returns the path of the file's parent directory as a QDir object.
Definition: FileName.cpp:465
Isis::FileName::FileName
FileName()
Constructs an empty FileName object.
Definition: FileName.cpp:31
Isis::FileName::highestVersionDate
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
Isis::IException
Isis exception class.
Definition: IException.h:91
Isis::FileName::operator!=
bool operator!=(const FileName &rhs)
Compares equality of two FileName objects.
Definition: FileName.cpp:568
Isis::FileName::toString
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::FileName::~FileName
~FileName()
Destroys the FileName object.
Definition: FileName.cpp:66
Isis::FileName::highestVersionNum
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
Isis::FileName::operator==
bool operator==(const FileName &rhs)
Compares equality of two FileName objects.
Definition: FileName.cpp:540
Isis::FileName::removeExtension
FileName removeExtension() const
Removes all extensions in the file name.
Definition: FileName.cpp:246
Isis::FileName::extension
QString extension() const
Returns the last extension of the file name.
Definition: FileName.cpp:178
Isis::FileName::isNumericallyVersioned
bool isNumericallyVersioned() const
Checks if the file name is versioned numerically.
Definition: FileName.cpp:293
std
Namespace for the standard library.
QPair
This is free and unencumbered software released into the public domain.
Definition: CubeIoHandler.h:23
Isis::FileName::Data::Data
Data()
Data constructor, creates a new Data object.
Definition: FileName.cpp:776
Isis::FileName::isVersioned
bool isVersioned() const
Checks to see if a file name is versioned by date or numerically.
Definition: FileName.cpp:281
Isis::FileName::highestVersion
FileName highestVersion() const
Searches the directory specified in the file name for the highest version of the file name.
Definition: FileName.cpp:313
Isis::FileName::original
QString original() const
Returns the full file name including the file path.
Definition: FileName.cpp:212
Isis::FileName::Data
This is the reference-counted data for FileName.
Definition: FileName.h:203
Isis::FileName::setExtension
FileName setExtension(const QString &extension) const
Sets all current file extensions to a new extension in the file name.
Definition: FileName.cpp:265
Isis::FileName::Data::setOriginal
void setOriginal(const QString &originalStr)
Sets the original file name, stored in m_originalFileNameString.
Definition: FileName.cpp:838
Isis::FileName::path
QString path() const
Returns the path of the file name.
Definition: FileName.cpp:103
Isis::FileName::attributes
QString attributes() const
Returns a QString of the attributes in a filename, attributes are expected to be of type CubeAttribut...
Definition: FileName.cpp:121
Isis
This is free and unencumbered software released into the public domain.
Definition: Apollo.h:16
Isis::FileName::validateVersioningState
void validateVersioningState() const
This verifies the class invariant when using versioning - that the FileName is in an acceptable state...
Definition: FileName.cpp:679
Isis::FileName::version
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