Isis 3 Programmer Reference
FileName.cpp
Go to the documentation of this file.
1 
24 #include "FileName.h"
25 
26 #include <cmath>
27 
28 #include <QDate>
29 #include <QDebug>
30 #include <QDir>
31 #include <QLocale>
32 #include <QPair>
33 #include <QString>
34 #include <QTemporaryFile>
35 
36 #include "Preference.h"
37 #include "IException.h"
38 #include "IString.h"
39 
40 using namespace std;
41 
42 namespace Isis {
43 
47  FileName::FileName() {
48  m_d = new Data;
49  }
50 
56  FileName::FileName(const char *file) {
57  m_d = new Data;
58  m_d->setOriginal(file);
59  }
60 
66  FileName::FileName(const QString &file) {
67  m_d = new Data;
68  m_d->setOriginal(file);
69  }
70 
76  FileName::FileName(const FileName &other) : m_d(other.m_d) {
77  }
78 
83  }
84 
100  QString FileName::originalPath() const {
101  return QFileInfo(m_d->original(false)).path();
102  }
103 
119  QString FileName::path() const {
120  return QFileInfo(expanded()).path();
121  }
122 
137  QString FileName::attributes() const {
138  QString result;
139  QString fileNameWithAttribs = QFileInfo(m_d->original(true)).fileName();
140 
141  int attribStartPos = fileNameWithAttribs.indexOf("+");
142 
143  if (attribStartPos != -1)
144  result = fileNameWithAttribs.mid(attribStartPos + 1);
145 
146  return result;
147  }
148 
161  QString FileName::baseName() const {
162  return QFileInfo(m_d->original(false)).completeBaseName();
163  }
164 
178  QString FileName::name() const {
179  return QFileInfo(m_d->original(false)).fileName();
180  }
181 
194  QString FileName::extension() const {
195  return QFileInfo(m_d->original(false)).suffix();
196  }
197 
212  QString FileName::expanded() const {
213  return m_d->expanded(false);
214  }
215 
228  QString FileName::original() const {
229  return m_d->original(true);
230  }
231 
241  FileName FileName::addExtension(const QString &newExtension) const {
242  FileName result = *this;
243 
244  if (result.extension() != newExtension) {
245  QString attributesStr = result.attributes();
246 
247  if (attributesStr == "")
248  result = FileName(result.originalPath() + "/" + result.name() + "." + newExtension);
249  else
250  result = FileName(result.originalPath() + "/" + result.name() + "." + newExtension
251  + "+" + attributesStr);
252  }
253 
254  return result;
255  }
256 
263  QString attributesStr = attributes();
264 
265  FileName result;
266  if (attributesStr == "")
267  result = FileName(originalPath() + "/" + baseName());
268  else
269  result = FileName(originalPath() + "/" + baseName() + "+" + attributesStr);
270 
271  return result;
272  }
273 
281  FileName FileName::setExtension(const QString &newExtension) const {
282  FileName result = *this;
283 
284  if (extension() != newExtension) {
285  result = result.removeExtension().addExtension(newExtension);
286  }
287 
288  return result;
289  }
290 
297  bool FileName::isVersioned() const {
299 
301  }
302 
310  return FileName(expanded()).name().contains("?");
311  }
312 
320  return FileName(expanded()).name().contains(QRegExp("\\{.*\\}"));
321  }
322 
331 
332  FileName result = *this;
333 
334  if (!isVersioned()) {
336  QObject::tr("Asked for highest version of file named [%1] in [%2] but there "
337  "are no version sequences in the name")
338  .arg(name()).arg(originalPath()),
339  _FILEINFO_);
340  }
341 
342  // Look for dates
343  if (isDateVersioned()) {
344  result = result.version(result.highestVersionDate());
345  }
346 
347  // Look for number's
348  if (isNumericallyVersioned()) {
349  result = result.version(result.highestVersionNum());
350  }
351 
352  return result;
353  }
354 
366 
367  FileName result = *this;
368 
369  if (!isVersioned()) {
371  QObject::tr("Asked for new version of file named [%1] in [%2] but there "
372  "are no version sequences in the name")
373  .arg(name()).arg(originalPath()),
374  _FILEINFO_);
375  }
376 
377  // Look for date
378  if (isDateVersioned()) {
379  result = result.version(QDate::currentDate());
380  }
381 
382  // Look for #'s
383  if (isNumericallyVersioned()) {
384  try {
385  result = result.version(result.highestVersionNum() + 1);
386  }
387  catch (IException &) {
388  result = result.version(1);
389  }
390  }
391 
392  if (result.fileExists()) {
394  QObject::tr("Could not generate unique new version of file named [%1] in "
395  "[%2] because the file [%3] exists")
396  .arg(name()).arg(originalPath()).arg(result.name()),
397  _FILEINFO_);
398 
399  }
400 
401  return result;
402  }
403 
414  FileName FileName::version(long versionNumber) const {
415  QString file = FileName(expanded()).name();
416 
417  int width = file.count("?");
418 
419  if (versionNumber < 0) {
421  QObject::tr("FileName does not support negative version numbers in the file name, "
422  "tried to get version [%1] in file named [%2]")
423  .arg(versionNumber).arg(originalPath() + "/" + file),
424  _FILEINFO_);
425  }
426 
427  if (versionNumber >= pow(10.0, width)) {
429  QObject::tr("FileName does not support version numbers greater than what would fit in "
430  "the file name, tried to get version [%1] in file named [%2]")
431  .arg(versionNumber).arg(originalPath() + "/" + file),
432  _FILEINFO_);
433  }
434 
436  QString &before = splitName.first;
437  QString &after = splitName.second;
438 
439  file = before + QString("%1").arg(QString::number(versionNumber), width, '0') + after;
440 
441  return FileName(originalPath() + "/" + file);
442  }
443 
453  FileName FileName::version(QDate versionDate) const {
454  QString newName = versionDate.toString(fileNameQDatePattern());
455 
456  return FileName(originalPath() + "/" + newName);
457  }
458 
465  bool FileName::fileExists() const {
466  return QFileInfo(expanded()).exists();
467  }
468 
481  QDir FileName::dir() const {
482  return QFileInfo(expanded()).dir();
483  }
484 
495  QString preppedFileName = QString("%1/%2XXXXXX.%3").arg(templateFileName.path())
496  .arg(templateFileName.baseName()).arg(templateFileName.extension());
497  QTemporaryFile tempFile(preppedFileName);
498  tempFile.setAutoRemove(false);
499 
500  if (!tempFile.open()) {
502  QObject::tr("Could not create a unique temporary file name based on [%1]")
503  .arg(templateFileName.original()),
504  _FILEINFO_);
505  }
506 
507  // We want to set the 'original' path as correctly as possible. So let's use the input original
508  // path with the output temp file's file name in our result.
509  FileName result;
510  QString newTempFileNameStr = templateFileName.originalPath() + "/" +
511  QFileInfo(tempFile.fileName()).fileName();
512  result = FileName(newTempFileNameStr);
513 
514  return result;
515  }
516 
531  QString FileName::toString() const {
532  return expanded();
533  }
534 
544  m_d = rhs.m_d;
545  return *this;
546  }
547 
556  bool FileName::operator==(const FileName &rhs) {
557  QString expandedOfThis = expanded();
558  QString canonicalOfThis = QFileInfo(expandedOfThis).canonicalFilePath();
559 
560  QString expandedOfRhs = rhs.expanded();
561  QString canonicalOfRhs = QFileInfo(expandedOfRhs).canonicalFilePath();
562 
563  // Cononical file paths return empty strings if the file does not exist. Either both canonicals
564  // are valid and the same (equal is initialized to true), or neither canonical is valid but
565  // the expandeds are the same (equal is set to true when it isn't initialized to true).
566  bool equal = (!canonicalOfThis.isEmpty() && canonicalOfThis == canonicalOfRhs);
567 
568  if (!equal) {
569  equal = (canonicalOfThis.isEmpty() && canonicalOfRhs.isEmpty() &&
570  expandedOfThis == expandedOfRhs);
571  }
572 
573  return equal;
574  }
575 
584  bool FileName::operator!=(const FileName &rhs) {
585  return !(*this == rhs);
586  }
587 
595  QString fileQDatePattern = fileNameQDatePattern();
596 
597  QPair<int, int> truncateRange(-1, -1);
598  if (fileQDatePattern.contains("?")) {
599  QString trueLengthName = name().replace(QRegExp("[{}]"), "");
600  truncateRange.first = trueLengthName.indexOf("?");
601  truncateRange.second = trueLengthName.lastIndexOf("?");
602  fileQDatePattern = fileQDatePattern.replace("?", "");
603  }
604 
605  QString file = name();
606 
607  QDate result;
608  QDate sputnikLaunch(1957, 10, 4);
609 
610  QString before = file.mid(0, file.indexOf("{"));
611  QString after = file.mid(file.lastIndexOf("}") + 1);
612 
613  QStringList nameFilters;
614 
615  nameFilters.append(before + "*" + after);
616  QStringList files = dir().entryList(nameFilters);
617 
618  // We can't sort the files to get our answer, so we need to go through every possible file.
619  foreach (QString foundFile, files) {
620  // Toss any numerical versioning sequence
621  if (truncateRange.first >= 0 && truncateRange.second > truncateRange.first) {
622  foundFile = foundFile.mid(0, truncateRange.first) +
623  foundFile.mid(truncateRange.second + 1);
624 
625  }
626  QDate fileDate = QLocale(QLocale::English, QLocale::UnitedStates).toDate(foundFile, fileQDatePattern);
627 
628  if (fileDate.isValid()) {
629  // No missions before Sputnik 1, so we must be in the new millenium
630  if (fileDate < sputnikLaunch)
631  fileDate = fileDate.addYears(100);
632 
633  if (!result.isValid() || fileDate > result) {
634  result = fileDate;
635  }
636  }
637  }
638 
639  if (!result.isValid()) {
641  QObject::tr("No existing files found with a date version matching [%1] in "
642  "[%2]")
643  .arg(FileName(expanded()).name()).arg(path()),
644  _FILEINFO_);
645  }
646 
647  return result;
648  }
649 
657  QString file = FileName(expanded()).name();
658  int result = 0;
659 
660  int width = file.count("?");
661 
663  QString &before = splitName.first;
664  QString &after = splitName.second;
665 
666  QStringList nameFilters;
667  nameFilters.append(before + QString("%1").arg("", width, '?') + after);
668  QStringList files = dir().entryList(nameFilters, QDir::NoFilter, QDir::Name);
669 
670  long foundValue = -1;
671  bool success = false;
672 
673  for (int i = files.count() - 1; !success && i >= 0; i--) {
674  foundValue = files[i].mid(before.count(), width).toLong(&success);
675  }
676 
677  if (success) {
678  result = foundValue;
679  }
680  else {
682  QObject::tr("No existing files found with a numerial version matching [%1] "
683  "in [%2]")
684  .arg(FileName(expanded()).name()).arg(path()),
685  _FILEINFO_);
686  }
687 
688  return result;
689  }
690 
696  QString file = QFileInfo(expanded()).fileName();
697 
698  if (file.contains(QRegExp("\\?\\?*[^?][^?]*\\?"))) {
700  QObject::tr("Only one numerical version sequence is allowed in a filename; "
701  "there are multiple in [%1]").arg(file),
702  _FILEINFO_);
703  }
704 
705  if (isDateVersioned()) {
706  QString fileDatePattern = FileName(expanded()).name();
707 
708  // {} needs to be removed from the fileDatePattern (i.e. an empty date version sequence).
709  // This prevents the replacement of {} with '' in the pattern, since
710  // Qt5's QDate.toString(pattern) handles these two adjacent single quotes in the pattern
711  // differently than Qt4 did.
712  fileDatePattern.replace(QRegExp("\\{\\}"), "");
713 
714  fileDatePattern = "'" + fileDatePattern.replace(QRegExp("[{}]"), "'") + "'";
715 
716  QString dated = QDate::currentDate().toString(fileDatePattern);
717  if (file.contains("'")) {
719  QObject::tr("Date version sequenced file names cannot have single quotes in them; "
720  "the file named [%1] is not usable").arg(file),
721  _FILEINFO_);
722  }
723  else if (dated == "") {
725  QObject::tr("The date version sequence is not usable in the file named [%1]").arg(file),
726  _FILEINFO_);
727  }
728  else if (dated == fileDatePattern.replace(QRegExp("'"), "")) {
730  QObject::tr("The date version sequences are not recognized in the file named [%1]")
731  .arg(file),
732  _FILEINFO_);
733  }
734  }
735  }
736 
744  // We need to quote everything not in {} with single quotes.
745  QString file = FileName(expanded()).name();
746 
747  // Current Text: {VAR}XXX{VAR}XXX{VAR} or XXX{VAR}XXX{VAR} or XXX{VAR}XXX or {VAR}XXX
748  file = file.replace(QRegExp("[{}]"), "'");
749 
750  // Current Text: 'VAR'XXX'VAR'XXX'VAR' or XXX'VAR'XXX'VAR' or XXX'VAR'XXX or 'VAR'XXX
751  if (file.startsWith("'"))
752  file = file.mid(1);
753  else
754  file = "'" + file;
755 
756  // Current Text: VAR'XXX'VAR'XXX'VAR' or 'XXX'VAR'XXX'VAR' or 'XXX'VAR'XXX or VAR'XXX
757  if (file.endsWith("'"))
758  file = file.mid(0, file.length() - 1);
759  else
760  file = file + "'";
761 
762  // Current Text: VAR'XXX'VAR'XXX'VAR or 'XXX'VAR'XXX'VAR or 'XXX'VAR'XXX' or VAR'XXX' -- VAR's
763  // are the only text not quoted; this is success.
764  return file;
765  }
766 
774  QString file = FileName(expanded()).name();
775  QString before;
776  QString after;
777 
778  if (!isNumericallyVersioned()) {
779  before = file;
780  }
781  else {
782  before = file.mid(0, file.indexOf("?"));
783  after = file.mid(file.lastIndexOf("?") + 1);
784  }
785 
786  return QPair<QString, QString>(before, after);
787  }
788 
793  m_originalFileNameString = NULL;
794  m_expandedFileNameString = NULL;
795 
796  m_originalFileNameString = new QString;
797  m_expandedFileNameString = new QString;
798  }
799 
805  FileName::Data::Data(const Data &other) : QSharedData(other) {
806  m_originalFileNameString = NULL;
807  m_expandedFileNameString = NULL;
808 
809  m_originalFileNameString = new QString(*other.m_originalFileNameString);
810  m_expandedFileNameString = new QString(*other.m_expandedFileNameString);
811  }
812 
817  delete m_originalFileNameString;
818  m_originalFileNameString = NULL;
819 
820  delete m_expandedFileNameString;
821  m_expandedFileNameString = NULL;
822  }
823 
833  QString FileName::Data::original(bool includeAttributes) const {
834  QString result = *m_originalFileNameString;
835 
836 
837  if (!includeAttributes) {
838  int attributesPos = result.indexOf("+");
839 
840  if (attributesPos != -1)
841  result = result.left(attributesPos);
842  }
843 
844  return result;
845  }
846 
854  void FileName::Data::setOriginal(const QString &originalStr) {
855  *m_originalFileNameString = originalStr;
856 
857  // Expand the file name and store that too.
858  QString expandedStr = original(true);
859 
860  int varSearchStartPos = 0;
861  int varStartPos = -1;
862  // Loop while there are any "$" at the current position or after
863  // Some "$" might be skipped if no translation can be found
864  while((varStartPos = expandedStr.indexOf("$", varSearchStartPos)) != -1) {
865  int varEndPos = expandedStr.indexOf(QRegExp("[^a-zA-Z{}0-9]"), varStartPos + 1);
866  if (varEndPos == -1)
867  varEndPos = expandedStr.length();
868 
869  bool variableValid = false;
870  int varNameLength = varEndPos - varStartPos;
871 
872  if (varNameLength > 0) {
873  QString varName = expandedStr.mid(varStartPos + 1, varEndPos - varStartPos - 1);
874 
875  if (varName.length()) {
876  if (varName[0] =='{' && varName[varName.length() - 1] == '}')
877  varName = varName.mid(1, varName.length() - 2);
878 
879  QString varValue;
880 
881  // Find the corresponding Isis Preference if one exists
882  if(Preference::Preferences().hasGroup("DataDirectory")) {
883  PvlGroup &dataDir = Preference::Preferences().findGroup("DataDirectory");
884  if(dataDir.hasKeyword(varName)) {
885  varValue = ((QString)dataDir[varName.toStdString().c_str()][0]);
886  }
887  }
888 
889  // Find the corresponding environment variable if one exists
890  if (varValue.isEmpty()) {
891  char *val;
892  val = getenv(varName.toStdString().c_str());
893  if(val != NULL) varValue = val;
894  }
895 
896  // Replace the $xxxx with the pref/env, but don't move
897  // the pointer. We may have replaced one $ for another.
898  // Note: May need to put a test for circular replaces in here
899  if (!varValue.isEmpty()) {
900  expandedStr = expandedStr.replace(varStartPos, varNameLength, varValue);
901  variableValid = true;
902  }
903  }
904  }
905 
906  if (variableValid) {
907  // We could expand multiple times...
908  varSearchStartPos = varStartPos;
909  }
910  else {
911  // We failed to understand this variable, move along
912  varSearchStartPos = varStartPos + 1;
913  }
914  }
915 
916  *m_expandedFileNameString = expandedStr;
917  }
918 
928  QString FileName::Data::expanded(bool includeAttributes) const {
929  QString result = *m_expandedFileNameString;
930 
931  if (!includeAttributes) {
932  int attributesPos = result.indexOf("+");
933 
934  if (attributesPos != -1)
935  result = result.left(attributesPos);
936  }
937 
938  return result;
939  }
940 }
QString path() const
Returns the path of the file name.
Definition: FileName.cpp:119
bool hasKeyword(const QString &name) const
Check to see if a keyword exists.
File name manipulation and expansion.
Definition: FileName.h:116
Data()
Data constructor, creates a new Data object.
Definition: FileName.cpp:792
FileName newVersion() const
Updates the file name to be the latest version.
Definition: FileName.cpp:364
~Data()
Destroys the Data object.
Definition: FileName.cpp:816
FileName addExtension(const QString &extension) const
Adds a new extension to the file name.
Definition: FileName.cpp:241
Namespace for the standard library.
QString name() const
Returns the name of the file excluding the path and the attributes in the file name.
Definition: FileName.cpp:178
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:594
bool isDateVersioned() const
Checks if the file name is versioned by date.
Definition: FileName.cpp:319
QString originalPath() const
Returns the path of the original file name.
Definition: FileName.cpp:100
QString original(bool includeAttributes) const
Returns the original file name, stored in m_originalFileNameString.
Definition: FileName.cpp:833
A type of error that occurred when performing an actual I/O operation.
Definition: IException.h:171
QString fileNameQDatePattern() const
This changes the files format.
Definition: FileName.cpp:743
QString expanded(bool includeAttributes) const
Returns the expanded file name, stored in m_expandedFileNameString.
Definition: FileName.cpp:928
Contains multiple PvlContainers.
Definition: PvlGroup.h:57
#define _FILEINFO_
Macro for the filename and line number.
Definition: IException.h:40
A type of error that cannot be classified as any of the other error types.
Definition: IException.h:134
QString expanded() const
Returns a QString of the full file name including the file path, excluding the attributes.
Definition: FileName.cpp:212
QString original() const
Returns the full file name including the file path.
Definition: FileName.cpp:228
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:656
QDir dir() const
Returns the path of the file&#39;s parent directory as a QDir object.
Definition: FileName.cpp:481
This is the reference-counted data for FileName.
Definition: FileName.h:219
FileName & operator=(const FileName &rhs)
Clears the current contents of the FileName object and reinitializes it with the argument.
Definition: FileName.cpp:543
void validateVersioningState() const
This verifies the class invariant when using versioning - that the FileName is in an acceptable state...
Definition: FileName.cpp:695
bool operator!=(const FileName &rhs)
Compares equality of two FileName objects.
Definition: FileName.cpp:584
QString baseName() const
Returns the name of the file without the path and without extensions.
Definition: FileName.cpp:161
QString toString() const
Returns a QString of the full file name including the file path, excluding the attributes with any Is...
Definition: FileName.cpp:531
FileName highestVersion() const
Searches the directory specified in the file name for the highest version of the file name...
Definition: FileName.cpp:329
Isis exception class.
Definition: IException.h:107
~FileName()
Destroys the FileName object.
Definition: FileName.cpp:82
FileName setExtension(const QString &extension) const
Sets all current file extensions to a new extension in the file name.
Definition: FileName.cpp:281
FileName()
Constructs an empty FileName object.
Definition: FileName.cpp:47
Namespace for ISIS/Bullet specific routines.
Definition: Apollo.h:31
bool operator==(const FileName &rhs)
Compares equality of two FileName objects.
Definition: FileName.cpp:556
QString extension() const
Returns the last extension of the file name.
Definition: FileName.cpp:194
bool isNumericallyVersioned() const
Checks if the file name is versioned numerically.
Definition: FileName.cpp:309
QString attributes() const
Returns a QString of the attributes in a filename, attributes are expected to be of type CubeAttribut...
Definition: FileName.cpp:137
QPair< QString, QString > splitNameAroundVersionNum() const
This returns a QPair of the text (before, after) a version number in a file.
Definition: FileName.cpp:773
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:414
bool fileExists() const
Returns true if the file exists; false otherwise.
Definition: FileName.cpp:465
static FileName createTempFile(FileName templateFileName="$TEMPORARY/temp")
Creates a temporary file and returns a FileName object created using the temporary file...
Definition: FileName.cpp:494
bool isVersioned() const
Checks to see if a file name is versioned by date or numerically.
Definition: FileName.cpp:297
void setOriginal(const QString &originalStr)
Sets the original file name, stored in m_originalFileNameString.
Definition: FileName.cpp:854
FileName removeExtension() const
Removes all extensions in the file name.
Definition: FileName.cpp:262