comparison data/fileio/FileSource.cpp @ 327:1d656dcda8ef

* some tweaks to improve usability of these classes in a console application
author Chris Cannam
date Fri, 02 Nov 2007 16:50:31 +0000
parents 82a2d3161e14
children 5cd7f6d10d47
comparison
equal deleted inserted replaced
326:bb6e4c46e202 327:1d656dcda8ef
24 #include <QApplication> 24 #include <QApplication>
25 #include <QProgressDialog> 25 #include <QProgressDialog>
26 #include <QHttpResponseHeader> 26 #include <QHttpResponseHeader>
27 27
28 #include <iostream> 28 #include <iostream>
29
30 //#define DEBUG_FILE_SOURCE 1
29 31
30 int 32 int
31 FileSource::m_count = 0; 33 FileSource::m_count = 0;
32 34
33 QMutex 35 QMutex
54 m_leaveLocalFile(false), 56 m_leaveLocalFile(false),
55 m_progressDialog(0), 57 m_progressDialog(0),
56 m_progressShowTimer(this), 58 m_progressShowTimer(this),
57 m_refCounted(false) 59 m_refCounted(false)
58 { 60 {
61 #ifdef DEBUG_FILE_SOURCE
59 std::cerr << "FileSource::FileSource(" << fileOrUrl.toStdString() << ")" << std::endl; 62 std::cerr << "FileSource::FileSource(" << fileOrUrl.toStdString() << ")" << std::endl;
63 #endif
60 64
61 if (!canHandleScheme(m_url)) { 65 if (!canHandleScheme(m_url)) {
62 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; 66 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
63 m_errorString = tr("Unsupported scheme in URL"); 67 m_errorString = tr("Unsupported scheme in URL");
64 return; 68 return;
104 m_leaveLocalFile(false), 108 m_leaveLocalFile(false),
105 m_progressDialog(0), 109 m_progressDialog(0),
106 m_progressShowTimer(this), 110 m_progressShowTimer(this),
107 m_refCounted(false) 111 m_refCounted(false)
108 { 112 {
113 #ifdef DEBUG_FILE_SOURCE
109 std::cerr << "FileSource::FileSource(" << url.toString().toStdString() << ") [as url]" << std::endl; 114 std::cerr << "FileSource::FileSource(" << url.toString().toStdString() << ") [as url]" << std::endl;
115 #endif
110 116
111 if (!canHandleScheme(m_url)) { 117 if (!canHandleScheme(m_url)) {
112 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; 118 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
113 m_errorString = tr("Unsupported scheme in URL"); 119 m_errorString = tr("Unsupported scheme in URL");
114 return; 120 return;
130 m_leaveLocalFile(false), 136 m_leaveLocalFile(false),
131 m_progressDialog(0), 137 m_progressDialog(0),
132 m_progressShowTimer(0), 138 m_progressShowTimer(0),
133 m_refCounted(false) 139 m_refCounted(false)
134 { 140 {
141 #ifdef DEBUG_FILE_SOURCE
135 std::cerr << "FileSource::FileSource(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl; 142 std::cerr << "FileSource::FileSource(" << m_url.toString().toStdString() << ") [copy ctor]" << std::endl;
143 #endif
136 144
137 if (!canHandleScheme(m_url)) { 145 if (!canHandleScheme(m_url)) {
138 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl; 146 std::cerr << "FileSource::FileSource: ERROR: Unsupported scheme in URL \"" << m_url.toString().toStdString() << "\"" << std::endl;
139 m_errorString = tr("Unsupported scheme in URL"); 147 m_errorString = tr("Unsupported scheme in URL");
140 return; 148 return;
142 150
143 if (!isRemote()) { 151 if (!isRemote()) {
144 m_localFilename = rf.m_localFilename; 152 m_localFilename = rf.m_localFilename;
145 } else { 153 } else {
146 QMutexLocker locker(&m_mapMutex); 154 QMutexLocker locker(&m_mapMutex);
155 #ifdef DEBUG_FILE_SOURCE
147 std::cerr << "FileSource::FileSource(copy ctor): ref count is " 156 std::cerr << "FileSource::FileSource(copy ctor): ref count is "
148 << m_refCountMap[m_url] << std::endl; 157 << m_refCountMap[m_url] << std::endl;
158 #endif
149 if (m_refCountMap[m_url] > 0) { 159 if (m_refCountMap[m_url] > 0) {
150 m_refCountMap[m_url]++; 160 m_refCountMap[m_url]++;
161 #ifdef DEBUG_FILE_SOURCE
151 std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; 162 std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl;
163 #endif
152 m_localFilename = m_remoteLocalMap[m_url]; 164 m_localFilename = m_remoteLocalMap[m_url];
153 m_refCounted = true; 165 m_refCounted = true;
154 } else { 166 } else {
155 m_ok = false; 167 m_ok = false;
156 m_lastStatus = 404; 168 m_lastStatus = 404;
160 m_done = true; 172 m_done = true;
161 } 173 }
162 174
163 FileSource::~FileSource() 175 FileSource::~FileSource()
164 { 176 {
177 #ifdef DEBUG_FILE_SOURCE
165 std::cerr << "FileSource(" << m_url.toString().toStdString() << ")::~FileSource" << std::endl; 178 std::cerr << "FileSource(" << m_url.toString().toStdString() << ")::~FileSource" << std::endl;
179 #endif
166 180
167 cleanup(); 181 cleanup();
168 182
169 if (isRemote() && !m_leaveLocalFile) deleteCacheFile(); 183 if (isRemote() && !m_leaveLocalFile) deleteCacheFile();
170 } 184 }
183 m_done = true; 197 m_done = true;
184 return; 198 return;
185 } 199 }
186 200
187 if (createCacheFile()) { 201 if (createCacheFile()) {
202 #ifdef DEBUG_FILE_SOURCE
188 std::cerr << "FileSource::init: Already have this one" << std::endl; 203 std::cerr << "FileSource::init: Already have this one" << std::endl;
204 #endif
189 m_ok = true; 205 m_ok = true;
190 if (!QFileInfo(m_localFilename).exists()) { 206 if (!QFileInfo(m_localFilename).exists()) {
191 m_lastStatus = 404; 207 m_lastStatus = 404;
192 } else { 208 } else {
193 m_lastStatus = 200; 209 m_lastStatus = 200;
200 m_localFile = new QFile(m_localFilename); 216 m_localFile = new QFile(m_localFilename);
201 m_localFile->open(QFile::WriteOnly); 217 m_localFile->open(QFile::WriteOnly);
202 218
203 QString scheme = m_url.scheme().toLower(); 219 QString scheme = m_url.scheme().toLower();
204 220
221 #ifdef DEBUG_FILE_SOURCE
205 std::cerr << "FileSource::init: Don't have local copy of \"" 222 std::cerr << "FileSource::init: Don't have local copy of \""
206 << m_url.toString().toStdString() << "\", retrieving" << std::endl; 223 << m_url.toString().toStdString() << "\", retrieving" << std::endl;
224 #endif
207 225
208 if (scheme == "http") { 226 if (scheme == "http") {
209 initHttp(); 227 initHttp();
210 } else if (scheme == "ftp") { 228 } else if (scheme == "ftp") {
211 initFtp(); 229 initFtp();
221 if (m_refCountMap[m_url] > 0) { 239 if (m_refCountMap[m_url] > 0) {
222 // someone else has been doing the same thing at the same time, 240 // someone else has been doing the same thing at the same time,
223 // but has got there first 241 // but has got there first
224 cleanup(); 242 cleanup();
225 m_refCountMap[m_url]++; 243 m_refCountMap[m_url]++;
244 #ifdef DEBUG_FILE_SOURCE
226 std::cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << std::endl; 245 std::cerr << "FileSource::init: Another FileSource has got there first, abandoning our download and using theirs" << std::endl;
246 #endif
227 m_localFilename = m_remoteLocalMap[m_url]; 247 m_localFilename = m_remoteLocalMap[m_url];
228 m_refCounted = true; 248 m_refCounted = true;
229 m_ok = true; 249 m_ok = true;
230 if (!QFileInfo(m_localFilename).exists()) { 250 if (!QFileInfo(m_localFilename).exists()) {
231 m_lastStatus = 404; 251 m_lastStatus = 404;
303 // get the whole URL ready-encoded and then split it on "/" as 323 // get the whole URL ready-encoded and then split it on "/" as
304 // appropriate... 324 // appropriate...
305 325
306 QString path = "/" + QString(m_url.toEncoded()).section('/', 3); 326 QString path = "/" + QString(m_url.toEncoded()).section('/', 3);
307 327
328 #ifdef DEBUG_FILE_SOURCE
308 std::cerr << "FileSource: path is \"" 329 std::cerr << "FileSource: path is \""
309 << path.toStdString() << "\"" << std::endl; 330 << path.toStdString() << "\"" << std::endl;
331 #endif
310 332
311 m_http->get(path, m_localFile); 333 m_http->get(path, m_localFile);
312 } 334 }
313 335
314 void 336 void
385 { 407 {
386 waitForStatus(); 408 waitForStatus();
387 bool available = true; 409 bool available = true;
388 if (!m_ok) available = false; 410 if (!m_ok) available = false;
389 else available = (m_lastStatus / 100 == 2); 411 else available = (m_lastStatus / 100 == 2);
412 #ifdef DEBUG_FILE_SOURCE
390 std::cerr << "FileSource::isAvailable: " << (available ? "yes" : "no") 413 std::cerr << "FileSource::isAvailable: " << (available ? "yes" : "no")
391 << std::endl; 414 << std::endl;
415 #endif
392 return available; 416 return available;
393 } 417 }
394 418
395 void 419 void
396 FileSource::waitForStatus() 420 FileSource::waitForStatus()
478 { 502 {
479 m_lastStatus = resp.statusCode(); 503 m_lastStatus = resp.statusCode();
480 if (m_lastStatus / 100 >= 4) { 504 if (m_lastStatus / 100 >= 4) {
481 m_errorString = QString("%1 %2") 505 m_errorString = QString("%1 %2")
482 .arg(resp.statusCode()).arg(resp.reasonPhrase()); 506 .arg(resp.statusCode()).arg(resp.reasonPhrase());
507 #ifdef DEBUG_FILE_SOURCE
483 std::cerr << "FileSource::responseHeaderReceived: " 508 std::cerr << "FileSource::responseHeaderReceived: "
484 << m_errorString.toStdString() << std::endl; 509 << m_errorString.toStdString() << std::endl;
510 #endif
485 } else { 511 } else {
512 #ifdef DEBUG_FILE_SOURCE
486 std::cerr << "FileSource::responseHeaderReceived: " 513 std::cerr << "FileSource::responseHeaderReceived: "
487 << m_lastStatus << std::endl; 514 << m_lastStatus << std::endl;
515 #endif
488 if (resp.hasContentType()) m_contentType = resp.contentType(); 516 if (resp.hasContentType()) m_contentType = resp.contentType();
489 } 517 }
490 emit statusAvailable(); 518 emit statusAvailable();
491 } 519 }
492 520
493 void 521 void
494 FileSource::ftpCommandFinished(int id, bool error) 522 FileSource::ftpCommandFinished(int id, bool error)
495 { 523 {
524 #ifdef DEBUG_FILE_SOURCE
496 std::cerr << "FileSource::ftpCommandFinished(" << id << ", " << error << ")" << std::endl; 525 std::cerr << "FileSource::ftpCommandFinished(" << id << ", " << error << ")" << std::endl;
526 #endif
497 527
498 if (!m_ftp) return; 528 if (!m_ftp) return;
499 529
500 QFtp::Command command = m_ftp->currentCommand(); 530 QFtp::Command command = m_ftp->currentCommand();
501 531
502 if (!error) { 532 if (!error) {
533 #ifdef DEBUG_FILE_SOURCE
503 std::cerr << "FileSource::ftpCommandFinished: success for command " 534 std::cerr << "FileSource::ftpCommandFinished: success for command "
504 << command << std::endl; 535 << command << std::endl;
536 #endif
505 return; 537 return;
506 } 538 }
507 539
508 if (command == QFtp::ConnectToHost) { 540 if (command == QFtp::ConnectToHost) {
509 m_errorString = tr("Failed to connect to FTP server"); 541 m_errorString = tr("Failed to connect to FTP server");
519 } 551 }
520 552
521 void 553 void
522 FileSource::dataTransferProgress(qint64 done, qint64 total) 554 FileSource::dataTransferProgress(qint64 done, qint64 total)
523 { 555 {
524 if (!m_progressDialog) return;
525
526 int percent = int((double(done) / double(total)) * 100.0 - 0.1); 556 int percent = int((double(done) / double(total)) * 100.0 - 0.1);
527 emit progress(percent); 557 emit progress(percent);
558
559 if (!m_progressDialog) return;
528 560
529 if (percent > 0) { 561 if (percent > 0) {
530 m_progressDialog->setValue(percent); 562 m_progressDialog->setValue(percent);
531 m_progressDialog->show(); 563 m_progressDialog->show();
532 } 564 }
543 } 575 }
544 576
545 void 577 void
546 FileSource::done(bool error) 578 FileSource::done(bool error)
547 { 579 {
580 emit progress(100);
581
582 #ifdef DEBUG_FILE_SOURCE
548 std::cerr << "FileSource::done(" << error << ")" << std::endl; 583 std::cerr << "FileSource::done(" << error << ")" << std::endl;
584 #endif
549 585
550 if (m_done) return; 586 if (m_done) return;
551
552 emit progress(100);
553 587
554 if (error) { 588 if (error) {
555 if (m_http) { 589 if (m_http) {
556 m_errorString = m_http->errorString(); 590 m_errorString = m_http->errorString();
557 } else if (m_ftp) { 591 } else if (m_ftp) {
575 error = true; 609 error = true;
576 } 610 }
577 } 611 }
578 612
579 if (error) { 613 if (error) {
614 #ifdef DEBUG_FILE_SOURCE
580 std::cerr << "FileSource::done: error is " << error << ", deleting cache file" << std::endl; 615 std::cerr << "FileSource::done: error is " << error << ", deleting cache file" << std::endl;
616 #endif
581 deleteCacheFile(); 617 deleteCacheFile();
582 } 618 }
583 619
584 m_ok = !error; 620 m_ok = !error;
585 m_done = true; 621 m_done = true;
587 } 623 }
588 624
589 void 625 void
590 FileSource::deleteCacheFile() 626 FileSource::deleteCacheFile()
591 { 627 {
628 #ifdef DEBUG_FILE_SOURCE
592 std::cerr << "FileSource::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl; 629 std::cerr << "FileSource::deleteCacheFile(\"" << m_localFilename.toStdString() << "\")" << std::endl;
630 #endif
593 631
594 cleanup(); 632 cleanup();
595 633
596 if (m_localFilename == "") { 634 if (m_localFilename == "") {
597 return; 635 return;
598 } 636 }
599 637
600 if (!isRemote()) { 638 if (!isRemote()) {
639 #ifdef DEBUG_FILE_SOURCE
601 std::cerr << "not a cache file" << std::endl; 640 std::cerr << "not a cache file" << std::endl;
641 #endif
602 return; 642 return;
603 } 643 }
604 644
605 if (m_refCounted) { 645 if (m_refCounted) {
606 646
607 QMutexLocker locker(&m_mapMutex); 647 QMutexLocker locker(&m_mapMutex);
608 m_refCounted = false; 648 m_refCounted = false;
609 649
610 if (m_refCountMap[m_url] > 0) { 650 if (m_refCountMap[m_url] > 0) {
611 m_refCountMap[m_url]--; 651 m_refCountMap[m_url]--;
652 #ifdef DEBUG_FILE_SOURCE
612 std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl; 653 std::cerr << "reduced ref count to " << m_refCountMap[m_url] << std::endl;
654 #endif
613 if (m_refCountMap[m_url] > 0) { 655 if (m_refCountMap[m_url] > 0) {
614 m_done = true; 656 m_done = true;
615 return; 657 return;
616 } 658 }
617 } 659 }
618 } 660 }
619 661
620 m_fileCreationMutex.lock(); 662 m_fileCreationMutex.lock();
621 663
622 if (!QFile(m_localFilename).remove()) { 664 if (!QFile(m_localFilename).remove()) {
665 #ifdef DEBUG_FILE_SOURCE
623 std::cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl; 666 std::cerr << "FileSource::deleteCacheFile: ERROR: Failed to delete file \"" << m_localFilename.toStdString() << "\"" << std::endl;
667 #endif
624 } else { 668 } else {
669 #ifdef DEBUG_FILE_SOURCE
625 std::cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl; 670 std::cerr << "FileSource::deleteCacheFile: Deleted cache file \"" << m_localFilename.toStdString() << "\"" << std::endl;
671 #endif
626 m_localFilename = ""; 672 m_localFilename = "";
627 } 673 }
628 674
629 m_fileCreationMutex.unlock(); 675 m_fileCreationMutex.unlock();
630 676
641 FileSource::createCacheFile() 687 FileSource::createCacheFile()
642 { 688 {
643 { 689 {
644 QMutexLocker locker(&m_mapMutex); 690 QMutexLocker locker(&m_mapMutex);
645 691
692 #ifdef DEBUG_FILE_SOURCE
646 std::cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl; 693 std::cerr << "FileSource::createCacheFile: refcount is " << m_refCountMap[m_url] << std::endl;
694 #endif
647 695
648 if (m_refCountMap[m_url] > 0) { 696 if (m_refCountMap[m_url] > 0) {
649 m_refCountMap[m_url]++; 697 m_refCountMap[m_url]++;
650 m_localFilename = m_remoteLocalMap[m_url]; 698 m_localFilename = m_remoteLocalMap[m_url];
699 #ifdef DEBUG_FILE_SOURCE
651 std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl; 700 std::cerr << "raised it to " << m_refCountMap[m_url] << std::endl;
701 #endif
652 m_refCounted = true; 702 m_refCounted = true;
653 return true; 703 return true;
654 } 704 }
655 } 705 }
656 706
657 QDir dir; 707 QDir dir;
658 try { 708 try {
659 dir = TempDirectory::getInstance()->getSubDirectoryPath("download"); 709 dir = TempDirectory::getInstance()->getSubDirectoryPath("download");
660 } catch (DirectoryCreationFailed f) { 710 } catch (DirectoryCreationFailed f) {
711 #ifdef DEBUG_FILE_SOURCE
661 std::cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl; 712 std::cerr << "FileSource::createCacheFile: ERROR: Failed to create temporary directory: " << f.what() << std::endl;
713 #endif
662 return ""; 714 return "";
663 } 715 }
664 716
665 QString filepart = m_url.path().section('/', -1, -1, 717 QString filepart = m_url.path().section('/', -1, -1,
666 QString::SectionSkipEmpty); 718 QString::SectionSkipEmpty);
680 filename = QString("%1.%2").arg(base).arg(extension); 732 filename = QString("%1.%2").arg(base).arg(extension);
681 } 733 }
682 734
683 QString filepath(dir.filePath(filename)); 735 QString filepath(dir.filePath(filename));
684 736
737 #ifdef DEBUG_FILE_SOURCE
685 std::cerr << "FileSource::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl; 738 std::cerr << "FileSource::createCacheFile: URL is \"" << m_url.toString().toStdString() << "\", dir is \"" << dir.path().toStdString() << "\", base \"" << base.toStdString() << "\", extension \"" << extension.toStdString() << "\", filebase \"" << filename.toStdString() << "\", filename \"" << filepath.toStdString() << "\"" << std::endl;
739 #endif
686 740
687 QMutexLocker fcLocker(&m_fileCreationMutex); 741 QMutexLocker fcLocker(&m_fileCreationMutex);
688 742
689 ++m_count; 743 ++m_count;
690 744
691 if (QFileInfo(filepath).exists() || 745 if (QFileInfo(filepath).exists() ||
692 !QFile(filepath).open(QFile::WriteOnly)) { 746 !QFile(filepath).open(QFile::WriteOnly)) {
693 747
748 #ifdef DEBUG_FILE_SOURCE
694 std::cerr << "FileSource::createCacheFile: Failed to create local file \"" 749 std::cerr << "FileSource::createCacheFile: Failed to create local file \""
695 << filepath.toStdString() << "\" for URL \"" 750 << filepath.toStdString() << "\" for URL \""
696 << m_url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl; 751 << m_url.toString().toStdString() << "\" (or file already exists): appending suffix instead" << std::endl;
697 752 #endif
698 753
699 if (extension == "") { 754 if (extension == "") {
700 filename = QString("%1_%2").arg(base).arg(m_count); 755 filename = QString("%1_%2").arg(base).arg(m_count);
701 } else { 756 } else {
702 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension); 757 filename = QString("%1_%2.%3").arg(base).arg(m_count).arg(extension);
704 filepath = dir.filePath(filename); 759 filepath = dir.filePath(filename);
705 760
706 if (QFileInfo(filepath).exists() || 761 if (QFileInfo(filepath).exists() ||
707 !QFile(filepath).open(QFile::WriteOnly)) { 762 !QFile(filepath).open(QFile::WriteOnly)) {
708 763
764 #ifdef DEBUG_FILE_SOURCE
709 std::cerr << "FileSource::createCacheFile: ERROR: Failed to create local file \"" 765 std::cerr << "FileSource::createCacheFile: ERROR: Failed to create local file \""
710 << filepath.toStdString() << "\" for URL \"" 766 << filepath.toStdString() << "\" for URL \""
711 << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl; 767 << m_url.toString().toStdString() << "\" (or file already exists)" << std::endl;
768 #endif
712 769
713 return ""; 770 return "";
714 } 771 }
715 } 772 }
716 773
774 #ifdef DEBUG_FILE_SOURCE
717 std::cerr << "FileSource::createCacheFile: url " 775 std::cerr << "FileSource::createCacheFile: url "
718 << m_url.toString().toStdString() << " -> local filename " 776 << m_url.toString().toStdString() << " -> local filename "
719 << filepath.toStdString() << std::endl; 777 << filepath.toStdString() << std::endl;
778 #endif
720 779
721 m_localFilename = filepath; 780 m_localFilename = filepath;
722 781
723 return false; 782 return false;
724 } 783 }
784
785 FileSourceProgressPrinter::FileSourceProgressPrinter() :
786 m_lastProgress(0)
787 {
788 }
789
790 FileSourceProgressPrinter::~FileSourceProgressPrinter()
791 {
792 if (m_lastProgress > 0 && m_lastProgress != 100) {
793 std::cerr << "\r\n";
794 }
795 }
796
797 void
798 FileSourceProgressPrinter::progress(int progress)
799 {
800 if (progress == m_lastProgress) return;
801 if (progress == 100) std::cerr << "\r\n";
802 else std::cerr << "\r" << progress << "%";
803 m_lastProgress = progress;
804 }
805