Skip to content

Commit

Permalink
Merge branch 'master' into StringDoubleMap
Browse files Browse the repository at this point in the history
  • Loading branch information
steffenaxer authored Mar 6, 2024
2 parents f5ed1fc + c161839 commit 1b3946c
Show file tree
Hide file tree
Showing 157 changed files with 2,353 additions and 1,485 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/code-coverage.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
13 changes: 13 additions & 0 deletions contribs/application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
<artifactId>gtfs2matsim</artifactId>
<version>master-33809c4f0f-1</version>
<exclusions>
<!-- Exclude unneeded dependencies and these with known CVE -->
<exclusion>
<groupId>org.geotools</groupId>
<artifactId>*</artifactId>
Expand All @@ -87,6 +88,18 @@
<groupId>org.matsim</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>com.amazonaws</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>com.graphql-java</groupId>
<artifactId>*</artifactId>
</exclusion>
<exclusion>
<groupId>org.postgresql</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,24 @@
package org.matsim.application;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.matsim.api.core.v01.Scenario;
import org.matsim.application.options.CrsOptions;
import org.matsim.application.options.InputOptions;
import org.matsim.application.options.OutputOptions;
import org.matsim.core.config.Config;
import org.matsim.core.config.ConfigAliases;
import org.matsim.core.config.ConfigGroup;
import org.matsim.core.config.ConfigUtils;
import org.matsim.core.scenario.ScenarioUtils;
import org.matsim.core.utils.io.IOUtils;
import picocli.CommandLine;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.UncheckedIOException;
Expand All @@ -21,13 +28,20 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.util.Optional;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

public class ApplicationUtils {

private static final Logger log = LogManager.getLogger(ApplicationUtils.class);

/**
* This encoding indicates command line was used to start the jar.
*/
private static final String WIN_CLI_ENCODING = "cp850";

private ApplicationUtils() {
}

Expand All @@ -46,7 +60,197 @@ public static String[] mergeArgs(String[] args, String... defaultArgs) {
}

/**
* Extends a context (usually config location) with an relative filename.
* Utility method to check if a jar might be run from the desktop (using double-click).
*/
public static boolean isRunFromDesktop() {

// check if gui was explicitly enabled
String env = System.getenv().getOrDefault("RUN_GUI", "undefined");
if (env.equalsIgnoreCase("true") || env.equals("1"))
return true;
else if (env.equalsIgnoreCase("false") || env.equals("0"))
return false;

String property = System.getProperty("RUN_GUI", "undefined");
if (property.equalsIgnoreCase("true") || property.equals("1"))
return true;
else if (property.equalsIgnoreCase("false") || property.equals("0"))
return false;

String macIdentifier = System.getenv().getOrDefault("__CFBundleIdentifier", "none");

if (macIdentifier.equals("com.apple.java.JarLauncher") || macIdentifier.equals("com.apple.JavaLauncher"))
return true;

String os = System.getProperty("os.name");

if (os.toLowerCase().startsWith("windows")) {

// presence of the prompt variable indicates that the jar was run from the command line
boolean hasPrompt = System.getenv().containsKey("PROMPT");

// this prompt is not set in PowerShell, so we need another check
if (hasPrompt)
return false;

// stdout.encoding from CLI are usually cp850
String encoding = System.getProperty("stdout.encoding", "none");
String sunEncoding = System.getProperty("sun.stdout.encoding", "none");

if (encoding.equals(WIN_CLI_ENCODING) || sunEncoding.equals(WIN_CLI_ENCODING))
return false;

// Run from intelij, will not start the gui by default
if (System.getenv().containsKey("IDEA_INITIAL_DIRECTORY"))
return false;
// also file.encoding=UTF-8, seems to be set by default in IntelliJ

// if no other cli indicators are present, we have to assume that the jar was run from the desktop
return true;
}

return false;

}

/**
* Apply run configuration in yaml format.
*/
public static void applyConfigUpdate(Config config, Path yaml) {

if (!Files.exists(yaml)) {
throw new IllegalArgumentException("Given config yaml does not exist: " + yaml);
}

ObjectMapper mapper = new ObjectMapper(new YAMLFactory()
.enable(YAMLGenerator.Feature.MINIMIZE_QUOTES));

ConfigAliases aliases = new ConfigAliases();
Deque<String> emptyStack = new ArrayDeque<>();

try (BufferedReader reader = Files.newBufferedReader(yaml)) {

JsonNode node = mapper.readTree(reader);

Iterator<Map.Entry<String, JsonNode>> fields = node.fields();

while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();
String configGroupName = aliases.resolveAlias(field.getKey(), emptyStack);
ConfigGroup group = config.getModules().get(configGroupName);
if (group == null) {
group = new ConfigGroup(configGroupName);
config.addModule(group);
}

applyNodeToConfigGroup(field.getValue(), group);
}

} catch (IOException e) {
throw new UncheckedIOException(e);
}

}

