• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

paulmthompson / WhiskerToolbox / 17920603410

22 Sep 2025 03:39PM UTC coverage: 71.97% (-0.05%) from 72.02%
17920603410

push

github

paulmthompson
all tests pass

277 of 288 new or added lines in 8 files covered. (96.18%)

520 existing lines in 35 files now uncovered.

40275 of 55961 relevant lines covered (71.97%)

1225.8 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

57.58
/src/DataManager/utils/TableView/core/TableView.cpp
1
#include "TableView.h"
2

3
#include "utils/TableView/adapters/DataManagerExtension.h"
4
#include "utils/TableView/columns/IColumn.h"
5
#include "utils/TableView/interfaces/ILineSource.h"
6
#include "utils/TableView/interfaces/IRowSelector.h"
7

8

9
#include <algorithm>
10
#include <set>
11
#include <stdexcept>
12

13
TableView::TableView(std::unique_ptr<IRowSelector> rowSelector,
112✔
14
                     std::shared_ptr<DataManagerExtension> dataManager)
112✔
15
    : m_rowSelector(std::move(rowSelector)),
112✔
16
      m_dataManager(std::move(dataManager)) {
112✔
17
    if (!m_rowSelector) {
112✔
18
        throw std::invalid_argument("IRowSelector cannot be null");
×
19
    }
20
    if (!m_dataManager) {
112✔
21
        throw std::invalid_argument("DataManagerExtension cannot be null");
×
22
    }
23
}
112✔
24

25
TableView::TableView(TableView && other) noexcept
47✔
26
    : m_rowSelector(std::move(other.m_rowSelector)),
47✔
27
      m_dataManager(std::move(other.m_dataManager)),
47✔
28
      m_columns(std::move(other.m_columns)),
47✔
29
      m_colNameToIndex(std::move(other.m_colNameToIndex)),
47✔
30
      m_planCache(std::move(other.m_planCache)),
47✔
31
      m_direct_entity_ids(std::move(other.m_direct_entity_ids)) {}
47✔
32

33
TableView & TableView::operator=(TableView && other) {
×
34
    if (this != &other) {
×
35
        m_rowSelector = std::move(other.m_rowSelector);
×
36
        m_dataManager = std::move(other.m_dataManager);
×
37
        m_columns = std::move(other.m_columns);
×
38
        m_colNameToIndex = std::move(other.m_colNameToIndex);
×
39
        m_planCache = std::move(other.m_planCache);
×
40
        m_direct_entity_ids = std::move(other.m_direct_entity_ids);
×
41
    }
42
    return *this;
×
43
}
44

45
size_t TableView::getRowCount() const {
322✔
46
    // Prefer expanded row count if any execution plan has entity-expanded rows cached
47
    for (auto const & entry : m_planCache) {
568✔
48
        if (!entry.second.getRows().empty()) {
265✔
49
            return entry.second.getRows().size();
19✔
50
        }
51
    }
52
    // If nothing cached yet, proactively attempt expansion using a line-source dependent column
53
    // Find a column whose source is an ILineSource
54
    for (auto const & column : m_columns) {
1,590✔
55
        try {
56
            auto const & dep = column->getSourceDependency();
1,301✔
57
            // Query line source without mutating the cache in a surprising way
58
            auto lineSource = m_dataManager->getLineSource(dep);
1,301✔
59
            if (lineSource) {
1,301✔
60
                // Trigger plan generation for this source to populate expansion rows
61
                auto & self = const_cast<TableView &>(*this);
14✔
62
                ExecutionPlan const & plan = self.getExecutionPlanFor(dep);
14✔
63
                if (!plan.getRows().empty()) {
14✔
64
                    return plan.getRows().size();
13✔
65
                }
66
                break; // only expand based on the first line source column
1✔
67
            }
68
        } catch (...) {
1,315✔
69
            // Ignore and continue
70
        }
×
71
    }
72
    return m_rowSelector->getRowCount();
290✔
73
}
74

75
size_t TableView::getColumnCount() const {
63✔
76
    return m_columns.size();
63✔
77
}
78

