v2.0.0
Loading...
Searching...
No Matches
bids_path.cpp
Go to the documentation of this file.
1//=============================================================================================================
12
13//=============================================================================================================
14// INCLUDES
15//=============================================================================================================
16
17#include "bids_path.h"
18#include "bids_const.h"
19
20//=============================================================================================================
21// QT INCLUDES
22//=============================================================================================================
23
24#include <QFileInfo>
25#include <QDirIterator>
26#include <QRegularExpression>
27
28//=============================================================================================================
29// USED NAMESPACES
30//=============================================================================================================
31
32using namespace BIDSLIB;
33
34//=============================================================================================================
35// DEFINE MEMBER METHODS
36//=============================================================================================================
37
41
42//=============================================================================================================
43
44BIDSPath::BIDSPath(const QString& sRoot,
45 const QString& sSubject,
46 const QString& sSession,
47 const QString& sTask,
48 const QString& sDatatype,
49 const QString& sSuffix,
50 const QString& sExtension)
51 : m_sRoot(sRoot)
52 , m_sSubject(sSubject)
53 , m_sSession(sSession)
54 , m_sTask(sTask)
55 , m_sDatatype(sDatatype)
56 , m_sSuffix(sSuffix)
57 , m_sExtension(sExtension)
58{
59}
60
61//=============================================================================================================
62
64 : m_sRoot(other.m_sRoot)
65 , m_sSubject(other.m_sSubject)
66 , m_sSession(other.m_sSession)
67 , m_sTask(other.m_sTask)
68 , m_sAcquisition(other.m_sAcquisition)
69 , m_sRun(other.m_sRun)
70 , m_sProcessing(other.m_sProcessing)
71 , m_sSpace(other.m_sSpace)
72 , m_sRecording(other.m_sRecording)
73 , m_sSplit(other.m_sSplit)
74 , m_sDescription(other.m_sDescription)
75 , m_sDatatype(other.m_sDatatype)
76 , m_sSuffix(other.m_sSuffix)
77 , m_sExtension(other.m_sExtension)
78{
79}
80
81//=============================================================================================================
82
86
87//=============================================================================================================
88// Setters
89//=============================================================================================================
90
91void BIDSPath::setRoot(const QString& sRoot) { m_sRoot = sRoot; }
92void BIDSPath::setSubject(const QString& sSubject) { m_sSubject = sSubject; }
93void BIDSPath::setSession(const QString& sSession) { m_sSession = sSession; }
94void BIDSPath::setTask(const QString& sTask) { m_sTask = sTask; }
95void BIDSPath::setAcquisition(const QString& sAcq) { m_sAcquisition = sAcq; }
96void BIDSPath::setRun(const QString& sRun) { m_sRun = zeroPad(sRun); }
97void BIDSPath::setProcessing(const QString& sProc) { m_sProcessing = sProc; }
98void BIDSPath::setSpace(const QString& sSpace) { m_sSpace = sSpace; }
99void BIDSPath::setRecording(const QString& sRec) { m_sRecording = sRec; }
100void BIDSPath::setSplit(const QString& sSplit) { m_sSplit = zeroPad(sSplit); }
101void BIDSPath::setDescription(const QString& sDesc) { m_sDescription = sDesc; }
102void BIDSPath::setDatatype(const QString& sDatatype) { m_sDatatype = sDatatype; }
103void BIDSPath::setSuffix(const QString& sSuffix) { m_sSuffix = sSuffix; }
104void BIDSPath::setExtension(const QString& sExtension) { m_sExtension = sExtension; }
105
106//=============================================================================================================
107// Getters
108//=============================================================================================================
109
110QString BIDSPath::root() const { return m_sRoot; }
111QString BIDSPath::subject() const { return m_sSubject; }
112QString BIDSPath::session() const { return m_sSession; }
113QString BIDSPath::task() const { return m_sTask; }
114QString BIDSPath::acquisition() const { return m_sAcquisition; }
115QString BIDSPath::run() const { return m_sRun; }
116QString BIDSPath::processing() const { return m_sProcessing; }
117QString BIDSPath::space() const { return m_sSpace; }
118QString BIDSPath::recording() const { return m_sRecording; }
119QString BIDSPath::split() const { return m_sSplit; }
120QString BIDSPath::description() const { return m_sDescription; }
121QString BIDSPath::datatype() const { return m_sDatatype; }
122QString BIDSPath::suffix() const { return m_sSuffix; }
123QString BIDSPath::extension() const { return m_sExtension; }
124
125//=============================================================================================================
126// Path construction
127//=============================================================================================================
128
129QString BIDSPath::basename() const
130{
131 QStringList parts;
132
133 // Build ordered entity key-value pairs
134 if(!m_sSubject.isEmpty())
135 parts << QStringLiteral("sub-") + m_sSubject;
136 if(!m_sSession.isEmpty())
137 parts << QStringLiteral("ses-") + m_sSession;
138 if(!m_sTask.isEmpty())
139 parts << QStringLiteral("task-") + m_sTask;
140 if(!m_sAcquisition.isEmpty())
141 parts << QStringLiteral("acq-") + m_sAcquisition;
142 if(!m_sRun.isEmpty())
143 parts << QStringLiteral("run-") + m_sRun;
144 if(!m_sProcessing.isEmpty())
145 parts << QStringLiteral("proc-") + m_sProcessing;
146 if(!m_sSpace.isEmpty())
147 parts << QStringLiteral("space-") + m_sSpace;
148 if(!m_sRecording.isEmpty())
149 parts << QStringLiteral("rec-") + m_sRecording;
150 if(!m_sSplit.isEmpty())
151 parts << QStringLiteral("split-") + m_sSplit;
152 if(!m_sDescription.isEmpty())
153 parts << QStringLiteral("desc-") + m_sDescription;
154
155 // Append suffix
156 if(!m_sSuffix.isEmpty())
157 parts << m_sSuffix;
158
159 QString name = parts.join(QStringLiteral("_"));
160
161 // Append extension
162 if(!m_sExtension.isEmpty())
163 name += m_sExtension;
164
165 return name;
166}
167
168//=============================================================================================================
169
170QString BIDSPath::directory() const
171{
172 QDir dir(m_sRoot);
173
174 if(!m_sSubject.isEmpty())
175 dir = QDir(dir.filePath(QStringLiteral("sub-") + m_sSubject));
176
177 if(!m_sSession.isEmpty())
178 dir = QDir(dir.filePath(QStringLiteral("ses-") + m_sSession));
179
180 if(!m_sDatatype.isEmpty())
181 dir = QDir(dir.filePath(m_sDatatype));
182
183 return dir.path() + QDir::separator();
184}
185
186//=============================================================================================================
187
188QString BIDSPath::filePath() const
189{
190 return QDir(directory()).filePath(basename());
191}
192
193//=============================================================================================================
194// Convenience methods
195//=============================================================================================================
196
197BIDSPath BIDSPath::withSuffix(const QString& sSuffix, const QString& sExtension) const
198{
199 BIDSPath result(*this);
200 result.m_sSuffix = sSuffix;
201 result.m_sExtension = sExtension;
202 return result;
203}
204
205//=============================================================================================================
206
208{
209 return withSuffix(QStringLiteral("channels"), QStringLiteral(".tsv"));
210}
211
212//=============================================================================================================
213
215{
216 // Electrodes file typically doesn't include task entity
217 BIDSPath result(*this);
218 result.m_sTask.clear();
219 result.m_sRun.clear();
220 result.m_sSuffix = QStringLiteral("electrodes");
221 result.m_sExtension = QStringLiteral(".tsv");
222 return result;
223}
224
225//=============================================================================================================
226
228{
229 // Coordsystem file typically doesn't include task entity
230 BIDSPath result(*this);
231 result.m_sTask.clear();
232 result.m_sRun.clear();
233 result.m_sSuffix = QStringLiteral("coordsystem");
234 result.m_sExtension = QStringLiteral(".json");
235 return result;
236}
237
238//=============================================================================================================
239
241{
242 return withSuffix(QStringLiteral("events"), QStringLiteral(".tsv"));
243}
244
245//=============================================================================================================
246
248{
249 return withSuffix(m_sSuffix.isEmpty() ? m_sDatatype : m_sSuffix,
250 QStringLiteral(".json"));
251}
252
253//=============================================================================================================
254
256{
257 return QFileInfo::exists(filePath());
258}
259
260//=============================================================================================================
261
263{
264 QDir dir(directory());
265 if(dir.exists())
266 return true;
267 return dir.mkpath(QStringLiteral("."));
268}
269
270//=============================================================================================================
271
272QList<BIDSPath> BIDSPath::match() const
273{
274 QList<BIDSPath> results;
275
276 QDir dir(directory());
277 if(!dir.exists())
278 return results;
279
280 // Build a glob pattern from the set entities
281 QString pattern = QStringLiteral("sub-") + (m_sSubject.isEmpty() ? QStringLiteral("*") : m_sSubject);
282
283 if(!m_sSession.isEmpty())
284 pattern += QStringLiteral("_ses-") + m_sSession;
285 else
286 pattern += QStringLiteral("*");
287
288 pattern += QStringLiteral("*"); // match remaining entities
289
290 if(!m_sSuffix.isEmpty())
291 pattern += QStringLiteral("_") + m_sSuffix;
292
293 if(!m_sExtension.isEmpty())
294 pattern += m_sExtension;
295 else
296 pattern += QStringLiteral(".*");
297
298 QStringList entries = dir.entryList({pattern}, QDir::Files);
299
300 // Parse matching filenames back into BIDSPath objects
301 static const QRegularExpression entityRx(QStringLiteral("(\\w+)-(\\w+)"));
302 for(const QString& entry : entries) {
303 BIDSPath p;
304 p.setRoot(m_sRoot);
305 p.setDatatype(m_sDatatype);
306
307 // Extract entities from filename
308 auto it = entityRx.globalMatch(entry);
309 while(it.hasNext()) {
310 auto match = it.next();
311 const QString key = match.captured(1);
312 const QString val = match.captured(2);
313
314 if(key == QStringLiteral("sub")) p.setSubject(val);
315 else if(key == QStringLiteral("ses")) p.setSession(val);
316 else if(key == QStringLiteral("task")) p.setTask(val);
317 else if(key == QStringLiteral("acq")) p.setAcquisition(val);
318 else if(key == QStringLiteral("run")) p.setRun(val);
319 else if(key == QStringLiteral("proc")) p.setProcessing(val);
320 else if(key == QStringLiteral("space")) p.setSpace(val);
321 else if(key == QStringLiteral("rec")) p.setRecording(val);
322 else if(key == QStringLiteral("split")) p.setSplit(val);
323 else if(key == QStringLiteral("desc")) p.setDescription(val);
324 }
325
326 // Extract suffix and extension
327 // The suffix is the last _<word> before the extension
328 int dotIdx = entry.lastIndexOf(QLatin1Char('.'));
329 if(dotIdx > 0) {
330 p.setExtension(entry.mid(dotIdx));
331 QString nameWithoutExt = entry.left(dotIdx);
332 int lastUnder = nameWithoutExt.lastIndexOf(QLatin1Char('_'));
333 if(lastUnder >= 0) {
334 p.setSuffix(nameWithoutExt.mid(lastUnder + 1));
335 }
336 }
337
338 results.append(p);
339 }
340
341 return results;
342}
343
344//=============================================================================================================
345// Validation
346//=============================================================================================================
347
348bool BIDSPath::isValidEntityValue(const QString& sValue)
349{
350 if(sValue.isEmpty())
351 return true;
352 // Entity values must not contain -, _, or /
353 static const QRegularExpression forbidden(QStringLiteral("[\\-_/]"));
354 return !sValue.contains(forbidden);
355}
356
357//=============================================================================================================
358// Operators
359//=============================================================================================================
360
362{
363 if(this != &other) {
364 m_sRoot = other.m_sRoot;
365 m_sSubject = other.m_sSubject;
366 m_sSession = other.m_sSession;
367 m_sTask = other.m_sTask;
368 m_sAcquisition = other.m_sAcquisition;
369 m_sRun = other.m_sRun;
370 m_sProcessing = other.m_sProcessing;
371 m_sSpace = other.m_sSpace;
372 m_sRecording = other.m_sRecording;
373 m_sSplit = other.m_sSplit;
374 m_sDescription = other.m_sDescription;
375 m_sDatatype = other.m_sDatatype;
376 m_sSuffix = other.m_sSuffix;
377 m_sExtension = other.m_sExtension;
378 }
379 return *this;
380}
381
382//=============================================================================================================
383
384bool operator==(const BIDSPath& a, const BIDSPath& b)
385{
386 return a.root() == b.root() &&
387 a.subject() == b.subject() &&
388 a.session() == b.session() &&
389 a.task() == b.task() &&
390 a.acquisition() == b.acquisition() &&
391 a.run() == b.run() &&
392 a.processing() == b.processing() &&
393 a.space() == b.space() &&
394 a.recording() == b.recording() &&
395 a.split() == b.split() &&
396 a.description() == b.description() &&
397 a.datatype() == b.datatype() &&
398 a.suffix() == b.suffix() &&
399 a.extension() == b.extension();
400}
401
402//=============================================================================================================
403// Static helpers
404//=============================================================================================================
405
406QString BIDSPath::zeroPad(const QString& sValue)
407{
408 if(sValue.isEmpty())
409 return sValue;
410
411 bool ok = false;
412 int num = sValue.toInt(&ok);
413 if(ok) {
414 return QStringLiteral("%1").arg(num, 2, 10, QLatin1Char('0'));
415 }
416 return sValue;
417}
Programmatic construction and matching of BIDS-compliant directories, filenames and sidecar paths.
Centralised BIDS vocabulary: datatype / suffix / extension whitelists, FIFF↔BIDS channel-type and coo...
bool operator==(const BIDSPath &a, const BIDSPath &b)
BIDS dataset reading, writing, path construction, and sidecar metadata handling for iEEG/EEG/MEG.
QString subject() const
void setSession(const QString &sSession)
Definition bids_path.cpp:93
void setSpace(const QString &sSpace)
Definition bids_path.cpp:98
QString filePath() const
QString session() const
void setRecording(const QString &sRecording)
Definition bids_path.cpp:99
void setSubject(const QString &sSubject)
Definition bids_path.cpp:92
QString split() const
void setDatatype(const QString &sDatatype)
QString description() const
BIDSPath electrodesTsvPath() const
bool exists() const
QString run() const
void setDescription(const QString &sDescription)
void setRun(const QString &sRun)
Definition bids_path.cpp:96
void setAcquisition(const QString &sAcquisition)
Definition bids_path.cpp:95
QString root() const
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)
Definition bids_path.cpp:94
BIDSPath & operator=(const BIDSPath &other)
QString extension() const
void setProcessing(const QString &sProcessing)
Definition bids_path.cpp:97
void setRoot(const QString &sRoot)
Definition bids_path.cpp:91
QString space() const
QString suffix() const
QString basename() const
void setSuffix(const QString &sSuffix)
QString processing() const
QString recording() const
BIDSPath coordsystemJsonPath() const
QList< BIDSPath > match() const
QString task() const
BIDSPath withSuffix(const QString &sSuffix, const QString &sExtension) const
BIDSPath eventsTsvPath() const
void setExtension(const QString &sExtension)
bool mkdirs() const
QString datatype() const
BIDSPath sidecarJsonPath() const