Skip to content

Commit b7808a7

Browse files
authored
Merge pull request #595 from fglock/fix/http-response-encoding-mime-name
fix: jcpan -t HTTP::Response::Encoding
2 parents 6d622d7 + cd8e103 commit b7808a7

3 files changed

Lines changed: 54 additions & 4 deletions

File tree

src/main/java/org/perlonjava/core/Configuration.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public final class Configuration {
3333
* Automatically populated by Gradle/Maven during build.
3434
* DO NOT EDIT MANUALLY - this value is replaced at build time.
3535
*/
36-
public static final String gitCommitId = "25b6fa935";
36+
public static final String gitCommitId = "52d34a6f8";
3737

3838
/**
3939
* Git commit date of the build (ISO format: YYYY-MM-DD).
@@ -48,7 +48,7 @@ public final class Configuration {
4848
* Parsed by App::perlbrew and other tools via: perl -V | grep "Compiled at"
4949
* DO NOT EDIT MANUALLY - this value is replaced at build time.
5050
*/
51-
public static final String buildTimestamp = "Apr 28 2026 18:03:29";
51+
public static final String buildTimestamp = "Apr 28 2026 19:44:33";
5252

5353
// Prevent instantiation
5454
private Configuration() {

src/main/java/org/perlonjava/runtime/perlmodule/Encode.java

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,18 +222,24 @@ public static void initialize() {
222222
Encode.class, "encoding_decode", RuntimeCode.methodType);
223223
java.lang.invoke.MethodHandle nameHandle = RuntimeCode.lookup.findStatic(
224224
Encode.class, "encoding_name", RuntimeCode.methodType);
225+
java.lang.invoke.MethodHandle mimeNameHandle = RuntimeCode.lookup.findStatic(
226+
Encode.class, "encoding_mime_name", RuntimeCode.methodType);
225227
RuntimeCode encodeCode = new RuntimeCode(encodeHandle, null, null);
226228
encodeCode.isStatic = true;
227229
RuntimeCode decodeCode = new RuntimeCode(decodeHandle, null, null);
228230
decodeCode.isStatic = true;
229231
RuntimeCode nameCode = new RuntimeCode(nameHandle, null, null);
230232
nameCode.isStatic = true;
233+
RuntimeCode mimeNameCode = new RuntimeCode(mimeNameHandle, null, null);
234+
mimeNameCode.isStatic = true;
231235
GlobalVariable.getGlobalCodeRef("Encode::Encoding::encode").set(
232236
new RuntimeScalar(encodeCode));
233237
GlobalVariable.getGlobalCodeRef("Encode::Encoding::decode").set(
234238
new RuntimeScalar(decodeCode));
235239
GlobalVariable.getGlobalCodeRef("Encode::Encoding::name").set(
236240
new RuntimeScalar(nameCode));
241+
GlobalVariable.getGlobalCodeRef("Encode::Encoding::mime_name").set(
242+
new RuntimeScalar(mimeNameCode));
237243
} catch (NoSuchMethodException | IllegalAccessException e) {
238244
System.err.println("Warning: Missing Encode::Encoding method: " + e.getMessage());
239245
}
@@ -863,9 +869,11 @@ public static RuntimeList find_encoding(RuntimeArray args, int ctx) {
863869

864870
try {
865871
Charset charset = getCharset(encodingName);
866-
// Create a blessed hash with the charset name
872+
// Create a blessed hash with both the Perl-canonical Name
873+
// (used by ->name) and the IANA MimeName (used by ->mime_name).
867874
RuntimeHash encObj = new RuntimeHash();
868-
encObj.put("Name", new RuntimeScalar(charset.name()));
875+
encObj.put("Name", new RuntimeScalar(perlCanonicalName(charset.name())));
876+
encObj.put("MimeName", new RuntimeScalar(charset.name()));
869877
RuntimeScalar ref = encObj.createReference();
870878
ReferenceOperators.bless(ref, new RuntimeScalar("Encode::Encoding"));
871879
return ref.getList();
@@ -875,6 +883,20 @@ public static RuntimeList find_encoding(RuntimeArray args, int ctx) {
875883
}
876884
}
877885

886+
/**
887+
* Maps a Java canonical charset name to Perl Encode's canonical
888+
* encoding name (as returned by C<< $enc->name >>). For encodings
889+
* we don't have a special mapping for, the Java name is returned
890+
* unchanged.
891+
*/
892+
private static String perlCanonicalName(String javaName) {
893+
switch (javaName) {
894+
case "US-ASCII": return "ascii";
895+
case "UTF-8": return "utf-8-strict";
896+
default: return javaName;
897+
}
898+
}
899+
878900
/**
879901
* find_mime_encoding($mime_name)
880902
* Looks up an encoding by its MIME name. Delegates to find_encoding
@@ -1004,6 +1026,23 @@ public static RuntimeList encoding_name(RuntimeArray args, int ctx) {
10041026
return hash.get("Name").getList();
10051027
}
10061028

1029+
/**
1030+
* Encode::Encoding->mime_name()
1031+
* Returns the IANA-registered MIME name of this encoding. Java's
1032+
* canonical Charset name matches the IANA preferred MIME name for
1033+
* the common encodings used here, so we reuse it.
1034+
*/
1035+
public static RuntimeList encoding_mime_name(RuntimeArray args, int ctx) {
1036+
if (args.isEmpty()) {
1037+
throw new IllegalStateException("Bad number of arguments for Encode::Encoding::mime_name");
1038+
}
1039+
1040+
RuntimeScalar self = args.get(0);
1041+
RuntimeHash hash = (RuntimeHash) self.value;
1042+
RuntimeScalar mime = hash.get("MimeName");
1043+
return (mime != null && mime.getDefinedBoolean() ? mime : hash.get("Name")).getList();
1044+
}
1045+
10071046
/**
10081047
* from_to($octets, $from_enc, $to_enc [, $check])
10091048
* Converts in-place the octet sequence from one encoding to another.

src/main/java/org/perlonjava/runtime/runtimetypes/HashSpecialVariable.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ public Set<Entry<String, RuntimeScalar>> entrySet() {
7979
Map<String, Integer> namedGroups = matcher.pattern().namedGroups();
8080
// Collect entries by decoded Perl name so that duplicate-name
8181
// captures (e.g. `(?<y>a)|(?<y>b)`) merge into a single key.
82+
// Note: Java's Pattern.namedGroups() returns an unordered map
83+
// (ImmutableCollections.MapN), so we must explicitly sort each
84+
// bucket by group number so that the *leftmost* alternative
85+
// wins (Perl semantics for $+{name}).
8286
java.util.Map<String, java.util.List<String>> byPerlName = new java.util.LinkedHashMap<>();
8387
for (String name : namedGroups.keySet()) {
8488
if (CaptureNameEncoder.isInternalCapture(name)) {
@@ -87,6 +91,9 @@ public Set<Entry<String, RuntimeScalar>> entrySet() {
8791
String perlName = CaptureNameEncoder.decodeGroupName(name);
8892
byPerlName.computeIfAbsent(perlName, k -> new java.util.ArrayList<>()).add(name);
8993
}
94+
for (java.util.List<String> jns : byPerlName.values()) {
95+
jns.sort(java.util.Comparator.comparingInt(namedGroups::get));
96+
}
9097
for (Map.Entry<String, java.util.List<String>> e : byPerlName.entrySet()) {
9198
String perlName = e.getKey();
9299
java.util.List<String> javaNames = e.getValue();
@@ -288,6 +295,10 @@ private static java.util.List<String> collectJavaNamesFor(Map<String, Integer> n
288295
out.add(jn);
289296
}
290297
}
298+
// Java's Pattern.namedGroups() doesn't preserve insertion order, so sort
299+
// by group number to match Perl's source order. This makes `$+{name}`
300+
// return the *leftmost* alternative for duplicate-named captures.
301+
out.sort(java.util.Comparator.comparingInt(namedGroups::get));
291302
return out;
292303
}
293304

0 commit comments

Comments
 (0)