79
std::vector<std::string> TableView::getColumnNames() const {
64✔
80
    std::vector<std::string> names;
64✔
81
    names.reserve(m_columns.size());
64✔
82

83
    for (auto const & column: m_columns) {
338✔
84
        names.push_back(column->getName());
274✔
85
    }
86

87
    return names;
64✔
88
}
×
89

90
bool TableView::hasColumn(std::string const & name) const {
649✔
91
    return m_colNameToIndex.find(name) != m_colNameToIndex.end();
649✔
92
}
93

94
std::type_info const & TableView::getColumnType(std::string const & name) const {
160✔
95
    auto it = m_colNameToIndex.find(name);
160✔
96
    if (it == m_colNameToIndex.end()) {
160✔
97
        throw std::runtime_error("Column '" + name + "' not found in table");
×
98
    }
99
    
100
    return m_columns[it->second]->getType();
320✔
101
}
102

103
std::type_index TableView::getColumnTypeIndex(std::string const & name) const {
160✔
104
    return std::type_index(getColumnType(name));
160✔
105
}
106

107
ColumnDataVariant TableView::getColumnDataVariant(std::string const & name) {
118✔
108
    auto type_index = getColumnTypeIndex(name);
118✔
109
    
110
    // Dispatch from element type_index to vector type using template metaprogramming
111
    std::optional<ColumnDataVariant> result;
118✔
112
    
113
    for_each_type<SupportedColumnElementTypes>([&](auto, auto type_instance) {
1,180✔
114
        using ElementType = std::decay_t<decltype(type_instance)>;
115
        
116
        if (!result.has_value() && type_index == std::type_index(typeid(ElementType))) {
1,062✔
117
            // Found matching element type, get the vector data
118
            auto vectorData = getColumnValues<ElementType>(name);
118✔
119
            result = vectorData;  // This will construct the variant with the correct vector type
118✔
120
        }
118✔
121
    });
1,062✔
122

123
    if (!result.has_value()) {
118✔
124
        throw std::runtime_error("Unsupported column type: " + std::string(type_index.name()) + 
×
125
                                " for column: " + name);
×
126
    }
127
    
128
    return result.value();
236✔
129
}
118✔
130

131

132
void TableView::materializeAll() {
17✔
133
    std::set<std::string> materializing;
17✔
134

135
    for (auto const & column: m_columns) {
97✔
136
        if (!column->isMaterialized()) {
80✔
137
            materializeColumn(column->getName(), materializing);
80✔
138
        }
139
    }
140
}
34✔
141

142
void TableView::clearCache() {
×
143
    // Clear column caches
144
    for (auto & column: m_columns) {
×
145
        column->clearCache();
×
146
    }
147

148
    // Clear execution plan cache
149
    m_planCache.clear();
×
150
}
×
151

152
ExecutionPlan const & TableView::getExecutionPlanFor(std::string const & sourceName) {
277✔
153
    // Check cache first
154
    auto it = m_planCache.find(sourceName);
277✔
155
    if (it != m_planCache.end()) {
277✔
156
        return it->second;
156✔
157
    }
158

159
    // Generate new plan
160
    ExecutionPlan plan = generateExecutionPlan(sourceName);
121✔
161

162
    // Store in cache and return reference
163
    auto [insertedIt, inserted] = m_planCache.emplace(sourceName, std::move(plan));
121✔
164
    if (!inserted) {
121✔
165
        throw std::runtime_error("Failed to cache ExecutionPlan for source: " + sourceName);
×
166
    }
167

168
    return insertedIt->second;
121✔
169
}
121✔
170

171
void TableView::addColumn(std::shared_ptr<IColumn> column) {
401✔
172
    if (!column) {
401✔
173
        throw std::invalid_argument("Column cannot be null");
×
174
    }
175

176
    std::string const & name = column->getName();
401✔
177

178
    // Check for duplicate names
179
    if (hasColumn(name)) {
401✔
180
        throw std::runtime_error("Column '" + name + "' already exists");
×
181
    }
182

183
    // Add to collections
184
    size_t index = m_columns.size();
401✔
185
    m_columns.push_back(std::move(column));
401✔
186
    m_colNameToIndex[name] = index;
401✔
187
}
802✔
188

