package dev.toad.msg.build; import dev.toad.msg.Code; import dev.toad.msg.Id; import dev.toad.msg.Payload; import dev.toad.msg.Token; import dev.toad.msg.Type; import dev.toad.msg.option.Host; import dev.toad.msg.option.Path; import dev.toad.msg.option.Query; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.URI; import java.net.URISyntaxException; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Optional; import java.util.function.BinaryOperator; import java.util.function.Function; import java.util.stream.Collectors; public final class Message implements MessageNeeds.Code, MessageNeeds.Destination, MessageNeeds.Type { HashMap> options = new HashMap<>(); Optional id = Optional.empty(); Optional token = Optional.empty(); Optional addr = Optional.empty(); Optional code = Optional.empty(); Optional type = Optional.empty(); Optional payload = Optional.empty(); Message() {} public static Message from(dev.toad.msg.Message other) { var builder = new Message(); Function key = o -> o.number(); Function> value = o -> o .values() .stream() .map(v -> v.toOwned()) .collect(Collectors.toCollection(ArrayList::new)); BinaryOperator> merge = ( a, b ) -> { a.addAll(b); return a; }; builder.options = other .options() .stream() .collect(Collectors.toMap(key, value, merge, () -> new HashMap<>())); builder.id = Optional.of(other.id()); builder.code = Optional.of(other.code()); builder.token = Optional.of(other.token()); builder.type = Optional.of(other.type()); builder.payload = Optional.of(other.payload()); builder.addr = other.addr(); return builder; } public static MessageNeeds.Destination builder() { return new Message(); } public MessageNeeds.Type uri(String uriStr) throws URISyntaxException, UnknownHostException { var uri = new URI(uriStr); var addr = InetAddress.getByName(uri.getHost()); var port = uri.getPort() > 0 ? uri.getPort() : uri.getScheme().equals("coaps") ? 5684 : 5683; this.addr = Optional.of(new InetSocketAddress(addr, port)); return this.option(new Host(uri.getHost())) .option(new Query(uri.getQuery())) .option(new Path(uri.getPath())); } public MessageNeeds.Type addr(InetSocketAddress addr) { this.addr = Optional.of(addr); return this; } public MessageNeeds.Code type(Type type) { this.type = Optional.of(type); return this; } public Message code(Code code) { this.code = Optional.of(code); return this; } public Message id(Id id) { this.id = Optional.of(id); return this; } public Message unsetId() { this.id = Optional.empty(); return this; } public Message token(Token token) { this.token = Optional.of(token); return this; } public Message unsetToken() { this.token = Optional.empty(); return this; } public Message unsetOption(long num) { this.options.remove(num); return this; } public Message payload(Payload payload) { this.payload = Optional.of(payload); return this.option(payload.contentFormat().get()); } public Message option( Function fun ) { var opt = fun.apply(Option.builder()); return this.option(opt); } public Message option(dev.toad.msg.Option opt) { return this.putOption(opt.number(), opt.values()); } public Message putOption(long number, List values) { this.options.put( number, values .stream() .map(v -> v.toOwned()) .collect(Collectors.toCollection(ArrayList::new)) ); return this; } public Message option(long number, List values) { var vals = Optional .ofNullable(this.options.get(number)) .orElse(new ArrayList<>()); vals.addAll(values.stream().map(v -> v.toOwned()).toList()); return this.putOption(number, List.copyOf(vals)); } public dev.toad.msg.Message build() { return new dev.toad.msg.owned.Message( this.addr, this.type.get(), this.code.get(), this.id.orElse(Id.defaultId()), this.token.orElse(Token.defaultToken()), this.payload.orElse(new Payload()), this.options.entrySet() .stream() .map(ent -> new dev.toad.msg.owned.Option(ent.getKey(), ent.getValue())) .collect(Collectors.toCollection(() -> new ArrayList<>())) ); } }