This is an automated email from the ASF dual-hosted git repository.

tballison pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tika.git


The following commit(s) were added to refs/heads/main by this push:
     new c42b10873c add shim so that tika-eval works for 4.0.0 vs 4.0.0-beta-1 
(#2975)
c42b10873c is described below

commit c42b10873ce7cfede66a5315dc2ca2baba1a39c4
Author: Tim Allison <[email protected]>
AuthorDate: Wed Jul 29 21:38:48 2026 -0400

    add shim so that tika-eval works for 4.0.0 vs 4.0.0-beta-1 (#2975)
---
 .../org/apache/tika/eval/app/io/ExtractReader.java | 76 ++++++++++++++++++++++
 .../apache/tika/eval/app/io/ExtractReaderTest.java | 34 ++++++++++
 .../test-dirs/legacy/beta1-style.doc.json          | 17 +++++
 .../resources/test-dirs/legacy/collision.doc.json  |  7 ++
 4 files changed, 134 insertions(+)

diff --git 
a/tika-eval/tika-eval-app/src/main/java/org/apache/tika/eval/app/io/ExtractReader.java
 
b/tika-eval/tika-eval-app/src/main/java/org/apache/tika/eval/app/io/ExtractReader.java
index 26ff7f22cf..bac92b48e4 100644
--- 
a/tika-eval/tika-eval-app/src/main/java/org/apache/tika/eval/app/io/ExtractReader.java
+++ 
b/tika-eval/tika-eval-app/src/main/java/org/apache/tika/eval/app/io/ExtractReader.java
@@ -25,8 +25,10 @@ import java.nio.charset.StandardCharsets;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -159,6 +161,9 @@ public class ExtractReader {
         try {
             if (fileSuffixes.format == FileSuffixes.FORMAT.JSON) {
                 metadataList = JsonMetadataList.fromJson(reader);
+                for (Metadata m : metadataList) {
+                    normalizeLegacyKeys(m);
+                }
                 if (alterMetadataList.equals(ALTER_METADATA_LIST.FIRST_ONLY) 
&& metadataList.size() > 1) {
                     while (metadataList.size() > 1) {
                         metadataList.remove(metadataList.size() - 1);
@@ -214,6 +219,77 @@ public class ExtractReader {
 
     }
 
+    // Pre-4.0 extract key -> 4.0 key, for the Tika-native fields tika-eval 
reads. Digest keys are
+    // handled by the prefix rule in normalizeLegacyKeys; 
Content-Type/Content-Length are standard
+    // names (unchanged) so they are not listed. New-side keys come from the 
live constants so this
+    // can't drift from the 4.0 declarations.
+    private static final Map<String, String> LEGACY_KEY_MAP = Map.ofEntries(
+            Map.entry("X-TIKA:content", 
TikaCoreProperties.TIKA_CONTENT.getName()),
+            Map.entry("X-TIKA:content_handler", 
TikaCoreProperties.TIKA_CONTENT_HANDLER.getName()),
+            Map.entry("X-TIKA:embedded_depth", 
TikaCoreProperties.EMBEDDED_DEPTH.getName()),
+            Map.entry("X-TIKA:embedded_resource_path", 
TikaCoreProperties.EMBEDDED_RESOURCE_PATH.getName()),
+            Map.entry("X-TIKA:final_embedded_resource_path", 
TikaCoreProperties.FINAL_EMBEDDED_RESOURCE_PATH.getName()),
+            Map.entry("X-TIKA:parse_time_millis", 
TikaCoreProperties.PARSE_TIME_MILLIS.getName()),
+            Map.entry("X-TIKA:resourceName", 
TikaCoreProperties.RESOURCE_NAME_KEY.getName()),
+            Map.entry("X-TIKA:detectedEncoding", 
TikaCoreProperties.DETECTED_ENCODING.getName()),
+            Map.entry("X-TIKA:encodingDetector", 
TikaCoreProperties.ENCODING_DETECTOR.getName()),
+            Map.entry("Content-Type-Hint", 
TikaCoreProperties.CONTENT_TYPE_HINT.getName()),
+            Map.entry("embeddedResourceType", 
TikaCoreProperties.EMBEDDED_RESOURCE_TYPE.getName()),
+            Map.entry("X-TIKA:EXCEPTION:container_exception", 
TikaCoreProperties.CONTAINER_EXCEPTION.getName()),
+            Map.entry("X-TIKA:EXCEPTION:embedded_exception", 
TikaCoreProperties.EMBEDDED_EXCEPTION.getName()));
+
+    private static final String LEGACY_DIGEST_PREFIX = 
TikaCoreProperties.LEGACY_TIKA_META_PREFIX
+            + "digest" + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER;
+
+    /**
+     * Pre-4.0 extracts (e.g. 4.0.0-beta-1) key Tika-native fields under 
X-TIKA:/camelCase names.
+     * Normalize the fields tika-eval reads to their 4.0 tk: keys so a 
cross-version compare reflects
+     * real diffs, not the rename. Harmless on 4.0 extracts: the legacy keys 
are simply absent.
+     */
+    private static void normalizeLegacyKeys(Metadata m) {
+        m.setTrusted(true);   // sanctioned trusted transformation: may write 
reserved tk: keys
+        try {
+            for (Map.Entry<String, String> e : LEGACY_KEY_MAP.entrySet()) {
+                remapLegacyKey(m, e.getKey(), e.getValue());
+            }
+            // digest keys: X-TIKA:digest:<alg> -> tk:digest:<alg> (algorithm 
unchanged; MD5 drives
+            // embedded-doc matching). names() is a snapshot, so remapping 
while iterating is safe.
+            for (String name : m.names()) {
+                if (name.startsWith(LEGACY_DIGEST_PREFIX)) {
+                    remapLegacyKey(m, name, TikaCoreProperties.TIKA_META_PREFIX
+                            + 
name.substring(TikaCoreProperties.LEGACY_TIKA_META_PREFIX.length()));
+                }
+            }
+        } finally {
+            m.setTrusted(false);
+        }
+    }
+
+    private static void remapLegacyKey(Metadata m, String legacyKey, String 
modernKey) {
+        String[] legacyVals = m.getValues(legacyKey);
+        if (legacyVals.length == 0) {
+            return;
+        }
+        String[] modernVals = m.getValues(modernKey);
+        if (modernVals.length > 0) {
+            // Both present: safe only if identical. Fail loud rather than 
silently clobber a value.
+            if (!Arrays.equals(legacyVals, modernVals)) {
+                throw new IllegalStateException("Extract has both legacy key 
'" + legacyKey
+                        + "' and modern key '" + modernKey + "' with different 
values; legacy-key "
+                        + "normalization would clobber. Extract is 
inconsistent.");
+            }
+        } else {
+            for (int i = 0; i < legacyVals.length; i++) {
+                if (i == 0) {
+                    m.set(modernKey, legacyVals[i]);
+                } else {
+                    m.add(modernKey, legacyVals[i]);
+                }
+            }
+        }
+        m.remove(legacyKey);
+    }
+
     public enum ALTER_METADATA_LIST {
         AS_IS,  //leave the metadata list as is
         FIRST_ONLY, //take only the metadata list for the "container" document
diff --git 
a/tika-eval/tika-eval-app/src/test/java/org/apache/tika/eval/app/io/ExtractReaderTest.java
 
b/tika-eval/tika-eval-app/src/test/java/org/apache/tika/eval/app/io/ExtractReaderTest.java
index 8c33d42ffc..e164bfe4fa 100644
--- 
a/tika-eval/tika-eval-app/src/test/java/org/apache/tika/eval/app/io/ExtractReaderTest.java
+++ 
b/tika-eval/tika-eval-app/src/test/java/org/apache/tika/eval/app/io/ExtractReaderTest.java
@@ -18,6 +18,8 @@ package org.apache.tika.eval.app.io;
 
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.io.IOException;
 import java.nio.file.Path;
@@ -88,6 +90,38 @@ public class ExtractReaderTest extends TikaTest {
                 .get(TikaCoreProperties.TIKA_CONTENT));
     }
 
+    @Test
+    public void testLegacyKeyNormalization() throws Exception {
+        // a pre-4.0 (4.0.0-beta-1 style) extract: X-TIKA:/camelCase 
Tika-native keys
+        Path f = 
getResourceAsFile("/test-dirs/legacy/beta1-style.doc.json").toPath();
+        List<Metadata> list = new ExtractReader().loadExtract(f);
+        assertEquals(2, list.size());
+
+        Metadata container = list.get(0);
+        // readable via the 4.0 Property constants that tika-eval uses
+        assertEquals("the quick brown fox", 
container.get(TikaCoreProperties.TIKA_CONTENT));
+        assertEquals("12", 
container.get(TikaCoreProperties.PARSE_TIME_MILLIS));
+        assertEquals("boom", 
container.get(TikaCoreProperties.CONTAINER_EXCEPTION));
+        assertEquals("abc123", container.get("tk:digest:MD5"));
+        // the legacy keys are gone
+        assertNull(container.get("X-TIKA:content"));
+        assertNull(container.get("X-TIKA:digest:MD5"));
+
+        Metadata embedded = list.get(1);
+        assertEquals("inner.txt", 
embedded.get(TikaCoreProperties.EMBEDDED_RESOURCE_PATH));
+        assertEquals("1", embedded.get(TikaCoreProperties.EMBEDDED_DEPTH));
+        assertEquals("inner.txt", 
embedded.get(TikaCoreProperties.RESOURCE_NAME_KEY));
+        assertEquals("ATTACHMENT", 
embedded.get(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE));
+        assertNull(embedded.get("X-TIKA:embedded_resource_path"));
+    }
+
+    @Test
+    public void testLegacyNormalizationCollisionFailsLoud() throws Exception {
+        // both the legacy and modern content key present with different 
values -> must not clobber
+        Path f = 
getResourceAsFile("/test-dirs/legacy/collision.doc.json").toPath();
+        assertThrows(IllegalStateException.class, () -> new 
ExtractReader().loadExtract(f));
+    }
+
     @Test
     public void testTextBasic() throws IOException {
         ExtractReader extractReader = new ExtractReader();
diff --git 
a/tika-eval/tika-eval-app/src/test/resources/test-dirs/legacy/beta1-style.doc.json
 
b/tika-eval/tika-eval-app/src/test/resources/test-dirs/legacy/beta1-style.doc.json
new file mode 100644
index 0000000000..d8938f4a06
--- /dev/null
+++ 
b/tika-eval/tika-eval-app/src/test/resources/test-dirs/legacy/beta1-style.doc.json
@@ -0,0 +1,17 @@
+[
+  {
+    "Content-Type" : "text/plain",
+    "X-TIKA:content" : "the quick brown fox",
+    "X-TIKA:parse_time_millis" : "12",
+    "X-TIKA:digest:MD5" : "abc123",
+    "X-TIKA:EXCEPTION:container_exception" : "boom"
+  },
+  {
+    "Content-Type" : "text/plain",
+    "X-TIKA:embedded_resource_path" : "inner.txt",
+    "X-TIKA:content" : "attachment contents",
+    "X-TIKA:embedded_depth" : "1",
+    "X-TIKA:resourceName" : "inner.txt",
+    "embeddedResourceType" : "ATTACHMENT"
+  }
+]
diff --git 
a/tika-eval/tika-eval-app/src/test/resources/test-dirs/legacy/collision.doc.json
 
b/tika-eval/tika-eval-app/src/test/resources/test-dirs/legacy/collision.doc.json
new file mode 100644
index 0000000000..47c2b342e1
--- /dev/null
+++ 
b/tika-eval/tika-eval-app/src/test/resources/test-dirs/legacy/collision.doc.json
@@ -0,0 +1,7 @@
+[
+  {
+    "Content-Type" : "text/plain",
+    "X-TIKA:content" : "legacy value",
+    "tk:content" : "modern value"
+  }
+]

Reply via email to