189
void TableView::materializeColumn(std::string const & columnName, std::set<std::string> & materializing) {
80✔
190
    // Check for circular dependencies
191
    if (materializing.find(columnName) != materializing.end()) {
80✔
192
        throw std::runtime_error("Circular dependency detected involving column: " + columnName);
×
193
    }
194

195
    // Check if column exists
196
    if (!hasColumn(columnName)) {
80✔
197
        throw std::runtime_error("Column '" + columnName + "' not found");
×
198
    }
199

200
    auto & column = m_columns[m_colNameToIndex[columnName]];
80✔
201

202
    // If already materialized, nothing to do
203
    if (column->isMaterialized()) {
80✔
204
        return;
×
205
    }
206

207
    // Mark as being materialized
208
    materializing.insert(columnName);
80✔
209

210
    // Materialize dependencies first
211
    auto const dependencies = column->getDependencies();
80✔
212
    for (auto const & dependency: dependencies) {
80✔
213
        if (hasColumn(dependency)) {
×
214
            materializeColumn(dependency, materializing);
×
215
        }
216
    }
217

218
    // Materialize this column
219
    column->materialize(this);// Use the IColumn interface method
80✔
220

221
    // Remove from materializing set
222
    materializing.erase(columnName);
80✔
223
}
80✔
224

