Skip to content

Commit c42b108

Browse files
authored
add shim so that tika-eval works for 4.0.0 vs 4.0.0-beta-1 (#2975)
1 parent 39f035c commit c42b108

4 files changed

Lines changed: 134 additions & 0 deletions

File tree

tika-eval/tika-eval-app/src/main/java/org/apache/tika/eval/app/io/ExtractReader.java

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,10 @@
2525
import java.nio.file.Files;
2626
import java.nio.file.Path;
2727
import java.util.ArrayList;
28+
import java.util.Arrays;
2829
import java.util.List;
2930
import java.util.Locale;
31+
import java.util.Map;
3032
import java.util.regex.Matcher;
3133
import java.util.regex.Pattern;
3234

@@ -159,6 +161,9 @@ public List<Metadata> loadExtract(Path extractFile) throws ExtractReaderExceptio
159161
try {
160162
if (fileSuffixes.format == FileSuffixes.FORMAT.JSON) {
161163
metadataList = JsonMetadataList.fromJson(reader);
164+
for (Metadata m : metadataList) {
165+
normalizeLegacyKeys(m);
166+
}
162167
if (alterMetadataList.equals(ALTER_METADATA_LIST.FIRST_ONLY) && metadataList.size() > 1) {
163168
while (metadataList.size() > 1) {
164169
metadataList.remove(metadataList.size() - 1);
@@ -214,6 +219,77 @@ private List<Metadata> generateListFromTextFile(Reader reader, FileSuffixes file
214219

215220
}
216221

222+
// Pre-4.0 extract key -> 4.0 key, for the Tika-native fields tika-eval reads. Digest keys are
223+
// handled by the prefix rule in normalizeLegacyKeys; Content-Type/Content-Length are standard
224+
// names (unchanged) so they are not listed. New-side keys come from the live constants so this
225+
// can't drift from the 4.0 declarations.
226+
private static final Map<String, String> LEGACY_KEY_MAP = Map.ofEntries(
227+
Map.entry("X-TIKA:content", TikaCoreProperties.TIKA_CONTENT.getName()),
228+
Map.entry("X-TIKA:content_handler", TikaCoreProperties.TIKA_CONTENT_HANDLER.getName()),
229+
Map.entry("X-TIKA:embedded_depth", TikaCoreProperties.EMBEDDED_DEPTH.getName()),
230+
Map.entry("X-TIKA:embedded_resource_path", TikaCoreProperties.EMBEDDED_RESOURCE_PATH.getName()),
231+
Map.entry("X-TIKA:final_embedded_resource_path", TikaCoreProperties.FINAL_EMBEDDED_RESOURCE_PATH.getName()),
232+
Map.entry("X-TIKA:parse_time_millis", TikaCoreProperties.PARSE_TIME_MILLIS.getName()),
233+
Map.entry("X-TIKA:resourceName", TikaCoreProperties.RESOURCE_NAME_KEY.getName()),
234+
Map.entry("X-TIKA:detectedEncoding", TikaCoreProperties.DETECTED_ENCODING.getName()),
235+
Map.entry("X-TIKA:encodingDetector", TikaCoreProperties.ENCODING_DETECTOR.getName()),
236+
Map.entry("Content-Type-Hint", TikaCoreProperties.CONTENT_TYPE_HINT.getName()),
237+
Map.entry("embeddedResourceType", TikaCoreProperties.EMBEDDED_RESOURCE_TYPE.getName()),
238+
Map.entry("X-TIKA:EXCEPTION:container_exception", TikaCoreProperties.CONTAINER_EXCEPTION.getName()),
239+
Map.entry("X-TIKA:EXCEPTION:embedded_exception", TikaCoreProperties.EMBEDDED_EXCEPTION.getName()));
240+
241+
private static final String LEGACY_DIGEST_PREFIX = TikaCoreProperties.LEGACY_TIKA_META_PREFIX
242+
+ "digest" + TikaCoreProperties.NAMESPACE_PREFIX_DELIMITER;
243+
244+
/**
245+
* Pre-4.0 extracts (e.g. 4.0.0-beta-1) key Tika-native fields under X-TIKA:/camelCase names.
246+
* Normalize the fields tika-eval reads to their 4.0 tk: keys so a cross-version compare reflects
247+
* real diffs, not the rename. Harmless on 4.0 extracts: the legacy keys are simply absent.
248+
*/
249+
private static void normalizeLegacyKeys(Metadata m) {
250+
m.setTrusted(true); // sanctioned trusted transformation: may write reserved tk: keys
251+
try {
252+
for (Map.Entry<String, String> e : LEGACY_KEY_MAP.entrySet()) {
253+
remapLegacyKey(m, e.getKey(), e.getValue());
254+
}
255+
// digest keys: X-TIKA:digest:<alg> -> tk:digest:<alg> (algorithm unchanged; MD5 drives
256+
// embedded-doc matching). names() is a snapshot, so remapping while iterating is safe.
257+
for (String name : m.names()) {
258+
if (name.startsWith(LEGACY_DIGEST_PREFIX)) {
259+
remapLegacyKey(m, name, TikaCoreProperties.TIKA_META_PREFIX
260+
+ name.substring(TikaCoreProperties.LEGACY_TIKA_META_PREFIX.length()));
261+
}
262+
}
263+
} finally {
264+
m.setTrusted(false);
265+
}
266+
}
267+
268+
private static void remapLegacyKey(Metadata m, String legacyKey, String modernKey) {
269+
String[] legacyVals = m.getValues(legacyKey);
270+
if (legacyVals.length == 0) {
271+
return;
272+
}
273+
String[] modernVals = m.getValues(modernKey);
274+
if (modernVals.length > 0) {
275+
// Both present: safe only if identical. Fail loud rather than silently clobber a value.
276+
if (!Arrays.equals(legacyVals, modernVals)) {
277+
throw new IllegalStateException("Extract has both legacy key '" + legacyKey
278+
+ "' and modern key '" + modernKey + "' with different values; legacy-key "
279+
+ "normalization would clobber. Extract is inconsistent.");
280+
}
281+
} else {
282+
for (int i = 0; i < legacyVals.length; i++) {
283+
if (i == 0) {
284+
m.set(modernKey, legacyVals[i]);
285+
} else {
286+
m.add(modernKey, legacyVals[i]);
287+
}
288+
}
289+
}
290+
m.remove(legacyKey);
291+
}
292+
217293
public enum ALTER_METADATA_LIST {
218294
AS_IS, //leave the metadata list as is
219295
FIRST_ONLY, //take only the metadata list for the "container" document

tika-eval/tika-eval-app/src/test/java/org/apache/tika/eval/app/io/ExtractReaderTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919

2020
import static org.junit.jupiter.api.Assertions.assertEquals;
21+
import static org.junit.jupiter.api.Assertions.assertNull;
22+
import static org.junit.jupiter.api.Assertions.assertThrows;
2123

2224
import java.io.IOException;
2325
import java.nio.file.Path;
@@ -88,6 +90,38 @@ public void testBasic() throws Exception {
8890
.get(TikaCoreProperties.TIKA_CONTENT));
8991
}
9092

93+
@Test
94+
public void testLegacyKeyNormalization() throws Exception {
95+
// a pre-4.0 (4.0.0-beta-1 style) extract: X-TIKA:/camelCase Tika-native keys
96+
Path f = getResourceAsFile("/test-dirs/legacy/beta1-style.doc.json").toPath();
97+
List<Metadata> list = new ExtractReader().loadExtract(f);
98+
assertEquals(2, list.size());
99+
100+
Metadata container = list.get(0);
101+
// readable via the 4.0 Property constants that tika-eval uses
102+
assertEquals("the quick brown fox", container.get(TikaCoreProperties.TIKA_CONTENT));
103+
assertEquals("12", container.get(TikaCoreProperties.PARSE_TIME_MILLIS));
104+
assertEquals("boom", container.get(TikaCoreProperties.CONTAINER_EXCEPTION));
105+
assertEquals("abc123", container.get("tk:digest:MD5"));
106+
// the legacy keys are gone
107+
assertNull(container.get("X-TIKA:content"));
108+
assertNull(container.get("X-TIKA:digest:MD5"));
109+
110+
Metadata embedded = list.get(1);
111+
assertEquals("inner.txt", embedded.get(TikaCoreProperties.EMBEDDED_RESOURCE_PATH));
112+
assertEquals("1", embedded.get(TikaCoreProperties.EMBEDDED_DEPTH));
113+
assertEquals("inner.txt", embedded.get(TikaCoreProperties.RESOURCE_NAME_KEY));
114+
assertEquals("ATTACHMENT", embedded.get(TikaCoreProperties.EMBEDDED_RESOURCE_TYPE));
115+
assertNull(embedded.get("X-TIKA:embedded_resource_path"));
116+
}
117+
118+
@Test
119+
public void testLegacyNormalizationCollisionFailsLoud() throws Exception {
120+
// both the legacy and modern content key present with different values -> must not clobber
121+
Path f = getResourceAsFile("/test-dirs/legacy/collision.doc.json").toPath();
122+
assertThrows(IllegalStateException.class, () -> new ExtractReader().loadExtract(f));
123+
}
124+
91125
@Test
92126
public void testTextBasic() throws IOException {
93127
ExtractReader extractReader = new ExtractReader();
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[
2+
{
3+
"Content-Type" : "text/plain",
4+
"X-TIKA:content" : "the quick brown fox",
5+
"X-TIKA:parse_time_millis" : "12",
6+
"X-TIKA:digest:MD5" : "abc123",
7+
"X-TIKA:EXCEPTION:container_exception" : "boom"
8+
},
9+
{
10+
"Content-Type" : "text/plain",
11+
"X-TIKA:embedded_resource_path" : "inner.txt",
12+
"X-TIKA:content" : "attachment contents",
13+
"X-TIKA:embedded_depth" : "1",
14+
"X-TIKA:resourceName" : "inner.txt",
15+
"embeddedResourceType" : "ATTACHMENT"
16+
}
17+
]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[
2+
{
3+
"Content-Type" : "text/plain",
4+
"X-TIKA:content" : "legacy value",
5+
"tk:content" : "modern value"
6+
}
7+
]

0 commit comments

Comments
 (0)