/**
* Sets the json config into
*/
private static void applyNodeToConfigGroup(JsonNode node, ConfigGroup group) {

Iterator<Map.Entry<String, JsonNode>> fields = node.fields();

while (fields.hasNext()) {
Map.Entry<String, JsonNode> field = fields.next();

if (isParameterSet(field.getValue())) {

// store the current parameters sets, newly added sets are not merged with each other
List<? extends ConfigGroup> params = new ArrayList<>(group.getParameterSets(field.getKey()));

for (JsonNode item : field.getValue()) {


// Special case of parameter sets that have only one entry
if (field.getValue().size() == 1 && params.size() == 1 && field.getValue().get(0).isObject()) {

applyNodeToConfigGroup(field.getValue().get(0), params.get(0));

} else {

applyNodeAsParameterSet(field.getKey(), item, group, params);
}
}
} else {

if (field.getValue().isTextual())
group.addParam(field.getKey(), field.getValue().textValue());
else if (field.getValue().isArray()) {
// arrays are joined using ","
Stream<JsonNode> stream = StreamSupport.stream(field.getValue().spliterator(), false);
String string = stream.map(n -> n.isTextual() ? n.textValue() : n.toString()).collect(Collectors.joining(","));
group.addParam(field.getKey(), string);
} else
group.addParam(field.getKey(), field.getValue().toString());
}
}
}

/**
* Any array of complex object can be considered a config group.
*/
private static boolean isParameterSet(JsonNode node) {

if (!node.isArray())
return false;

// any object can be assigned as parameter set
for (JsonNode el : node) {
if (!el.isObject())
return false;
}

return true;
}

/**
* Handle possible update and creation of parameter sets within a config group.
*/
private static void applyNodeAsParameterSet(String groupName, JsonNode item, ConfigGroup group, List<? extends ConfigGroup> params) {

Iterator<Map.Entry<String, JsonNode>> it = item.fields();

// There was at least one matching group
boolean matched = false;

while (!params.isEmpty() && it.hasNext()) {

Map.Entry<String, JsonNode> attr = it.next();
List<? extends ConfigGroup> candidates = params.stream()
.filter(p -> p.getParams().containsKey(attr.getKey()))
.filter(p -> p.getParams().get(attr.getKey())
.equals(attr.getValue().isTextual() ? attr.getValue().textValue() : attr.getValue().toString()))
.toList();

if (candidates.isEmpty())
break;

matched = true;
params = candidates;
}

if (params.size() > 1) {
throw new IllegalArgumentException("Ambiguous parameter set: " + item);
} else if (params.size() == 1 && matched) {
applyNodeToConfigGroup(item, params.get(0));
} else {
ConfigGroup newGroup = group.createParameterSet(groupName);
group.addParameterSet(newGroup);
applyNodeToConfigGroup(item, newGroup);
}
}

/**
* Extends a context (usually config location) with a 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)}.
*
Expand Down Expand Up @@ -75,10 +279,11 @@ public static Path globFile(Path path, String pattern) {
PathMatcher m = path.getFileSystem().getPathMatcher("glob:" + pattern);

try {
return Files.list(path)
.filter(p -> m.matches(p.getFileName()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No " + pattern + " file found."));
try (Stream<Path> list = Files.list(path)) {
return list.filter(p -> m.matches(p.getFileName()))
.findFirst()
.orElseThrow(() -> new IllegalStateException("No " + pattern + " file found."));
}
} catch (IOException e) {
throw new RuntimeException(e);
}
Expand Down
Loading

0 comments on commit 1b3946c

Please sign in to comment.