Skip to content

Commit

Permalink
Merge pull request #18 from oclay1st/develop
Browse files Browse the repository at this point in the history
Improve API
  • Loading branch information
oclay1st authored Mar 15, 2024
2 parents db55e70 + f17c272 commit 81a3d58
Show file tree
Hide file tree
Showing 18 changed files with 134 additions and 137 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ build/

### VS Code ###
.vscode/
cspell.json
4 changes: 2 additions & 2 deletions src/main/java/io/github/oclay1st/wfdb/filters/Filter.java
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ public int hashCode() {

@Override
public String toString() {
return "Filter [startTime = " + startTime + ", endTime = " + endTime + ", signals = " + Arrays.toString(signals)
+ "]";
String signalsText = Arrays.toString(signals);
return "Filter [startTime = " + startTime + ", endTime = " + endTime + ", signals = " + signalsText + "]";
}

}
31 changes: 15 additions & 16 deletions src/main/java/io/github/oclay1st/wfdb/filters/FilterProcessor.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.oclay1st.wfdb.filters;

import java.util.List;
import java.util.stream.IntStream;

import io.github.oclay1st.wfdb.records.HeaderRecord;
Expand Down Expand Up @@ -36,7 +37,7 @@ public static FilterProcessor process(Filter filter, SingleSegmentHeader header)
long duration = header.record().durationTime().toMillis();
long startMilliseconds = filter.startTime() != null ? filter.startTime() : 0;
long endMilliseconds = filter.endTime() != null ? filter.endTime() : duration;
int[] indices = IntStream.range(0, header.signals().length).toArray();
int[] indices = IntStream.range(0, header.signals().size()).toArray();
if (filter.signals() != null) {
indices = filter.signals();
}
Expand Down Expand Up @@ -80,16 +81,14 @@ private HeaderRecord generateHeaderRecord() {
*
* @return a new array of {@link HeaderSignal}
*/
private HeaderSignal[] generateHeaderSignals() {
HeaderSignal[] headerSignals = new HeaderSignal[filter.signals().length];
for (int i = 0; i < headerSignals.length; i++) {
HeaderSignal headerSignal = header.signals()[filter.signals()[i]];
headerSignals[i] = new HeaderSignal(headerSignal.filename(), headerSignal.format(),
headerSignal.samplesPerFrame(), headerSignal.skew(), headerSignal.bytesOffset(),
headerSignal.adcGain(), headerSignal.baseline(), headerSignal.unit(), headerSignal.adcResolution(),
headerSignal.adcZero(), 0, 0, headerSignal.blockSize(), headerSignal.description());
}
return headerSignals;
private List<HeaderSignal> generateHeaderSignals() {
return IntStream.of(filter.signals())
.mapToObj(index -> {
HeaderSignal signal = header.signals().get(index);
return new HeaderSignal(signal.filename(), signal.format(), signal.samplesPerFrame(), signal.skew(),
signal.bytesOffset(), signal.adcGain(), signal.baseline(), signal.unit(),
signal.adcResolution(), signal.adcZero(), 0, 0, signal.blockSize(), signal.description());
}).toList();
}

/**
Expand All @@ -98,13 +97,13 @@ private HeaderSignal[] generateHeaderSignals() {
* @param headerSignals the array of header signals
* @return a {@link BytesRange} instance
*/
public BytesRange calculateBytesRange(HeaderSignal[] headerSignals) {
public BytesRange calculateBytesRange(List<HeaderSignal> headerSignals) {
// Take the format, the bytes offset of the first header signals because by
// definition those values are the same in the signal file
float bytesPerSample = headerSignals[0].format().bytesPerSample();
int bytesOffset = headerSignals[0].bytesOffset();
long start = bytesOffset + calculateByteIndex(filter.startTime(), bytesPerSample, headerSignals.length);
long end = bytesOffset + calculateByteIndex(filter.endTime(), bytesPerSample, headerSignals.length);
float bytesPerSample = headerSignals.get(0).format().bytesPerSample();
int bytesOffset = headerSignals.get(0).bytesOffset();
long start = bytesOffset + calculateByteIndex(filter.startTime(), bytesPerSample, headerSignals.size());
long end = bytesOffset + calculateByteIndex(filter.endTime(), bytesPerSample, headerSignals.size());
return new BytesRange(start, end);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
package io.github.oclay1st.wfdb.records;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.IntStream;

import io.github.oclay1st.wfdb.exceptions.ParseException;
import io.github.oclay1st.wfdb.utils.CommonUtil;
Expand Down Expand Up @@ -60,7 +60,7 @@ public record HeaderSignal(String filename, SignalFormat format, int samplesPerF
*
* <pre>
* As an example the text line may look like:
* 100.dat 212 200 11 1024 995 -22131 0 MLII
* 100.dat 212 200 11 1024 995 -22131 0 II
* </pre>
*
* @param text the text that represents the signals info
Expand Down Expand Up @@ -112,13 +112,13 @@ public boolean matchChecksum(int[] samples) {
* @return the checksum value
*/
public static int calculateChecksum(int[] samples) {
int sum = Arrays.stream(samples).sum();
int sum = IntStream.of(samples).sum();
int unsignedChecksum = Math.floorMod(sum, 65536);
return unsignedChecksum > 32767 ? unsignedChecksum - 65536 : unsignedChecksum;
}

/**
* Returns the header signal representation
* Returns the header signal representation.
*
* @return the text line representation
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import io.github.oclay1st.wfdb.exceptions.ParseException;
Expand All @@ -13,10 +14,10 @@
* Represent the info from a multi-segment header.
*
* @param record the header record
* @param segments the array of header segments
* @param segments the list of header segments
* @param comments the comments about the record
*/
public record MultiSegmentHeader(HeaderRecord record, HeaderSegment[] segments, String comments) { // NOSONAR
public record MultiSegmentHeader(HeaderRecord record, List<HeaderSegment> segments, String comments) { // NOSONAR

/**
* Creates an instance of a MultiSegmentHeader class.
Expand Down Expand Up @@ -51,11 +52,10 @@ public record MultiSegmentHeader(HeaderRecord record, HeaderSegment[] segments,
public static MultiSegmentHeader parse(InputStream input) throws IOException, ParseException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
HeaderRecord headerRecord = null;
HeaderSegment[] headerSegments = null;
List<HeaderSegment> headerSegments = new ArrayList<>();
String headerLine;
StringBuilder commentsBuilder = new StringBuilder();
boolean headerRecordProcessed = false;
int segmentIndex = 0;
while ((headerLine = reader.readLine()) != null) {
String stripedHeaderLine = headerLine.strip();
if (stripedHeaderLine.isEmpty()) {
Expand All @@ -65,11 +65,9 @@ public static MultiSegmentHeader parse(InputStream input) throws IOException, Pa
commentsBuilder.append('\n').append(stripedHeaderLine);
} else if (!headerRecordProcessed) {
headerRecord = HeaderRecord.parse(stripedHeaderLine);
headerSegments = new HeaderSegment[headerRecord.numberOfSegments()];
headerRecordProcessed = true;
} else {
headerSegments[segmentIndex] = HeaderSegment.parse(stripedHeaderLine);
segmentIndex++;
headerSegments.add(HeaderSegment.parse(stripedHeaderLine));
}
}
return new MultiSegmentHeader(headerRecord, headerSegments, commentsBuilder.toString());
Expand All @@ -95,18 +93,18 @@ public String toTextBlock() {
@Override
public String toString() {
return "MultiSegmentHeader [headerRecord = " + record + ", headerSegments = "
+ Arrays.toString(segments) + ", comments = " + comments + "]";
+ segments + ", comments = " + comments + "]";
}

@Override
public boolean equals(Object object) {
return object instanceof MultiSegmentHeader instance && record.equals(instance.record)
&& Arrays.equals(segments, instance.segments) && comments.equals(instance.comments);
&& segments.equals(instance.segments) && comments.equals(instance.comments);
}

@Override
public int hashCode() {
return 31 * Objects.hash(record) + Arrays.hashCode(segments) + Objects.hash(comments);
return Objects.hash(record, segments, comments);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import io.github.oclay1st.wfdb.exceptions.ParseException;
Expand All @@ -13,9 +14,9 @@
* Represents a multi-segment record.
*
* @param header the multi-segment header {@link MultiSegmentHeader}
* @param records the array of single-segment records
* @param records the list of single-segment records
*/
public record MultiSegmentRecord(MultiSegmentHeader header, SingleSegmentRecord[] records) {
public record MultiSegmentRecord(MultiSegmentHeader header, List<SingleSegmentRecord> records) {

/**
* Creates an instance of a MultiSegmentRecord class.
Expand All @@ -41,10 +42,10 @@ public static MultiSegmentRecord parse(Path recordPath) throws IOException, Pars
try (InputStream inputStream = Files.newInputStream(headerFilePath)) {
// Parse the multi-segment header file
MultiSegmentHeader header = MultiSegmentHeader.parse(inputStream);
SingleSegmentRecord[] singleSegmentRecords = new SingleSegmentRecord[header.segments().length];
for (int i = 0; i < header.segments().length; i++) {
Path segmentRecordPath = recordPath.resolveSibling(header.segments()[i].name());
singleSegmentRecords[i] = SingleSegmentRecord.parse(segmentRecordPath);
List<SingleSegmentRecord> singleSegmentRecords = new ArrayList<>(header.segments().size());
for (HeaderSegment segment : header.segments()) {
Path segmentRecordPath = recordPath.resolveSibling(segment.name());
singleSegmentRecords.add(SingleSegmentRecord.parse(segmentRecordPath));
}
return new MultiSegmentRecord(header, singleSegmentRecords);
}
Expand All @@ -53,17 +54,17 @@ public static MultiSegmentRecord parse(Path recordPath) throws IOException, Pars
@Override
public boolean equals(Object object) {
return object instanceof MultiSegmentRecord instance && header.equals(instance.header)
&& Arrays.equals(records, instance.records);
&& records.equals(instance.records);
}

@Override
public int hashCode() {
return 31 * Objects.hash(header) + Arrays.hashCode(records);
return Objects.hash(header, records);
}

@Override
public String toString() {
return "MultiSegmentRecord = [ header=" + header + ", records=" + Arrays.toString(records) + ']';
return "MultiSegmentRecord = [ header=" + header + ", records=" + records + ']';
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,24 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import io.github.oclay1st.wfdb.exceptions.ParseException;

/**
* Represents a single-segment record header.
*
* @param record the header record
* @param signals the array of header signals
* @param signals the list of header signals
* @param comments the comments about the record
*/
public record SingleSegmentHeader(HeaderRecord record, HeaderSignal[] signals, String comments) { // NOSONAR
public record SingleSegmentHeader(HeaderRecord record, List<HeaderSignal> signals, String comments) { // NOSONAR

/**
* Creates an instance of a SingleSegmentHeader class.
Expand All @@ -29,7 +30,9 @@ public record SingleSegmentHeader(HeaderRecord record, HeaderSignal[] signals, S
* @param signals the array or header signals. Can't be null.
* @param comments the comments about the record. Can't be null.
*/
public SingleSegmentHeader {
public SingleSegmentHeader

{
Objects.requireNonNull(record);
Objects.requireNonNull(signals);
Objects.requireNonNull(comments);
Expand Down Expand Up @@ -57,8 +60,7 @@ public record SingleSegmentHeader(HeaderRecord record, HeaderSignal[] signals, S
public static SingleSegmentHeader parse(InputStream input) throws IOException, ParseException {
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
HeaderRecord headerRecord = null;
HeaderSignal[] headerSignals = null;
int signalIndex = 0;
List<HeaderSignal> headerSignals = new ArrayList<>();
String headerLine;
boolean headerRecordProcessed = false;
StringBuilder commentsBuilder = new StringBuilder();
Expand All @@ -71,11 +73,9 @@ public static SingleSegmentHeader parse(InputStream input) throws IOException, P
commentsBuilder.append('\n').append(stripedHeaderLine);
} else if (!headerRecordProcessed) {
headerRecord = HeaderRecord.parse(stripedHeaderLine);
headerSignals = new HeaderSignal[headerRecord.numberOfSignals()];
headerRecordProcessed = true;
} else {
headerSignals[signalIndex] = HeaderSignal.parse(stripedHeaderLine);
signalIndex++;
headerSignals.add(HeaderSignal.parse(stripedHeaderLine));
}
}
return new SingleSegmentHeader(headerRecord, headerSignals, commentsBuilder.toString());
Expand All @@ -87,7 +87,7 @@ public static SingleSegmentHeader parse(InputStream input) throws IOException, P
* @return the group of header signals
*/
public Map<String, List<HeaderSignal>> groupHeaderSignalsByFilename() {
return Arrays.stream(signals)
return signals.stream()
.collect(Collectors.groupingBy(HeaderSignal::filename, LinkedHashMap::new, Collectors.toList()));
}

Expand Down Expand Up @@ -115,35 +115,37 @@ public String toTextBlock() {
* @return a new {@link SingleSegmentHeader} instance
*/
public SingleSegmentHeader generateChecksumCopy(int[][] samplesPerSignal) {
HeaderSignal[] newHeaderSignals = new HeaderSignal[signals.length];
for (int i = 0; i < signals.length; i++) {
HeaderSignal headerSignal = signals[i];
int initialValue = samplesPerSignal[i][0];
int checksum = HeaderSignal.calculateChecksum(samplesPerSignal[i]);
newHeaderSignals[i] = new HeaderSignal(headerSignal.filename(), headerSignal.format(),
headerSignal.samplesPerFrame(), headerSignal.skew(), headerSignal.bytesOffset(),
headerSignal.adcGain(), headerSignal.baseline(), headerSignal.unit(), headerSignal.adcResolution(),
headerSignal.adcZero(), initialValue, checksum, headerSignal.blockSize(),
headerSignal.description());
}
List<HeaderSignal> newHeaderSignals = IntStream.range(0, signals.size())
.mapToObj(index -> calculateSignalChecksums(signals.get(index), samplesPerSignal[index]))
.toList();
return new SingleSegmentHeader(record, newHeaderSignals, comments);
}

private HeaderSignal calculateSignalChecksums(HeaderSignal headerSignal, int[] samples) {
int initialValue = samples[0];
int checksum = HeaderSignal.calculateChecksum(samples);
return new HeaderSignal(headerSignal.filename(), headerSignal.format(),
headerSignal.samplesPerFrame(), headerSignal.skew(), headerSignal.bytesOffset(),
headerSignal.adcGain(), headerSignal.baseline(), headerSignal.unit(), headerSignal.adcResolution(),
headerSignal.adcZero(), initialValue, checksum, headerSignal.blockSize(),
headerSignal.description());
}

@Override
public String toString() {
return "SingleSegmentHeader [headerRecord = " + record + ", headerSignals = "
+ Arrays.toString(signals) + ", comments = " + comments + "]";
+ signals + ", comments = " + comments + "]";
}

@Override
public boolean equals(Object object) {
return object instanceof SingleSegmentHeader instance && record.equals(instance.record)
&& Arrays.equals(signals, instance.signals) && comments.equals(instance.comments);
&& signals.equals(instance.signals) && comments.equals(instance.comments);
}

@Override
public int hashCode() {
return 31 * Objects.hash(record) + Arrays.hashCode(signals) + Objects.hash(comments);
return Objects.hash(record, signals, comments);
}

}
Loading

0 comments on commit 81a3d58

Please sign in to comment.