225
ExecutionPlan TableView::generateExecutionPlan(std::string const & sourceName) {
121✔
226
    // Try to get the data source to understand its structure
227
    // First try as analog source
228

229
    
230

231
    auto analogSource = m_dataManager->getAnalogSource(sourceName);
121✔
232
    if (analogSource) {
121✔
233
        // Generate plan based on row selector type for analog data
234
        if (auto intervalSelector = dynamic_cast<IntervalSelector *>(m_rowSelector.get())) {
48✔
235
            auto const & intervals = intervalSelector->getIntervals();
25✔
236
            auto timeFrame = intervalSelector->getTimeFrame();
25✔
237
            return ExecutionPlan(intervals, timeFrame);
25✔
238
        }
25✔
239

240
        if (auto timestampSelector = dynamic_cast<TimestampSelector *>(m_rowSelector.get())) {
23✔
241
            auto const & indices = timestampSelector->getTimestamps();
23✔
242
            auto timeFrame = timestampSelector->getTimeFrame();
23✔
243
            return ExecutionPlan(indices, timeFrame);
23✔
244
        }
23✔
245

246
        if (auto indexSelector = dynamic_cast<IndexSelector *>(m_rowSelector.get())) {
×
247
            auto const & indices = indexSelector->getIndices();
×
248
            std::vector<TimeFrameIndex> timeFrameIndices;
×
249
            timeFrameIndices.reserve(indices.size());
×
250

251
            for (size_t index: indices) {
×
252
                timeFrameIndices.emplace_back(static_cast<int64_t>(index));
×
253
            }
254

255
            std::cout << "WARNING: IndexSelector is not supported for analog data" << std::endl;
×
256
            return ExecutionPlan(std::move(timeFrameIndices), nullptr);
×
257
        }
×
258
    }
259

260
    // Try as interval source
261
    auto intervalSource = m_dataManager->getIntervalSource(sourceName);
73✔
262
    if (intervalSource) {
73✔
263
        // Generate plan based on row selector type for interval data
264
        if (auto intervalSelector = dynamic_cast<IntervalSelector *>(m_rowSelector.get())) {
30✔
265
            auto const & intervals = intervalSelector->getIntervals();
22✔
266
            auto timeFrame = intervalSelector->getTimeFrame();
22✔
267
            return ExecutionPlan(intervals, timeFrame);
22✔
268
        }
22✔
269

270
        if (auto timestampSelector = dynamic_cast<TimestampSelector *>(m_rowSelector.get())) {
8✔
271
            auto const & indices = timestampSelector->getTimestamps();
8✔
272
            auto timeFrame = timestampSelector->getTimeFrame();
8✔
273
            return ExecutionPlan(indices, timeFrame);
8✔
274
        }
8✔
275

276
        if (auto indexSelector = dynamic_cast<IndexSelector *>(m_rowSelector.get())) {
×
277
            auto const & indices = indexSelector->getIndices();
×
278
            std::vector<TimeFrameIndex> timeFrameIndices;
×
279
            timeFrameIndices.reserve(indices.size());
×
280

281
            for (size_t index: indices) {
×
282
                timeFrameIndices.emplace_back(static_cast<int64_t>(index));
×
283
            }
284

285
            std::cout << "WARNING: IndexSelector is not supported for interval data" << std::endl;
×
286
            return ExecutionPlan(std::move(timeFrameIndices), nullptr);
×
287
        }
×
288
    }
289

290
    // Try as event source
291
    auto eventSource = m_dataManager->getEventSource(sourceName);
43✔
292
    if (eventSource) {
43✔
293
        // Generate plan based on row selector type for event data
294
        if (auto intervalSelector = dynamic_cast<IntervalSelector *>(m_rowSelector.get())) {
24✔
295
            auto const & intervals = intervalSelector->getIntervals();
24✔
296
            auto timeFrame = intervalSelector->getTimeFrame();
24✔
297
            return ExecutionPlan(intervals, timeFrame);
24✔
298
        }
24✔
299

300
        if (auto timestampSelector = dynamic_cast<TimestampSelector *>(m_rowSelector.get())) {
×
301
            auto const & indices = timestampSelector->getTimestamps();
×
302
            auto timeFrame = timestampSelector->getTimeFrame();
×
303
            return ExecutionPlan(indices, timeFrame);
×
304
        }
×
305

306
        if (auto indexSelector = dynamic_cast<IndexSelector *>(m_rowSelector.get())) {
×
307
            auto const & indices = indexSelector->getIndices();
×
308
            std::vector<TimeFrameIndex> timeFrameIndices;
×
309
            timeFrameIndices.reserve(indices.size());
×
310

311
            for (size_t index: indices) {
×
312
                timeFrameIndices.emplace_back(static_cast<int64_t>(index));
×
313
            }
314

315
            std::cout << "WARNING: IndexSelector is not supported for event data" << std::endl;
×
316

317
            return ExecutionPlan(std::move(timeFrameIndices), nullptr);
×
318
        }
×
319
    }
320

321
    // Try as line source
322
    auto lineSource = m_dataManager->getLineSource(sourceName);
19✔
323
    if (lineSource) {
19✔
324
        // Default-on entity expansion for TimestampSelector
325
        if (auto timestampSelector = dynamic_cast<TimestampSelector *>(m_rowSelector.get())) {
18✔
326
            auto const & timestamps = timestampSelector->getTimestamps();
18✔
327
            auto timeFrame = timestampSelector->getTimeFrame();
18✔
328

329
            ExecutionPlan plan(std::vector<TimeFrameIndex>{}, timeFrame);
18✔
330
            // Build expanded rows: one row per line at that timestamp; drop timestamps with zero lines
331
            std::vector<RowId> rows;
18✔
332
            rows.reserve(timestamps.size());
18✔
333
            std::map<TimeFrameIndex, std::pair<size_t,size_t>> spans;
18✔
334

335
            // Determine if table contains any non-line columns; if so, we include singleton rows
336
            bool anyNonLineColumn = false;
18✔
337
            for (auto const & col : m_columns) {
146✔
338
                try {
339
                    auto const & dep = col->getSourceDependency();
129✔
340
                    if (!m_dataManager->getLineSource(dep)) {
129✔
341
                        anyNonLineColumn = true;
1✔
342
                        break;
1✔
343
                    }
344
                } catch (...) {
129✔
345
                    // Ignore
346
                }
×
347
            }
348

349
            size_t cursor = 0;
18✔
350
            for (auto const & t : timestamps) {
76✔
351
                auto const count = lineSource->getEntityCountAt(t);
58✔
352
                if (count == 0) {
58✔
353
                    if (anyNonLineColumn) {
8✔
354
                        spans.emplace(t, std::make_pair(cursor, static_cast<size_t>(1)));
3✔
355
                        rows.push_back(RowId{t, std::nullopt});
3✔
356
                        ++cursor;
3✔
357
                    }
358
                } else {
359
                    spans.emplace(t, std::make_pair(cursor, static_cast<size_t>(count)));
50✔
360
                    for (size_t i = 0; i < count; ++i) {
129✔
361
                        rows.push_back(RowId{t, static_cast<int>(i)});
79✔
362
                        ++cursor;
79✔
363
                    }
364
                }
365
            }
366

367
            plan.setRows(std::move(rows));
18✔
368
            plan.setTimeToRowSpan(std::move(spans));
18✔
369
            plan.setSourceId(DataSourceNameInterner::instance().intern(lineSource->getName()));
18✔
370
            plan.setSourceKind(ExecutionPlan::DataSourceKind::Line);
18✔
371
            return plan;
18✔
372
        }
18✔
373

374
        // IntervalSelector: keep legacy behavior (no expansion) for now
375
        if (auto intervalSelector = dynamic_cast<IntervalSelector *>(m_rowSelector.get())) {
×
376
            auto const & intervals = intervalSelector->getIntervals();
×
377
            auto timeFrame = intervalSelector->getTimeFrame();
×
378
            return ExecutionPlan(intervals, timeFrame);
×
379
        }
×
380

381
        if (auto indexSelector = dynamic_cast<IndexSelector *>(m_rowSelector.get())) {
×
382
            auto const & indices = indexSelector->getIndices();
×
383
            std::vector<TimeFrameIndex> timeFrameIndices;
×
384
            timeFrameIndices.reserve(indices.size());
×
385
            for (size_t index: indices) {
×
386
                timeFrameIndices.emplace_back(static_cast<int64_t>(index));
×
387
            }
388
            std::cout << "WARNING: IndexSelector is not supported for line data" << std::endl;
×
389
            auto plan = ExecutionPlan(std::move(timeFrameIndices), nullptr);
×
390
            plan.setSourceId(DataSourceNameInterner::instance().intern(lineSource->getName()));
×
391
            plan.setSourceKind(ExecutionPlan::DataSourceKind::Line);
×
392
            return plan;
×
393
        }
×
394
    }
395

396
    // Generic fallback: generate plan solely based on row selector when the source is unknown
397
    if (auto intervalSelector = dynamic_cast<IntervalSelector *>(m_rowSelector.get())) {
1✔
398
        auto const & intervals = intervalSelector->getIntervals();
×
399
        auto timeFrame = intervalSelector->getTimeFrame();
×
400
        std::cout << "WARNING: Data source '" << sourceName
401
                  << "' not found. Generating plan from IntervalSelector only." << std::endl;
×
402
        return ExecutionPlan(intervals, timeFrame);
×
403
    }
×
404

405
    if (auto timestampSelector = dynamic_cast<TimestampSelector *>(m_rowSelector.get())) {
1✔
406
        auto const & indices = timestampSelector->getTimestamps();
1✔
407
        auto timeFrame = timestampSelector->getTimeFrame();
1✔
408
        std::cout << "WARNING: Data source '" << sourceName
409
                  << "' not found. Generating plan from TimestampSelector only." << std::endl;
1✔
410
        return ExecutionPlan(indices, timeFrame);
1✔
411
    }
1✔
412

413
    if (auto indexSelector = dynamic_cast<IndexSelector *>(m_rowSelector.get())) {
×
414
        auto const & indices = indexSelector->getIndices();
×
415
        std::vector<TimeFrameIndex> timeFrameIndices;
×
416
        timeFrameIndices.reserve(indices.size());
×
417
        for (size_t index: indices) {
×
418
            timeFrameIndices.emplace_back(static_cast<int64_t>(index));
×
419
        }
420
        std::cout << "WARNING: Data source '" << sourceName
421
                  << "' not found. Generating plan from IndexSelector only." << std::endl;
×
422
        return ExecutionPlan(std::move(timeFrameIndices), nullptr);
×
423
    }
×
424

425
    throw std::runtime_error("Data source '" + sourceName + "' not found as analog, interval, event, or line source");
×
426
}
121✔
427

