Skip to content

Commit a171f4c

Browse files
authored
Merge pull request #39 from qbicsoftware/development
Introduce path formatter (#35)
2 parents aabd17a + 2add687 commit a171f4c

File tree

4 files changed

+178
-4
lines changed

4 files changed

+178
-4
lines changed

measurement-provider/pom.xml

+30
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,36 @@
1313
<artifactId>measurement-provider</artifactId>
1414
<version>1.0.6</version>
1515
<packaging>jar</packaging>
16+
<dependencies>
17+
<dependency>
18+
<groupId>org.spockframework</groupId>
19+
<artifactId>spock-core</artifactId>
20+
<version>2.4-M4-groovy-4.0</version>
21+
<scope>test</scope>
22+
</dependency>
23+
<dependency>
24+
<groupId>org.apache.groovy</groupId>
25+
<artifactId>groovy-all</artifactId>
26+
<version>4.0.24</version>
27+
<type>pom</type>
28+
<scope>test</scope>
29+
</dependency>
30+
<!-- JUnit Platform for Spock -->
31+
<dependency>
32+
<groupId>org.junit.platform</groupId>
33+
<artifactId>junit-platform-launcher</artifactId>
34+
<version>1.9.2</version> <!-- Ensure it's at least 1.7+ -->
35+
<scope>test</scope>
36+
</dependency>
37+
38+
<!-- Optional: For Running Spock with JUnit 5 -->
39+
<dependency>
40+
<groupId>org.junit.jupiter</groupId>
41+
<artifactId>junit-jupiter-engine</artifactId>
42+
<version>5.9.2</version>
43+
<scope>test</scope>
44+
</dependency>
45+
</dependencies>
1646

1747
<build>
1848
<plugins>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package life.qbic.data_download.measurements.api;
2+
3+
import java.nio.file.InvalidPathException;
4+
import java.nio.file.Path;
5+
import java.nio.file.Paths;
6+
import java.util.ArrayList;
7+
import java.util.Collection;
8+
import java.util.List;
9+
import java.util.Objects;
10+
import java.util.regex.Pattern;
11+
12+
/**
13+
* PathFormatter applies filters to a given String value that represents a path.
14+
*
15+
* @since 1.1.0
16+
*/
17+
public class PathFormatter {
18+
19+
private final List<String> filter;
20+
21+
private PathFormatter() {
22+
filter = new ArrayList<>();
23+
}
24+
25+
private PathFormatter(Collection<String> filter) {
26+
this.filter = new ArrayList<>(filter);
27+
}
28+
29+
/**
30+
* Creates a new instance of {@link PathFormatter} with a collection of filter regex expression.
31+
*
32+
* @param filter the terms to filter out
33+
* @return a {@link PathFormatter} instance
34+
* @since 1.1.0
35+
*/
36+
public static PathFormatter with(Collection<String> filter) {
37+
Objects.requireNonNull(filter);
38+
return new PathFormatter(filter);
39+
}
40+
41+
private static String apply(Path p, Collection<String> filter) {
42+
Path result = p.isAbsolute() ? p.getRoot() : Paths.get("");
43+
44+
for (Path part : p) {
45+
if (matchesAny(part.toString(), filter)) {
46+
continue;
47+
}
48+
result = result.resolve(part);
49+
}
50+
return result.toString();
51+
}
52+
53+
private static boolean matchesAny(String s, Collection<String> filter) {
54+
return filter.stream().anyMatch(s::matches);
55+
}
56+
57+
/**
58+
* Applies the configured filter to the provided String.
59+
* <p>
60+
* The provided String is expected to be a valid {@link java.nio.file.Path}, otherwise an
61+
* {@link InvalidPathException} is thrown.
62+
* <p>
63+
* Format iterates through every part of the path and checks them agains the configured filters.
64+
* <p>
65+
* The filter matching works the same as {@link Pattern#matches(String, CharSequence)}.
66+
* <p>
67+
* The resulting String representation of a path will be removed of any matching filter terms.
68+
*
69+
* <pre>
70+
* List<String> filters = Arrays.asList("removeme");
71+
* var formatter = PathFormatter.with(filters);
72+
*
73+
* System.out.println(formatter("/example/removeme/path"));
74+
*
75+
* // Prints out "/example/path"
76+
* </pre>
77+
* <p>
78+
* Format has an implicit check for {@link Path#isAbsolute()} and preserves an absolute or
79+
* relative path.
80+
*
81+
* @param s the String value to format
82+
* @return the formatted new String, after the filter has been applied
83+
* @throws InvalidPathException in case the String is not a valid path
84+
* @since 1.1.0
85+
*/
86+
public String format(String s) throws InvalidPathException {
87+
return apply(Path.of(s), filter);
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package life.qbic.data_download.measurement.api
2+
3+
import life.qbic.data_download.measurements.api.PathFormatter
4+
import spock.lang.Specification
5+
6+
class PathFormatterSpec extends Specification {
7+
8+
def "Given a String with a to filtered part, the filtered part must not be part of the resulting String"() {
9+
given:
10+
var filter = Arrays.asList("awesome", "wehaa")
11+
12+
and:
13+
var f = PathFormatter.with(filter)
14+
15+
when:
16+
var result = f.format(input)
17+
18+
then:
19+
result == expected
20+
21+
where:
22+
input | expected
23+
"/my/awesome/path/wehaa" | "/my/path"
24+
"a/relative/wehaa/awesome/path" | "a/relative/path"
25+
}
26+
27+
def "Given a String that contains a UUID-4 in its path, the formatter shall filter it out"() {
28+
given:
29+
var filter = Arrays.asList("^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\$")
30+
31+
and:
32+
var f = PathFormatter.with(filter)
33+
34+
when:
35+
var result = f.format(input)
36+
37+
then:
38+
result == expected
39+
40+
where:
41+
input | expected
42+
"/my/f47ac10b-58cc-4372-a567-0e02b2c3d479/path/" | "/my/path"
43+
"a/relative/f47ac10b-58cc-4372-a567-0e02b2c3d479/path" | "a/relative/path"
44+
}
45+
}

openbis-connector/src/main/java/life/qbic/data_download/openbis/DatasetFileStreamReaderImpl.java

+14-4
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,29 @@
66
import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownload;
77
import ch.ethz.sis.openbis.generic.dssapi.v3.dto.datasetfile.download.DataSetFileDownloadReader;
88
import java.io.InputStream;
9-
import java.util.Optional;
9+
import java.util.List;
10+
import java.util.Objects;
1011
import life.qbic.data_download.measurements.api.DataFile;
1112
import life.qbic.data_download.measurements.api.FileInfo;
1213
import life.qbic.data_download.measurements.api.MeasurementDataReader;
14+
import life.qbic.data_download.measurements.api.PathFormatter;
1315

1416
/**
1517
* Reads openbis data streams
1618
*/
1719
public class DatasetFileStreamReaderImpl implements MeasurementDataReader {
1820

1921
private DataSetFileDownloadReader dataSetFileDownloadReader;
20-
private final String ignoredPrefix;
22+
23+
private final PathFormatter formatter;
24+
25+
private static final String UUID_REGEX = "^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$";
2126

2227
public DatasetFileStreamReaderImpl(String ignoredPrefix) {
28+
Objects.requireNonNull(ignoredPrefix);
2329
dataSetFileDownloadReader = null;
24-
this.ignoredPrefix = Optional.ofNullable(ignoredPrefix).orElse("");
30+
var filter = List.of(ignoredPrefix, UUID_REGEX);
31+
this.formatter = PathFormatter.with(filter);
2532
}
2633

2734

@@ -55,7 +62,10 @@ public DataFile nextDataFile() {
5562
.toEpochMilli() : -1;
5663
long lastModifiedMillis = nonNull(dataStore) ? dataStore.getModificationDate().toInstant()
5764
.toEpochMilli() : -1;
58-
FileInfo fileInfo = new FileInfo(fileDownload.getDataSetFile().getPath().replaceFirst(ignoredPrefix, ""),
65+
66+
var cleanedPath = formatter.format(fileDownload.getDataSetFile().getPath());
67+
68+
FileInfo fileInfo = new FileInfo(cleanedPath,
5969
fileDownload.getDataSetFile().getFileLength(),
6070
Integer.toUnsignedLong(fileDownload.getDataSetFile().getChecksumCRC32()),
6171
creationMillis,

0 commit comments

Comments
 (0)