27#include <QDirIterator>
28#include <QRegularExpression>
47 const QString& sSubject,
48 const QString& sSession,
50 const QString& sDatatype,
51 const QString& sSuffix,
52 const QString& sExtension)
57, m_sDatatype(sDatatype)
59, m_sExtension(sExtension)
66: m_sRoot(other.m_sRoot)
67, m_sSubject(other.m_sSubject)
68, m_sSession(other.m_sSession)
69, m_sTask(other.m_sTask)
70, m_sAcquisition(other.m_sAcquisition)
72, m_sProcessing(other.m_sProcessing)
73, m_sSpace(other.m_sSpace)
74, m_sRecording(other.m_sRecording)
75, m_sSplit(other.m_sSplit)
76, m_sDescription(other.m_sDescription)
77, m_sDatatype(other.m_sDatatype)
78, m_sSuffix(other.m_sSuffix)
79, m_sExtension(other.m_sExtension)
99 m_sSubject = sSubject;
103 m_sSession = sSession;
111 m_sAcquisition = sAcq;
115 m_sRun = zeroPad(sRun);
119 m_sProcessing = sProc;
131 m_sSplit = zeroPad(sSplit);
135 m_sDescription = sDesc;
139 m_sDatatype = sDatatype;
147 m_sExtension = sExtension;
172 return m_sAcquisition;
180 return m_sProcessing;
196 return m_sDescription;
220 if (!m_sSubject.isEmpty())
221 parts << QStringLiteral(
"sub-") + m_sSubject;
222 if (!m_sSession.isEmpty())
223 parts << QStringLiteral(
"ses-") + m_sSession;
224 if (!m_sTask.isEmpty())
225 parts << QStringLiteral(
"task-") + m_sTask;
226 if (!m_sAcquisition.isEmpty())
227 parts << QStringLiteral(
"acq-") + m_sAcquisition;
228 if (!m_sRun.isEmpty())
229 parts << QStringLiteral(
"run-") + m_sRun;
230 if (!m_sProcessing.isEmpty())
231 parts << QStringLiteral(
"proc-") + m_sProcessing;
232 if (!m_sSpace.isEmpty())
233 parts << QStringLiteral(
"space-") + m_sSpace;
234 if (!m_sRecording.isEmpty())
235 parts << QStringLiteral(
"recording-") + m_sRecording;
236 if (!m_sSplit.isEmpty())
237 parts << QStringLiteral(
"split-") + m_sSplit;
238 if (!m_sDescription.isEmpty())
239 parts << QStringLiteral(
"desc-") + m_sDescription;
242 if (!m_sSuffix.isEmpty())
245 QString name = parts.join(QStringLiteral(
"_"));
248 if (!m_sExtension.isEmpty())
249 name += m_sExtension;
260 if (!m_sSubject.isEmpty())
261 dir = QDir(dir.filePath(QStringLiteral(
"sub-") + m_sSubject));
263 if (!m_sSession.isEmpty())
264 dir = QDir(dir.filePath(QStringLiteral(
"ses-") + m_sSession));
266 if (!m_sDatatype.isEmpty())
267 dir = QDir(dir.filePath(m_sDatatype));
269 return dir.path() + QDir::separator();
286 result.m_sSuffix = sSuffix;
287 result.m_sExtension = sExtension;
295 return withSuffix(QStringLiteral(
"channels"), QStringLiteral(
".tsv"));
304 result.m_sTask.clear();
305 result.m_sRun.clear();
306 result.m_sSuffix = QStringLiteral(
"electrodes");
307 result.m_sExtension = QStringLiteral(
".tsv");
317 result.m_sTask.clear();
318 result.m_sRun.clear();
319 result.m_sSuffix = QStringLiteral(
"coordsystem");
320 result.m_sExtension = QStringLiteral(
".json");
328 return withSuffix(QStringLiteral(
"events"), QStringLiteral(
".tsv"));
335 return withSuffix(m_sSuffix.isEmpty() ? m_sDatatype : m_sSuffix,
336 QStringLiteral(
".json"));
343 return QFileInfo::exists(
filePath());
353 return dir.mkpath(QStringLiteral(
"."));
361 QList<BIDSPath> results;
362 if (m_sRoot.isEmpty() || !QDir(m_sRoot).
exists())
365 QDirIterator it(m_sRoot, QDir::Files, QDirIterator::Subdirectories);
366 while (it.hasNext()) {
367 const QFileInfo file(it.next());
368 const QString name = file.fileName();
369 const int dot = name.indexOf(QLatin1Char(
'.'));
376 const QString dirName = file.dir().dirName();
377 if (!dirName.startsWith(QStringLiteral(
"sub-")) && !dirName.startsWith(QStringLiteral(
"ses-")))
381 const QStringList parts = name.left(dot).split(QLatin1Char(
'_'));
382 for (
int i = 0; i < parts.size() && valid; ++i) {
383 const int dash = parts[i].indexOf(QLatin1Char(
'-'));
386 valid = (i == parts.size() - 1);
390 const QString key = parts[i].left(dash);
391 const QString val = parts[i].mid(dash + 1);
392 if (key == QStringLiteral(
"sub"))
394 else if (key == QStringLiteral(
"ses"))
396 else if (key == QStringLiteral(
"task"))
398 else if (key == QStringLiteral(
"acq"))
400 else if (key == QStringLiteral(
"run"))
402 else if (key == QStringLiteral(
"proc"))
404 else if (key == QStringLiteral(
"space"))
406 else if (key == QStringLiteral(
"recording"))
408 else if (key == QStringLiteral(
"split"))
410 else if (key == QStringLiteral(
"desc"))
413 if (!valid || p.
subject().isEmpty())
416 const QList<std::pair<QString, QString>> wanted{
417 {m_sSubject, p.
subject()}, {m_sSession, p.
session()}, {m_sTask, p.
task()}, {m_sAcquisition, p.
acquisition()}, {m_sRun, p.
run()}, {m_sProcessing, p.
processing()}, {m_sSpace, p.
space()}, {m_sRecording, p.
recording()}, {m_sSplit, p.
split()}, {m_sDescription, p.
description()}, {m_sDatatype, p.
datatype()}, {m_sSuffix, p.
suffix()}, {m_sExtension, p.
extension()}};
418 if (std::all_of(wanted.cbegin(), wanted.cend(), [](
const auto& w) { return w.first.isEmpty() || w.first == w.second; }))
422 std::sort(results.begin(), results.end(), [](
const BIDSPath& a,
const BIDSPath& b) { return a.filePath() < b.filePath(); });
432 if (sValue.isEmpty())
435 static const QRegularExpression forbidden(QStringLiteral(
"[\\-_/]"));
436 return !sValue.contains(forbidden);
445 if (
this != &other) {
446 m_sRoot = other.m_sRoot;
447 m_sSubject = other.m_sSubject;
448 m_sSession = other.m_sSession;
449 m_sTask = other.m_sTask;
450 m_sAcquisition = other.m_sAcquisition;
451 m_sRun = other.m_sRun;
452 m_sProcessing = other.m_sProcessing;
453 m_sSpace = other.m_sSpace;
454 m_sRecording = other.m_sRecording;
455 m_sSplit = other.m_sSplit;
456 m_sDescription = other.m_sDescription;
457 m_sDatatype = other.m_sDatatype;
458 m_sSuffix = other.m_sSuffix;
459 m_sExtension = other.m_sExtension;
468QString BIDSPath::zeroPad(
const QString& sValue)
470 if (sValue.isEmpty())
474 int num = sValue.toInt(&ok);
476 return QStringLiteral(
"%1").arg(num, 2, 10, QLatin1Char(
'0'));
Centralised BIDS vocabulary: datatype / suffix / extension whitelists, FIFF↔BIDS channel-type and coo...
Programmatic construction and matching of BIDS-compliant directories, filenames and sidecar paths.
BIDS dataset reading, writing, path construction, and sidecar metadata handling for iEEG/EEG/MEG.
void setSession(const QString &sSession)
void setSpace(const QString &sSpace)
void setRecording(const QString &sRecording)
void setSubject(const QString &sSubject)
void setDatatype(const QString &sDatatype)
QString description() const
BIDSPath electrodesTsvPath() const
void setDescription(const QString &sDescription)
void setRun(const QString &sRun)
void setAcquisition(const QString &sAcquisition)
void setSplit(const QString &sSplit)
BIDSPath channelsTsvPath() const
static bool isValidEntityValue(const QString &sValue)
QString directory() const
QString acquisition() const
void setTask(const QString &sTask)
BIDSPath & operator=(const BIDSPath &other)
QString extension() const
void setProcessing(const QString &sProcessing)
void setRoot(const QString &sRoot)
void setSuffix(const QString &sSuffix)
QString processing() const
QString recording() const
BIDSPath coordsystemJsonPath() const
QList< BIDSPath > match() const
BIDSPath withSuffix(const QString &sSuffix, const QString &sExtension) const
BIDSPath eventsTsvPath() const
void setExtension(const QString &sExtension)
BIDSPath sidecarJsonPath() const