From 26490dfa5b1fe6ff5669754ed600a577da86266f Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Mon, 13 Apr 2026 11:23:54 +0100 Subject: [PATCH 01/72] debugprint --- .../org/greenbytes/http/sfv/BooleanItem.java | 6 ++++ .../greenbytes/http/sfv/ByteSequenceItem.java | 6 ++++ .../java/org/greenbytes/http/sfv/CLI.java | 1 + .../org/greenbytes/http/sfv/DateItem.java | 5 ++++ .../org/greenbytes/http/sfv/DecimalItem.java | 6 ++++ .../org/greenbytes/http/sfv/Dictionary.java | 7 +++++ .../http/sfv/DisplayStringItem.java | 6 ++++ .../org/greenbytes/http/sfv/InnerList.java | 11 +++++++ .../org/greenbytes/http/sfv/IntegerItem.java | 8 +++++ .../org/greenbytes/http/sfv/OuterList.java | 12 ++++++++ .../org/greenbytes/http/sfv/Parameters.java | 30 ++++++++++++++----- .../org/greenbytes/http/sfv/StringItem.java | 7 +++++ .../org/greenbytes/http/sfv/TokenItem.java | 7 +++++ .../java/org/greenbytes/http/sfv/Type.java | 2 ++ 14 files changed, 106 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index b4e8f77..b85b49f 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -55,6 +55,12 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s =indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + return sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + } + @Override public Boolean get() { return value; diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index 122a161..d9d5368 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -59,6 +59,12 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + return sb.append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + } + + @Override public ByteBuffer get() { // this returns a wrapper around a copy so that the object itself diff --git a/src/main/java/org/greenbytes/http/sfv/CLI.java b/src/main/java/org/greenbytes/http/sfv/CLI.java index cf84492..fa8cd92 100644 --- a/src/main/java/org/greenbytes/http/sfv/CLI.java +++ b/src/main/java/org/greenbytes/http/sfv/CLI.java @@ -61,6 +61,7 @@ private static void dump(PrintStream out, String as, Type result) { out.println(as + ": " + ANSI_GREEN + result.serialize() + " " + ANSI_RESET + ANSI_FAINT + "(" + result.getClass().getSimpleName() + ")" + ANSI_RESET); + out.println(result.serializeToForDebug(new StringBuilder(), 2)); } private static void diagnostics(PrintStream out, String as, ParseException ex) { diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index 5aa22b1..e605707 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -59,6 +59,11 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + return sb.append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + } + @Override public Long get() { return value; diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index ee5bff4..43ae6fa 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -102,6 +102,12 @@ public String serialize() { return serializeTo(new StringBuilder(20)).toString(); } + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s = String.format("%" + indentLevel + "s", ""); + return sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + } + @Override public BigDecimal get() { return BigDecimal.valueOf(value, 3); diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index ef89d9e..bf7a956 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -66,4 +66,11 @@ public StringBuilder serializeTo(StringBuilder sb) { public String serialize() { return serializeTo(new StringBuilder()).toString(); } + + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s = String.format("%" + indentLevel + "s", ""); + sb = sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + return sb; + } } diff --git a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java index f10765d..6e9599c 100755 --- a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java @@ -64,6 +64,12 @@ public String serialize() { return serializeTo(new StringBuilder(2 + value.length())).toString(); } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s = String.format("%" + indentLevel + "s", ""); + return sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + } + @Override public String get() { return this.value; diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 83b9331..ffc85d8 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -56,6 +56,17 @@ public StringBuilder serializeTo(StringBuilder sb) { return sb; } + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s = String.format("%" + indentLevel + "s", ""); + sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + + for (ListElement le : value) { + sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + } + + return sb; + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index d2d31ed..62aeb5b 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -58,6 +58,14 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(value).append(classn).append("\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)).append("\n"); + } + @Override public Long get() { return value; diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index 3faa40b..a9d6017 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -47,6 +47,18 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s = String.format("%" + indentLevel + "s", ""); + sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + + for (ListElement le : value) { + sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + } + + return sb; + } + @Override public List> get() { return value; diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index af45931..d752e96 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -61,6 +61,28 @@ public StringBuilder serializeTo(StringBuilder sb) { return sb; } + /** + * Serialize this parameter. + * @return serialization + */ + public String serialize() { + return serializeTo(new StringBuilder()).toString(); + } + + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + if (!delegate.isEmpty()) { + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + sb.append(indent).append(serialize()).append(classn).append("\n"); + for (Map.Entry> e : delegate.entrySet()) { + sb.append(" " + indent).append(e.getKey()).append(" -> ").append(e.getValue().serializeToForDebug(sb, 0)).append("\n"); + } + return sb; + } else { + return sb; + } + } + private static Map> checkAndTransformMap(Map map) { Map> result = new LinkedHashMap<>( Objects.requireNonNull(map, "Map must not be null").size()); @@ -217,12 +239,4 @@ public int size() { public Collection> values() { return delegate.values(); } - - /** - * Serialize this parameter. - * @return serialization - */ - public String serialize() { - return serializeTo(new StringBuilder()).toString(); - } } diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index dae8be5..adab32a 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -55,6 +55,13 @@ public StringBuilder serializeTo(StringBuilder sb) { return sb; } + @Override + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String s = String.format("%" + indentLevel + "s", ""); + return sb.append(s).append(value).append(" (").append(this.getClass().getSimpleName()).append(")\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)).append("\n"); + } + @Override public String serialize() { return serializeTo(new StringBuilder(2 + value.length())).toString(); diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index 8c276da..2881adc 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -52,6 +52,13 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(value).append(classn).append("\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + } + @Override public String get() { return this.value; diff --git a/src/main/java/org/greenbytes/http/sfv/Type.java b/src/main/java/org/greenbytes/http/sfv/Type.java index 6af9a8b..37e6a53 100644 --- a/src/main/java/org/greenbytes/http/sfv/Type.java +++ b/src/main/java/org/greenbytes/http/sfv/Type.java @@ -24,6 +24,8 @@ public interface Type extends Supplier { */ StringBuilder serializeTo(StringBuilder sb); + StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel); + /** * Serialize. * From 0ffaa11cc3314ff79699ef31371de1eb7ef3982b Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 15 Apr 2026 17:15:14 +0100 Subject: [PATCH 02/72] debugprint --- .../java/org/greenbytes/http/sfv/BooleanItem.java | 6 ++++-- src/main/java/org/greenbytes/http/sfv/Dictionary.java | 11 +++++++++-- .../java/org/greenbytes/http/sfv/IntegerItem.java | 2 +- src/main/java/org/greenbytes/http/sfv/Parameters.java | 3 ++- 4 files changed, 16 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index b85b49f..bc37b82 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -57,8 +57,10 @@ public String serialize() { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s =indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - return sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(value ? "?1" : "?0").append(classn).append("\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index bf7a956..b009f38 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -69,8 +69,15 @@ public String serialize() { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s = String.format("%" + indentLevel + "s", ""); - sb = sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + sb.append(indent).append(serialize()).append(classn).append("\n"); + for (Map.Entry> e : value.entrySet()) { + String name = e.getKey(); + ListElement dict = e.getValue(); + sb.append(" " + indent).append(e.getKey()).append(" -> "); + e.getValue().serializeToForDebug(sb, indentLevel); + } return sb; } } diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index 62aeb5b..2c544d9 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -63,7 +63,7 @@ public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = " (" + this.getClass().getSimpleName() + ")"; return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)).append("\n"); + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index d752e96..2d715b5 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -75,7 +75,8 @@ public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { String classn = " (" + this.getClass().getSimpleName() + ")"; sb.append(indent).append(serialize()).append(classn).append("\n"); for (Map.Entry> e : delegate.entrySet()) { - sb.append(" " + indent).append(e.getKey()).append(" -> ").append(e.getValue().serializeToForDebug(sb, 0)).append("\n"); + sb.append(" " + indent).append(e.getKey()).append(" -> "); + e.getValue().serializeToForDebug(sb, 0); } return sb; } else { From 7498a788911e58e8af925027f5dfff418da47846 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 15:41:04 +0100 Subject: [PATCH 03/72] serializeForToDebug consistent --- src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java | 4 +++- src/main/java/org/greenbytes/http/sfv/DateItem.java | 4 +++- src/main/java/org/greenbytes/http/sfv/DecimalItem.java | 5 +++-- src/main/java/org/greenbytes/http/sfv/Dictionary.java | 6 ++---- .../java/org/greenbytes/http/sfv/DisplayStringItem.java | 5 +++-- src/main/java/org/greenbytes/http/sfv/InnerList.java | 5 +++-- src/main/java/org/greenbytes/http/sfv/OuterList.java | 5 +++-- src/main/java/org/greenbytes/http/sfv/StringItem.java | 5 +++-- 8 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index d9d5368..98d1bf0 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -61,7 +61,9 @@ public String serialize() { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - return sb.append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(serialize()).append(classn).append("\n"); } diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index e605707..7c46036 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -61,7 +61,9 @@ public String serialize() { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - return sb.append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(serialize()).append(classn).append("\n"); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 43ae6fa..9ec579e 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -103,8 +103,9 @@ public String serialize() { } public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s = String.format("%" + indentLevel + "s", ""); - return sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n") + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(serialize()).append(classn) .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); } diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index b009f38..64a622a 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -73,10 +73,8 @@ public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { String classn = " (" + this.getClass().getSimpleName() + ")"; sb.append(indent).append(serialize()).append(classn).append("\n"); for (Map.Entry> e : value.entrySet()) { - String name = e.getKey(); - ListElement dict = e.getValue(); - sb.append(" " + indent).append(e.getKey()).append(" -> "); - e.getValue().serializeToForDebug(sb, indentLevel); + sb.append(indent +" ").append(e.getKey()).append(" -> \n"); + e.getValue().serializeToForDebug(sb, indentLevel + 2); } return sb; } diff --git a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java index 6e9599c..f0549d6 100755 --- a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java @@ -66,8 +66,9 @@ public String serialize() { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s = String.format("%" + indentLevel + "s", ""); - return sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(serialize()).append(classn).append("\n"); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index ffc85d8..6b7ab9a 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -57,8 +57,9 @@ public StringBuilder serializeTo(StringBuilder sb) { } public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s = String.format("%" + indentLevel + "s", ""); - sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + sb.append(indent).append(serialize()).append(classn).append("\n"); for (ListElement le : value) { sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2)); diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index a9d6017..506bc1f 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -49,8 +49,9 @@ public String serialize() { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s = String.format("%" + indentLevel + "s", ""); - sb.append(s).append(serialize()).append(" (").append(this.getClass().getSimpleName()).append(")\n"); + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + sb.append(indent).append(serialize()).append(classn).append("\n"); for (ListElement le : value) { sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2)); diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index adab32a..8d8319c 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -57,8 +57,9 @@ public StringBuilder serializeTo(StringBuilder sb) { @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { - String s = String.format("%" + indentLevel + "s", ""); - return sb.append(s).append(value).append(" (").append(this.getClass().getSimpleName()).append(")\n") + String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; + String classn = " (" + this.getClass().getSimpleName() + ")"; + return sb.append(indent).append(value).append(classn).append("\n") .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)).append("\n"); } From 6d6f091fabd7a004d0ae076d2ca98c977f7e43b8 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 16:53:30 +0100 Subject: [PATCH 04/72] add formatter for class names --- src/main/java/org/greenbytes/http/sfv/BooleanItem.java | 7 ++++--- .../java/org/greenbytes/http/sfv/ByteSequenceItem.java | 5 +++-- src/main/java/org/greenbytes/http/sfv/CLI.java | 3 ++- src/main/java/org/greenbytes/http/sfv/DateItem.java | 6 +++--- src/main/java/org/greenbytes/http/sfv/DecimalItem.java | 7 ++++--- src/main/java/org/greenbytes/http/sfv/Dictionary.java | 7 ++++--- .../java/org/greenbytes/http/sfv/DisplayStringItem.java | 5 +++-- src/main/java/org/greenbytes/http/sfv/InnerList.java | 7 ++++--- src/main/java/org/greenbytes/http/sfv/IntegerItem.java | 7 ++++--- src/main/java/org/greenbytes/http/sfv/OuterList.java | 7 ++++--- src/main/java/org/greenbytes/http/sfv/Parameters.java | 6 +++--- src/main/java/org/greenbytes/http/sfv/StringItem.java | 7 ++++--- src/main/java/org/greenbytes/http/sfv/TokenItem.java | 7 ++++--- src/main/java/org/greenbytes/http/sfv/Type.java | 3 ++- 14 files changed, 48 insertions(+), 36 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index bc37b82..6de400c 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -1,6 +1,7 @@ package org.greenbytes.http.sfv; import java.util.Objects; +import java.util.function.Function; /** * Represents a Boolean. @@ -56,11 +57,11 @@ public String serialize() { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(value ? "?1" : "?0").append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index 98d1bf0..6c16b17 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -4,6 +4,7 @@ import java.util.Arrays; import java.util.Base64; import java.util.Objects; +import java.util.function.Function; /** * Represents a Byte Sequence. @@ -60,9 +61,9 @@ public String serialize() { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(serialize()).append(classn).append("\n"); } diff --git a/src/main/java/org/greenbytes/http/sfv/CLI.java b/src/main/java/org/greenbytes/http/sfv/CLI.java index fa8cd92..8a8aef2 100644 --- a/src/main/java/org/greenbytes/http/sfv/CLI.java +++ b/src/main/java/org/greenbytes/http/sfv/CLI.java @@ -61,7 +61,8 @@ private static void dump(PrintStream out, String as, Type result) { out.println(as + ": " + ANSI_GREEN + result.serialize() + " " + ANSI_RESET + ANSI_FAINT + "(" + result.getClass().getSimpleName() + ")" + ANSI_RESET); - out.println(result.serializeToForDebug(new StringBuilder(), 2)); + out.println(result.serializeToForDebug(new StringBuilder(), 2, + cls -> " " + ANSI_FAINT + "(" + cls.getSimpleName() + ")" + ANSI_RESET)); } private static void diagnostics(PrintStream out, String as, ParseException ex) { diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index 7c46036..cbc43e9 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -1,6 +1,7 @@ package org.greenbytes.http.sfv; import java.util.Objects; +import java.util.function.Function; /** * Represents a Date. @@ -59,10 +60,9 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } - @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(serialize()).append(classn).append("\n"); } diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 9ec579e..836f796 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -2,6 +2,7 @@ import java.math.BigDecimal; import java.util.Objects; +import java.util.function.Function; /** * Represents a Decimal. @@ -102,11 +103,11 @@ public String serialize() { return serializeTo(new StringBuilder(20)).toString(); } - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(serialize()).append(classn) - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index 64a622a..a977df5 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -2,6 +2,7 @@ import java.util.Collections; import java.util.Map; +import java.util.function.Function; /** * Represents a Dictionary. @@ -68,13 +69,13 @@ public String serialize() { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); sb.append(indent).append(serialize()).append(classn).append("\n"); for (Map.Entry> e : value.entrySet()) { sb.append(indent +" ").append(e.getKey()).append(" -> \n"); - e.getValue().serializeToForDebug(sb, indentLevel + 2); + e.getValue().serializeToForDebug(sb, indentLevel + 2, formatter); } return sb; } diff --git a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java index f0549d6..2179bda 100755 --- a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java @@ -2,6 +2,7 @@ import java.nio.charset.StandardCharsets; import java.util.Objects; +import java.util.function.Function; /** * Represents a Display String. @@ -65,9 +66,9 @@ public String serialize() { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(serialize()).append(classn).append("\n"); } diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 6b7ab9a..7d40452 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Objects; +import java.util.function.Function; /** * Represents an Inner List. @@ -56,13 +57,13 @@ public StringBuilder serializeTo(StringBuilder sb) { return sb; } - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); sb.append(indent).append(serialize()).append(classn).append("\n"); for (ListElement le : value) { - sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } return sb; diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index 2c544d9..c85f894 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -1,6 +1,7 @@ package org.greenbytes.http.sfv; import java.util.Objects; +import java.util.function.Function; /** * Represents an Integer. @@ -59,11 +60,11 @@ public String serialize() { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index 506bc1f..d74716e 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.Objects; +import java.util.function.Function; /** * Represents a List. @@ -48,13 +49,13 @@ public String serialize() { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); sb.append(indent).append(serialize()).append(classn).append("\n"); for (ListElement le : value) { - sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } return sb; diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index 2d715b5..6beb2f0 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -69,14 +69,14 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { if (!delegate.isEmpty()) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); sb.append(indent).append(serialize()).append(classn).append("\n"); for (Map.Entry> e : delegate.entrySet()) { sb.append(" " + indent).append(e.getKey()).append(" -> "); - e.getValue().serializeToForDebug(sb, 0); + e.getValue().serializeToForDebug(sb, 0, formatter); } return sb; } else { diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 8d8319c..9acbc19 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -1,6 +1,7 @@ package org.greenbytes.http.sfv; import java.util.Objects; +import java.util.function.Function; /** * Represents a String. @@ -56,11 +57,11 @@ public StringBuilder serializeTo(StringBuilder sb) { } @Override - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)).append("\n"); + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)).append("\n"); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index 2881adc..1d7300b 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -1,6 +1,7 @@ package org.greenbytes.http.sfv; import java.util.Objects; +import java.util.function.Function; /** * Represents a Token. @@ -52,11 +53,11 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel) { + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = " (" + this.getClass().getSimpleName() + ")"; + String classn = formatter.apply(this.getClass()); return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2)); + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/Type.java b/src/main/java/org/greenbytes/http/sfv/Type.java index 37e6a53..2d9be63 100644 --- a/src/main/java/org/greenbytes/http/sfv/Type.java +++ b/src/main/java/org/greenbytes/http/sfv/Type.java @@ -1,5 +1,6 @@ package org.greenbytes.http.sfv; +import java.util.function.Function; import java.util.function.Supplier; /** @@ -24,7 +25,7 @@ public interface Type extends Supplier { */ StringBuilder serializeTo(StringBuilder sb); - StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel); + StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function classFormatter); /** * Serialize. From 87e2d0a18eed9a4dd1944ceef16626da52042a4f Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 19:43:39 +0100 Subject: [PATCH 05/72] ByteSequenceItem --- .../org/greenbytes/http/sfv/ByteSequenceItem.java | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index 6c16b17..fa57839 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -46,15 +46,18 @@ public Parameters getParams() { return params; } - @Override - public StringBuilder serializeTo(StringBuilder sb) { + private StringBuilder serializeToNoParams(StringBuilder sb) { sb.append(':'); sb.append(ENCODER.encodeToString(this.value)); sb.append(':'); - params.serializeTo(sb); return sb; } + @Override + public StringBuilder serializeTo(StringBuilder sb) { + return params.serializeTo(serializeToNoParams(sb)); + } + @Override public String serialize() { return serializeTo(new StringBuilder()).toString(); @@ -64,7 +67,8 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serialize()).append(classn).append("\n"); + return sb.append(indent).append(serializeToNoParams(new StringBuilder())).append(classn).append("\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)).append("\n"); } From 0257c4e5d5d6ab752f9ad3a7e5ab329504ea1811 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 22:40:26 +0100 Subject: [PATCH 06/72] DateItem --- src/main/java/org/greenbytes/http/sfv/DateItem.java | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index cbc43e9..61606d6 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -47,12 +47,13 @@ public Parameters getParams() { return params; } + public StringBuilder serializeToNoParams(StringBuilder sb) { + return sb.append('@').append(value); + } + @Override public StringBuilder serializeTo(StringBuilder sb) { - sb.append('@'); - sb.append(value); - params.serializeTo(sb); - return sb; + return params.serializeTo(serializeToNoParams(sb)); } @Override @@ -63,7 +64,8 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serialize()).append(classn).append("\n"); + return sb.append(indent).append(serializeToNoParams(new StringBuilder())).append(classn).append("\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)).append("\n"); } @Override From 25cc9a552f0ecd052324b06e593e188969d3d804 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 22:43:39 +0100 Subject: [PATCH 07/72] DecimalItem --- src/main/java/org/greenbytes/http/sfv/DecimalItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 836f796..0c8f888 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -106,7 +106,7 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serialize()).append(classn) + return sb.append(indent).append(serialize()).append(classn).append("\n") .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } From e0596b3c18d74ee389551f5b56517834770eb100 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 22:51:19 +0100 Subject: [PATCH 08/72] DecimalItem --- .../java/org/greenbytes/http/sfv/DecimalItem.java | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 0c8f888..e206100 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -71,8 +71,7 @@ public Parameters getParams() { return params; } - @Override - public StringBuilder serializeTo(StringBuilder sb) { + public StringBuilder serializeToNoParams(StringBuilder sb) { String sign = value < 0 ? "-" : ""; @@ -93,11 +92,14 @@ public StringBuilder serializeTo(StringBuilder sb) { } } - params.serializeTo(sb); - return sb; } + @Override + public StringBuilder serializeTo(StringBuilder sb) { + return params.serializeTo(serializeToNoParams(sb)); + } + @Override public String serialize() { return serializeTo(new StringBuilder(20)).toString(); @@ -106,8 +108,8 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serialize()).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); + return sb.append(indent).append(serializeToNoParams(sb).append(classn).append("\n") + .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter))); } @Override From 922fb565b9bd6277c5f959607916a4c831599fe2 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 18 Apr 2026 23:09:32 +0100 Subject: [PATCH 09/72] DecimalItem --- src/main/java/org/greenbytes/http/sfv/DecimalItem.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index e206100..37692b6 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -108,8 +108,10 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serializeToNoParams(sb).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter))); + sb = sb.append(indent); + sb = serializeToNoParams(sb); + sb = sb.append(classn).append("\n"); + return params.serializeToForDebug(sb, indentLevel + 2, formatter); } @Override From 517ed28de193c251ff14697c890d03886e3783de Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:08:45 +0100 Subject: [PATCH 10/72] InnerList --- .../java/org/greenbytes/http/sfv/InnerList.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 7d40452..b24b7e7 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -38,8 +38,7 @@ public InnerList withParams(Parameters params) { return new InnerList(this.value, Objects.requireNonNull(params, "params must not be null")); } - @Override - public StringBuilder serializeTo(StringBuilder sb) { + private StringBuilder serializeToNoParams(StringBuilder sb) { String separator = ""; sb.append('('); @@ -52,21 +51,27 @@ public StringBuilder serializeTo(StringBuilder sb) { sb.append(')'); - params.serializeTo(sb); - return sb; } + @Override + public StringBuilder serializeTo(StringBuilder sb) { + return params.serializeTo(serializeToNoParams(sb)); + } + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - sb.append(indent).append(serialize()).append(classn).append("\n"); + + sb.append(indent); + sb = serializeToNoParams(sb); + sb.append(classn).append("\n"); for (ListElement le : value) { sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } - return sb; + return params.serializeToForDebug(sb, indentLevel, formatter); } @Override From 787a18d6f6a61cc2e807f50fb6012fb436083570 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:13:50 +0100 Subject: [PATCH 11/72] BooleanItem --- src/main/java/org/greenbytes/http/sfv/BooleanItem.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index 6de400c..f1738df 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -60,8 +60,10 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(value ? "?1" : "?0").append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); + + sb.append(indent).append(value ? "?1" : "?0").append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From b6a0c3677ca36c9a019102a4b188cf2c0e6387ba Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:18:31 +0100 Subject: [PATCH 12/72] ByteSequenceItem --- .../java/org/greenbytes/http/sfv/ByteSequenceItem.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index fa57839..9760fc5 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -67,8 +67,12 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serializeToNoParams(new StringBuilder())).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)).append("\n"); + + sb = sb.append(indent); + sb = serializeToNoParams(sb); + sb.append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } From 6971aaf0f002ecf1ce4cdaf4c6a36bba26fe3239 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:23:27 +0100 Subject: [PATCH 13/72] DateItem --- src/main/java/org/greenbytes/http/sfv/DateItem.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index 61606d6..b454d8b 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -64,8 +64,12 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serializeToNoParams(new StringBuilder())).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)).append("\n"); + + sb = sb.append(indent); + sb = serializeToNoParams(sb); + sb.append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From ff852c7bcee523d48717d027363488e3a79d4d9f Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:25:26 +0100 Subject: [PATCH 14/72] DecimalItem --- src/main/java/org/greenbytes/http/sfv/DecimalItem.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 37692b6..70e5c62 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -108,10 +108,12 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); + sb = sb.append(indent); sb = serializeToNoParams(sb); sb = sb.append(classn).append("\n"); - return params.serializeToForDebug(sb, indentLevel + 2, formatter); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From 9a6a48c59998c06f8ce07ca842385386c6a19b52 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:29:33 +0100 Subject: [PATCH 15/72] IntegerItem --- src/main/java/org/greenbytes/http/sfv/IntegerItem.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index c85f894..99a7eda 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -63,8 +63,10 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); + + sb = sb.append(indent).append(value).append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From b398794547382370d8c48ad397f9dacf57a72fb1 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:32:39 +0100 Subject: [PATCH 16/72] StringItem --- src/main/java/org/greenbytes/http/sfv/StringItem.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 9acbc19..114380a 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -60,8 +60,10 @@ public StringBuilder serializeTo(StringBuilder sb) { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)).append("\n"); + + sb = sb.append(indent).append(value).append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From 44b488adc2ed1691eb5242710717546643ae5989 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:36:41 +0100 Subject: [PATCH 17/72] DisplayStringItem --- .../greenbytes/http/sfv/DisplayStringItem.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java index 2179bda..d791e27 100755 --- a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java @@ -42,8 +42,7 @@ public Parameters getParams() { return params; } - @Override - public StringBuilder serializeTo(StringBuilder sb) { + private StringBuilder serializeToNoParams(StringBuilder sb) { sb.append("%\""); byte[] octets = value.getBytes(StandardCharsets.UTF_8); for (byte b : octets) { @@ -56,7 +55,13 @@ public StringBuilder serializeTo(StringBuilder sb) { } } sb.append('"'); - params.serializeTo(sb); + return sb; + } + + @Override + public StringBuilder serializeTo(StringBuilder sb) { + sb = serializeToNoParams(sb); + sb = params.serializeTo(sb); return sb; } @@ -69,7 +74,12 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(serialize()).append(classn).append("\n"); + + sb = sb.append(indent); + sb = serializeToNoParams(sb); + sb.append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From 980f2a7451f2a7700300c340f598977353777cb2 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:40:26 +0100 Subject: [PATCH 18/72] TokenItem --- src/main/java/org/greenbytes/http/sfv/TokenItem.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index 1d7300b..16765dd 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -56,8 +56,10 @@ public String serialize() { public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); - return sb.append(indent).append(value).append(classn).append("\n") - .append(params.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); + + sb = sb.append(indent).append(value).append(classn).append("\n"); + sb = params.serializeToForDebug(sb, indentLevel + 2, formatter); + return sb; } @Override From 0f385786cc515ae31b69d7988e355679a6649b57 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:46:24 +0100 Subject: [PATCH 19/72] Javadoc --- src/main/java/org/greenbytes/http/sfv/Type.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/org/greenbytes/http/sfv/Type.java b/src/main/java/org/greenbytes/http/sfv/Type.java index 2d9be63..7e59323 100644 --- a/src/main/java/org/greenbytes/http/sfv/Type.java +++ b/src/main/java/org/greenbytes/http/sfv/Type.java @@ -25,6 +25,15 @@ public interface Type extends Supplier { */ StringBuilder serializeTo(StringBuilder sb); + /** + * Serialize debubg information to an existing {@link StringBuilder}. + * + * @param sb + * where to serialize to + * @param indentLevel how much to indent + * @param classFormatter to format the classname when desires (can be a function that returns an empty string) + * @return the {@link StringBuilder} so calls can be chained. + */ StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function classFormatter); /** From 767efe6f9ebf0a3b51fcf51abd3faea9b492a011 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 14:53:22 +0100 Subject: [PATCH 20/72] README --- README.md | 33 +++++++++++++------ .../org/greenbytes/http/sfv/InnerList.java | 2 +- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index af69a38..e5278ad 100644 --- a/README.md +++ b/README.md @@ -39,18 +39,31 @@ gives: ## Testing Client Here's a command line tool which will feed all arguments into the parser (as if obtained -from multiple field lines), parsed as Item, List, or Dictionary: +from multiple field lines), parsed as Item, List, or Dictionary, and return diagnostic +information. ``` -$ java -jar target/structured-fields-0.6-SNAPSHOT.jar ':cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:' 'x' - -Item: >>:cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:,x<< - ----------------------------------------------^ (0x2c) Extra characters in string parsed as Item - -List: :cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:, x (OuterList) - -Dict: >>:cHJldGVuZCB0aGlzIGlzIGJpbmFyeSBjb250ZW50Lg==:,x<< - ^ (0x3a) Key must start with LCALPHA or '*': ':' (\u003a) +$ $ java -jar target/structured-fields-0.6-SNAPSHOT.jar 'date;v=@1' 'number;v=123' '( token );bool' + +Item: >>date;v=@1,number;v=123,( token );bool<< + ---------^ (0x2c) Extra characters in string parsed as Item + +List: date;v=@1, number;v=123, (token);bool (OuterList) + date;v=@1, number;v=123, (token);bool (OuterList) + date (TokenItem) + ;v=@1 (Parameters) + v -> @1 (DateItem) + number (TokenItem) + ;v=123 (Parameters) + v -> 123 (IntegerItem) + (token) (InnerList) + token (TokenItem) + ;bool (Parameters) + bool -> ?1 (BooleanItem) + + +Dict: >>date;v=@1,number;v=123,( token );bool<< + -----------------------^ (0x28) Key must start with LCALPHA or '*': '(' (\u0028) ``` diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index b24b7e7..d3debc5 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -71,7 +71,7 @@ public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Func sb.append(le.serializeToForDebug(new StringBuilder(), indentLevel + 2, formatter)); } - return params.serializeToForDebug(sb, indentLevel, formatter); + return params.serializeToForDebug(sb, indentLevel + 2, formatter); } @Override From adb48e58901896f93f9661bbf40f4c15e64dd39c Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 17:50:30 +0100 Subject: [PATCH 21/72] Parameters --- .../org/greenbytes/http/sfv/Parameters.java | 20 ++++++++++++++ .../org/greenbytes/http/sfv/ItemAPITests.java | 26 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index 6beb2f0..32bb1b4 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -45,6 +45,22 @@ public static Parameters valueOf(Map value) { return new Parameters(value); } + public static Parameters valueOf(Object... obs) { + if (obs.length % 2 != 0) { + throw new IllegalArgumentException("requires even number of arguments"); + } else { + Map map = new LinkedHashMap<>(); + for (int i = 0; i < obs.length; i += 2) { + String key = obs[i].toString(); + if (map.containsKey(key)) { + throw new IllegalArgumentException("key " + key + " already exists"); + } + map.put(key, obs[i + 1]); + } + return valueOf(map); + } + } + /** * Serialize this parameter to a {@linkplain StringBuilder} * @param sb to serialize to @@ -119,6 +135,10 @@ private static Item asItem(String key, Object o) { return ByteSequenceItem.valueOf((byte[]) o); } else if (o instanceof BigDecimal) { return DecimalItem.valueOf((BigDecimal)o); + } else if (o instanceof Double) { + return DecimalItem.valueOf(BigDecimal.valueOf((Double)o)); + } else if (o instanceof Float) { + return DecimalItem.valueOf(BigDecimal.valueOf((Float)o)); } else { throw new IllegalArgumentException("Can't map value for parameter '" + key + "': " + o.getClass()); } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 39d1df3..23058da 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -6,9 +6,11 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; +import java.math.BigInteger; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; +import java.util.stream.Collectors; import org.junit.Test; @@ -190,6 +192,7 @@ public void testParameters() { m.put("o", new byte[0]); m.put("d", new BigDecimal("0.1")); Parameters p = Parameters.valueOf(m); + assertEquals(StringItem.class, p.get("*").getClass()); assertEquals(IntegerItem.class, p.get("i").getClass()); assertEquals(IntegerItem.class, p.get("l").getClass()); @@ -198,6 +201,29 @@ public void testParameters() { assertEquals(DecimalItem.class, p.get("d").getClass()); } + @Test + public void testParameters2() { + + Parameters p = Parameters.valueOf("*", "star", "i", 1, "l", 2L, "b", false, + "o", new byte[0], "d", 0.155d, "d2", BigDecimal.valueOf(12345), "d3", 3.14f); + + assertEquals(StringItem.class, p.get("*").getClass()); + assertEquals(IntegerItem.class, p.get("i").getClass()); + assertEquals(IntegerItem.class, p.get("l").getClass()); + assertEquals(BooleanItem.class, p.get("b").getClass()); + assertEquals(ByteSequenceItem.class, p.get("o").getClass()); + assertEquals(DecimalItem.class, p.get("d").getClass()); + assertEquals(DecimalItem.class, p.get("d2").getClass()); + + assertEquals(";*=\"star\";i=1;l=2;b=?0;o=::;d=0.155;d2=12345.0;d3=3.14", p.serialize()); + + Map sermap = p.entrySet().stream(). + collect(Collectors.toMap(Map.Entry::getKey, x -> x.getValue().serialize())); + + assertEquals("?0", sermap.get("b")); + assertEquals("12345.0", sermap.get("d2")); + } + @Test public void testParametersUnmodifiable() { From 8f549e80a4a485735ff8704663b2d0ad6ff379f6 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 19 Apr 2026 19:29:55 +0100 Subject: [PATCH 22/72] Parameters --- .../org/greenbytes/http/sfv/Parameters.java | 23 +++++++---- .../org/greenbytes/http/sfv/ItemAPITests.java | 40 +++++++++++++------ 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index 32bb1b4..086bd58 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -180,10 +180,6 @@ public Set>> entrySet() { return delegate.entrySet(); } - public boolean equals(Object o) { - return Objects.equals(delegate, o); - } - @Override public void forEach(BiConsumer> action) { delegate.forEach(action); @@ -198,10 +194,6 @@ public Item getOrDefault(Object key, Item defaultValue) { return delegate.getOrDefault(key, defaultValue); } - public int hashCode() { - return delegate.hashCode(); - } - public boolean isEmpty() { return delegate.isEmpty(); } @@ -260,4 +252,19 @@ public int size() { public Collection> values() { return delegate.values(); } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Parameters)) { + return false; + } else { + Parameters that = (Parameters) o; + return Objects.equals(delegate, that.delegate); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(delegate); + } } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 23058da..2857134 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -6,7 +6,6 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; -import java.math.BigInteger; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; @@ -183,15 +182,7 @@ public void testByteSequenceInvalid() { @Test public void testParameters() { - - Map m = new LinkedHashMap<>(); - m.put("*", "star"); - m.put("i", 1); - m.put("l", 2L); - m.put("b", false); - m.put("o", new byte[0]); - m.put("d", new BigDecimal("0.1")); - Parameters p = Parameters.valueOf(m); + Parameters p = createParams1(); assertEquals(StringItem.class, p.get("*").getClass()); assertEquals(IntegerItem.class, p.get("i").getClass()); @@ -204,8 +195,7 @@ public void testParameters() { @Test public void testParameters2() { - Parameters p = Parameters.valueOf("*", "star", "i", 1, "l", 2L, "b", false, - "o", new byte[0], "d", 0.155d, "d2", BigDecimal.valueOf(12345), "d3", 3.14f); + Parameters p = createParams2(); assertEquals(StringItem.class, p.get("*").getClass()); assertEquals(IntegerItem.class, p.get("i").getClass()); @@ -224,6 +214,32 @@ public void testParameters2() { assertEquals("12345.0", sermap.get("d2")); } + @Test + public void testParametersEquals() { + Parameters p1 = createParams1(); + Parameters p2 = createParams2(); + assertEquals(p1.serialize(), p2.serialize()); + assertEquals(p1, p2); + } + + private static Parameters createParams1() { + Map m = new LinkedHashMap<>(); + m.put("*", "star"); + m.put("i", 1); + m.put("l", 2L); + m.put("b", false); + m.put("o", new byte[0]); + m.put("d", new BigDecimal("0.155")); + m.put("d2", BigDecimal.valueOf(12345)); + m.put("d3", 3.14f); + return Parameters.valueOf(m); + } + + private static Parameters createParams2() { + return Parameters.valueOf("*", "star", "i", 1, "l", 2L, "b", false, + "o", new byte[0], "d", 0.155d, "d2", BigDecimal.valueOf(12345), "d3", 3.14f); + } + @Test public void testParametersUnmodifiable() { From 4e5ac2db5bd110c6d0e3e26f55278811a73358d7 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Mon, 20 Apr 2026 22:05:09 +0100 Subject: [PATCH 23/72] OuterList --- .../org/greenbytes/http/sfv/OuterList.java | 27 +++++++++++++++++-- .../org/greenbytes/http/sfv/StringItem.java | 21 ++++++++++----- .../org/greenbytes/http/sfv/ItemAPITests.java | 25 +++++++++++++++++ 3 files changed, 65 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index d74716e..976c612 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -1,8 +1,11 @@ package org.greenbytes.http.sfv; +import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Function; +import java.util.stream.Collectors; /** * Represents a List. @@ -18,18 +21,38 @@ private OuterList(List> value) { this.value = Objects.requireNonNull(value, "value must not be null"); } + /** + * @deprecated use {@link #of(List)} instead. + */ + @Deprecated + public static OuterList valueOf(List> value) { + return new OuterList(value); + } + /** * Creates an {@link OuterList} instance representing the specified * {@code List} value. - * + * * @param value * a {@code List} value. * @return a {@link OuterList} representing {@code value}. */ - public static OuterList valueOf(List> value) { + public static OuterList of(List> value) { return new OuterList(value); } + /** + * Creates an {@link OuterList} instance representing the specified + * {@code ListElement} values. + * + * @param values + * {@code ListItem} values. + * @return a {@link OuterList} representing {@code values}. + */ + public static OuterList of(ListElement... values) { + return of(Arrays.stream(values).collect(Collectors.toList())); + } + @Override public StringBuilder serializeTo(StringBuilder sb) { String separator = ""; diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 114380a..09ad405 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -1,5 +1,6 @@ package org.greenbytes.http.sfv; +import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -20,17 +21,25 @@ private StringItem(String value, Parameters params) { } /** - * Creates a {@link StringItem} instance representing the specified - * {@code String} value. - * - * @param value - * a {@code String} value. - * @return a {@link StringItem} representing {@code value}. + * @deprecated use {@link #of(String)} instead. */ + @Deprecated public static StringItem valueOf(String value) { return new StringItem(value, Parameters.EMPTY); } + /** + * Creates a {@link StringItem} instance representing the specified + * {@code String} value. + * + * @param value + * a {@code String} value. + * @return a {@link StringItem} representing {@code value}. + */ + public static StringItem of(String value) { + return new StringItem(value, Parameters.EMPTY); + } + @Override public StringItem withParams(Parameters params) { return new StringItem(this.value, Objects.requireNonNull(params, "params must not be null")); diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 2857134..7079b9c 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -6,8 +6,10 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -281,4 +283,27 @@ public void testInvalidParameterValues() { } catch (IllegalArgumentException expected) { } } + + @Test + public void testListConstruction() { + // RFC 9651, Section 3.1 + OuterList l1 = createList1(); + OuterList l2 = createList2(); + assertEquals("\"sugar\", \"tee\", \"rum\"", l2.serialize()); + assertEquals(l1.serialize(), l2.serialize()); + assertEquals(l1, l2); + } + + private static OuterList createList1() { + List> list = new ArrayList<>(); + list.add(StringItem.valueOf("sugar")); + list.add(StringItem.valueOf("tee")); + list.add(StringItem.valueOf("rum")); + + return OuterList.of(list); + } + + private static OuterList createList2() { + return OuterList.of(StringItem.of("sugar"), StringItem.of("tee"), StringItem.of("rum")); + } } From 3addaada8258ddf202cd00c2793af4b00223d62d Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Tue, 21 Apr 2026 06:43:28 +0100 Subject: [PATCH 24/72] refactor conversion to Item into Utils --- .../org/greenbytes/http/sfv/Parameters.java | 33 +---------------- .../java/org/greenbytes/http/sfv/Utils.java | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index 086bd58..694d502 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -1,6 +1,5 @@ package org.greenbytes.http.sfv; -import java.math.BigDecimal; import java.util.Collection; import java.util.Collections; import java.util.LinkedHashMap; @@ -105,7 +104,7 @@ private static Map> checkAndTransformMap(Map map Objects.requireNonNull(map, "Map must not be null").size()); for (Map.Entry entry : map.entrySet()) { String key = Utils.checkKey(entry.getKey()); - Item value = asItem(key, entry.getValue()); + Item value = Utils.asBareItem(entry.getValue()); if (!value.getParams().isEmpty()) { throw new IllegalArgumentException("Parameter value for '" + key + "' must be bare item (no parameters)"); } @@ -114,36 +113,6 @@ private static Map> checkAndTransformMap(Map map return result; } - private static Item asItem(String key, Object o) { - if (o instanceof Item) { - if (o instanceof Parameterizable) { - Parameterizable p = ((Parameterizable)o); - if (!p.getParams().isEmpty()) { - throw new IllegalArgumentException("Can't map value for parameter '" + key + "': " + o.getClass() + " carries parameters"); - } - } - return (Item) o; - } else if (o instanceof Integer) { - return IntegerItem.valueOf(((Integer) o).longValue()); - } else if (o instanceof Long) { - return IntegerItem.valueOf((Long) o); - } else if (o instanceof String) { - return StringItem.valueOf((String) o); - } else if (o instanceof Boolean) { - return BooleanItem.valueOf((Boolean) o); - } else if (o instanceof byte[]) { - return ByteSequenceItem.valueOf((byte[]) o); - } else if (o instanceof BigDecimal) { - return DecimalItem.valueOf((BigDecimal)o); - } else if (o instanceof Double) { - return DecimalItem.valueOf(BigDecimal.valueOf((Double)o)); - } else if (o instanceof Float) { - return DecimalItem.valueOf(BigDecimal.valueOf((Float)o)); - } else { - throw new IllegalArgumentException("Can't map value for parameter '" + key + "': " + o.getClass()); - } - } - // delegate methods, autogenerated public void clear() { diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index 99d915b..3e22d50 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -1,5 +1,6 @@ package org.greenbytes.http.sfv; +import java.math.BigDecimal; import java.util.Map; import java.util.Objects; @@ -57,4 +58,39 @@ protected static Map> checkKeys(Map asBareItem(Object o) { + if (o instanceof Item) { + if (o instanceof Parameterizable) { + Parameterizable p = ((Parameterizable)o); + if (!p.getParams().isEmpty()) { + throw new IllegalArgumentException("Can't map value " + o + " (" + o.getClass() + "): carries parameters."); + } + } + return (Item) o; + } else if (o instanceof Integer) { + return IntegerItem.valueOf(((Integer) o).longValue()); + } else if (o instanceof Long) { + return IntegerItem.valueOf((Long) o); + } else if (o instanceof String) { + return StringItem.valueOf((String) o); + } else if (o instanceof Boolean) { + return BooleanItem.valueOf((Boolean) o); + } else if (o instanceof byte[]) { + return ByteSequenceItem.valueOf((byte[]) o); + } else if (o instanceof BigDecimal) { + return DecimalItem.valueOf((BigDecimal)o); + } else if (o instanceof Double) { + return DecimalItem.valueOf(BigDecimal.valueOf((Double)o)); + } else if (o instanceof Float) { + return DecimalItem.valueOf(BigDecimal.valueOf((Float)o)); + } else { + throw new IllegalArgumentException("Can't map value " + o.toString() + " (" + o.getClass() + "): carries parameters."); + } + } } From 594b24687b6d0af5634e816ad0b07f879b13163e Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Tue, 21 Apr 2026 06:58:20 +0100 Subject: [PATCH 25/72] conversion method should handle strings that are not SF strings --- src/test/java/org/greenbytes/http/sfv/UtilsTest.java | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 src/test/java/org/greenbytes/http/sfv/UtilsTest.java diff --git a/src/test/java/org/greenbytes/http/sfv/UtilsTest.java b/src/test/java/org/greenbytes/http/sfv/UtilsTest.java new file mode 100644 index 0000000..e70193e --- /dev/null +++ b/src/test/java/org/greenbytes/http/sfv/UtilsTest.java @@ -0,0 +1,4 @@ +import junit.framework.TestCase; +public class UtilsTest extends TestCase { + +} \ No newline at end of file From 8d09850ba5e721c002d350dc1b6d9a74c740fa24 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Tue, 21 Apr 2026 19:33:46 +0100 Subject: [PATCH 26/72] Outerlist with war objects --- .../org/greenbytes/http/sfv/OuterList.java | 15 ++++++++-- .../org/greenbytes/http/sfv/StringItem.java | 1 - .../java/org/greenbytes/http/sfv/Utils.java | 6 +++- .../org/greenbytes/http/sfv/ItemAPITests.java | 2 +- .../org/greenbytes/http/sfv/UtilsTest.java | 29 +++++++++++++++++-- 5 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index 976c612..956d291 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -1,7 +1,6 @@ package org.greenbytes.http.sfv; import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -31,7 +30,19 @@ public static OuterList valueOf(List> value) { /** * Creates an {@link OuterList} instance representing the specified - * {@code List} value. + * {@linkplain Object} values after best-effort conversiob to {@linkplain Item}. + * + * @param values {@link Object}s to populate the list with + * @return a {@link OuterList} representing {@code values}. + */ + public static OuterList valueOf(Object... values) { + return of(Arrays.stream(values).map(v -> Utils.asBareItem(v)) + .collect(Collectors.toList())); + } + + /** + * Creates an {@link OuterList} instance representing the specified + * {@linkplain List} value. * * @param value * a {@code List} value. diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 09ad405..843c51e 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -1,6 +1,5 @@ package org.greenbytes.http.sfv; -import java.util.List; import java.util.Objects; import java.util.function.Function; diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index 3e22d50..e7e3df4 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -78,7 +78,11 @@ protected static Item asBareItem(Object o) { } else if (o instanceof Long) { return IntegerItem.valueOf((Long) o); } else if (o instanceof String) { - return StringItem.valueOf((String) o); + try { + return StringItem.valueOf((String) o); + } catch (IllegalArgumentException ia) { + return DisplayStringItem.valueOf((String) o); + } } else if (o instanceof Boolean) { return BooleanItem.valueOf((Boolean) o); } else if (o instanceof byte[]) { diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 7079b9c..f122cc7 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -304,6 +304,6 @@ private static OuterList createList1() { } private static OuterList createList2() { - return OuterList.of(StringItem.of("sugar"), StringItem.of("tee"), StringItem.of("rum")); + return OuterList.valueOf("sugar", "tee", "rum"); } } diff --git a/src/test/java/org/greenbytes/http/sfv/UtilsTest.java b/src/test/java/org/greenbytes/http/sfv/UtilsTest.java index e70193e..75688e2 100644 --- a/src/test/java/org/greenbytes/http/sfv/UtilsTest.java +++ b/src/test/java/org/greenbytes/http/sfv/UtilsTest.java @@ -1,4 +1,27 @@ -import junit.framework.TestCase; -public class UtilsTest extends TestCase { - +package org.greenbytes.http.sfv; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class UtilsTest { + + @Test + public void testConversionFromStringWhichIsNotAValidSFString() { + // sanity check + Item simple = Utils.asBareItem("foobar"); + assertEquals(StringItem.class, simple.getClass()); + + Item notSimple = Utils.asBareItem("qux: \u83D0\uDCA9"); + assertEquals(DisplayStringItem.class, notSimple.getClass()); + } + + // TODO: when constructing Items, need to check validity of arguments + // Test below should fail, for instance + + @Test + public void testConversionFromStringWhichHasUnpairedSurrogates() { + Item what = Utils.asBareItem("qux: \u83D0\uDCA9 what?"); + assertEquals(DisplayStringItem.class, what.getClass()); + } } \ No newline at end of file From 99500e4a7ea4cf723502889eea525fefdc393654 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Tue, 21 Apr 2026 20:59:43 +0100 Subject: [PATCH 27/72] example from 3.1.1 --- .../org/greenbytes/http/sfv/ItemAPITests.java | 36 ++++++++++++++++--- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index f122cc7..b4bf53c 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -7,6 +7,7 @@ import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -285,16 +286,16 @@ public void testInvalidParameterValues() { } @Test - public void testListConstruction() { + public void testListConstructionBareItems() { // RFC 9651, Section 3.1 - OuterList l1 = createList1(); - OuterList l2 = createList2(); + OuterList l1 = createListBareItems1(); + OuterList l2 = createListBareItems2(); assertEquals("\"sugar\", \"tee\", \"rum\"", l2.serialize()); assertEquals(l1.serialize(), l2.serialize()); assertEquals(l1, l2); } - private static OuterList createList1() { + private static OuterList createListBareItems1() { List> list = new ArrayList<>(); list.add(StringItem.valueOf("sugar")); list.add(StringItem.valueOf("tee")); @@ -303,7 +304,32 @@ private static OuterList createList1() { return OuterList.of(list); } - private static OuterList createList2() { + private static OuterList createListBareItems2() { return OuterList.valueOf("sugar", "tee", "rum"); } + + @Test + public void testListConstructionWithInnerLists() { + // RFC 9651, Section 3.1.1 + List> inner1 = new ArrayList<>(); + inner1.add(StringItem.of("foo")); + inner1.add(StringItem.of("bar")); + + List> inner2 = Collections.singletonList(StringItem.of("baz")); + + List> inner3 = new ArrayList<>(); + inner3.add(StringItem.of("bat")); + inner3.add(StringItem.of("one")); + + List> inner4 = Collections.emptyList(); + + List> combined = new ArrayList<>(); + combined.add(InnerList.valueOf(inner1)); + combined.add(InnerList.valueOf(inner2)); + combined.add(InnerList.valueOf(inner3)); + combined.add(InnerList.valueOf(inner4)); + + OuterList ol = OuterList.of(combined); + assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol.serialize()); + } } From 7ebc5f85b4cdca977a17917a0b41dc025566d51f Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 22 Apr 2026 16:22:02 +0100 Subject: [PATCH 28/72] InnerList --- .../org/greenbytes/http/sfv/InnerList.java | 12 ++++++-- .../org/greenbytes/http/sfv/ItemAPITests.java | 28 +++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index d3debc5..6aac922 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -21,15 +21,23 @@ private InnerList(List> value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + /** + * @deprecated - use {@linkplain #of(List)} instead. + */ + @Deprecated + public static InnerList valueOf(List> value) { + return new InnerList(value, Parameters.EMPTY); + } + /** * Creates an {@link InnerList} instance representing the specified * {@code List} value. - * + * * @param value * a {@code List} value. * @return a {@link InnerList} representing {@code value}. */ - public static InnerList valueOf(List> value) { + public static InnerList of(List> value) { return new InnerList(value, Parameters.EMPTY); } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index b4bf53c..7e0e9c1 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -311,6 +311,14 @@ private static OuterList createListBareItems2() { @Test public void testListConstructionWithInnerLists() { // RFC 9651, Section 3.1.1 + OuterList ol1 = createListBareInnerLists1(); + OuterList ol2 = createListBareInnerLists2(); + assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol1.serialize()); + assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol2.serialize()); + assertEquals(ol1, ol2); + } + + private static OuterList createListBareInnerLists1() { List> inner1 = new ArrayList<>(); inner1.add(StringItem.of("foo")); inner1.add(StringItem.of("bar")); @@ -329,7 +337,23 @@ public void testListConstructionWithInnerLists() { combined.add(InnerList.valueOf(inner3)); combined.add(InnerList.valueOf(inner4)); - OuterList ol = OuterList.of(combined); - assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol.serialize()); + return OuterList.of(combined); + } + + private static OuterList createListBareInnerLists2() { + List> inner1 = new ArrayList<>(); + inner1.add(StringItem.of("foo")); + inner1.add(StringItem.of("bar")); + + List> inner2 = Collections.singletonList(StringItem.of("baz")); + + List> inner3 = new ArrayList<>(); + inner3.add(StringItem.of("bat")); + inner3.add(StringItem.of("one")); + + List> inner4 = Collections.emptyList(); + + return OuterList.of(InnerList.valueOf(inner1), InnerList.valueOf(inner2), + InnerList.valueOf(inner3), InnerList.valueOf(inner4)); } } From 1f07b5fa0723062ed6f49712adde70765828c27b Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 22 Apr 2026 19:08:11 +0100 Subject: [PATCH 29/72] InnerList --- .../org/greenbytes/http/sfv/InnerList.java | 14 ++++++++++++++ .../org/greenbytes/http/sfv/ItemAPITests.java | 18 +++++------------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 6aac922..80f2464 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -1,8 +1,10 @@ package org.greenbytes.http.sfv; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.Function; +import java.util.stream.Collectors; /** * Represents an Inner List. @@ -41,6 +43,18 @@ public static InnerList of(List> value) { return new InnerList(value, Parameters.EMPTY); } + /** + * Creates an {@link InnerList} instance representing the specified + * {@code Item} values. + * + * @param values + * {@code Item} values. + * @return a {@link InnerList} representing {@code values}. + */ + public static InnerList of(Item... values) { + return of(Arrays.stream(values).collect(Collectors.toList())); + } + @Override public InnerList withParams(Parameters params) { return new InnerList(this.value, Objects.requireNonNull(params, "params must not be null")); diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 7e0e9c1..f0760d4 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -341,19 +341,11 @@ private static OuterList createListBareInnerLists1() { } private static OuterList createListBareInnerLists2() { - List> inner1 = new ArrayList<>(); - inner1.add(StringItem.of("foo")); - inner1.add(StringItem.of("bar")); - - List> inner2 = Collections.singletonList(StringItem.of("baz")); - - List> inner3 = new ArrayList<>(); - inner3.add(StringItem.of("bat")); - inner3.add(StringItem.of("one")); - - List> inner4 = Collections.emptyList(); + InnerList inner1 = InnerList.of(StringItem.of("foo"), StringItem.of("bar")); + InnerList inner2 = InnerList.of(StringItem.of("baz")); + InnerList inner3 = InnerList.of(StringItem.of("bat"), StringItem.of("one")); + InnerList inner4 = InnerList.of(); - return OuterList.of(InnerList.valueOf(inner1), InnerList.valueOf(inner2), - InnerList.valueOf(inner3), InnerList.valueOf(inner4)); + return OuterList.of(inner1, inner2, inner3, inner4); } } From 5c2fba98726b3ffb806a182b7822fcc75ff1f892 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 22 Apr 2026 19:14:20 +0100 Subject: [PATCH 30/72] InnerList --- src/main/java/org/greenbytes/http/sfv/InnerList.java | 12 ++++++++++++ src/main/java/org/greenbytes/http/sfv/OuterList.java | 2 +- .../java/org/greenbytes/http/sfv/ItemAPITests.java | 6 +++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 80f2464..2e0a444 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -55,6 +55,18 @@ public static InnerList of(Item... values) { return of(Arrays.stream(values).collect(Collectors.toList())); } + /** + * Creates an {@link InnerList} instance representing the specified + * {@linkplain Object} values after best-effort conversion to {@linkplain Item}s. + * + * @param values {@link Object}s to populate the list with + * @return a {@link InnerList} representing {@code values}. + */ + public static InnerList valueOf(Object... values) { + return of(Arrays.stream(values).map(v -> Utils.asBareItem(v)) + .collect(Collectors.toList())); + } + @Override public InnerList withParams(Parameters params) { return new InnerList(this.value, Objects.requireNonNull(params, "params must not be null")); diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index 956d291..b33c4be 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -30,7 +30,7 @@ public static OuterList valueOf(List> value) { /** * Creates an {@link OuterList} instance representing the specified - * {@linkplain Object} values after best-effort conversiob to {@linkplain Item}. + * {@linkplain Object} values after best-effort conversion to {@linkplain Item}. * * @param values {@link Object}s to populate the list with * @return a {@link OuterList} representing {@code values}. diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index f0760d4..ce70d41 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -341,9 +341,9 @@ private static OuterList createListBareInnerLists1() { } private static OuterList createListBareInnerLists2() { - InnerList inner1 = InnerList.of(StringItem.of("foo"), StringItem.of("bar")); - InnerList inner2 = InnerList.of(StringItem.of("baz")); - InnerList inner3 = InnerList.of(StringItem.of("bat"), StringItem.of("one")); + InnerList inner1 = InnerList.valueOf("foo", "bar"); + InnerList inner2 = InnerList.valueOf("baz"); + InnerList inner3 = InnerList.valueOf("bat", "one"); InnerList inner4 = InnerList.of(); return OuterList.of(inner1, inner2, inner3, inner4); From 32773278fffe28784496c411f2fd70ccdd83a3f7 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 22 Apr 2026 19:58:39 +0100 Subject: [PATCH 31/72] InnerListTest --- .../org/greenbytes/http/sfv/ItemAPITests.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index ce70d41..d1d293a 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -348,4 +348,34 @@ private static OuterList createListBareInnerLists2() { return OuterList.of(inner1, inner2, inner3, inner4); } + + @Test + public void testListConstructionWithInnerListsWithParams() { + // RFC 9651, Section 3.1.1 + OuterList ol1 = createParametrizedInnerLists1(); + assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol1.serialize()); + } + + private static OuterList createParametrizedInnerLists1() { + List> inner1 = new ArrayList<>(); + Map itemParam1 = new LinkedHashMap<>(); + itemParam1.put("a", IntegerItem.valueOf(1)); + itemParam1.put("b", IntegerItem.valueOf(2)); + inner1.add(StringItem.of("foo").withParams(Parameters.valueOf(itemParam1))); + Map itemParamOuter1 = new LinkedHashMap<>(); + itemParamOuter1.put("lvl", IntegerItem.valueOf(5)); + InnerList linner1 = InnerList.of(inner1).withParams(Parameters.valueOf(itemParamOuter1)); + + List> inner2 = new ArrayList<>(); + inner2.add(StringItem.of("bar")); + Map itemParamOuter2 = new LinkedHashMap<>(); + itemParamOuter2.put("lvl", IntegerItem.valueOf(1)); + InnerList linner2 = InnerList.of(inner2).withParams(Parameters.valueOf(itemParamOuter2)); + + List> combined = new ArrayList<>(); + combined.add(linner1); + combined.add(linner2); + + return OuterList.of(combined); + } } From 4d35087c2ecd7e92534305b25225d6ab9e5ac3f4 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 22 Apr 2026 21:27:47 +0100 Subject: [PATCH 32/72] InnerListTest --- .../org/greenbytes/http/sfv/Parameters.java | 12 +++++++-- .../org/greenbytes/http/sfv/ItemAPITests.java | 25 +++++++++++++------ 2 files changed, 28 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index 694d502..70ae252 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -28,6 +28,14 @@ private Parameters(Map value) { this.delegate = Collections.unmodifiableMap(checkAndTransformMap(value)); } + /** + * @deprecated use {@linkplain #of(Map)} + */ + @Deprecated + public static Parameters valueOf(Map value) { + return new Parameters(value); + } + /** * Creates an unmodifiable {@link Parameters} instance representing the * specified {@code Map} value. @@ -35,12 +43,12 @@ private Parameters(Map value) { * Note that the {@link Map} implementation that is used here needs to * iterate predictably based on insertion order, such as * {@link java.util.LinkedHashMap}. - * + * * @param value * a {@code Map} value * @return a {@link Parameters} representing {@code value}. */ - public static Parameters valueOf(Map value) { + public static Parameters of(Map value) { return new Parameters(value); } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index d1d293a..1ad0589 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -353,23 +353,24 @@ private static OuterList createListBareInnerLists2() { public void testListConstructionWithInnerListsWithParams() { // RFC 9651, Section 3.1.1 OuterList ol1 = createParametrizedInnerLists1(); + OuterList ol2 = createParametrizedInnerLists2(); assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol1.serialize()); + assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol2.serialize()); + assertEquals(ol1, ol2); } private static OuterList createParametrizedInnerLists1() { List> inner1 = new ArrayList<>(); Map itemParam1 = new LinkedHashMap<>(); - itemParam1.put("a", IntegerItem.valueOf(1)); - itemParam1.put("b", IntegerItem.valueOf(2)); + itemParam1.put("a", 1); + itemParam1.put("b", 2); inner1.add(StringItem.of("foo").withParams(Parameters.valueOf(itemParam1))); - Map itemParamOuter1 = new LinkedHashMap<>(); - itemParamOuter1.put("lvl", IntegerItem.valueOf(5)); + Map itemParamOuter1 = Collections.singletonMap("lvl", 5); InnerList linner1 = InnerList.of(inner1).withParams(Parameters.valueOf(itemParamOuter1)); - List> inner2 = new ArrayList<>(); - inner2.add(StringItem.of("bar")); + List> inner2 = Collections.singletonList(StringItem.of("bar")); Map itemParamOuter2 = new LinkedHashMap<>(); - itemParamOuter2.put("lvl", IntegerItem.valueOf(1)); + itemParamOuter2.put("lvl", 1); InnerList linner2 = InnerList.of(inner2).withParams(Parameters.valueOf(itemParamOuter2)); List> combined = new ArrayList<>(); @@ -378,4 +379,14 @@ private static OuterList createParametrizedInnerLists1() { return OuterList.of(combined); } + + private static OuterList createParametrizedInnerLists2() { + InnerList linner1 = InnerList.of( + StringItem.of("foo").withParams(Parameters.valueOf("a", 1, "b", 2))) + .withParams(Parameters.valueOf("lvl", 5)); + + InnerList linner2 = InnerList.valueOf("bar").withParams(Parameters.valueOf("lvl", 1)); + + return OuterList.of(linner1, linner2); + } } From f7974cceba8ab55c21e7a293151bd0209bb66199 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Fri, 24 Apr 2026 12:34:56 +0100 Subject: [PATCH 33/72] dict --- .../java/org/greenbytes/http/sfv/ItemAPITests.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 1ad0589..2188547 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -6,6 +6,7 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -389,4 +390,14 @@ private static OuterList createParametrizedInnerLists2() { return OuterList.of(linner1, linner2); } + + @Test + public void testDictConstruction() { + // RFC 9651, Section 3.2 + Map> map = new LinkedHashMap<>(); + map.put("en", StringItem.of("Applepie")); + map.put("da", ByteSequenceItem.valueOf("Æbletærte".getBytes(StandardCharsets.UTF_8))); + Dictionary dict = Dictionary.valueOf(map); + assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict.serialize()); + } } From 6529d001df657b79b720235237cfe6980d0cdb6c Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Fri, 24 Apr 2026 15:13:44 +0100 Subject: [PATCH 34/72] dict --- .../org/greenbytes/http/sfv/Dictionary.java | 43 ++++++++++++++++++- .../java/org/greenbytes/http/sfv/Parser.java | 2 +- .../org/greenbytes/http/sfv/ItemAPITests.java | 17 +++++++- 3 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index a977df5..c62701f 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -1,7 +1,9 @@ package org.greenbytes.http.sfv; import java.util.Collections; +import java.util.LinkedHashMap; import java.util.Map; +import java.util.Objects; import java.util.function.Function; /** @@ -31,10 +33,34 @@ private Dictionary(Map> value) { * a {@code Map} value * @return a {@link Dictionary} representing {@code value}. */ - public static Dictionary valueOf(Map> value) { + public static Dictionary of(Map> value) { return new Dictionary(value); } + /** + * Creates a {@link Dictionary} instance representing the values + * (key/value pairs) + * + * @param obs + * a sequence of key/value pairs + * @return a {@link Dictionary} representing the {@code values}. + */ + public static Dictionary valueOf(Object... obs) { + if (obs.length % 2 != 0) { + throw new IllegalArgumentException("requires even number of arguments, got: " + obs.length); + } else { + Map> map = new LinkedHashMap<>(); + for (int i = 0; i < obs.length; i += 2) { + String key = obs[i].toString(); + if (map.containsKey(key)) { + throw new IllegalArgumentException("key " + key + " already exists"); + } + map.put(key, Utils.asBareItem(obs[i + 1])); + } + return of(map); + } + } + @Override public Map> get() { return value; @@ -79,4 +105,19 @@ public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Func } return sb; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Dictionary)) { + return false; + } else { + Dictionary that = (Dictionary) o; + return Objects.equals(value, that.value); + } + } + + @Override + public int hashCode() { + return Objects.hashCode(value); + } } diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 2c85907..f4c3e5d 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -631,7 +631,7 @@ private Dictionary internalParseDictionary() { } } - return Dictionary.valueOf(result); + return Dictionary.of(result); } /** diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 2188547..5202535 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -394,10 +394,23 @@ private static OuterList createParametrizedInnerLists2() { @Test public void testDictConstruction() { // RFC 9651, Section 3.2 + Dictionary dict1 = createDictionarySimple1(); + Dictionary dict2 = createDictionarySimple2(); + assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict1.serialize()); + assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict2.serialize()); + assertEquals(dict1, dict2); + } + + private static Dictionary createDictionarySimple1() { Map> map = new LinkedHashMap<>(); map.put("en", StringItem.of("Applepie")); map.put("da", ByteSequenceItem.valueOf("Æbletærte".getBytes(StandardCharsets.UTF_8))); - Dictionary dict = Dictionary.valueOf(map); - assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict.serialize()); + return Dictionary.of(map); + } + + private static Dictionary createDictionarySimple2() { + return Dictionary.valueOf( + "en", "Applepie", + "da", "Æbletærte".getBytes(StandardCharsets.UTF_8)); } } From 996424e3d073b0284f728f74794c895c23370dad Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 26 Apr 2026 19:53:49 +0100 Subject: [PATCH 35/72] dict --- .../org/greenbytes/http/sfv/BooleanItem.java | 12 ++++++-- .../org/greenbytes/http/sfv/Dictionary.java | 2 +- .../java/org/greenbytes/http/sfv/Utils.java | 13 +++++++++ .../org/greenbytes/http/sfv/ItemAPITests.java | 28 ++++++++++++++++++- 4 files changed, 51 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index f1738df..7088617 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -22,15 +22,23 @@ private BooleanItem(boolean value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + /** + * @deprecated - use {#{@linkplain #of(boolean)} instead. + */ + @Deprecated + public static BooleanItem valueOf(boolean value) { + return value ? TRUE : FALSE; + } + /** * Creates a {@link BooleanItem} instance representing the specified * {@code boolean} value. - * + * * @param value * a {@code boolean} value. * @return a {@link BooleanItem} representing {@code value}. */ - public static BooleanItem valueOf(boolean value) { + public static BooleanItem of(boolean value) { return value ? TRUE : FALSE; } diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index c62701f..045bc50 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -55,7 +55,7 @@ public static Dictionary valueOf(Object... obs) { if (map.containsKey(key)) { throw new IllegalArgumentException("key " + key + " already exists"); } - map.put(key, Utils.asBareItem(obs[i + 1])); + map.put(key, Utils.asItem(obs[i + 1])); } return of(map); } diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index e7e3df4..beda6ad 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -72,6 +72,19 @@ protected static Item asBareItem(Object o) { throw new IllegalArgumentException("Can't map value " + o + " (" + o.getClass() + "): carries parameters."); } } + } + return asItem(o); + } + + /** + * Converts from native Java object + *

+ * Samae as {@linkplain #asBareItem(Object)}, but allowing {@linkplain Parameters} + * @param o to convert + * @return convert to {@linkplain Item} + */ + protected static Item asItem(Object o) { + if (o instanceof Item) { return (Item) o; } else if (o instanceof Integer) { return IntegerItem.valueOf(((Integer) o).longValue()); diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 5202535..79dba1c 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -392,7 +392,7 @@ private static OuterList createParametrizedInnerLists2() { } @Test - public void testDictConstruction() { + public void testDictConstructionSimple() { // RFC 9651, Section 3.2 Dictionary dict1 = createDictionarySimple1(); Dictionary dict2 = createDictionarySimple2(); @@ -413,4 +413,30 @@ private static Dictionary createDictionarySimple2() { "en", "Applepie", "da", "Æbletærte".getBytes(StandardCharsets.UTF_8)); } + + @Test + public void testDictConstruction() { + // RFC 9651, Section 3.2 + // Example-Dict: a=?0, b, c; foo=bar + Dictionary dict1 = createDictionary1(); + Dictionary dict2 = createDictionary2(); + assertEquals("a=?0, b, c;foo=bar", dict1.serialize()); + assertEquals("a=?0, b, c;foo=bar", dict2.serialize()); + assertEquals(dict1, dict2); + } + + private static Dictionary createDictionary1() { + Map> map = new LinkedHashMap<>(); + map.put("a", BooleanItem.valueOf(false)); + map.put("b", BooleanItem.valueOf(true)); + map.put("c", BooleanItem.valueOf(true).withParams(Parameters.valueOf(Collections.singletonMap("foo", TokenItem.valueOf("bar"))))); + return Dictionary.of(map); + } + + private static Dictionary createDictionary2() { + return Dictionary.valueOf( + "a", false, + "b", true, + "c", BooleanItem.of(true).withParams(Parameters.valueOf("foo", TokenItem.valueOf("bar")))); + } } From 1ef84ca508554bac6b19e97dd4c9d494dbf27088 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Mon, 27 Apr 2026 19:28:13 +0100 Subject: [PATCH 36/72] Decimalitem, Dictionary --- .../org/greenbytes/http/sfv/DecimalItem.java | 12 +++++++++ .../org/greenbytes/http/sfv/Dictionary.java | 8 +++++- .../java/org/greenbytes/http/sfv/Utils.java | 2 +- .../org/greenbytes/http/sfv/ItemAPITests.java | 26 +++++++++++++++++++ 4 files changed, 46 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 70e5c62..1432e91 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -61,6 +61,18 @@ public static DecimalItem valueOf(BigDecimal value) { return valueOf(permille.longValue()); } + /** + * Creates a {@link DecimalItem} instance representing the specified + * {@code Double} value, with potential rounding. + * + * @param value + * a {@code Double} value. + * @return a {@link DecimalItem} representing {@code value}. + */ + public static DecimalItem valueOf(double value) { + return valueOf(BigDecimal.valueOf(value)); + } + @Override public DecimalItem withParams(Parameters params) { return new DecimalItem(this.value, Objects.requireNonNull(params, "params must not be null")); diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index 045bc50..27d17be 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -1,5 +1,6 @@ package org.greenbytes.http.sfv; +import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; @@ -52,10 +53,15 @@ public static Dictionary valueOf(Object... obs) { Map> map = new LinkedHashMap<>(); for (int i = 0; i < obs.length; i += 2) { String key = obs[i].toString(); + Object value = obs[i + 1]; if (map.containsKey(key)) { throw new IllegalArgumentException("key " + key + " already exists"); } - map.put(key, Utils.asItem(obs[i + 1])); + if (value instanceof ListElement) { + map.put(key, (ListElement) value); + } else { + map.put(key, Utils.asItem(obs[i + 1])); + } } return of(map); } diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index beda6ad..d9da3d8 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -107,7 +107,7 @@ protected static Item asItem(Object o) { } else if (o instanceof Float) { return DecimalItem.valueOf(BigDecimal.valueOf((Float)o)); } else { - throw new IllegalArgumentException("Can't map value " + o.toString() + " (" + o.getClass() + "): carries parameters."); + throw new IllegalArgumentException("Can't map value " + o.toString() + " (" + o.getClass() + ")"); } } } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 79dba1c..41647c0 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -439,4 +439,30 @@ private static Dictionary createDictionary2() { "b", true, "c", BooleanItem.of(true).withParams(Parameters.valueOf("foo", TokenItem.valueOf("bar")))); } + + @Test + public void testDictConstructionWithInnerList() { + // RFC 9651, Section 3.2 + // Example-Dict: rating=1.5, feelings=(joy sadness) + Dictionary dict1 = createDictionaryWithInnerList1(); + Dictionary dict2 = createDictionaryWithInnerList2(); + assertEquals("rating=1.5, feelings=(joy sadness)", dict1.serialize()); + assertEquals("rating=1.5, feelings=(joy sadness)", dict2.serialize()); + assertEquals(dict1, dict2); + } + + private static Dictionary createDictionaryWithInnerList1() { + Map> map = new LinkedHashMap<>(); + map.put("rating", DecimalItem.valueOf(BigDecimal.valueOf(1.5f))); + List> li = new ArrayList<>(); + li.add(TokenItem.valueOf("joy")); + li.add(TokenItem.valueOf("sadness")); + map.put("feelings", InnerList.of(li)); + return Dictionary.of(map); + } + + private static Dictionary createDictionaryWithInnerList2() { + return Dictionary.valueOf("rating", 1.5f, + "feelings", InnerList.of(TokenItem.valueOf("joy"), TokenItem.valueOf("sadness"))); + } } From d183f76645d83e21278582fa953a758a8db3e0e2 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Thu, 30 Apr 2026 17:13:38 +0100 Subject: [PATCH 37/72] dict --- .../org/greenbytes/http/sfv/ItemAPITests.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 41647c0..6cd558b 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -465,4 +465,39 @@ private static Dictionary createDictionaryWithInnerList2() { return Dictionary.valueOf("rating", 1.5f, "feelings", InnerList.of(TokenItem.valueOf("joy"), TokenItem.valueOf("sadness"))); } + + @Test + public void testDictConstructionMix() { + // RFC 9651, Section 3.2 + // Example-Dict: aa=(1 2), b=3, c=4;aa=bb, d=(5 6);valid + Map> map = new LinkedHashMap<>(); + + List> inner1 = new ArrayList<>(); + inner1.add(IntegerItem.valueOf(1)); + inner1.add(IntegerItem.valueOf(2)); + InnerList linner1 = InnerList.of(inner1); + + Map p3 = new LinkedHashMap<>(); + p3.put("aa", TokenItem.valueOf("bb")); + Parameters params3 = Parameters.of(p3); + + List> inner4 = new ArrayList<>(); + inner4.add(IntegerItem.valueOf(5)); + inner4.add(IntegerItem.valueOf(6)); + InnerList linner4 = InnerList.of(inner4); + + Map p4 = new LinkedHashMap<>(); + p4.put("valid", BooleanItem.valueOf(true)); + Parameters params4 = Parameters.of(p4); + + map.put("aa", linner1); + map.put("b", IntegerItem.valueOf(3)); + map.put("c", IntegerItem.valueOf(4).withParams(params3)); + map.put("d", linner4.withParams(params4)); + + Dictionary dict1 = Dictionary.of(map); + assertEquals("aa=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict1.serialize()); +// assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict2.serialize()); +// assertEquals(dict1, dict2); + } } From 5cc0f45d50a16439bc7f7e6bc7126a88de4c2de9 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Thu, 30 Apr 2026 17:42:08 +0100 Subject: [PATCH 38/72] dict --- .../org/greenbytes/http/sfv/ItemAPITests.java | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 6cd558b..1be18eb 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -469,7 +469,15 @@ private static Dictionary createDictionaryWithInnerList2() { @Test public void testDictConstructionMix() { // RFC 9651, Section 3.2 - // Example-Dict: aa=(1 2), b=3, c=4;aa=bb, d=(5 6);valid + // Example-Dict: a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid + Dictionary dict1 = createDictionaryMix1(); + Dictionary dict2 = createDictionaryMix2(); + assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict1.serialize()); + assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict2.serialize()); + assertEquals(dict1, dict2); + } + + private static Dictionary createDictionaryMix1() { Map> map = new LinkedHashMap<>(); List> inner1 = new ArrayList<>(); @@ -479,7 +487,7 @@ public void testDictConstructionMix() { Map p3 = new LinkedHashMap<>(); p3.put("aa", TokenItem.valueOf("bb")); - Parameters params3 = Parameters.of(p3); + Parameters params3 = Parameters.valueOf(p3); List> inner4 = new ArrayList<>(); inner4.add(IntegerItem.valueOf(5)); @@ -490,14 +498,20 @@ public void testDictConstructionMix() { p4.put("valid", BooleanItem.valueOf(true)); Parameters params4 = Parameters.of(p4); - map.put("aa", linner1); + map.put("a", linner1); map.put("b", IntegerItem.valueOf(3)); map.put("c", IntegerItem.valueOf(4).withParams(params3)); map.put("d", linner4.withParams(params4)); - Dictionary dict1 = Dictionary.of(map); - assertEquals("aa=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict1.serialize()); -// assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict2.serialize()); -// assertEquals(dict1, dict2); + return Dictionary.of(map); + } + + private static Dictionary createDictionaryMix2() { + return Dictionary.valueOf("a", InnerList.valueOf(1, 2), + "b", 3, + "c", IntegerItem.valueOf(4). + withParams(Parameters.valueOf("aa", TokenItem.valueOf("bb"))), + "d", InnerList.valueOf(5, 6). + withParams(Parameters.valueOf("valid", true))); } } From 5005cf65322e9eeef4b3877872ee50891174d69d Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 17:03:08 +0100 Subject: [PATCH 39/72] test restruct --- .../org/greenbytes/http/sfv/ItemAPITests.java | 104 -------------- .../http/sfv/RRFC9651ExamplesTest.java | 131 ++++++++++++++++++ 2 files changed, 131 insertions(+), 104 deletions(-) create mode 100755 src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 1be18eb..7f149f7 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -286,110 +286,6 @@ public void testInvalidParameterValues() { } } - @Test - public void testListConstructionBareItems() { - // RFC 9651, Section 3.1 - OuterList l1 = createListBareItems1(); - OuterList l2 = createListBareItems2(); - assertEquals("\"sugar\", \"tee\", \"rum\"", l2.serialize()); - assertEquals(l1.serialize(), l2.serialize()); - assertEquals(l1, l2); - } - - private static OuterList createListBareItems1() { - List> list = new ArrayList<>(); - list.add(StringItem.valueOf("sugar")); - list.add(StringItem.valueOf("tee")); - list.add(StringItem.valueOf("rum")); - - return OuterList.of(list); - } - - private static OuterList createListBareItems2() { - return OuterList.valueOf("sugar", "tee", "rum"); - } - - @Test - public void testListConstructionWithInnerLists() { - // RFC 9651, Section 3.1.1 - OuterList ol1 = createListBareInnerLists1(); - OuterList ol2 = createListBareInnerLists2(); - assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol1.serialize()); - assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol2.serialize()); - assertEquals(ol1, ol2); - } - - private static OuterList createListBareInnerLists1() { - List> inner1 = new ArrayList<>(); - inner1.add(StringItem.of("foo")); - inner1.add(StringItem.of("bar")); - - List> inner2 = Collections.singletonList(StringItem.of("baz")); - - List> inner3 = new ArrayList<>(); - inner3.add(StringItem.of("bat")); - inner3.add(StringItem.of("one")); - - List> inner4 = Collections.emptyList(); - - List> combined = new ArrayList<>(); - combined.add(InnerList.valueOf(inner1)); - combined.add(InnerList.valueOf(inner2)); - combined.add(InnerList.valueOf(inner3)); - combined.add(InnerList.valueOf(inner4)); - - return OuterList.of(combined); - } - - private static OuterList createListBareInnerLists2() { - InnerList inner1 = InnerList.valueOf("foo", "bar"); - InnerList inner2 = InnerList.valueOf("baz"); - InnerList inner3 = InnerList.valueOf("bat", "one"); - InnerList inner4 = InnerList.of(); - - return OuterList.of(inner1, inner2, inner3, inner4); - } - - @Test - public void testListConstructionWithInnerListsWithParams() { - // RFC 9651, Section 3.1.1 - OuterList ol1 = createParametrizedInnerLists1(); - OuterList ol2 = createParametrizedInnerLists2(); - assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol1.serialize()); - assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol2.serialize()); - assertEquals(ol1, ol2); - } - - private static OuterList createParametrizedInnerLists1() { - List> inner1 = new ArrayList<>(); - Map itemParam1 = new LinkedHashMap<>(); - itemParam1.put("a", 1); - itemParam1.put("b", 2); - inner1.add(StringItem.of("foo").withParams(Parameters.valueOf(itemParam1))); - Map itemParamOuter1 = Collections.singletonMap("lvl", 5); - InnerList linner1 = InnerList.of(inner1).withParams(Parameters.valueOf(itemParamOuter1)); - - List> inner2 = Collections.singletonList(StringItem.of("bar")); - Map itemParamOuter2 = new LinkedHashMap<>(); - itemParamOuter2.put("lvl", 1); - InnerList linner2 = InnerList.of(inner2).withParams(Parameters.valueOf(itemParamOuter2)); - - List> combined = new ArrayList<>(); - combined.add(linner1); - combined.add(linner2); - - return OuterList.of(combined); - } - - private static OuterList createParametrizedInnerLists2() { - InnerList linner1 = InnerList.of( - StringItem.of("foo").withParams(Parameters.valueOf("a", 1, "b", 2))) - .withParams(Parameters.valueOf("lvl", 5)); - - InnerList linner2 = InnerList.valueOf("bar").withParams(Parameters.valueOf("lvl", 1)); - - return OuterList.of(linner1, linner2); - } @Test public void testDictConstructionSimple() { diff --git a/src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java new file mode 100755 index 0000000..92250e7 --- /dev/null +++ b/src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java @@ -0,0 +1,131 @@ +package org.greenbytes.http.sfv; + +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.junit.Assert.assertEquals; + +public class RRFC9651ExamplesTest { + + // RFC 9651, Section 3.1 + // Example-List: sugar, tea, rum + + @Test + public void testListConstructionBareItems() { + OuterList l1 = createListBareItems1(); + OuterList l2 = createListBareItems2(); + assertEquals("\"sugar\", \"tee\", \"rum\"", l2.serialize()); + assertEquals(l1.serialize(), l2.serialize()); + assertEquals(l1, l2); + } + + // chatty API + private static OuterList createListBareItems1() { + List> list = new ArrayList<>(); + list.add(StringItem.valueOf("sugar")); + list.add(StringItem.valueOf("tee")); + list.add(StringItem.valueOf("rum")); + + return OuterList.of(list); + } + + // concise API + private static OuterList createListBareItems2() { + return OuterList.valueOf("sugar", "tee", "rum"); + } + + // RFC 9651, Section 3.1.1 + // Example-List: ("foo" "bar"), ("baz"), ("bat" "one"), () + + @Test + public void testListConstructionWithInnerLists() { + OuterList ol1 = createListBareInnerLists1(); + OuterList ol2 = createListBareInnerLists2(); + assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol1.serialize()); + assertEquals("(\"foo\" \"bar\"), (\"baz\"), (\"bat\" \"one\"), ()", ol2.serialize()); + assertEquals(ol1, ol2); + } + + // chatty API + private static OuterList createListBareInnerLists1() { + List> inner1 = new ArrayList<>(); + inner1.add(StringItem.of("foo")); + inner1.add(StringItem.of("bar")); + + List> inner2 = Collections.singletonList(StringItem.of("baz")); + + List> inner3 = new ArrayList<>(); + inner3.add(StringItem.of("bat")); + inner3.add(StringItem.of("one")); + + List> inner4 = Collections.emptyList(); + + List> combined = new ArrayList<>(); + combined.add(InnerList.valueOf(inner1)); + combined.add(InnerList.valueOf(inner2)); + combined.add(InnerList.valueOf(inner3)); + combined.add(InnerList.valueOf(inner4)); + + return OuterList.of(combined); + } + + // concise API + private static OuterList createListBareInnerLists2() { + InnerList inner1 = InnerList.valueOf("foo", "bar"); + InnerList inner2 = InnerList.valueOf("baz"); + InnerList inner3 = InnerList.valueOf("bat", "one"); + InnerList inner4 = InnerList.of(); + + return OuterList.of(inner1, inner2, inner3, inner4); + } + + // RFC 9651, Section 3.1.1 + // Example-List: ("foo"; a=1;b=2);lvl=5, ("bar" "baz");lvl=1 + + @Test + public void testListConstructionWithInnerListsWithParams() { + OuterList ol1 = createParametrizedInnerLists1(); + OuterList ol2 = createParametrizedInnerLists2(); + assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol1.serialize()); + assertEquals("(\"foo\";a=1;b=2);lvl=5, (\"bar\");lvl=1", ol2.serialize()); + assertEquals(ol1, ol2); + } + + // chatty API + private static OuterList createParametrizedInnerLists1() { + List> inner1 = new ArrayList<>(); + Map itemParam1 = new LinkedHashMap<>(); + itemParam1.put("a", 1); + itemParam1.put("b", 2); + inner1.add(StringItem.of("foo").withParams(Parameters.valueOf(itemParam1))); + Map itemParamOuter1 = Collections.singletonMap("lvl", 5); + InnerList linner1 = InnerList.of(inner1).withParams(Parameters.valueOf(itemParamOuter1)); + + List> inner2 = Collections.singletonList(StringItem.of("bar")); + Map itemParamOuter2 = new LinkedHashMap<>(); + itemParamOuter2.put("lvl", 1); + InnerList linner2 = InnerList.of(inner2).withParams(Parameters.valueOf(itemParamOuter2)); + + List> combined = new ArrayList<>(); + combined.add(linner1); + combined.add(linner2); + + return OuterList.of(combined); + } + + // concise API + private static OuterList createParametrizedInnerLists2() { + InnerList linner1 = InnerList.of( + StringItem.of("foo").withParams(Parameters.valueOf("a", 1, "b", 2))) + .withParams(Parameters.valueOf("lvl", 5)); + + InnerList linner2 = InnerList.valueOf("bar").withParams(Parameters.valueOf("lvl", 1)); + + return OuterList.of(linner1, linner2); + } +} From ed2c56a8bf47974f44592a18313996c0d215c48e Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 17:03:44 +0100 Subject: [PATCH 40/72] test restruct --- .../sfv/{RRFC9651ExamplesTest.java => RFC9651ExamplesTest.java} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename src/test/java/org/greenbytes/http/sfv/{RRFC9651ExamplesTest.java => RFC9651ExamplesTest.java} (96%) diff --git a/src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java similarity index 96% rename from src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java rename to src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 92250e7..ef3e40d 100755 --- a/src/test/java/org/greenbytes/http/sfv/RRFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -10,7 +10,7 @@ import static org.junit.Assert.assertEquals; -public class RRFC9651ExamplesTest { +public class RFC9651ExamplesTest { // RFC 9651, Section 3.1 // Example-List: sugar, tea, rum From e0369b58275a32d4a45a544c2abed1571deeabb0 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 17:37:15 +0100 Subject: [PATCH 41/72] parameters/lists --- .../http/sfv/RFC9651ExamplesTest.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index ef3e40d..21053ab 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -5,6 +5,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -128,4 +129,38 @@ private static OuterList createParametrizedInnerLists2() { return OuterList.of(linner1, linner2); } + + // RFC 9651, Section 3.1.2 + // Example-List: abc;a=1;b=2; cde_456, (ghi;jk=4 l);q="9";r=w + + @Test + public void testParameters() { + Map map1 = new LinkedHashMap<>(); + map1.put("a", IntegerItem.valueOf(1)); + map1.put("b", IntegerItem.valueOf(2)); + map1.put("cde_456", BooleanItem.valueOf(true)); + TokenItem l1 = TokenItem.valueOf("abc"). + withParams(Parameters.valueOf(map1)); + + List> lc2 = new ArrayList<>(); + TokenItem t21 = TokenItem.valueOf("ghi"). + withParams(Parameters.valueOf( + Collections.singletonMap("jk", IntegerItem.valueOf(4)))); + + lc2.add(t21); + lc2.add(TokenItem.valueOf("l")); + + Map map2 = new LinkedHashMap<>(); + map2.put("q", StringItem.valueOf("9")); + map2.put("r", TokenItem.valueOf("w")); + Parameters p2 = Parameters.valueOf(map2); + InnerList l2 = InnerList.of(lc2).withParams(p2); + + List> value = new LinkedList<>(); + value.add(l1); + value.add(l2); + + OuterList ol1 = OuterList.valueOf(value); + assertEquals("abc;a=1;b=2;cde_456, (ghi;jk=4 l);q=\"9\";r=w", ol1.serialize()); + } } From 81e8ffbd56e2bfda7760da249294bef88f56eeb5 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 17:48:29 +0100 Subject: [PATCH 42/72] parameters/lists --- .../http/sfv/RFC9651ExamplesTest.java | 46 +++++++++++++++++-- 1 file changed, 42 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 21053ab..25aae97 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -134,7 +134,16 @@ private static OuterList createParametrizedInnerLists2() { // Example-List: abc;a=1;b=2; cde_456, (ghi;jk=4 l);q="9";r=w @Test - public void testParameters() { + public void testComplexListOfParams() { + OuterList ol1 = createComplexListOfParams1(); + OuterList ol2 = createComplexListOfParams2(); + assertEquals("abc;a=1;b=2;cde_456, (ghi;jk=4 l);q=\"9\";r=w", ol1.serialize()); + assertEquals("abc;a=1;b=2;cde_456, (ghi;jk=4 l);q=\"9\";r=w", ol2.serialize()); + assertEquals(ol1, ol2); + } + + // chatty API + private static OuterList createComplexListOfParams1() { Map map1 = new LinkedHashMap<>(); map1.put("a", IntegerItem.valueOf(1)); map1.put("b", IntegerItem.valueOf(2)); @@ -145,7 +154,7 @@ public void testParameters() { List> lc2 = new ArrayList<>(); TokenItem t21 = TokenItem.valueOf("ghi"). withParams(Parameters.valueOf( - Collections.singletonMap("jk", IntegerItem.valueOf(4)))); + Collections.singletonMap("jk", IntegerItem.valueOf(4)))); lc2.add(t21); lc2.add(TokenItem.valueOf("l")); @@ -160,7 +169,36 @@ public void testParameters() { value.add(l1); value.add(l2); - OuterList ol1 = OuterList.valueOf(value); - assertEquals("abc;a=1;b=2;cde_456, (ghi;jk=4 l);q=\"9\";r=w", ol1.serialize()); + return OuterList.valueOf(value); + } + + // concise API + private static OuterList createComplexListOfParams2() { + Map map1 = new LinkedHashMap<>(); + map1.put("a", IntegerItem.valueOf(1)); + map1.put("b", IntegerItem.valueOf(2)); + map1.put("cde_456", BooleanItem.valueOf(true)); + TokenItem l1 = TokenItem.valueOf("abc"). + withParams(Parameters.valueOf(map1)); + + List> lc2 = new ArrayList<>(); + TokenItem t21 = TokenItem.valueOf("ghi"). + withParams(Parameters.valueOf( + Collections.singletonMap("jk", IntegerItem.valueOf(4)))); + + lc2.add(t21); + lc2.add(TokenItem.valueOf("l")); + + Map map2 = new LinkedHashMap<>(); + map2.put("q", StringItem.valueOf("9")); + map2.put("r", TokenItem.valueOf("w")); + Parameters p2 = Parameters.valueOf(map2); + InnerList l2 = InnerList.of(lc2).withParams(p2); + + List> value = new LinkedList<>(); + value.add(l1); + value.add(l2); + + return OuterList.valueOf(value); } } From 2c99f15cb5c491016477a15a4d29b9b87308b6cf Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 18:26:32 +0100 Subject: [PATCH 43/72] valueOfParameters --- .../org/greenbytes/http/sfv/BooleanItem.java | 5 +++ .../greenbytes/http/sfv/ByteSequenceItem.java | 5 +++ .../org/greenbytes/http/sfv/DateItem.java | 5 +++ .../org/greenbytes/http/sfv/DecimalItem.java | 5 +++ .../http/sfv/DisplayStringItem.java | 5 +++ .../org/greenbytes/http/sfv/InnerList.java | 5 +++ .../org/greenbytes/http/sfv/IntegerItem.java | 16 +++++++-- .../greenbytes/http/sfv/Parameterizable.java | 2 ++ .../org/greenbytes/http/sfv/StringItem.java | 5 +++ .../org/greenbytes/http/sfv/TokenItem.java | 17 ++++++++-- .../org/greenbytes/http/sfv/ItemAPITests.java | 12 +++---- .../http/sfv/RFC9651ExamplesTest.java | 33 ++++++------------- 12 files changed, 82 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index 7088617..d7fe936 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -52,6 +52,11 @@ public BooleanItem withParams(Parameters params) { return new BooleanItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public BooleanItem withParamValuesOf(Object... obs) { + return new BooleanItem(this.value, Parameters.valueOf(obs)); + } + @Override public StringBuilder serializeTo(StringBuilder sb) { sb.append(value ? "?1" : "?0"); diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index 9760fc5..95bf3bf 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -41,6 +41,11 @@ public ByteSequenceItem withParams(Parameters params) { return new ByteSequenceItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public ByteSequenceItem withParamValuesOf(Object... obs) { + return new ByteSequenceItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index b454d8b..329b27e 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -42,6 +42,11 @@ public DateItem withParams(Parameters params) { return new DateItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public DateItem withParamValuesOf(Object... obs) { + return new DateItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 1432e91..8392ac5 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -78,6 +78,11 @@ public DecimalItem withParams(Parameters params) { return new DecimalItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public DecimalItem withParamValuesOf(Object... obs) { + return new DecimalItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java index d791e27..c847a35 100755 --- a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java @@ -37,6 +37,11 @@ public DisplayStringItem withParams(Parameters params) { return new DisplayStringItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public DisplayStringItem withParamValuesOf(Object... obs) { + return new DisplayStringItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 2e0a444..09c4cb0 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -72,6 +72,11 @@ public InnerList withParams(Parameters params) { return new InnerList(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public InnerList withParamValuesOf(Object... obs) { + return new InnerList(this.value, Parameters.valueOf(obs)); + } + private StringBuilder serializeToNoParams(StringBuilder sb) { String separator = ""; diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index 99a7eda..213d6d5 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -25,15 +25,22 @@ private IntegerItem(long value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + /** + * @deprecated use {@link #of(long)} instead. + */ + public static IntegerItem valueOf(long value) { + return new IntegerItem(value, Parameters.EMPTY); + } + /** * Creates an {@link IntegerItem} instance representing the specified * {@code long} value. - * + * * @param value * a {@code long} value. * @return a {@link IntegerItem} representing {@code value}. */ - public static IntegerItem valueOf(long value) { + public static IntegerItem of(long value) { return new IntegerItem(value, Parameters.EMPTY); } @@ -42,6 +49,11 @@ public IntegerItem withParams(Parameters params) { return new IntegerItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public IntegerItem withParamValuesOf(Object... obs) { + return new IntegerItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/Parameterizable.java b/src/main/java/org/greenbytes/http/sfv/Parameterizable.java index 4fe831f..a58e921 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameterizable.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameterizable.java @@ -20,6 +20,8 @@ public interface Parameterizable extends Type { */ Parameterizable withParams(Parameters params); + Parameterizable withParamValuesOf(Object... obs); + /** * Get the {@link Parameters} of this {@link Item}. * diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 843c51e..508b265 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -44,6 +44,11 @@ public StringItem withParams(Parameters params) { return new StringItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public StringItem withParamValuesOf(Object... obs) { + return new StringItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index 16765dd..698fc3f 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -19,15 +19,23 @@ private TokenItem(String value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + /** + * @deprecated use {@link #of(String)} instead. + */ + @Deprecated + public static TokenItem valueOf(String value) { + return new TokenItem(value, Parameters.EMPTY); + } + /** * Creates a {@link TokenItem} instance representing the specified * {@code String} value. - * + * * @param value * a {@code String} value. * @return a {@link TokenItem} representing {@code value}. */ - public static TokenItem valueOf(String value) { + public static TokenItem of(String value) { return new TokenItem(value, Parameters.EMPTY); } @@ -36,6 +44,11 @@ public TokenItem withParams(Parameters params) { return new TokenItem(this.value, Objects.requireNonNull(params, "params must not be null")); } + @Override + public TokenItem withParamValuesOf(Object... obs) { + return new TokenItem(this.value, Parameters.valueOf(obs)); + } + @Override public Parameters getParams() { return params; diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 7f149f7..8c7c0fb 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -325,7 +325,7 @@ private static Dictionary createDictionary1() { Map> map = new LinkedHashMap<>(); map.put("a", BooleanItem.valueOf(false)); map.put("b", BooleanItem.valueOf(true)); - map.put("c", BooleanItem.valueOf(true).withParams(Parameters.valueOf(Collections.singletonMap("foo", TokenItem.valueOf("bar"))))); + map.put("c", BooleanItem.valueOf(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); return Dictionary.of(map); } @@ -333,7 +333,7 @@ private static Dictionary createDictionary2() { return Dictionary.valueOf( "a", false, "b", true, - "c", BooleanItem.of(true).withParams(Parameters.valueOf("foo", TokenItem.valueOf("bar")))); + "c", BooleanItem.of(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); } @Test @@ -377,8 +377,8 @@ private static Dictionary createDictionaryMix1() { Map> map = new LinkedHashMap<>(); List> inner1 = new ArrayList<>(); - inner1.add(IntegerItem.valueOf(1)); - inner1.add(IntegerItem.valueOf(2)); + inner1.add(IntegerItem.of(1)); + inner1.add(IntegerItem.of(2)); InnerList linner1 = InnerList.of(inner1); Map p3 = new LinkedHashMap<>(); @@ -406,8 +406,8 @@ private static Dictionary createDictionaryMix2() { return Dictionary.valueOf("a", InnerList.valueOf(1, 2), "b", 3, "c", IntegerItem.valueOf(4). - withParams(Parameters.valueOf("aa", TokenItem.valueOf("bb"))), + withParamValuesOf("aa", TokenItem.valueOf("bb")), "d", InnerList.valueOf(5, 6). - withParams(Parameters.valueOf("valid", true))); + withParamValuesOf("valid", true)); } } diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 25aae97..b2addc8 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -77,12 +77,10 @@ private static OuterList createListBareInnerLists1() { // concise API private static OuterList createListBareInnerLists2() { - InnerList inner1 = InnerList.valueOf("foo", "bar"); - InnerList inner2 = InnerList.valueOf("baz"); - InnerList inner3 = InnerList.valueOf("bat", "one"); - InnerList inner4 = InnerList.of(); - - return OuterList.of(inner1, inner2, inner3, inner4); + return OuterList.of(InnerList.valueOf("foo", "bar"), + InnerList.valueOf("baz"), + InnerList.valueOf("bat", "one"), + InnerList.of()); } // RFC 9651, Section 3.1.1 @@ -174,31 +172,20 @@ private static OuterList createComplexListOfParams1() { // concise API private static OuterList createComplexListOfParams2() { - Map map1 = new LinkedHashMap<>(); - map1.put("a", IntegerItem.valueOf(1)); - map1.put("b", IntegerItem.valueOf(2)); - map1.put("cde_456", BooleanItem.valueOf(true)); - TokenItem l1 = TokenItem.valueOf("abc"). - withParams(Parameters.valueOf(map1)); + TokenItem l1 = TokenItem.of("abc"). + withParamValuesOf("a", 1, "b", 2, "cde_456", true); List> lc2 = new ArrayList<>(); - TokenItem t21 = TokenItem.valueOf("ghi"). - withParams(Parameters.valueOf( - Collections.singletonMap("jk", IntegerItem.valueOf(4)))); - + TokenItem t21 = TokenItem.of("ghi").withParamValuesOf("jk", 4); lc2.add(t21); - lc2.add(TokenItem.valueOf("l")); + lc2.add(TokenItem.of("l")); - Map map2 = new LinkedHashMap<>(); - map2.put("q", StringItem.valueOf("9")); - map2.put("r", TokenItem.valueOf("w")); - Parameters p2 = Parameters.valueOf(map2); - InnerList l2 = InnerList.of(lc2).withParams(p2); + InnerList l2 = InnerList.valueOf(lc2).withParamValuesOf("q", "9", "r", TokenItem.of("w")); List> value = new LinkedList<>(); value.add(l1); value.add(l2); - return OuterList.valueOf(value); + return OuterList.of(value); } } From db1e60aec86dcbb7615abe4821eaec14780253d4 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 18:31:46 +0100 Subject: [PATCH 44/72] innerlist can take param. items --- src/main/java/org/greenbytes/http/sfv/InnerList.java | 2 +- .../org/greenbytes/http/sfv/RFC9651ExamplesTest.java | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 09c4cb0..5f3c04e 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -63,7 +63,7 @@ public static InnerList of(Item... values) { * @return a {@link InnerList} representing {@code values}. */ public static InnerList valueOf(Object... values) { - return of(Arrays.stream(values).map(v -> Utils.asBareItem(v)) + return of(Arrays.stream(values).map(v -> Utils.asItem(v)) .collect(Collectors.toList())); } diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index b2addc8..8887e6a 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -175,12 +175,10 @@ private static OuterList createComplexListOfParams2() { TokenItem l1 = TokenItem.of("abc"). withParamValuesOf("a", 1, "b", 2, "cde_456", true); - List> lc2 = new ArrayList<>(); - TokenItem t21 = TokenItem.of("ghi").withParamValuesOf("jk", 4); - lc2.add(t21); - lc2.add(TokenItem.of("l")); - - InnerList l2 = InnerList.valueOf(lc2).withParamValuesOf("q", "9", "r", TokenItem.of("w")); + InnerList l2 = InnerList.valueOf( + TokenItem.of("ghi").withParamValuesOf("jk", 4), + TokenItem.of("l")). + withParamValuesOf("q", "9", "r", TokenItem.of("w")); List> value = new LinkedList<>(); value.add(l1); From 995c86414668a1b70d482968e513f04e776f79b5 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 18:47:05 +0100 Subject: [PATCH 45/72] outerlist can take list item in addition to items --- .../java/org/greenbytes/http/sfv/OuterList.java | 2 +- .../java/org/greenbytes/http/sfv/Utils.java | 17 +++++++++++++++-- .../http/sfv/RFC9651ExamplesTest.java | 6 +----- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index b33c4be..c3c2a8d 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -36,7 +36,7 @@ public static OuterList valueOf(List> value) { * @return a {@link OuterList} representing {@code values}. */ public static OuterList valueOf(Object... values) { - return of(Arrays.stream(values).map(v -> Utils.asBareItem(v)) + return of(Arrays.stream(values).map(v -> Utils.asListElement(v)) .collect(Collectors.toList())); } diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index d9da3d8..d568c1a 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -77,9 +77,9 @@ protected static Item asBareItem(Object o) { } /** - * Converts from native Java object + * Converts from [{@linkplain Item} or native Java object *

- * Samae as {@linkplain #asBareItem(Object)}, but allowing {@linkplain Parameters} + * Same as {@linkplain #asBareItem(Object)}, but allowing {@linkplain Parameters} * @param o to convert * @return convert to {@linkplain Item} */ @@ -110,4 +110,17 @@ protected static Item asItem(Object o) { throw new IllegalArgumentException("Can't map value " + o.toString() + " (" + o.getClass() + ")"); } } + + /** + * Same as {@linkplain #asItem(Object)}, but allowing also allowing {@linkplain InnerList} + * @param o to convert + * @return convert to {@linkplain ListElement} + */ + protected static ListElement asListElement(Object o) { + if (o instanceof InnerList) { + return (InnerList) o; + } else { + return asItem(o); + } + } } diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 8887e6a..f303e1f 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -180,10 +180,6 @@ private static OuterList createComplexListOfParams2() { TokenItem.of("l")). withParamValuesOf("q", "9", "r", TokenItem.of("w")); - List> value = new LinkedList<>(); - value.add(l1); - value.add(l2); - - return OuterList.of(value); + return OuterList.valueOf(l1, l2); } } From 23d68ecdbc7119ff90f8034ba2eae9a69fb6b9f9 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 18:54:11 +0100 Subject: [PATCH 46/72] restruct --- .../org/greenbytes/http/sfv/ItemAPITests.java | 52 ----------------- .../http/sfv/RFC9651ExamplesTest.java | 57 +++++++++++++++++++ 2 files changed, 57 insertions(+), 52 deletions(-) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 8c7c0fb..d67acc2 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -6,9 +6,7 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; @@ -286,56 +284,6 @@ public void testInvalidParameterValues() { } } - - @Test - public void testDictConstructionSimple() { - // RFC 9651, Section 3.2 - Dictionary dict1 = createDictionarySimple1(); - Dictionary dict2 = createDictionarySimple2(); - assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict1.serialize()); - assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict2.serialize()); - assertEquals(dict1, dict2); - } - - private static Dictionary createDictionarySimple1() { - Map> map = new LinkedHashMap<>(); - map.put("en", StringItem.of("Applepie")); - map.put("da", ByteSequenceItem.valueOf("Æbletærte".getBytes(StandardCharsets.UTF_8))); - return Dictionary.of(map); - } - - private static Dictionary createDictionarySimple2() { - return Dictionary.valueOf( - "en", "Applepie", - "da", "Æbletærte".getBytes(StandardCharsets.UTF_8)); - } - - @Test - public void testDictConstruction() { - // RFC 9651, Section 3.2 - // Example-Dict: a=?0, b, c; foo=bar - Dictionary dict1 = createDictionary1(); - Dictionary dict2 = createDictionary2(); - assertEquals("a=?0, b, c;foo=bar", dict1.serialize()); - assertEquals("a=?0, b, c;foo=bar", dict2.serialize()); - assertEquals(dict1, dict2); - } - - private static Dictionary createDictionary1() { - Map> map = new LinkedHashMap<>(); - map.put("a", BooleanItem.valueOf(false)); - map.put("b", BooleanItem.valueOf(true)); - map.put("c", BooleanItem.valueOf(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); - return Dictionary.of(map); - } - - private static Dictionary createDictionary2() { - return Dictionary.valueOf( - "a", false, - "b", true, - "c", BooleanItem.of(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); - } - @Test public void testDictConstructionWithInnerList() { // RFC 9651, Section 3.2 diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index f303e1f..481b1fc 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -2,6 +2,7 @@ import org.junit.Test; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; @@ -182,4 +183,60 @@ private static OuterList createComplexListOfParams2() { return OuterList.valueOf(l1, l2); } + + // RFC 9651, Section 3.2 + // Example-Dict: en="Applepie", da=:w4ZibGV0w6ZydGU=: + + @Test + public void testDictConstructionSimple() { + Dictionary dict1 = createDictionarySimple1(); + Dictionary dict2 = createDictionarySimple2(); + assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict1.serialize()); + assertEquals("en=\"Applepie\", da=:w4ZibGV0w6ZydGU=:", dict2.serialize()); + assertEquals(dict1, dict2); + } + + // chatty API + private static Dictionary createDictionarySimple1() { + Map> map = new LinkedHashMap<>(); + map.put("en", StringItem.of("Applepie")); + map.put("da", ByteSequenceItem.valueOf("Æbletærte".getBytes(StandardCharsets.UTF_8))); + return Dictionary.of(map); + } + + // concise API + private static Dictionary createDictionarySimple2() { + return Dictionary.valueOf( + "en", "Applepie", + "da", "Æbletærte".getBytes(StandardCharsets.UTF_8)); + } + + // RFC 9651, Section 3.2 + // Example-Dict: a=?0, b, c; foo=bar + + @Test + public void testDictConstruction() { + Dictionary dict1 = createDictionary1(); + Dictionary dict2 = createDictionary2(); + assertEquals("a=?0, b, c;foo=bar", dict1.serialize()); + assertEquals("a=?0, b, c;foo=bar", dict2.serialize()); + assertEquals(dict1, dict2); + } + + // chatty API + private static Dictionary createDictionary1() { + Map> map = new LinkedHashMap<>(); + map.put("a", BooleanItem.valueOf(false)); + map.put("b", BooleanItem.valueOf(true)); + map.put("c", BooleanItem.valueOf(true).withParams(Parameters.of(Collections.singletonMap("foo", TokenItem.valueOf("bar"))))); + return Dictionary.of(map); + } + + // concise API + private static Dictionary createDictionary2() { + return Dictionary.valueOf( + "a", false, + "b", true, + "c", BooleanItem.of(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); + } } From 2b6f57e2ac68e5ace72d73fb90030068d974f502 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 18:55:27 +0100 Subject: [PATCH 47/72] restruct --- .../org/greenbytes/http/sfv/ItemAPITests.java | 25 ---------------- .../http/sfv/RFC9651ExamplesTest.java | 30 +++++++++++++++++++ 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index d67acc2..6f6b2e1 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -284,31 +284,6 @@ public void testInvalidParameterValues() { } } - @Test - public void testDictConstructionWithInnerList() { - // RFC 9651, Section 3.2 - // Example-Dict: rating=1.5, feelings=(joy sadness) - Dictionary dict1 = createDictionaryWithInnerList1(); - Dictionary dict2 = createDictionaryWithInnerList2(); - assertEquals("rating=1.5, feelings=(joy sadness)", dict1.serialize()); - assertEquals("rating=1.5, feelings=(joy sadness)", dict2.serialize()); - assertEquals(dict1, dict2); - } - - private static Dictionary createDictionaryWithInnerList1() { - Map> map = new LinkedHashMap<>(); - map.put("rating", DecimalItem.valueOf(BigDecimal.valueOf(1.5f))); - List> li = new ArrayList<>(); - li.add(TokenItem.valueOf("joy")); - li.add(TokenItem.valueOf("sadness")); - map.put("feelings", InnerList.of(li)); - return Dictionary.of(map); - } - - private static Dictionary createDictionaryWithInnerList2() { - return Dictionary.valueOf("rating", 1.5f, - "feelings", InnerList.of(TokenItem.valueOf("joy"), TokenItem.valueOf("sadness"))); - } @Test public void testDictConstructionMix() { diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 481b1fc..c0c92cf 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -2,6 +2,7 @@ import org.junit.Test; +import java.math.BigDecimal; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -239,4 +240,33 @@ private static Dictionary createDictionary2() { "b", true, "c", BooleanItem.of(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); } + + // RFC 9651, Section 3.2 + // Example-Dict: rating=1.5, feelings=(joy sadness) + + @Test + public void testDictConstructionWithInnerList() { + Dictionary dict1 = createDictionaryWithInnerList1(); + Dictionary dict2 = createDictionaryWithInnerList2(); + assertEquals("rating=1.5, feelings=(joy sadness)", dict1.serialize()); + assertEquals("rating=1.5, feelings=(joy sadness)", dict2.serialize()); + assertEquals(dict1, dict2); + } + + // chatty API + private static Dictionary createDictionaryWithInnerList1() { + Map> map = new LinkedHashMap<>(); + map.put("rating", DecimalItem.valueOf(BigDecimal.valueOf(1.5f))); + List> li = new ArrayList<>(); + li.add(TokenItem.valueOf("joy")); + li.add(TokenItem.valueOf("sadness")); + map.put("feelings", InnerList.of(li)); + return Dictionary.of(map); + } + + // concise API + private static Dictionary createDictionaryWithInnerList2() { + return Dictionary.valueOf("rating", 1.5f, + "feelings", InnerList.of(TokenItem.valueOf("joy"), TokenItem.valueOf("sadness"))); + } } From 9ce99b4e0cf96891222506fe73e06e83878e677f Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 2 May 2026 18:57:02 +0100 Subject: [PATCH 48/72] restruct --- .../org/greenbytes/http/sfv/ItemAPITests.java | 52 ------------------- .../http/sfv/RFC9651ExamplesTest.java | 52 +++++++++++++++++++ 2 files changed, 52 insertions(+), 52 deletions(-) diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 6f6b2e1..2857134 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -6,10 +6,8 @@ import static org.junit.Assert.fail; import java.math.BigDecimal; -import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -283,54 +281,4 @@ public void testInvalidParameterValues() { } catch (IllegalArgumentException expected) { } } - - - @Test - public void testDictConstructionMix() { - // RFC 9651, Section 3.2 - // Example-Dict: a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid - Dictionary dict1 = createDictionaryMix1(); - Dictionary dict2 = createDictionaryMix2(); - assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict1.serialize()); - assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict2.serialize()); - assertEquals(dict1, dict2); - } - - private static Dictionary createDictionaryMix1() { - Map> map = new LinkedHashMap<>(); - - List> inner1 = new ArrayList<>(); - inner1.add(IntegerItem.of(1)); - inner1.add(IntegerItem.of(2)); - InnerList linner1 = InnerList.of(inner1); - - Map p3 = new LinkedHashMap<>(); - p3.put("aa", TokenItem.valueOf("bb")); - Parameters params3 = Parameters.valueOf(p3); - - List> inner4 = new ArrayList<>(); - inner4.add(IntegerItem.valueOf(5)); - inner4.add(IntegerItem.valueOf(6)); - InnerList linner4 = InnerList.of(inner4); - - Map p4 = new LinkedHashMap<>(); - p4.put("valid", BooleanItem.valueOf(true)); - Parameters params4 = Parameters.of(p4); - - map.put("a", linner1); - map.put("b", IntegerItem.valueOf(3)); - map.put("c", IntegerItem.valueOf(4).withParams(params3)); - map.put("d", linner4.withParams(params4)); - - return Dictionary.of(map); - } - - private static Dictionary createDictionaryMix2() { - return Dictionary.valueOf("a", InnerList.valueOf(1, 2), - "b", 3, - "c", IntegerItem.valueOf(4). - withParamValuesOf("aa", TokenItem.valueOf("bb")), - "d", InnerList.valueOf(5, 6). - withParamValuesOf("valid", true)); - } } diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index c0c92cf..d2ea248 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -269,4 +269,56 @@ private static Dictionary createDictionaryWithInnerList2() { return Dictionary.valueOf("rating", 1.5f, "feelings", InnerList.of(TokenItem.valueOf("joy"), TokenItem.valueOf("sadness"))); } + + // RFC 9651, Section 3.2 + // Example-Dict: a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid + + @Test + public void testDictConstructionMix() { + Dictionary dict1 = createDictionaryMix1(); + Dictionary dict2 = createDictionaryMix2(); + assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict1.serialize()); + assertEquals("a=(1 2), b=3, c=4;aa=bb, d=(5 6);valid", dict2.serialize()); + assertEquals(dict1, dict2); + } + + // chatty API + private static Dictionary createDictionaryMix1() { + Map> map = new LinkedHashMap<>(); + + List> inner1 = new ArrayList<>(); + inner1.add(IntegerItem.of(1)); + inner1.add(IntegerItem.of(2)); + InnerList linner1 = InnerList.of(inner1); + + Map p3 = new LinkedHashMap<>(); + p3.put("aa", TokenItem.valueOf("bb")); + Parameters params3 = Parameters.valueOf(p3); + + List> inner4 = new ArrayList<>(); + inner4.add(IntegerItem.valueOf(5)); + inner4.add(IntegerItem.valueOf(6)); + InnerList linner4 = InnerList.of(inner4); + + Map p4 = new LinkedHashMap<>(); + p4.put("valid", BooleanItem.valueOf(true)); + Parameters params4 = Parameters.of(p4); + + map.put("a", linner1); + map.put("b", IntegerItem.valueOf(3)); + map.put("c", IntegerItem.valueOf(4).withParams(params3)); + map.put("d", linner4.withParams(params4)); + + return Dictionary.of(map); + } + + // concise API + private static Dictionary createDictionaryMix2() { + return Dictionary.valueOf("a", InnerList.valueOf(1, 2), + "b", 3, + "c", IntegerItem.valueOf(4). + withParamValuesOf("aa", TokenItem.valueOf("bb")), + "d", InnerList.valueOf(5, 6). + withParamValuesOf("valid", true)); + } } From 01d93ee4c4381de0c4fccc55d5791491df5ac310 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 10:03:42 +0100 Subject: [PATCH 49/72] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2f7896d --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +target/ From d07fb212a19deeade9eddd5326e1564997eb2967 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 11:51:20 +0100 Subject: [PATCH 50/72] gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2f7896d..96ef862 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ target/ +.idea/ From da5415e81baa2a307b821b46e4fb6ef92b06bb4c Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 12:08:16 +0100 Subject: [PATCH 51/72] apideprecations --- src/main/java/org/greenbytes/http/sfv/BooleanItem.java | 8 -------- src/main/java/org/greenbytes/http/sfv/Parser.java | 8 ++++---- src/main/java/org/greenbytes/http/sfv/Utils.java | 2 +- .../java/org/greenbytes/http/sfv/EqualityTest.java | 10 +++++----- .../java/org/greenbytes/http/sfv/ItemAPITests.java | 4 ++-- .../java/org/greenbytes/http/sfv/ParametersTest.java | 8 ++++---- .../org/greenbytes/http/sfv/RFC9651ExamplesTest.java | 10 +++++----- 7 files changed, 21 insertions(+), 29 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index d7fe936..9f68b0e 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -22,14 +22,6 @@ private BooleanItem(boolean value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } - /** - * @deprecated - use {#{@linkplain #of(boolean)} instead. - */ - @Deprecated - public static BooleanItem valueOf(boolean value) { - return value ? TRUE : FALSE; - } - /** * Creates a {@link BooleanItem} instance representing the specified * {@code boolean} value. diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index f4c3e5d..8ccc334 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -434,7 +434,7 @@ private BooleanItem internalParseBareBoolean() { throw complaint(String.format("Expected '0' or '1' in Boolean, found '%c'", c)); } - return BooleanItem.valueOf(c == '1'); + return BooleanItem.of(c == '1'); } private BooleanItem internalParseBoolean() { @@ -483,7 +483,7 @@ private Parameters internalParseParameters() { advance(); removeLeadingSP(); String name = internalParseKey(); - Item value = BooleanItem.valueOf(true); + Item value = BooleanItem.of(true); if (peek() == '=') { advance(); value = internalParseBareItem(); @@ -612,7 +612,7 @@ private Dictionary internalParseDictionary() { advance(); member = internalParseItemOrInnerList(); } else { - member = BooleanItem.valueOf(true).withParams(internalParseParameters()); + member = BooleanItem.of(true).withParams(internalParseParameters()); } result.put(name, member); @@ -687,7 +687,7 @@ public OuterList parseList() { List> result = internalParseOuterList(); removeLeadingSP(); assertEmpty("Extra characters in string parsed as List"); - return OuterList.valueOf(result); + return OuterList.of(result); } /** diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index d568c1a..48fea97 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -97,7 +97,7 @@ protected static Item asItem(Object o) { return DisplayStringItem.valueOf((String) o); } } else if (o instanceof Boolean) { - return BooleanItem.valueOf((Boolean) o); + return BooleanItem.of((Boolean) o); } else if (o instanceof byte[]) { return ByteSequenceItem.valueOf((byte[]) o); } else if (o instanceof BigDecimal) { diff --git a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java index 93f8c5a..2638730 100644 --- a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java +++ b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java @@ -68,9 +68,9 @@ public void testTokenItemEquality() { @Test public void testBooleanItemEquality() { - BooleanItem b1 = BooleanItem.valueOf(true); - BooleanItem b2 = BooleanItem.valueOf(true); - BooleanItem b3 = BooleanItem.valueOf(false); + BooleanItem b1 = BooleanItem.of(true); + BooleanItem b2 = BooleanItem.of(true); + BooleanItem b3 = BooleanItem.of(false); // FALSE and TRUE are singletons assertSame(b1, b2); @@ -79,7 +79,7 @@ public void testBooleanItemEquality() { assertNotSame(b1, b3); assertNotEquals(b1, b3); - BooleanItem b4 = BooleanItem.valueOf(false).withParams(getParameters("c", "d")); + BooleanItem b4 = BooleanItem.of(false).withParams(getParameters("c", "d")); assertNotSame(b1, b4); assertNotEquals(b1, b4); } @@ -171,7 +171,7 @@ public void testIntegerItemEquality() { @Test public void testOuterListEquality() { - BooleanItem b = BooleanItem.valueOf(true); + BooleanItem b = BooleanItem.of(true); DecimalItem d = DecimalItem.valueOf(BigDecimal.valueOf(10.5)); OuterList l1 = OuterList.valueOf((Arrays.asList(b, d))); diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 2857134..3eb3b1c 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -18,11 +18,11 @@ public class ItemAPITests { @Test public void testBoolean() { - BooleanItem b0 = BooleanItem.valueOf(false); + BooleanItem b0 = BooleanItem.of(false); assertEquals(false, b0.get()); assertEquals("?0", b0.serialize()); - BooleanItem b1 = BooleanItem.valueOf(true); + BooleanItem b1 = BooleanItem.of(true); assertEquals(true, b1.get()); assertEquals("?1", b1.serialize()); } diff --git a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java index fff5800..f4ec2cd 100644 --- a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java +++ b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java @@ -65,7 +65,7 @@ public void testReplaceAll1() { @Test public void testParametersEquality() { Map m1 = new HashMap(); - m1.put("a", BooleanItem.valueOf(true)); + m1.put("a", BooleanItem.of(true)); Map m2 = new HashMap(); m2.put("b", IntegerItem.valueOf(12)); m2.put("c", StringItem.valueOf("hello")); @@ -121,13 +121,13 @@ public void canRemoveParams() { @Test(expected = IllegalArgumentException.class) public void testParamsStrictnessReParametersInValues() { Map map1 = new HashMap<>(); - map1.put("foo", BooleanItem.valueOf(false)); + map1.put("foo", BooleanItem.of(false)); Map map2 = new HashMap<>(); map2.put("qux", 0); - BooleanItem.valueOf(true).withParams(Parameters.valueOf(map2)); - map1.put("bar", BooleanItem.valueOf(true).withParams(Parameters.valueOf(map2))); + BooleanItem.of(true).withParams(Parameters.valueOf(map2)); + map1.put("bar", BooleanItem.of(true).withParams(Parameters.valueOf(map2))); // this needs to fail because the second parameter's value has parameters Parameters.valueOf(map1); diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index d2ea248..5bee36b 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -147,7 +147,7 @@ private static OuterList createComplexListOfParams1() { Map map1 = new LinkedHashMap<>(); map1.put("a", IntegerItem.valueOf(1)); map1.put("b", IntegerItem.valueOf(2)); - map1.put("cde_456", BooleanItem.valueOf(true)); + map1.put("cde_456", BooleanItem.of(true)); TokenItem l1 = TokenItem.valueOf("abc"). withParams(Parameters.valueOf(map1)); @@ -227,9 +227,9 @@ public void testDictConstruction() { // chatty API private static Dictionary createDictionary1() { Map> map = new LinkedHashMap<>(); - map.put("a", BooleanItem.valueOf(false)); - map.put("b", BooleanItem.valueOf(true)); - map.put("c", BooleanItem.valueOf(true).withParams(Parameters.of(Collections.singletonMap("foo", TokenItem.valueOf("bar"))))); + map.put("a", BooleanItem.of(false)); + map.put("b", BooleanItem.of(true)); + map.put("c", BooleanItem.of(true).withParams(Parameters.of(Collections.singletonMap("foo", TokenItem.valueOf("bar"))))); return Dictionary.of(map); } @@ -301,7 +301,7 @@ private static Dictionary createDictionaryMix1() { InnerList linner4 = InnerList.of(inner4); Map p4 = new LinkedHashMap<>(); - p4.put("valid", BooleanItem.valueOf(true)); + p4.put("valid", BooleanItem.of(true)); Parameters params4 = Parameters.of(p4); map.put("a", linner1); From 8a867d43edbc9af66ae5eab5345fe875c5081093 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 12:12:09 +0100 Subject: [PATCH 52/72] apideprecations --- src/main/java/org/greenbytes/http/sfv/Parser.java | 2 +- src/main/java/org/greenbytes/http/sfv/StringItem.java | 8 -------- src/main/java/org/greenbytes/http/sfv/Utils.java | 2 +- .../java/org/greenbytes/http/sfv/EqualityTest.java | 10 +++++----- .../java/org/greenbytes/http/sfv/ItemAPITests.java | 4 ++-- .../java/org/greenbytes/http/sfv/ParametersTest.java | 2 +- .../org/greenbytes/http/sfv/RFC9651ExamplesTest.java | 8 ++++---- 7 files changed, 14 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 8ccc334..76b934b 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -240,7 +240,7 @@ private StringItem internalParseBareString() { outputString.append(c); } else { if (c == '"') { - return StringItem.valueOf(outputString.toString()); + return StringItem.of(outputString.toString()); } else if (c < 0x20 || c >= 0x7f) { throw complaint("Invalid character in String at position " + position()); } else { diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 508b265..598e8c8 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -19,14 +19,6 @@ private StringItem(String value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } - /** - * @deprecated use {@link #of(String)} instead. - */ - @Deprecated - public static StringItem valueOf(String value) { - return new StringItem(value, Parameters.EMPTY); - } - /** * Creates a {@link StringItem} instance representing the specified * {@code String} value. diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index 48fea97..c38f83d 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -92,7 +92,7 @@ protected static Item asItem(Object o) { return IntegerItem.valueOf((Long) o); } else if (o instanceof String) { try { - return StringItem.valueOf((String) o); + return StringItem.of((String) o); } catch (IllegalArgumentException ia) { return DisplayStringItem.valueOf((String) o); } diff --git a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java index 2638730..3510f6d 100644 --- a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java +++ b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java @@ -29,21 +29,21 @@ public void testParametersEquality() { @Test public void testStringItemEquality() { - StringItem s1 = StringItem.valueOf("a"); - StringItem s2 = StringItem.valueOf("a"); + StringItem s1 = StringItem.of("a"); + StringItem s2 = StringItem.of("a"); assertNotSame(s1, s2); assertEquals(s1, s2); - StringItem s3 = StringItem.valueOf("b"); + StringItem s3 = StringItem.of("b"); assertNotEquals(s1, s3); - StringItem s4 = StringItem.valueOf("a").withParams(getParameters("a", "b")); + StringItem s4 = StringItem.of("a").withParams(getParameters("a", "b")); assertNotSame(s1, s4); assertNotEquals(s1, s4); HashMap m5 = new LinkedHashMap<>(); m5.put("c", "d"); Parameters p5 = Parameters.valueOf(m5); - StringItem s5 = StringItem.valueOf("a").withParams(p5); + StringItem s5 = StringItem.of("a").withParams(p5); assertNotSame(s4, s5); assertNotEquals(s4, s5); } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 3eb3b1c..94a6da0 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -109,7 +109,7 @@ public void testString() { String[] tests = new String[] { "", "'", "\"", "\\" }; for (String s : tests) { - StringItem item = StringItem.valueOf(s); + StringItem item = StringItem.of(s); assertEquals(s, item.get()); // TODO: figure out how to check the serialization without copying // the actual impl code @@ -123,7 +123,7 @@ public void testStringInvalid() { for (String s : tests) { try { - StringItem item = StringItem.valueOf(s); + StringItem item = StringItem.of(s); fail("should fail for '" + s + "' but got '" + item.get() + "'"); } catch (IllegalArgumentException expected) { } diff --git a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java index f4ec2cd..38ab628 100644 --- a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java +++ b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java @@ -68,7 +68,7 @@ public void testParametersEquality() { m1.put("a", BooleanItem.of(true)); Map m2 = new HashMap(); m2.put("b", IntegerItem.valueOf(12)); - m2.put("c", StringItem.valueOf("hello")); + m2.put("c", StringItem.of("hello")); Parameters p1 = Parameters.valueOf(m1); Parameters p2 = Parameters.valueOf(m1); diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 5bee36b..cbfd907 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -30,9 +30,9 @@ public void testListConstructionBareItems() { // chatty API private static OuterList createListBareItems1() { List> list = new ArrayList<>(); - list.add(StringItem.valueOf("sugar")); - list.add(StringItem.valueOf("tee")); - list.add(StringItem.valueOf("rum")); + list.add(StringItem.of("sugar")); + list.add(StringItem.of("tee")); + list.add(StringItem.of("rum")); return OuterList.of(list); } @@ -160,7 +160,7 @@ private static OuterList createComplexListOfParams1() { lc2.add(TokenItem.valueOf("l")); Map map2 = new LinkedHashMap<>(); - map2.put("q", StringItem.valueOf("9")); + map2.put("q", StringItem.of("9")); map2.put("r", TokenItem.valueOf("w")); Parameters p2 = Parameters.valueOf(map2); InnerList l2 = InnerList.of(lc2).withParams(p2); From 92c3ff917f4ff173f4cebd12ca664c6a0538ab3d Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 12:43:12 +0100 Subject: [PATCH 53/72] apideprecations --- .../org/greenbytes/http/sfv/Dictionary.java | 1 - .../org/greenbytes/http/sfv/Parameters.java | 23 +++++++++++-------- .../java/org/greenbytes/http/sfv/Parser.java | 3 +-- .../org/greenbytes/http/sfv/EqualityTest.java | 8 +++---- .../org/greenbytes/http/sfv/ItemAPITests.java | 8 +++---- .../greenbytes/http/sfv/ParametersTest.java | 10 ++++---- .../http/sfv/RFC9651ExamplesTest.java | 14 +++++------ 7 files changed, 34 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index 27d17be..1196078 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -1,6 +1,5 @@ package org.greenbytes.http.sfv; -import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index 70ae252..d4d6f1f 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -28,14 +28,6 @@ private Parameters(Map value) { this.delegate = Collections.unmodifiableMap(checkAndTransformMap(value)); } - /** - * @deprecated use {@linkplain #of(Map)} - */ - @Deprecated - public static Parameters valueOf(Map value) { - return new Parameters(value); - } - /** * Creates an unmodifiable {@link Parameters} instance representing the * specified {@code Map} value. @@ -52,9 +44,20 @@ public static Parameters of(Map value) { return new Parameters(value); } + /** + * Creates an unmodifiable {@link Parameters} instance representing + * the specified {@linkplain Object}s. + *

+ * @param obs (needs to be an even-number of {@linkplain Object}s) + * @return a {@link Parameters} representing {@code obs}. + */ public static Parameters valueOf(Object... obs) { + if (obs.length == 1 && obs[0] instanceof Map) { + throw new IllegalArgumentException("requires even number of arguments, got: " + + obs[0].getClass().getName() + " - did you mean to use 'of()'?"); + } if (obs.length % 2 != 0) { - throw new IllegalArgumentException("requires even number of arguments"); + throw new IllegalArgumentException("requires even number of arguments, got: " + obs.length); } else { Map map = new LinkedHashMap<>(); for (int i = 0; i < obs.length; i += 2) { @@ -64,7 +67,7 @@ public static Parameters valueOf(Object... obs) { } map.put(key, obs[i + 1]); } - return valueOf(map); + return of(map); } } diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 76b934b..bf5bcc9 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -492,7 +492,7 @@ private Parameters internalParseParameters() { } } - return Parameters.valueOf(result); + return Parameters.of(result); } private Item internalParseBareItem() { @@ -582,7 +582,6 @@ private List> internalParseBareInnerList() { throw complaint("Expected SP or ')' in Inner List, got: " + format(c)); } } - } if (!done) { diff --git a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java index 3510f6d..094dbb2 100644 --- a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java +++ b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java @@ -21,8 +21,8 @@ public void testParametersEquality() { m1.put("a", "b"); HashMap m2 = new LinkedHashMap<>(); m2.put("a", "b"); - Parameters p1 = Parameters.valueOf(m1); - Parameters p2 = Parameters.valueOf(m2); + Parameters p1 = Parameters.of(m1); + Parameters p2 = Parameters.of(m2); assertNotSame(p1, p2); assertEquals(p1, p2); } @@ -42,7 +42,7 @@ public void testStringItemEquality() { HashMap m5 = new LinkedHashMap<>(); m5.put("c", "d"); - Parameters p5 = Parameters.valueOf(m5); + Parameters p5 = Parameters.of(m5); StringItem s5 = StringItem.of("a").withParams(p5); assertNotSame(s4, s5); assertNotEquals(s4, s5); @@ -188,6 +188,6 @@ public void testOuterListEquality() { private static Parameters getParameters(String a, String b) { HashMap m = new LinkedHashMap<>(); m.put(a, b); - return Parameters.valueOf(m); + return Parameters.of(m); } } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 94a6da0..44c3e8b 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -232,7 +232,7 @@ private static Parameters createParams1() { m.put("d", new BigDecimal("0.155")); m.put("d2", BigDecimal.valueOf(12345)); m.put("d3", 3.14f); - return Parameters.valueOf(m); + return Parameters.of(m); } private static Parameters createParams2() { @@ -245,7 +245,7 @@ public void testParametersUnmodifiable() { Map m = new HashMap<>(); m.put("test", "test"); - Parameters p = Parameters.valueOf(m); + Parameters p = Parameters.of(m); assertThrows( UnsupportedOperationException.class, @@ -271,12 +271,12 @@ public void testInvalidParameterValues() { Map itemParam = new LinkedHashMap<>(); itemParam.put("foo", IntegerItem.valueOf(2)); - IntegerItem iitem = IntegerItem.valueOf(1).withParams(Parameters.valueOf(itemParam)); + IntegerItem iitem = IntegerItem.valueOf(1).withParams(Parameters.of(itemParam)); Map m = new LinkedHashMap<>(); m.put("bar", iitem); try { - Parameters test = Parameters.valueOf(m); + Parameters test = Parameters.of(m); fail("Parameters containing non-bare Item should fail, but got: " + test.serialize()); } catch (IllegalArgumentException expected) { } diff --git a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java index 38ab628..1dde8af 100644 --- a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java +++ b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java @@ -12,7 +12,7 @@ public class ParametersTest { - Parameters params = Parameters.valueOf(new HashMap<>()); + Parameters params = Parameters.of(Collections.emptyMap()); // Test that all write operations fail @@ -70,9 +70,9 @@ public void testParametersEquality() { m2.put("b", IntegerItem.valueOf(12)); m2.put("c", StringItem.of("hello")); - Parameters p1 = Parameters.valueOf(m1); - Parameters p2 = Parameters.valueOf(m1); - Parameters p3 = Parameters.valueOf(m2); + Parameters p1 = Parameters.of(m1); + Parameters p2 = Parameters.of(m1); + Parameters p3 = Parameters.of(m2); assertNotSame(p1, p2); assertEquals(p1, p2); @@ -83,7 +83,7 @@ public void testParametersEquality() { @Test public void canRemoveParams() { - Parameters empty = Parameters.valueOf(Collections.emptyMap()); + Parameters empty = Parameters.of(Collections.emptyMap()); BooleanItem boi = Parser.parseBoolean("?1;b"); assertEquals("?1;b", boi.serialize()); diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index cbfd907..f30d8b9 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -103,14 +103,14 @@ private static OuterList createParametrizedInnerLists1() { Map itemParam1 = new LinkedHashMap<>(); itemParam1.put("a", 1); itemParam1.put("b", 2); - inner1.add(StringItem.of("foo").withParams(Parameters.valueOf(itemParam1))); + inner1.add(StringItem.of("foo").withParams(Parameters.of(itemParam1))); Map itemParamOuter1 = Collections.singletonMap("lvl", 5); - InnerList linner1 = InnerList.of(inner1).withParams(Parameters.valueOf(itemParamOuter1)); + InnerList linner1 = InnerList.of(inner1).withParams(Parameters.of(itemParamOuter1)); List> inner2 = Collections.singletonList(StringItem.of("bar")); Map itemParamOuter2 = new LinkedHashMap<>(); itemParamOuter2.put("lvl", 1); - InnerList linner2 = InnerList.of(inner2).withParams(Parameters.valueOf(itemParamOuter2)); + InnerList linner2 = InnerList.of(inner2).withParams(Parameters.of(itemParamOuter2)); List> combined = new ArrayList<>(); combined.add(linner1); @@ -149,11 +149,11 @@ private static OuterList createComplexListOfParams1() { map1.put("b", IntegerItem.valueOf(2)); map1.put("cde_456", BooleanItem.of(true)); TokenItem l1 = TokenItem.valueOf("abc"). - withParams(Parameters.valueOf(map1)); + withParams(Parameters.of(map1)); List> lc2 = new ArrayList<>(); TokenItem t21 = TokenItem.valueOf("ghi"). - withParams(Parameters.valueOf( + withParams(Parameters.of( Collections.singletonMap("jk", IntegerItem.valueOf(4)))); lc2.add(t21); @@ -162,7 +162,7 @@ private static OuterList createComplexListOfParams1() { Map map2 = new LinkedHashMap<>(); map2.put("q", StringItem.of("9")); map2.put("r", TokenItem.valueOf("w")); - Parameters p2 = Parameters.valueOf(map2); + Parameters p2 = Parameters.of(map2); InnerList l2 = InnerList.of(lc2).withParams(p2); List> value = new LinkedList<>(); @@ -293,7 +293,7 @@ private static Dictionary createDictionaryMix1() { Map p3 = new LinkedHashMap<>(); p3.put("aa", TokenItem.valueOf("bb")); - Parameters params3 = Parameters.valueOf(p3); + Parameters params3 = Parameters.of(p3); List> inner4 = new ArrayList<>(); inner4.add(IntegerItem.valueOf(5)); From 1ee99dc1d652e8da2778668210766e67a2fb2deb Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 12:46:57 +0100 Subject: [PATCH 54/72] apideprecations --- .../java/org/greenbytes/http/sfv/Parser.java | 2 +- .../org/greenbytes/http/sfv/TokenItem.java | 8 ------- .../org/greenbytes/http/sfv/EqualityTest.java | 10 ++++----- .../org/greenbytes/http/sfv/ItemAPITests.java | 4 ++-- .../http/sfv/RFC9651ExamplesTest.java | 22 +++++++++---------- 5 files changed, 19 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index bf5bcc9..9e3c319 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -362,7 +362,7 @@ private TokenItem internalParseBareToken() { } } - return TokenItem.valueOf(outputString.toString()); + return TokenItem.of(outputString.toString()); } private TokenItem internalParseToken() { diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index 698fc3f..674f95c 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -19,14 +19,6 @@ private TokenItem(String value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } - /** - * @deprecated use {@link #of(String)} instead. - */ - @Deprecated - public static TokenItem valueOf(String value) { - return new TokenItem(value, Parameters.EMPTY); - } - /** * Creates a {@link TokenItem} instance representing the specified * {@code String} value. diff --git a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java index 094dbb2..5bd5c05 100644 --- a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java +++ b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java @@ -50,18 +50,18 @@ public void testStringItemEquality() { @Test public void testTokenItemEquality() { - TokenItem t1 = TokenItem.valueOf("a"); - TokenItem t = TokenItem.valueOf("a"); + TokenItem t1 = TokenItem.of("a"); + TokenItem t = TokenItem.of("a"); assertNotSame(t1, t); assertEquals(t1, t); - TokenItem t3 = TokenItem.valueOf("b"); + TokenItem t3 = TokenItem.of("b"); assertNotEquals(t1, t3); - TokenItem t4 = TokenItem.valueOf("a").withParams(getParameters("a", "b")); + TokenItem t4 = TokenItem.of("a").withParams(getParameters("a", "b")); assertNotSame(t1, t4); assertNotEquals(t1, t4); - TokenItem t5 = TokenItem.valueOf("a").withParams(getParameters("c", "d")); + TokenItem t5 = TokenItem.of("a").withParams(getParameters("c", "d")); assertNotSame(t4, t5); assertNotEquals(t4, t5); } diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 44c3e8b..8209d9d 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -136,7 +136,7 @@ public void testToken() { String[] tests = new String[] { "*", "x", "*-/", "foo.bar-qux" }; for (String s : tests) { - TokenItem item = TokenItem.valueOf(s); + TokenItem item = TokenItem.of(s); assertEquals(s, item.get()); // TODO: figure out how to check the serialization without copying // the actual impl code @@ -150,7 +150,7 @@ public void testTokenInvalid() { for (String s : tests) { try { - TokenItem item = TokenItem.valueOf(s); + TokenItem item = TokenItem.of(s); fail("should fail for '" + s + "' but got '" + item.get() + "'"); } catch (IllegalArgumentException expected) { } diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index f30d8b9..de9ba9c 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -148,20 +148,20 @@ private static OuterList createComplexListOfParams1() { map1.put("a", IntegerItem.valueOf(1)); map1.put("b", IntegerItem.valueOf(2)); map1.put("cde_456", BooleanItem.of(true)); - TokenItem l1 = TokenItem.valueOf("abc"). + TokenItem l1 = TokenItem.of("abc"). withParams(Parameters.of(map1)); List> lc2 = new ArrayList<>(); - TokenItem t21 = TokenItem.valueOf("ghi"). + TokenItem t21 = TokenItem.of("ghi"). withParams(Parameters.of( Collections.singletonMap("jk", IntegerItem.valueOf(4)))); lc2.add(t21); - lc2.add(TokenItem.valueOf("l")); + lc2.add(TokenItem.of("l")); Map map2 = new LinkedHashMap<>(); map2.put("q", StringItem.of("9")); - map2.put("r", TokenItem.valueOf("w")); + map2.put("r", TokenItem.of("w")); Parameters p2 = Parameters.of(map2); InnerList l2 = InnerList.of(lc2).withParams(p2); @@ -229,7 +229,7 @@ private static Dictionary createDictionary1() { Map> map = new LinkedHashMap<>(); map.put("a", BooleanItem.of(false)); map.put("b", BooleanItem.of(true)); - map.put("c", BooleanItem.of(true).withParams(Parameters.of(Collections.singletonMap("foo", TokenItem.valueOf("bar"))))); + map.put("c", BooleanItem.of(true).withParams(Parameters.of(Collections.singletonMap("foo", TokenItem.of("bar"))))); return Dictionary.of(map); } @@ -238,7 +238,7 @@ private static Dictionary createDictionary2() { return Dictionary.valueOf( "a", false, "b", true, - "c", BooleanItem.of(true).withParamValuesOf("foo", TokenItem.valueOf("bar"))); + "c", BooleanItem.of(true).withParamValuesOf("foo", TokenItem.of("bar"))); } // RFC 9651, Section 3.2 @@ -258,8 +258,8 @@ private static Dictionary createDictionaryWithInnerList1() { Map> map = new LinkedHashMap<>(); map.put("rating", DecimalItem.valueOf(BigDecimal.valueOf(1.5f))); List> li = new ArrayList<>(); - li.add(TokenItem.valueOf("joy")); - li.add(TokenItem.valueOf("sadness")); + li.add(TokenItem.of("joy")); + li.add(TokenItem.of("sadness")); map.put("feelings", InnerList.of(li)); return Dictionary.of(map); } @@ -267,7 +267,7 @@ private static Dictionary createDictionaryWithInnerList1() { // concise API private static Dictionary createDictionaryWithInnerList2() { return Dictionary.valueOf("rating", 1.5f, - "feelings", InnerList.of(TokenItem.valueOf("joy"), TokenItem.valueOf("sadness"))); + "feelings", InnerList.of(TokenItem.of("joy"), TokenItem.of("sadness"))); } // RFC 9651, Section 3.2 @@ -292,7 +292,7 @@ private static Dictionary createDictionaryMix1() { InnerList linner1 = InnerList.of(inner1); Map p3 = new LinkedHashMap<>(); - p3.put("aa", TokenItem.valueOf("bb")); + p3.put("aa", TokenItem.of("bb")); Parameters params3 = Parameters.of(p3); List> inner4 = new ArrayList<>(); @@ -317,7 +317,7 @@ private static Dictionary createDictionaryMix2() { return Dictionary.valueOf("a", InnerList.valueOf(1, 2), "b", 3, "c", IntegerItem.valueOf(4). - withParamValuesOf("aa", TokenItem.valueOf("bb")), + withParamValuesOf("aa", TokenItem.of("bb")), "d", InnerList.valueOf(5, 6). withParamValuesOf("valid", true)); } From 00151ce0b90f657b9197824a958be9797024795c Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 12:50:58 +0100 Subject: [PATCH 55/72] apideprecations --- .../org/greenbytes/http/sfv/IntegerItem.java | 7 ------- .../java/org/greenbytes/http/sfv/Parser.java | 2 +- src/main/java/org/greenbytes/http/sfv/Utils.java | 4 ++-- .../org/greenbytes/http/sfv/EqualityTest.java | 6 +++--- .../org/greenbytes/http/sfv/ItemAPITests.java | 10 +++++----- .../org/greenbytes/http/sfv/ParametersTest.java | 2 +- .../greenbytes/http/sfv/RFC9651ExamplesTest.java | 16 ++++++++-------- 7 files changed, 20 insertions(+), 27 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index 213d6d5..faf6e16 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -25,13 +25,6 @@ private IntegerItem(long value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } - /** - * @deprecated use {@link #of(long)} instead. - */ - public static IntegerItem valueOf(long value) { - return new IntegerItem(value, Parameters.EMPTY); - } - /** * Creates an {@link IntegerItem} instance representing the specified * {@code long} value. diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 9e3c319..671d5d7 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -175,7 +175,7 @@ private NumberItem internalParseBareIntegerOrDecimal() { if (!isDecimal) { long l = Long.parseLong(inputNumber.toString()); - return IntegerItem.valueOf(sign * l); + return IntegerItem.of(sign * l); } else { long l = computeLong(inputNumber); return DecimalItem.valueOf(sign * l); diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index c38f83d..11c92d6 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -87,9 +87,9 @@ protected static Item asItem(Object o) { if (o instanceof Item) { return (Item) o; } else if (o instanceof Integer) { - return IntegerItem.valueOf(((Integer) o).longValue()); + return IntegerItem.of(((Integer) o).longValue()); } else if (o instanceof Long) { - return IntegerItem.valueOf((Long) o); + return IntegerItem.of((Long) o); } else if (o instanceof String) { try { return StringItem.of((String) o); diff --git a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java index 5bd5c05..88c94eb 100644 --- a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java +++ b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java @@ -154,9 +154,9 @@ public void testDisplayStringItemEquality() { @Test public void testIntegerItemEquality() { - IntegerItem i1 = IntegerItem.valueOf(1); - IntegerItem i2 = IntegerItem.valueOf(1); - IntegerItem i3 = IntegerItem.valueOf(2); + IntegerItem i1 = IntegerItem.of(1); + IntegerItem i2 = IntegerItem.of(1); + IntegerItem i3 = IntegerItem.of(2); assertNotSame(i1, i2); assertEquals(i1, i2); diff --git a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java index 8209d9d..8208e4c 100644 --- a/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java +++ b/src/test/java/org/greenbytes/http/sfv/ItemAPITests.java @@ -33,7 +33,7 @@ public void testInteger() { long[] tests = new long[] { 0L, -0L, 999999999999999L, -999999999999999L }; for (long l : tests) { - IntegerItem item = IntegerItem.valueOf(l); + IntegerItem item = IntegerItem.of(l); assertEquals(Long.valueOf(l), item.get()); assertEquals(l, item.getAsLong()); assertEquals(Long.valueOf(l).toString(), item.serialize()); @@ -48,7 +48,7 @@ public void testIntegerInvalid() { for (Long l : tests) { try { - IntegerItem item = IntegerItem.valueOf(l); + IntegerItem item = IntegerItem.of(l); fail("should fail for " + l + " but got '" + item.get() + "'"); } catch (IllegalArgumentException expected) { } @@ -259,7 +259,7 @@ public void testInvalidParameterKeys() { Map m = new LinkedHashMap<>(); for (String key : tests) { m.clear(); - m.put(key, IntegerItem.valueOf(1)); + m.put(key, IntegerItem.of(1)); assertThrows("should throe", IllegalArgumentException.class, () -> Parameters.valueOf(m)); @@ -270,8 +270,8 @@ public void testInvalidParameterKeys() { public void testInvalidParameterValues() { Map itemParam = new LinkedHashMap<>(); - itemParam.put("foo", IntegerItem.valueOf(2)); - IntegerItem iitem = IntegerItem.valueOf(1).withParams(Parameters.of(itemParam)); + itemParam.put("foo", IntegerItem.of(2)); + IntegerItem iitem = IntegerItem.of(1).withParams(Parameters.of(itemParam)); Map m = new LinkedHashMap<>(); m.put("bar", iitem); diff --git a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java index 1dde8af..79bce38 100644 --- a/src/test/java/org/greenbytes/http/sfv/ParametersTest.java +++ b/src/test/java/org/greenbytes/http/sfv/ParametersTest.java @@ -67,7 +67,7 @@ public void testParametersEquality() { Map m1 = new HashMap(); m1.put("a", BooleanItem.of(true)); Map m2 = new HashMap(); - m2.put("b", IntegerItem.valueOf(12)); + m2.put("b", IntegerItem.of(12)); m2.put("c", StringItem.of("hello")); Parameters p1 = Parameters.of(m1); diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index de9ba9c..b342085 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -145,8 +145,8 @@ public void testComplexListOfParams() { // chatty API private static OuterList createComplexListOfParams1() { Map map1 = new LinkedHashMap<>(); - map1.put("a", IntegerItem.valueOf(1)); - map1.put("b", IntegerItem.valueOf(2)); + map1.put("a", IntegerItem.of(1)); + map1.put("b", IntegerItem.of(2)); map1.put("cde_456", BooleanItem.of(true)); TokenItem l1 = TokenItem.of("abc"). withParams(Parameters.of(map1)); @@ -154,7 +154,7 @@ private static OuterList createComplexListOfParams1() { List> lc2 = new ArrayList<>(); TokenItem t21 = TokenItem.of("ghi"). withParams(Parameters.of( - Collections.singletonMap("jk", IntegerItem.valueOf(4)))); + Collections.singletonMap("jk", IntegerItem.of(4)))); lc2.add(t21); lc2.add(TokenItem.of("l")); @@ -296,8 +296,8 @@ private static Dictionary createDictionaryMix1() { Parameters params3 = Parameters.of(p3); List> inner4 = new ArrayList<>(); - inner4.add(IntegerItem.valueOf(5)); - inner4.add(IntegerItem.valueOf(6)); + inner4.add(IntegerItem.of(5)); + inner4.add(IntegerItem.of(6)); InnerList linner4 = InnerList.of(inner4); Map p4 = new LinkedHashMap<>(); @@ -305,8 +305,8 @@ private static Dictionary createDictionaryMix1() { Parameters params4 = Parameters.of(p4); map.put("a", linner1); - map.put("b", IntegerItem.valueOf(3)); - map.put("c", IntegerItem.valueOf(4).withParams(params3)); + map.put("b", IntegerItem.of(3)); + map.put("c", IntegerItem.of(4).withParams(params3)); map.put("d", linner4.withParams(params4)); return Dictionary.of(map); @@ -316,7 +316,7 @@ private static Dictionary createDictionaryMix1() { private static Dictionary createDictionaryMix2() { return Dictionary.valueOf("a", InnerList.valueOf(1, 2), "b", 3, - "c", IntegerItem.valueOf(4). + "c", IntegerItem.of(4). withParamValuesOf("aa", TokenItem.of("bb")), "d", InnerList.valueOf(5, 6). withParamValuesOf("valid", true)); From b8f8e5b418de6d459295d29ab1a0234b9ac67472 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 13:03:30 +0100 Subject: [PATCH 56/72] apideprecations --- src/main/java/org/greenbytes/http/sfv/OuterList.java | 8 -------- src/main/java/org/greenbytes/http/sfv/Parser.java | 2 +- src/test/java/org/greenbytes/http/sfv/EqualityTest.java | 6 +++--- .../java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java | 2 +- 4 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index c3c2a8d..fd8c9eb 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -20,14 +20,6 @@ private OuterList(List> value) { this.value = Objects.requireNonNull(value, "value must not be null"); } - /** - * @deprecated use {@link #of(List)} instead. - */ - @Deprecated - public static OuterList valueOf(List> value) { - return new OuterList(value); - } - /** * Creates an {@link OuterList} instance representing the specified * {@linkplain Object} values after best-effort conversion to {@linkplain Item}. diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 671d5d7..956da37 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -743,7 +743,7 @@ public static OuterList parseList(String input) { Parser p = new Parser(input); List> result = p.internalParseOuterList(); p.assertEmpty("Extra characters in string parsed as List"); - return OuterList.valueOf(result); + return OuterList.of(result); } /** diff --git a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java index 88c94eb..743bf21 100644 --- a/src/test/java/org/greenbytes/http/sfv/EqualityTest.java +++ b/src/test/java/org/greenbytes/http/sfv/EqualityTest.java @@ -174,9 +174,9 @@ public void testOuterListEquality() { BooleanItem b = BooleanItem.of(true); DecimalItem d = DecimalItem.valueOf(BigDecimal.valueOf(10.5)); - OuterList l1 = OuterList.valueOf((Arrays.asList(b, d))); - OuterList l2 = OuterList.valueOf((Arrays.asList(b, d))); - OuterList l3 = OuterList.valueOf((Arrays.asList(b, d, b))); + OuterList l1 = OuterList.of((Arrays.asList(b, d))); + OuterList l2 = OuterList.of((Arrays.asList(b, d))); + OuterList l3 = OuterList.of((Arrays.asList(b, d, b))); assertNotSame(l1, l2); assertEquals(l1, l2); diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index b342085..462a87b 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -169,7 +169,7 @@ private static OuterList createComplexListOfParams1() { value.add(l1); value.add(l2); - return OuterList.valueOf(value); + return OuterList.of(value); } // concise API From 51fa477b554aec4fdc171f5da4404255d27e25eb Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 13:15:01 +0100 Subject: [PATCH 57/72] apideprecations --- src/main/java/org/greenbytes/http/sfv/InnerList.java | 8 -------- src/main/java/org/greenbytes/http/sfv/Parser.java | 2 +- .../java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java | 8 ++++---- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 5f3c04e..6dfaa96 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -23,14 +23,6 @@ private InnerList(List> value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } - /** - * @deprecated - use {@linkplain #of(List)} instead. - */ - @Deprecated - public static InnerList valueOf(List> value) { - return new InnerList(value, Parameters.EMPTY); - } - /** * Creates an {@link InnerList} instance representing the specified * {@code List} value. diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 956da37..f2ecc70 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -594,7 +594,7 @@ private List> internalParseBareInnerList() { private InnerList internalParseInnerList() { List> result = internalParseBareInnerList(); Parameters params = internalParseParameters(); - return InnerList.valueOf(result).withParams(params); + return InnerList.of(result).withParams(params); } private Dictionary internalParseDictionary() { diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 462a87b..2569460 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -69,10 +69,10 @@ private static OuterList createListBareInnerLists1() { List> inner4 = Collections.emptyList(); List> combined = new ArrayList<>(); - combined.add(InnerList.valueOf(inner1)); - combined.add(InnerList.valueOf(inner2)); - combined.add(InnerList.valueOf(inner3)); - combined.add(InnerList.valueOf(inner4)); + combined.add(InnerList.of(inner1)); + combined.add(InnerList.of(inner2)); + combined.add(InnerList.of(inner3)); + combined.add(InnerList.of(inner4)); return OuterList.of(combined); } From 552eb70799f9afac3652dd7ea1a8f5d43b87945c Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 13:18:32 +0100 Subject: [PATCH 58/72] apideprecations --- src/main/java/org/greenbytes/http/sfv/InnerList.java | 3 +-- src/main/java/org/greenbytes/http/sfv/OuterList.java | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 6dfaa96..7470bdf 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -55,8 +55,7 @@ public static InnerList of(Item... values) { * @return a {@link InnerList} representing {@code values}. */ public static InnerList valueOf(Object... values) { - return of(Arrays.stream(values).map(v -> Utils.asItem(v)) - .collect(Collectors.toList())); + return of(Arrays.stream(values).map(Utils::asItem).collect(Collectors.toList())); } @Override diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index fd8c9eb..1390583 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -28,8 +28,7 @@ private OuterList(List> value) { * @return a {@link OuterList} representing {@code values}. */ public static OuterList valueOf(Object... values) { - return of(Arrays.stream(values).map(v -> Utils.asListElement(v)) - .collect(Collectors.toList())); + return of(Arrays.stream(values).map(Utils::asListElement).collect(Collectors.toList())); } /** From f5bb2592edac86cca5025a6076e1982eb9180909 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 13:25:10 +0100 Subject: [PATCH 59/72] generics --- src/main/java/org/greenbytes/http/sfv/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index f2ecc70..bafd68f 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -891,7 +891,7 @@ public static String parseKey(String input) { * "https://www.rfc-editor.org/rfc/rfc9651.html#parse-number">Section * 4.2.4 of RFC 9651 */ - public static NumberItem parseIntegerOrDecimal(String input) { + public static NumberItem parseIntegerOrDecimal(String input) { Parser p = new Parser(input); NumberItem result = p.internalParseIntegerOrDecimal(); p.assertEmpty("Extra characters in string parsed as Integer or Decimal"); From f120d4af2c77b888b845f6bc069d2b8d6a7f527b Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 13:29:07 +0100 Subject: [PATCH 60/72] generics --- src/main/java/org/greenbytes/http/sfv/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index bafd68f..773118a 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -137,7 +137,7 @@ private DateItem internalParseBareDate() { return DateItem.valueOf(sign * l); } - private NumberItem internalParseBareIntegerOrDecimal() { + private NumberItem internalParseBareIntegerOrDecimal() { boolean isDecimal = false; int sign = 1; StringBuilder inputNumber = new StringBuilder(20); From 51aa80923b66c9c90dd5274b124c5708b73b69fe Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sun, 3 May 2026 13:31:04 +0100 Subject: [PATCH 61/72] generics --- src/main/java/org/greenbytes/http/sfv/Parser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parser.java b/src/main/java/org/greenbytes/http/sfv/Parser.java index 773118a..c8e8443 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parser.java +++ b/src/main/java/org/greenbytes/http/sfv/Parser.java @@ -893,7 +893,7 @@ public static String parseKey(String input) { */ public static NumberItem parseIntegerOrDecimal(String input) { Parser p = new Parser(input); - NumberItem result = p.internalParseIntegerOrDecimal(); + NumberItem result = p.internalParseIntegerOrDecimal(); p.assertEmpty("Extra characters in string parsed as Integer or Decimal"); return result; } From 1cdb2a9bb52db903e44e97b6d255f157313fd3a3 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Wed, 6 May 2026 14:01:20 +0100 Subject: [PATCH 62/72] apiparseexample --- .../http/sfv/RFC9651ExamplesTest.java | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 2569460..8ca4169 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -3,6 +3,7 @@ import org.junit.Test; import java.math.BigDecimal; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; @@ -12,6 +13,8 @@ import java.util.Map; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.fail; public class RFC9651ExamplesTest { @@ -321,4 +324,91 @@ private static Dictionary createDictionaryMix2() { "d", InnerList.valueOf(5, 6). withParamValuesOf("valid", true)); } + + // RFC 9651, Section 3.2 + // Foo-Example: 2; foourl="https://foo.example.com/" + // (parse and validate) + + @Test + public void testParseAndValidateExample() { + + { + Foo foo = parseAndValidateExample("2; foourl=\"https://foo.example.com/\"", null); + assertEquals(2, foo.amount); + assertEquals("https://foo.example.com/", foo.url.toString()); + } + + { + Foo foo = parseAndValidateExample("5", null); + assertEquals(5, foo.amount); + assertNull(foo.url); + } + + { + Foo foo = parseAndValidateExample("5; foourl=\"foo\"", URI.create("https://example.org/")); + assertEquals(5, foo.amount); + assertEquals("https://example.org/foo", foo.url.toString()); + } + + try { + parseAndValidateExample("11; foourl=\"https://foo.example.com/\"", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + parseAndValidateExample("9.0; foourl=\"https://foo.example.com/\"", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + parseAndValidateExample("2; foourl=\"https:oh no//foo.example.com/\"", null); + fail(); + } catch (IllegalArgumentException expected) { + } + + try { + parseAndValidateExample("2; foourl=\"relative\"", null); + fail(); + } catch (NullPointerException expected) { + } + } + + private static class Foo { + int amount; + URI url; + } + + private static Foo parseAndValidateExample(String serialization, URI baseUri) { + Item item = Parser.parseItem(serialization); + + if (! (item instanceof IntegerItem)) { + throw new IllegalArgumentException("not a IntegerItem (was " + item.getClass().getSimpleName() + ")"); + } + + long amountOfFoo = ((IntegerItem) item).get(); + + if (amountOfFoo < 0 || amountOfFoo > 10) { + throw new IllegalArgumentException("invalid amountOfFoo (was " + amountOfFoo + ")"); + } + + Item fooURLParam = item.getParams().get("foourl"); + if (fooURLParam != null && !(fooURLParam instanceof StringItem)) { + throw new IllegalArgumentException("foourl not a StringItem (was " + fooURLParam.getClass().getSimpleName() + ")"); + } + + URI url = null; + if (fooURLParam != null) { + url = URI.create(((StringItem) fooURLParam).get()); + if (! url.isAbsolute()) { + url = baseUri.resolve(url); + } + } + + Foo foo = new Foo(); + foo.amount = (int) amountOfFoo; + foo.url = url; + return foo; + } } From 9e714e9a03eb0de162f587fe5f69e04d6a88c040 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Fri, 8 May 2026 11:00:32 +0100 Subject: [PATCH 63/72] remove conflicts --- src/main/java/org/greenbytes/http/sfv/Dictionary.java | 6 ------ src/main/java/org/greenbytes/http/sfv/InnerList.java | 6 ------ src/main/java/org/greenbytes/http/sfv/OuterList.java | 3 --- src/main/java/org/greenbytes/http/sfv/Parameters.java | 3 --- 4 files changed, 18 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index 80e4c99..1196078 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -3,10 +3,7 @@ import java.util.Collections; import java.util.LinkedHashMap; import java.util.Map; -<<<<<<< api import java.util.Objects; -======= ->>>>>>> main import java.util.function.Function; /** @@ -113,7 +110,6 @@ public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Func } return sb; } -<<<<<<< api @Override public boolean equals(Object o) { @@ -129,6 +125,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hashCode(value); } -======= ->>>>>>> main } diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 7ce1ac7..7470bdf 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -4,10 +4,7 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; -<<<<<<< api import java.util.stream.Collectors; -======= ->>>>>>> main /** * Represents an Inner List. @@ -66,14 +63,11 @@ public InnerList withParams(Parameters params) { return new InnerList(this.value, Objects.requireNonNull(params, "params must not be null")); } -<<<<<<< api @Override public InnerList withParamValuesOf(Object... obs) { return new InnerList(this.value, Parameters.valueOf(obs)); } -======= ->>>>>>> main private StringBuilder serializeToNoParams(StringBuilder sb) { String separator = ""; diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index ef5735b..1390583 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -4,10 +4,7 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; -<<<<<<< api import java.util.stream.Collectors; -======= ->>>>>>> main /** * Represents a List. diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index c4e18da..d4d6f1f 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -232,7 +232,6 @@ public int size() { public Collection> values() { return delegate.values(); } -<<<<<<< api @Override public boolean equals(Object o) { @@ -248,6 +247,4 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hashCode(delegate); } -======= ->>>>>>> main } From 10450424d5a05eeea9740c0448802382c097a24e Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Fri, 8 May 2026 12:40:18 +0100 Subject: [PATCH 64/72] javadoc for utils --- .../greenbytes/http/sfv/Parameterizable.java | 20 +++++++++ .../java/org/greenbytes/http/sfv/Utils.java | 41 ++++++++++++++----- 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parameterizable.java b/src/main/java/org/greenbytes/http/sfv/Parameterizable.java index a58e921..76b803a 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameterizable.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameterizable.java @@ -20,6 +20,26 @@ public interface Parameterizable extends Type { */ Parameterizable withParams(Parameters params); + /** + * Given an existing {@link Item}, return a new instance with the specified + * {@link Parameters}, specified as a sequence of {@linkplain Object}s. + *

+ * The sequence consists of name/value pairs, where the length needs to be + * even-numbered. Each pair consist of a parameter name (thus will + * be converted to a {@link String}), and an {@linkplain Object} that + * will be converted on a best-effort to an {@link Item}. + *

+ * Example from Section 2.1 of RFC 9651 + *

+     * IntegerItem fooWithParams = IntegerItem.of(2).
+     *                               withParamsOfValue("foourl", "https://foo.example.com");
+     * 
+ * + * @param obs sequence of name/valid pairs + * @return new instance with specified {@link Parameters}, as converted from + * the Object sequence.. + */ Parameterizable withParamValuesOf(Object... obs); /** diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index 11c92d6..f5b967d 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -1,6 +1,7 @@ package org.greenbytes.http.sfv; import java.math.BigDecimal; +import java.util.List; import java.util.Map; import java.util.Objects; @@ -12,25 +13,38 @@ public class Utils { private Utils() { } - /** check for character to be a decimal digit */ + /** + * Check for character to be a decimal digit. + * @param c character to check + * @return {@code true} if and only if a digit + */ protected static boolean isDigit(char c) { return c >= '0' && c <= '9'; } - /** check for character to be lowercase alphanumeric */ + /** + * Check for character to be lowercase ASCII alpha character. + * @param c character to check + * @return {@code true} if and only if lowercase ASCII alpha + */ protected static boolean isLcAlpha(char c) { return (c >= 'a' && c <= 'z'); } - /** check for character to be alphanumeric */ + /** + * Check for character to be lowercase or uppercase ASCII alpha character. + * @param c character to check + * @return {@code true} if and only if lowercase or uppercase ASCII alpha + */ protected static boolean isAlpha(char c) { return (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z'); } /** - * Checks a key for validity + * Checks key for validity. * @param value to check * @return checked value + * @throws IllegalArgumentException when invalid */ protected static String checkKey(String value) { if (value == null || value.isEmpty()) { @@ -48,9 +62,11 @@ protected static String checkKey(String value) { } /** - * Checks all keys in map for validity + * Checks all keys + * in {@linkplain Map} for validity * @param value map to check * @return checked map + * @throws IllegalArgumentException when invalid key found */ protected static Map> checkKeys(Map> value) { for (String key : Objects.requireNonNull(value, "value must not be null").keySet()) { @@ -60,9 +76,10 @@ protected static Map> checkKeys(Map asBareItem(Object o) { if (o instanceof Item) { @@ -77,11 +94,12 @@ protected static Item asBareItem(Object o) { } /** - * Converts from [{@linkplain Item} or native Java object + * Converts an {@linkplain Object} to an {@link Item} (on a best-effort basis). *

* Same as {@linkplain #asBareItem(Object)}, but allowing {@linkplain Parameters} * @param o to convert - * @return convert to {@linkplain Item} + * @return converted to {@linkplain Item} + * @throws IllegalArgumentException when it can't be converted */ protected static Item asItem(Object o) { if (o instanceof Item) { @@ -112,9 +130,12 @@ protected static Item asItem(Object o) { } /** - * Same as {@linkplain #asItem(Object)}, but allowing also allowing {@linkplain InnerList} + * Converts an {@linkplain Object} to an {@linkplain List} of {@linkplain Item}s + * (on a best-effort basis). + * Same as {@linkplain #asItem(Object)}, but also allowing {@linkplain InnerList} * @param o to convert * @return convert to {@linkplain ListElement} + * @throws IllegalArgumentException when it can't be converted */ protected static ListElement asListElement(Object o) { if (o instanceof InnerList) { From dac5dd92b00a5c09617a4d70a0b14c223328b781 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 9 May 2026 17:17:50 +0100 Subject: [PATCH 65/72] javadoc --- src/main/java/org/greenbytes/http/sfv/Utils.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index f5b967d..dfc421e 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -96,7 +96,20 @@ protected static Item asBareItem(Object o) { /** * Converts an {@linkplain Object} to an {@link Item} (on a best-effort basis). *

+ * Currently mapped: + *

    + *
  • {@linkplain Item} → {@linkplain Item}
  • + *
  • {@linkplain Integer} → {@linkplain IntegerItem}
  • + *
  • {@linkplain Long} → {@linkplain IntegerItem}
  • + *
  • {@linkplain String} → {@linkplain StringItem} or {@linkplain DisplayStringItem}
  • + *
  • {@linkplain Boolean} → {@linkplain BooleanItem}
  • + *
  • {@code byte[]} → {@linkplain ByteSequenceItem}
  • + *
  • {@linkplain BigDecimal} → {@linkplain DecimalItem}
  • + *
  • {@linkplain Double} → {@linkplain DecimalItem}
  • + *
  • {@linkplain Float} → {@linkplain DecimalItem}
  • + *
* Same as {@linkplain #asBareItem(Object)}, but allowing {@linkplain Parameters} + *

* @param o to convert * @return converted to {@linkplain Item} * @throws IllegalArgumentException when it can't be converted From f8552b4c5dd26949a51427c9e0914caf72aabc33 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 9 May 2026 17:18:05 +0100 Subject: [PATCH 66/72] javadoc --- src/main/java/org/greenbytes/http/sfv/Utils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Utils.java b/src/main/java/org/greenbytes/http/sfv/Utils.java index dfc421e..4d4d786 100644 --- a/src/main/java/org/greenbytes/http/sfv/Utils.java +++ b/src/main/java/org/greenbytes/http/sfv/Utils.java @@ -109,7 +109,6 @@ protected static Item asBareItem(Object o) { *

  • {@linkplain Float} → {@linkplain DecimalItem}
  • * * Same as {@linkplain #asBareItem(Object)}, but allowing {@linkplain Parameters} - *

    * @param o to convert * @return converted to {@linkplain Item} * @throws IllegalArgumentException when it can't be converted From f162bbb78011f5e846f56fbb3c04b0c8eafd6b28 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 9 May 2026 17:18:24 +0100 Subject: [PATCH 67/72] javadoc --- src/main/java/org/greenbytes/http/sfv/Parameters.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index d4d6f1f..a27a88f 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -47,7 +47,6 @@ public static Parameters of(Map value) { /** * Creates an unmodifiable {@link Parameters} instance representing * the specified {@linkplain Object}s. - *

    * @param obs (needs to be an even-number of {@linkplain Object}s) * @return a {@link Parameters} representing {@code obs}. */ From 0f46c2cfa381a65ac334be8e0289e10fb7d71299 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 9 May 2026 17:29:36 +0100 Subject: [PATCH 68/72] api --- src/main/java/org/greenbytes/http/sfv/DateItem.java | 1 + src/main/java/org/greenbytes/http/sfv/DecimalItem.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index 329b27e..ca2d6a9 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -66,6 +66,7 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 8392ac5..c76f736 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -88,7 +88,7 @@ public Parameters getParams() { return params; } - public StringBuilder serializeToNoParams(StringBuilder sb) { + private StringBuilder serializeToNoParams(StringBuilder sb) { String sign = value < 0 ? "-" : ""; From 1e839324fe687cd0e5db6fa4c5db79b0af624c6d Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 9 May 2026 17:30:12 +0100 Subject: [PATCH 69/72] api --- src/main/java/org/greenbytes/http/sfv/DateItem.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index ca2d6a9..734d7c5 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -52,7 +52,7 @@ public Parameters getParams() { return params; } - public StringBuilder serializeToNoParams(StringBuilder sb) { + private StringBuilder serializeToNoParams(StringBuilder sb) { return sb.append('@').append(value); } From bc895e9ddd9b835d788522acd42f149fd4559093 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Sat, 9 May 2026 17:35:45 +0100 Subject: [PATCH 70/72] api --- .../java/org/greenbytes/http/sfv/DecimalItem.java | 1 + .../java/org/greenbytes/http/sfv/InnerList.java | 1 + .../java/org/greenbytes/http/sfv/Parameters.java | 15 ++++++++++++--- .../java/org/greenbytes/http/sfv/TokenItem.java | 1 + src/main/java/org/greenbytes/http/sfv/Type.java | 2 +- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index c76f736..0532714 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -122,6 +122,7 @@ public String serialize() { return serializeTo(new StringBuilder(20)).toString(); } + @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index 7470bdf..eea776b 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -89,6 +89,7 @@ public StringBuilder serializeTo(StringBuilder sb) { return params.serializeTo(serializeToNoParams(sb)); } + @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); diff --git a/src/main/java/org/greenbytes/http/sfv/Parameters.java b/src/main/java/org/greenbytes/http/sfv/Parameters.java index a27a88f..6233207 100644 --- a/src/main/java/org/greenbytes/http/sfv/Parameters.java +++ b/src/main/java/org/greenbytes/http/sfv/Parameters.java @@ -94,14 +94,23 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } - public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { + /** + * Serialize debug information to an existing {@link StringBuilder}. + * + * @param sb + * where to serialize to + * @param indentLevel how much to indent + * @param classFormatter to format the classname when desires (can be a function that returns an empty string) + * @return the {@link StringBuilder} so calls can be chained. + */ + public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function classFormatter) { if (!delegate.isEmpty()) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; - String classn = formatter.apply(this.getClass()); + String classn = classFormatter.apply(this.getClass()); sb.append(indent).append(serialize()).append(classn).append("\n"); for (Map.Entry> e : delegate.entrySet()) { sb.append(" " + indent).append(e.getKey()).append(" -> "); - e.getValue().serializeToForDebug(sb, 0, formatter); + e.getValue().serializeToForDebug(sb, 0, classFormatter); } return sb; } else { diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index 674f95c..df049a4 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -58,6 +58,7 @@ public String serialize() { return serializeTo(new StringBuilder()).toString(); } + @Override public StringBuilder serializeToForDebug(StringBuilder sb, int indentLevel, Function formatter) { String indent = indentLevel != 0 ? String.format("%" + indentLevel + "s", "") : ""; String classn = formatter.apply(this.getClass()); diff --git a/src/main/java/org/greenbytes/http/sfv/Type.java b/src/main/java/org/greenbytes/http/sfv/Type.java index 7e59323..48a886a 100644 --- a/src/main/java/org/greenbytes/http/sfv/Type.java +++ b/src/main/java/org/greenbytes/http/sfv/Type.java @@ -26,7 +26,7 @@ public interface Type extends Supplier { StringBuilder serializeTo(StringBuilder sb); /** - * Serialize debubg information to an existing {@link StringBuilder}. + * Serialize debug information to an existing {@link StringBuilder}. * * @param sb * where to serialize to From 91c7a426ae6faa057d7b241077245d96074fcc22 Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Tue, 12 May 2026 13:40:00 +0100 Subject: [PATCH 71/72] type enum --- .../org/greenbytes/http/sfv/BooleanItem.java | 5 +++++ .../greenbytes/http/sfv/ByteSequenceItem.java | 5 +++++ .../org/greenbytes/http/sfv/DateItem.java | 5 +++++ .../org/greenbytes/http/sfv/DecimalItem.java | 5 +++++ .../org/greenbytes/http/sfv/Dictionary.java | 5 +++++ .../http/sfv/DisplayStringItem.java | 5 +++++ .../org/greenbytes/http/sfv/InnerList.java | 5 +++++ .../org/greenbytes/http/sfv/IntegerItem.java | 5 +++++ .../org/greenbytes/http/sfv/OuterList.java | 5 +++++ .../org/greenbytes/http/sfv/SfDataType.java | 22 +++++++++++++++++++ .../org/greenbytes/http/sfv/StringItem.java | 5 +++++ .../org/greenbytes/http/sfv/TokenItem.java | 5 +++++ .../java/org/greenbytes/http/sfv/Type.java | 5 +++++ .../http/sfv/RFC9651ExamplesTest.java | 6 ++--- 14 files changed, 85 insertions(+), 3 deletions(-) create mode 100755 src/main/java/org/greenbytes/http/sfv/SfDataType.java diff --git a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java index 9f68b0e..cdb71af 100644 --- a/src/main/java/org/greenbytes/http/sfv/BooleanItem.java +++ b/src/main/java/org/greenbytes/http/sfv/BooleanItem.java @@ -22,6 +22,11 @@ private BooleanItem(boolean value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.BOOLEAN; + } + /** * Creates a {@link BooleanItem} instance representing the specified * {@code boolean} value. diff --git a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java index 95bf3bf..fdda778 100644 --- a/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java +++ b/src/main/java/org/greenbytes/http/sfv/ByteSequenceItem.java @@ -24,6 +24,11 @@ private ByteSequenceItem(byte[] value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.BYTESEQUENCE; + } + /** * Creates a {@link ByteSequenceItem} instance representing the specified * {@code byte[]} value. diff --git a/src/main/java/org/greenbytes/http/sfv/DateItem.java b/src/main/java/org/greenbytes/http/sfv/DateItem.java index 734d7c5..817b3ca 100644 --- a/src/main/java/org/greenbytes/http/sfv/DateItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DateItem.java @@ -25,6 +25,11 @@ private DateItem(long value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.DATE; + } + /** * Creates an {@link DateItem} instance representing the specified * {@code long} value. diff --git a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java index 0532714..266271c 100644 --- a/src/main/java/org/greenbytes/http/sfv/DecimalItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DecimalItem.java @@ -36,6 +36,11 @@ private DecimalItem(long value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.DECIMAL; + } + /** * Creates a {@link DecimalItem} instance representing the specified * {@code long} value, where the implied divisor is {@code 1000}. diff --git a/src/main/java/org/greenbytes/http/sfv/Dictionary.java b/src/main/java/org/greenbytes/http/sfv/Dictionary.java index 1196078..a88c85d 100644 --- a/src/main/java/org/greenbytes/http/sfv/Dictionary.java +++ b/src/main/java/org/greenbytes/http/sfv/Dictionary.java @@ -21,6 +21,11 @@ private Dictionary(Map> value) { this.value = Collections.unmodifiableMap(Utils.checkKeys(value)); } + @Override + public SfDataType getType() { + return SfDataType.DICTIONARY; + } + /** * Creates a {@link Dictionary} instance representing the specified * {@code Map} value. diff --git a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java index c847a35..16e5cf8 100755 --- a/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/DisplayStringItem.java @@ -20,6 +20,11 @@ private DisplayStringItem(String value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.DISPLAYSTRING; + } + /** * Creates a {@link DisplayStringItem} instance representing the specified * {@code String} value. diff --git a/src/main/java/org/greenbytes/http/sfv/InnerList.java b/src/main/java/org/greenbytes/http/sfv/InnerList.java index eea776b..48c0983 100644 --- a/src/main/java/org/greenbytes/http/sfv/InnerList.java +++ b/src/main/java/org/greenbytes/http/sfv/InnerList.java @@ -23,6 +23,11 @@ private InnerList(List> value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.INNERLIST; + } + /** * Creates an {@link InnerList} instance representing the specified * {@code List} value. diff --git a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java index faf6e16..91b3a12 100644 --- a/src/main/java/org/greenbytes/http/sfv/IntegerItem.java +++ b/src/main/java/org/greenbytes/http/sfv/IntegerItem.java @@ -25,6 +25,11 @@ private IntegerItem(long value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.INTEGER; + } + /** * Creates an {@link IntegerItem} instance representing the specified * {@code long} value. diff --git a/src/main/java/org/greenbytes/http/sfv/OuterList.java b/src/main/java/org/greenbytes/http/sfv/OuterList.java index 1390583..009c5a1 100644 --- a/src/main/java/org/greenbytes/http/sfv/OuterList.java +++ b/src/main/java/org/greenbytes/http/sfv/OuterList.java @@ -20,6 +20,11 @@ private OuterList(List> value) { this.value = Objects.requireNonNull(value, "value must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.LIST; + } + /** * Creates an {@link OuterList} instance representing the specified * {@linkplain Object} values after best-effort conversion to {@linkplain Item}. diff --git a/src/main/java/org/greenbytes/http/sfv/SfDataType.java b/src/main/java/org/greenbytes/http/sfv/SfDataType.java new file mode 100755 index 0000000..bd9db96 --- /dev/null +++ b/src/main/java/org/greenbytes/http/sfv/SfDataType.java @@ -0,0 +1,22 @@ +package org.greenbytes.http.sfv; + +/** + * Types of Structured Data + */ +public enum SfDataType { + // RFC 9651, Section 3.1 + LIST, + // RFC 9651, Section 3.1.1 + INNERLIST, + // RFC 9651, Section 3.2 + DICTIONARY, + // RFC 9651, Section 3.3 + BOOLEAN, + BYTESEQUENCE, + DATE, + DECIMAL, + DISPLAYSTRING, + INTEGER, + STRING, + TOKEN +} diff --git a/src/main/java/org/greenbytes/http/sfv/StringItem.java b/src/main/java/org/greenbytes/http/sfv/StringItem.java index 598e8c8..7cad4fd 100644 --- a/src/main/java/org/greenbytes/http/sfv/StringItem.java +++ b/src/main/java/org/greenbytes/http/sfv/StringItem.java @@ -19,6 +19,11 @@ private StringItem(String value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.STRING; + } + /** * Creates a {@link StringItem} instance representing the specified * {@code String} value. diff --git a/src/main/java/org/greenbytes/http/sfv/TokenItem.java b/src/main/java/org/greenbytes/http/sfv/TokenItem.java index df049a4..6856bdf 100644 --- a/src/main/java/org/greenbytes/http/sfv/TokenItem.java +++ b/src/main/java/org/greenbytes/http/sfv/TokenItem.java @@ -19,6 +19,11 @@ private TokenItem(String value, Parameters params) { this.params = Objects.requireNonNull(params, "params must not be null"); } + @Override + public SfDataType getType() { + return SfDataType.TOKEN; + } + /** * Creates a {@link TokenItem} instance representing the specified * {@code String} value. diff --git a/src/main/java/org/greenbytes/http/sfv/Type.java b/src/main/java/org/greenbytes/http/sfv/Type.java index 48a886a..bd321f7 100644 --- a/src/main/java/org/greenbytes/http/sfv/Type.java +++ b/src/main/java/org/greenbytes/http/sfv/Type.java @@ -16,6 +16,11 @@ */ public interface Type extends Supplier { + /** + * @return Structured Field Data Type + */ + SfDataType getType(); + /** * Serialize to an existing {@link StringBuilder}. * diff --git a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java index 8ca4169..31473b5 100755 --- a/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java +++ b/src/test/java/org/greenbytes/http/sfv/RFC9651ExamplesTest.java @@ -383,7 +383,7 @@ private static class Foo { private static Foo parseAndValidateExample(String serialization, URI baseUri) { Item item = Parser.parseItem(serialization); - if (! (item instanceof IntegerItem)) { + if (SfDataType.INTEGER != item.getType()) { throw new IllegalArgumentException("not a IntegerItem (was " + item.getClass().getSimpleName() + ")"); } @@ -394,7 +394,7 @@ private static Foo parseAndValidateExample(String serialization, URI baseUri) { } Item fooURLParam = item.getParams().get("foourl"); - if (fooURLParam != null && !(fooURLParam instanceof StringItem)) { + if (fooURLParam != null && SfDataType.INTEGER != item.getType()) { throw new IllegalArgumentException("foourl not a StringItem (was " + fooURLParam.getClass().getSimpleName() + ")"); } @@ -407,7 +407,7 @@ private static Foo parseAndValidateExample(String serialization, URI baseUri) { } Foo foo = new Foo(); - foo.amount = (int) amountOfFoo; + foo.amount = (int) amountOfFoo; foo.url = url; return foo; } From ef30ca4ac91109427c558059f1d80222dfdd238e Mon Sep 17 00:00:00 2001 From: Julian Reschke Date: Tue, 12 May 2026 13:40:12 +0100 Subject: [PATCH 72/72] type enum --- src/main/java/org/greenbytes/http/sfv/SfDataType.java | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 src/main/java/org/greenbytes/http/sfv/SfDataType.java diff --git a/src/main/java/org/greenbytes/http/sfv/SfDataType.java b/src/main/java/org/greenbytes/http/sfv/SfDataType.java old mode 100755 new mode 100644