428
RowDescriptor TableView::getRowDescriptor(size_t row_index) const {
×
429
    if (m_rowSelector) {
×
430
        return m_rowSelector->getDescriptor(row_index);
×
431
    }
432
    return std::monostate{};
×
433
}
434

435
std::vector<EntityId> TableView::getRowEntityIds(size_t row_index) const {
20✔
436
    // First check if we have direct EntityIds (for transformed tables)
437
    if (!m_direct_entity_ids.empty()) {
20✔
438
        if (row_index < m_direct_entity_ids.size()) {
×
439
            return m_direct_entity_ids[row_index];
×
440
        }
UNCOV
441
        return {};
×
442
    }
443
    
444
    // Final fallback: collect EntityIDs from all columns (for mixed/derived sources)
445
    std::set<EntityId> entity_set;
20✔
446
    for (auto const & column : m_columns) {
160✔
447
        //if (column->hasEntityIds()) {
448
            // Use the column's getCellEntityIds method
449
            auto cell_entities = column->getCellEntityIds(row_index);
140✔
450
            for (EntityId entity_id : cell_entities) {
280✔
451
                if (entity_id != 0) {
140✔
452
                    entity_set.insert(entity_id);
140✔
453
                }
454
            }
455
        //}
456
    }
140✔
457
    
458
    // Return the unified set of EntityIDs for this row
459
    return std::vector<EntityId>(entity_set.begin(), entity_set.end());
60✔
460
}
20✔
461

