diff --git a/.github/workflows/code-coverage.yaml b/.github/workflows/code-coverage.yaml
index bdf0d7bfcde..8a7054763c5 100644
--- a/.github/workflows/code-coverage.yaml
+++ b/.github/workflows/code-coverage.yaml
@@ -26,7 +26,7 @@ jobs:
run: mvn verify -P jacoco --batch-mode --also-make --projects matsim -Dmaven.test.redirectTestOutputToFile -Dmatsim.preferLocalDtds=true
- name: Push coverage to CodeCov
- uses: codecov/codecov-action@v3
+ uses: codecov/codecov-action@v4
with:
files: ./matsim/target/site/jacoco/jacoco.xml
diff --git a/.github/workflows/deploy-on-pr-merge.yaml b/.github/workflows/deploy-on-pr-merge.yaml
index ef046e1bdb0..11a60d440a6 100644
--- a/.github/workflows/deploy-on-pr-merge.yaml
+++ b/.github/workflows/deploy-on-pr-merge.yaml
@@ -46,7 +46,7 @@ jobs:
- name: Submit Dependency Graph
# Generate a complete dependency graph and submit the graph to the GitHub repository.
# The goal is to improve security alerts from dependabot, because dependabot is not able to compute the complete dependency graph.
- uses: advanced-security/maven-dependency-submission-action@v3
+ uses: advanced-security/maven-dependency-submission-action@v4
env:
MAVEN_OPTS: -Xmx2g
diff --git a/.github/workflows/verify-push.yaml b/.github/workflows/verify-push.yaml
index a4eb837efcb..a0d383b992d 100644
--- a/.github/workflows/verify-push.yaml
+++ b/.github/workflows/verify-push.yaml
@@ -75,7 +75,7 @@ jobs:
- name: Detect changes against master
# we only want to build matsim (module) if changes are not limited to contribs
id: detect-changes
- uses: dorny/paths-filter@v2
+ uses: dorny/paths-filter@v3
if: ${{matrix.module == 'matsim'}}
with:
filters: |
diff --git a/contribs/analysis/pom.xml b/contribs/analysis/pom.xml
index 3894c728d93..8238a60b8bb 100644
--- a/contribs/analysis/pom.xml
+++ b/contribs/analysis/pom.xml
@@ -32,7 +32,7 @@
org.apache.httpcomponents.client5
httpclient5
- 5.3
+ 5.3.1
diff --git a/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java b/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java
index 89086cb00d3..bc9c97f08b3 100644
--- a/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java
+++ b/contribs/application/src/main/java/org/matsim/application/ApplicationUtils.java
@@ -31,10 +31,25 @@ public class ApplicationUtils {
private ApplicationUtils() {
}
+
+ /**
+ * Merge given arguments with custom ones.
+ *
+ * @param args given args, usually input from command line / main method
+ * @param defaultArgs default arguments that will be added to existing ones.
+ */
+ public static String[] mergeArgs(String[] args, String... defaultArgs) {
+ String[] mergedArgs = new String[args.length + defaultArgs.length];
+ System.arraycopy(args, 0, mergedArgs, 0, args.length);
+ System.arraycopy(defaultArgs, 0, mergedArgs, args.length, defaultArgs.length);
+ return mergedArgs;
+ }
+
/**
* Extends a context (usually config location) with an relative filename.
* If the results is a local file, the path will be returned. Otherwise, it will be an url.
* The results can be used as input for command line parameter or {@link IOUtils#resolveFileOrResource(String)}.
+ *
* @return string with path or URL
*/
public static String resolve(URL context, String filename) {
@@ -61,9 +76,9 @@ public static Path globFile(Path path, String pattern) {
try {
return Files.list(path)
- .filter(p -> m.matches(p.getFileName()))
- .findFirst()
- .orElseThrow(() -> new IllegalStateException("No " + pattern + " file found."));
+ .filter(p -> m.matches(p.getFileName()))
+ .findFirst()
+ .orElseThrow(() -> new IllegalStateException("No " + pattern + " file found."));
} catch (IOException e) {
throw new RuntimeException(e);
}
@@ -154,11 +169,19 @@ public static void checkCommand(Class extends MATSimAppCommand> command) {
boolean input = false;
boolean output = false;
for (Field field : fields) {
- if (field.getType().equals(InputOptions.class))
+ if (field.getType().equals(InputOptions.class)) {
input = true;
+ CommandLine.Mixin mixin = field.getAnnotation(CommandLine.Mixin.class);
+ if (mixin == null)
+ throw new IllegalArgumentException(String.format("The command %s has no @Mixin annotation for InputOptions %s.", command, field.getName()));
+ }
- if (field.getType().equals(OutputOptions.class))
+ if (field.getType().equals(OutputOptions.class)) {
output = true;
+ CommandLine.Mixin mixin = field.getAnnotation(CommandLine.Mixin.class);
+ if (mixin == null)
+ throw new IllegalArgumentException(String.format("The command %s has no @Mixin annotation for OutputOptions %s.", command, field.getName()));
+ }
}
if (!input) {
@@ -213,7 +236,7 @@ public static Path matchInput(String name, Path dir) {
return path.get();
// Match more general pattern at last
- path = matchPattern( ".+\\.[a-zA-Z0-9]*_" + name + "\\..+", dir);
+ path = matchPattern(".+\\.[a-zA-Z0-9]*_" + name + "\\..+", dir);
if (path.isPresent())
return path.get();
diff --git a/contribs/application/src/main/java/org/matsim/application/CommandRunner.java b/contribs/application/src/main/java/org/matsim/application/CommandRunner.java
index 56f4549b678..b55fc548a56 100644
--- a/contribs/application/src/main/java/org/matsim/application/CommandRunner.java
+++ b/contribs/application/src/main/java/org/matsim/application/CommandRunner.java
@@ -247,6 +247,22 @@ public Path getRequiredPath(Class extends MATSimAppCommand> command, String fi
return buildPath(spec, command).resolve(file);
}
+ /**
+ * Return the output of a command with a placeholder.
+ * @param file file name, which must contain a %s, which will be replaced by the placeholder
+ */
+ public Path getRequiredPath(Class extends MATSimAppCommand> command, String file, String placeholder) {
+ CommandSpec spec = ApplicationUtils.getSpec(command);
+ if (!ArrayUtils.contains(spec.produces(), file))
+ throw new IllegalArgumentException(String.format("Command %s does not declare output %s", command, file));
+ if (!file.contains("%s"))
+ throw new IllegalArgumentException(String.format("File %s does not contain placeholder %%s", file));
+
+ file = String.format(file, placeholder);
+
+ return buildPath(spec, command).resolve(file);
+ }
+
/**
* Base path for the runner.
*/
@@ -279,8 +295,8 @@ public void add(Class extends MATSimAppCommand> command, String... args) {
if (args.length != 0) {
String[] existing = this.args.get(command);
if (existing != null && existing.length > 0 && !Arrays.equals(existing, args)) {
- throw new IllegalArgumentException(String.format("Command %s already registered with args %s, can not define different args as %s",
- command.toString(), Arrays.toString(existing), Arrays.toString(args)));
+ throw new IllegalArgumentException(String.format("Command %s already registered with args %s, can not define different args as %s (name '%s').",
+ command.toString(), Arrays.toString(existing), Arrays.toString(args), name));
}
}
@@ -298,6 +314,22 @@ public void add(Class extends MATSimAppCommand> command, String... args) {
}
}
+ /**
+ * Insert args for an already existing command. If the command was not added, this does nothing.
+ */
+ public void insertArgs(Class extends MATSimAppCommand> command, String... args) {
+
+ if (!this.args.containsKey(command))
+ return;
+
+ String[] existing = this.args.get(command);
+ String[] newArgs = new String[existing.length + args.length];
+ System.arraycopy(args, 0, newArgs, 0, args.length);
+ System.arraycopy(existing, 0, newArgs, args.length, existing.length);
+
+ this.args.put(command, newArgs);
+ }
+
/**
* Set specific shape file for certain command.
*/
diff --git a/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java b/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java
index 36345aa2a79..5000c232482 100644
--- a/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java
+++ b/contribs/application/src/main/java/org/matsim/application/analysis/LogFileAnalysis.java
@@ -171,7 +171,6 @@ private void renderWarnings(BufferedWriter writer, Set warnings) throws
} else {
Map> grouped = warnings.stream().collect(Collectors.groupingBy(w -> w.module, Collectors.toList()));
- writer.write(String.format("Warnings found in %d module%s ❌
\n\n", grouped.size(), grouped.size() > 1 ? "s" : ""));
for (Map.Entry> e : grouped.entrySet()) {
@@ -188,9 +187,7 @@ private void renderWarnings(BufferedWriter writer, Set warnings) throws
writer.write("""