Skip to content

Type Safe API

GroundbreakingMC edited this page Oct 3, 2025 · 1 revision

Type-Safe API

TOMLy provides type-safe methods for accessing TOML values with automatic conversion and default value support.

Important

Methods returning lists may return null instead of an empty collection. This allows unambiguous detection of unset values without invoking additional methods.

String Methods

getStr()

Get a string value or null if missing:

String name = doc.getStr("app.name");
if (name != null) {
    System.out.println("App name: " + name);
}
``

### getStr() with Default

Get a string value or return a default:

```java
String name = doc.getStr("app.name", "DefaultApp");
String env = doc.getStr("environment", "production");

getStr() with Supplier

Compute default value lazily:

String name = doc.getStr("app.name", () -> {
    return System.getProperty("app.name", "DefaultApp");
});

String timestamp = doc.getStr("created", () -> 
    LocalDateTime.now().toString()
);

String List Methods

getStrList()

List<String> hosts = doc.getStrList("servers.hosts");
// Returns null if missing

if (hosts != null) {
    hosts.forEach(System.out::println);
}

getStrList() with Default

List<String> hosts = doc.getStrList("servers.hosts", List.of("localhost"));
List<String> tags = doc.getStrList("tags", List.of());

getStrList() with Supplier

List<String> hosts = doc.getStrList("servers.hosts", () -> {
    return List.of("localhost", "127.0.0.1");
});

Integer Methods

TOML integers are parsed as Integer if in range, otherwise as Long.

getInt()

Integer port = doc.getInt("server.port");
if (port != null) {
    startServer(port);
}

getInt() with Default

int port = doc.getInt("server.port", 8080);
int timeout = doc.getInt("timeout", 30);
int retries = doc.getInt("retries", 3);

getInt() with Supplier

int port = doc.getInt("server.port", () -> {
    String envPort = System.getenv("PORT");
    return envPort != null ? Integer.parseInt(envPort) : 8080;
});

Integer List Methods

Returns fastutil's IntList for memory efficiency:

getIntList()

IntList ports = doc.getIntList("server.ports");
if (ports != null) {
    for (int i = 0; i < ports.size(); i++) {
        int port = ports.getInt(i);
        System.out.println("Port: " + port);
    }
}

getIntList() with Default

import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntLists;

IntList ports = doc.getIntList("server.ports", IntLists.emptyList());
IntList defaults = doc.getIntList("defaults", new IntArrayList(new int[]{1, 2, 3}));

getIntList() with Supplier

IntList ports = doc.getIntList("server.ports", () ->
    new IntArrayList(new int[]{8080, 8081, 8082})
);

Long Methods

For values that might exceed Integer range:

getLong()

Long userId = doc.getLong("user.id");
Long timestamp = doc.getLong("timestamp");

getLong() with Default

long userId = doc.getLong("user.id", 0L);
long maxSize = doc.getLong("max_size", 1000000L);

getLong() with Supplier

long timestamp = doc.getLong("timestamp", () ->
    System.currentTimeMillis()
);

Long List Methods

Returns fastutil's LongList:

getLongList()

LongList ids = doc.getLongList("user.ids");
if (ids != null) {
    ids.forEach(id -> processUser(id));
}

getLongList() with Default

import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.longs.LongLists;

LongList ids = doc.getLongList("user.ids", LongLists.emptyList());

Double Methods

For floating-point numbers:

getDbl()

Double price = doc.getDbl("product.price");
Double rating = doc.getDbl("rating");

TOML supports special float values:

tomlinfinity = inf
neg_infinity = -inf
not_a_number = nan
Double inf = doc.getDbl("infinity");        // Double.POSITIVE_INFINITY
Double negInf = doc.getDbl("neg_infinity"); // Double.NEGATIVE_INFINITY
Double nan = doc.getDbl("not_a_number");    // Double.NaN

getDbl() with Default

double price = doc.getDbl("product.price", 0.0);
double tax = doc.getDbl("tax_rate", 0.15);

getDbl() with Supplier

double price = doc.getDbl("product.price", () ->
    calculateDefaultPrice()
);

Double List Methods

Returns fastutil's DoubleList:

getDblList()

DoubleList prices = doc.getDblList("products.prices");
if (prices != null) {
    double total = prices.doubleStream().sum();
}

getDblList() with Default

import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.doubles.DoubleLists;

DoubleList prices = doc.getDblList("prices", DoubleLists.emptyList());

Boolean Methods

getBool()

Boolean debug = doc.getBool("app.debug");
if (debug != null && debug) {
    enableDebugMode();
}

getBool() with Default

boolean debug = doc.getBool("app.debug", false);
boolean enabled = doc.getBool("feature.enabled", true);

getBool() with Supplier

boolean debug = doc.getBool("app.debug", () ->
    "development".equals(System.getenv("ENV"))
);

Boolean List Methods

Returns fastutil's BooleanList:

getBoolList()

BooleanList flags = doc.getBoolList("feature.flags");
if (flags != null) {
    for (int i = 0; i < flags.size(); i++) {
        boolean flag = flags.getBoolean(i);
        System.out.println("Flag " + i + ": " + flag);
    }
}

getBoolList() with Default

import it.unimi.dsi.fastutil.booleans.BooleanArrayList;
import it.unimi.dsi.fastutil.booleans.BooleanLists;

BooleanList flags = doc.getBoolList("flags", BooleanLists.emptyList());

Date and Time Methods

TOMLy supports all TOML date/time types.

Local Date

Date without time or timezone:

release_date = 2025-10-01
birthday = 1990-05-15
LocalDate release = doc.getLocalDate("release_date");
LocalDate birthday = doc.getLocalDate("birthday", LocalDate.now());
LocalDate defaultDate = doc.getLocalDate("date", () -> LocalDate.of(2025, 1, 1));

Local Time

Time without date or timezone:

start_time = 09:30:00
end_time = 17:45:30.500
LocalTime start = doc.getLocalTime("start_time");
LocalTime end = doc.getLocalTime("end_time", LocalTime.of(17, 0));
LocalTime defaultTime = doc.getLocalTime("time", () -> LocalTime.now());

Local DateTime

Date and time without timezone:

created_at = 2025-10-01T09:30:00
updated_at = 2025-10-01T17:45:30.500
LocalDateTime created = doc.getLocalDateTime("created_at");
LocalDateTime updated = doc.getLocalDateTime("updated_at", LocalDateTime.now());
LocalDateTime defaultDt = doc.getLocalDateTime("dt", () ->
    LocalDateTime.of(2025, 1, 1, 0, 0)
);

Offset DateTime

Date, time, and timezone:

timestamp = 2025-10-01T09:30:00+02:00
utc_time = 2025-10-01T09:30:00Z
OffsetDateTime timestamp = doc.getOffsetDateTime("timestamp");
OffsetDateTime utc = doc.getOffsetDateTime("utc_time");
OffsetDateTime defaultOdt = doc.getOffsetDateTime("odt", () ->
    OffsetDateTime.now()
);

Date/Time Lists

List<LocalDate> dates = doc.getLocalDateList("important_dates");
List<LocalTime> times = doc.getLocalTimeList("schedule");
List<LocalDateTime> events = doc.getLocalDateTimeList("events");
List<OffsetDateTime> timestamps = doc.getOffsetDateTimeList("logs");

// With defaults
List<LocalDate> dates = doc.getLocalDateList("dates", List.of(LocalDate.now()));

Collection Methods

getSectionList()

For arrays of tables:

[[servers]]
name = "web1"
ip = "192.168.1.10"

[[servers]]
name = "web2"
ip = "192.168.1.11"
List<TomlDocument> servers = doc.getSectionList("servers");
if (servers != null) {
    for (TomlDocument server : servers) {
        String name = server.getStr("name");
        String ip = server.getStr("ip");
        System.out.println(name + " -> " + ip);
    }
}

getList()

For mixed-type arrays:

mixed = [1, "text", true, 3.14]
List<Object> mixed = doc.getList("mixed");
if (mixed != null) {
    for (Object obj : mixed) {
        if (obj instanceof Long) {
            System.out.println("Number: " + obj);
        } else if (obj instanceof String) {
            System.out.println("String: " + obj);
        }
    }
}

Low-Level Access

get()

Get the raw Node object:

import com.github.groundbreakingmc.tomly.nodes.Node;

Node node = doc.get("server.port");
if (node != null) {
    Object value = node.value();
    int line = node.line();
    int column = node.column();
    List<String> comments = node.headerComments();
}

raw()

Convert node value to plain Java object:

Object value = doc.raw("server.port");   // Returns Long
Map<String, Object> section = doc.raw(); // Entire document as Map

hasPath()

Check if a path exists:

if (doc.hasPath("database.url")) {
    String url = doc.getStr("database.url");
    connectToDatabase(url);
} else {
    System.out.println("Database not configured");
}

getSection()

Navigate to a table:

TomlDocument server = doc.getSection("server");
if (server != null) {
    String host = server.getStr("host");
    int port = server.getInt("port", 8080);
}

Number Format Support

TOML supports multiple number formats:

# Decimal
decimal = 42

# Hexadecimal
hex = 0x2A

# Octal
octal = 0o52

# Binary
binary = 0b101010

# With underscores for readability
large = 1_000_000

All are accessed the same way:

long decimal = doc.getLong("decimal");  // 42
long hex = doc.getLong("hex");          // 42
long octal = doc.getLong("octal");      // 42
long binary = doc.getLong("binary");    // 42
long large = doc.getLong("large");      // 1000000

Type Conversion

Values are NOT automatically converted between incompatible types:

port = 8080        # Integer
price = 19.99      # Float
enabled = true     # Boolean
name = "MyApp"     # String
// Correct
int port = doc.getInt("port", 0);

// Returns 8080.0 because TOMLy automaticaly converts numbers
Double portDouble = doc.getDbl("port");

// Wrong - returns 0 because "MyApp" is not a number
int nameInt = doc.getInt("name", 0); // 0

Best Practices

1. Use Appropriate Types

Match the getter to your expected TOML type:

// Good
int port = doc.getInt("server.port", 8080);

// Avoid if you know it's an int
Long portLong = doc.getLong("server.port");

2. Provide Sensible Defaults

Always provide defaults for optional configuration:

int timeout = doc.getInt("timeout", 30);
String env = doc.getStr("environment", "production");
boolean debug = doc.getBool("debug", false);

3. Use Suppliers for Expensive Defaults

// Bad - always creates the list
List<String> hosts = doc.getStrList("hosts", createDefaultHosts());

// Good - only creates if needed
List<String> hosts = doc.getStrList("hosts", () -> createDefaultHosts());

4. Navigate to Sections

// Less efficient
String host = doc.getStr("server.host");
int port = doc.getInt("server.port");
int timeout = doc.getInt("server.timeout");

// More efficient
TomlDocument server = doc.getSection("server");
String host = server.getStr("host");
int port = server.getInt("port");
int timeout = server.getInt("timeout");

5. Avoid Redundant Existence Checks

Do not do this:

if (doc.hasPath("server.host")) {
    String host = doc.getStr("server.host");
}

Instead, simply check for null:

String host = doc.getStr("server.host");
if (host != null) {
    // use host
}

6. Load Config Values Once

Avoid fetching the same configuration value multiple times at runtime.
Instead, read values once during initialization and store them in variables:

Example:

// Initialization
public class ConfigValues {
    
    private String host;
    private int port;
    private int timeout;

    public void setup() {
        TomlDocument doc = ...
        
        host = doc.getStr("server.host");
        port = doc.getInt("server.port");
        timeout = doc.getInt("server.timeout");
    }
    
    // ... Getters
}

ConfigValues configValues = new ConfigValues();
configValues.setup();

// Use later
ServerConnection connection = new ServerConnection(
        configValues.getHost(),
        configValues.getPort(),
        configValues.getTimeout()
);

This improves performance and keeps your code cleaner.

Complete Example

public class AppConfig {
    
    private final Path configPath;
    
    private String appName;
    private String environment;
    private boolean debug;
    private ServerConfig serverConfig;
    private List<String> enabledFeatures;
    private Map<String, Object> databaseConfig;
    
    public AppConfig(Path configPath) {
        this.configPath = configPath;
    }
    
    public void setup() {
        TomlDucument doc = Tomly.parse(configPath, false, PreserveOptions.defaultOptions());

        appName = doc.getStr("app.name", "MyApp");
        environment = doc.getStr("app.environment", "production");
        debug = doc.getBool("app.debug", false);

        TomlDocument server = doc.getSection("server");
        if (server == null) {
            serverConfig = new ServerConfig("localhost", 8080, 30);
        } else {
            String host = server.getStr("host", "localhost");
            int port = server.getInt("port", 8080);
            int timeout = server.getInt("timeout", 30);
            serverConfig = new ServerConfig(host, port, timeout);
        }
        
        enabledFeatures = doc.getStrList("features.enabled", Collections.emptyList());

        TomlDocument db = doc.getSection("database");
        databaseConfig = db != null ? Map.copyOf(db.raw()) : Map.of();
    }
    
    public String getAppName() {
        return appName;
    }
    
    public String getEnvironment() {
        return environment;
    }
    
    public boolean isDebugEnabled() {
        return debug;
    }
    
    public ServerConfig getServerConfig() {
        return serverConfig;
    }
    
    public List<String> getEnabledFeatures() {
        return enabledFeatures;
    }
    
    public Map<String, Object> getDatabaseConfig() {
        return databaseConfig;
    }
}

Next Steps

Clone this wiki locally