462
bool TableView::hasEntityColumn() const {
×
463
    // Check if we have direct EntityIds first
464
    if (!m_direct_entity_ids.empty()) {
×
465
        return true;
×
466
    }
467
    
468
    // Fallback to execution plan-based check
469
    size_t row_count = getRowCount();
×
470
    if (row_count == 0) {
×
471
        return false;
×
472
    }
473
    
474
    // Check the first row to see if EntityIds are available
475
    auto entity_ids = getRowEntityIds(0);
×
476
    return !entity_ids.empty();
×
UNCOV
477
}
×
478

479
std::vector<std::vector<EntityId>> TableView::getEntityIds() const {
5✔
480
    // If we have direct EntityIds, return them
481
    if (!m_direct_entity_ids.empty()) {
5✔
482
        return m_direct_entity_ids;
2✔
483
    }
484
    
485
    // Fallback to execution plan-based EntityIds
486
    std::vector<std::vector<EntityId>> all_entity_ids;
3✔
487
    size_t row_count = getRowCount();
3✔
488
    
489
    for (size_t i = 0; i < row_count; ++i) {
18✔
490
        auto row_entity_ids = getRowEntityIds(i);
15✔
491
        if (!row_entity_ids.empty()) {
15✔
492
            all_entity_ids.push_back(std::move(row_entity_ids));
15✔
493
        } else {
UNCOV
494
            all_entity_ids.push_back({});
×
495
        }
496
    }
15✔
497
    
498
    return all_entity_ids;
3✔
499
}
3✔
500

501
void TableView::setDirectEntityIds(std::vector<std::vector<EntityId>> entity_ids) {
2✔
502
    m_direct_entity_ids = std::move(entity_ids);
2✔
503
}
2✔
504

UNCOV
505
std::unique_ptr<IRowSelector> TableView::cloneRowSelectorFiltered(std::vector<size_t> const & keep_indices) const {
×
UNCOV
506
    if (!m_rowSelector) {
×
UNCOV
507
        return nullptr;
×
508
    }
509

510
    // IndexSelector
UNCOV
511
    if (auto indexSelector = dynamic_cast<IndexSelector const *>(m_rowSelector.get())) {
×
UNCOV
512
        std::vector<size_t> const & indices = indexSelector->getIndices();
×
UNCOV
513
        std::vector<size_t> filtered;
×
UNCOV
514
        filtered.reserve(keep_indices.size());
×
UNCOV
515
        for (size_t k : keep_indices) {
×
UNCOV
516
            if (k < indices.size()) {
×
517
                filtered.push_back(indices[k]);
×
518
            }
519
        }
UNCOV
520
        return std::make_unique<IndexSelector>(std::move(filtered));
×
UNCOV
521
    }
×
522

523
    // TimestampSelector
UNCOV
524
    if (auto timestampSelector = dynamic_cast<TimestampSelector const *>(m_rowSelector.get())) {
×
UNCOV
525
        auto const & timestamps = timestampSelector->getTimestamps();
×
UNCOV
526
        auto timeFrame = timestampSelector->getTimeFrame();
×
UNCOV
527
        std::vector<TimeFrameIndex> filtered;
×
528
        filtered.reserve(keep_indices.size());
×
529
        for (size_t k : keep_indices) {
×
530
            if (k < timestamps.size()) {
×
UNCOV
531
                filtered.push_back(timestamps[k]);
×
532
            }
533
        }
534
        return std::make_unique<TimestampSelector>(std::move(filtered), timeFrame);
×
535
    }
×
536

537
    // IntervalSelector
538
    if (auto intervalSelector = dynamic_cast<IntervalSelector const *>(m_rowSelector.get())) {
×
539
        auto const & intervals = intervalSelector->getIntervals();
×
540
        auto timeFrame = intervalSelector->getTimeFrame();
×
UNCOV
541
        std::vector<TimeFrameInterval> filtered;
×
UNCOV
542
        filtered.reserve(keep_indices.size());
×
543
        for (size_t k : keep_indices) {
×
544
            if (k < intervals.size()) {
×
UNCOV
545
                filtered.push_back(intervals[k]);
×
546
            }
547
        }
548
        return std::make_unique<IntervalSelector>(std::move(filtered), timeFrame);
×
549
    }
×
550

551
    // Fallback: preserve by indices if unknown type
552
    std::vector<size_t> identity;
×
553
    identity.reserve(keep_indices.size());
×
554
    for (size_t k : keep_indices) identity.push_back(k);
×
UNCOV
555
    return std::make_unique<IndexSelector>(std::move(identity));
×
UNCOV
556
}
×
557

558
bool TableView::hasColumnEntityIds(std::string const & columnName) const {
2✔
559
    auto it = m_colNameToIndex.find(columnName);
2✔
560
    if (it == m_colNameToIndex.end()) {
2✔
561
        return false;
×
562
    }
563
    
564
    size_t columnIndex = it->second;
2✔
565
    if (columnIndex >= m_columns.size()) {
2✔
566
        return false;
×
567
    }
568
    
569
    return m_columns[columnIndex]->hasEntityIds();
2✔
570
}
571

572
ColumnEntityIds TableView::getColumnEntityIds(std::string const & columnName) const {
19✔
573
    auto it = m_colNameToIndex.find(columnName);
19✔
574
    if (it == m_colNameToIndex.end()) {
19✔
575
        return {};
×
576
    }
577
    
578
    size_t columnIndex = it->second;
19✔
579
    if (columnIndex >= m_columns.size()) {
19✔
UNCOV
580
        return {};
×
581
    }
582
    
583
    return m_columns[columnIndex]->getColumnEntityIds();
19✔
584
}
585

586
std::vector<EntityId> TableView::getCellEntityIds(std::string const & columnName, size_t rowIndex) const {
40✔
587
    auto it = m_colNameToIndex.find(columnName);
40✔
588
    if (it == m_colNameToIndex.end()) {
40✔
589
        return {};
×
590
    }
591
    
592
    size_t columnIndex = it->second;
40✔
593
    if (columnIndex >= m_columns.size()) {
40✔
UNCOV
594
        return {};
×
595
    }
596
    
597
    return m_columns[columnIndex]->getCellEntityIds(rowIndex);
40✔
598
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc