|
| 1 | +<!-- |
| 2 | + Licensed to the Apache Software Foundation (ASF) under one or more |
| 3 | + contributor license agreements. See the NOTICE file distributed with |
| 4 | + this work for additional information regarding copyright ownership. |
| 5 | + The ASF licenses this file to You under the Apache License, Version 2.0 |
| 6 | + (the "License"); you may not use this file except in compliance with |
| 7 | + the License. You may obtain a copy of the License at |
| 8 | +
|
| 9 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | +
|
| 11 | + Unless required by applicable law or agreed to in writing, software |
| 12 | + distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | + See the License for the specific language governing permissions and |
| 15 | + limitations under the License. |
| 16 | +--> |
| 17 | +# tika-metadata-schema |
| 18 | + |
| 19 | +A machine-readable schema of Apache Tika's metadata keys, plus a registry-driven validator |
| 20 | +(`MetadataKeyValidator`) that classifies any key as CLOSED / OPEN / TEMPLATE / UNKNOWN. |
| 21 | + |
| 22 | +**Scope: `tika-core` + the standard parser bundle.** The heavier/optional parser families |
| 23 | +(scientific, sqlite3, nlp, vlm) are *not* scanned — pulling their runtime deps (netcdf, grib, |
| 24 | +opennlp, DL4J, sqlite-jdbc) into a build-time schema module isn't worth it. Their keys are the only |
| 25 | +ones absent (e.g. `sqlite3:`, `vlm:`, `grib:`, `netcdf:`, `ctakes:`, `NER_`). `MetadataCoverageTest` |
| 26 | +enforces this: any module declaring keys that is neither scanned nor on its explicit out-of-scope |
| 27 | +list fails the build, so nothing escapes *silently*. |
| 28 | + |
| 29 | +## `metadata-keys.json` — the closed set (generated + gated) |
| 30 | +Every key Tika declares as a `Property` constant, plus the bounded digest cross-product |
| 31 | +(`X-TIKA:digest:<ALGORITHM>[:<ENCODING>]`, enumerated from `DigestDef`). Each record: |
| 32 | +`{ key, namespace, valueType, cardinality }`. |
| 33 | + |
| 34 | +**Generated, never hand-edited.** `SchemaGenerator` scans the parser classpath for classes that |
| 35 | +declare a `Property` field, force-loads them, reads the global `Property` table, and writes stable |
| 36 | +sorted JSON. `MetadataSchemaTest` regenerates in-memory and asserts it matches the committed file, so |
| 37 | +the registry can never drift from the declarations. |
| 38 | + |
| 39 | +Regenerate after adding/changing a `Property` **or** a `PassthroughPrefix` (writes both files): |
| 40 | +``` |
| 41 | +java -cp <tika-metadata-schema + deps classpath> \ |
| 42 | + org.apache.tika.metadata.schema.SchemaGenerator \ |
| 43 | + src/main/resources/org/apache/tika/metadata/metadata-keys.json \ |
| 44 | + src/main/resources/org/apache/tika/metadata/metadata-open-namespaces.json |
| 45 | +``` |
| 46 | + |
| 47 | +## `metadata-open-namespaces.json` — the open sets (generated + gated) |
| 48 | +The **prefixes** under which parsers mint file-controlled key names at runtime — names that are not |
| 49 | +`Property` constants, so the individual keys cannot be enumerated (scraped HTML `<meta>` under |
| 50 | +`html:`, OOXML `custom:`, email `Message:Raw-Header:`, Access `MDB_PROP:`, Vorbis comments, FLV |
| 51 | +attributes, unmapped image/XMP tags, …). Each record: `{ prefix, provenance, description }`. |
| 52 | + |
| 53 | +**Generated from the `PassthroughPrefix` declarations, never hand-edited.** Every such prefix is a |
| 54 | +registered `PassthroughPrefix` constant; `SchemaGenerator` reads that registry the same way it reads |
| 55 | +the `Property` table, and `MetadataSchemaTest` gates it identically. Adding a passthrough prefix in a |
| 56 | +parser and forgetting to regenerate fails the build. |
| 57 | + |
| 58 | +Not covered here: **templates** — parameterized key families like XMP `rdf:Alt` language variants |
| 59 | +`<base-key>:<lang>` (`dc:title:fr`), where the *suffix* rather than the prefix is open. These are |
| 60 | +documented by rule, not enumerated. |
| 61 | + |
| 62 | +## `metadata-string-keys.json` — legacy bare-String closed keys (curated + gated) |
| 63 | +A handful of closed keys predate `Property` and are still declared as bare `String` constants |
| 64 | +(`HttpHeaders.CONTENT_TYPE` = `Content-Type`, the `Content-*`/`Location` family, `Message-*` / |
| 65 | +`Multipart-*`, `tika:chunks`). They self-register nowhere, so the `Property` scan can't see them — |
| 66 | +yet Tika emits them constantly. Each record: `{ key, source }`. |
| 67 | + |
| 68 | +**Curated, but gated against the code:** `MetadataStringKeysTest` reflects each `source` constant |
| 69 | +(e.g. `HttpHeaders.CONTENT_TYPE`) and asserts its live value equals `key`, so a rename/retype/value |
| 70 | +change fails the build. The right long-term fix is to make these `Property` constants (then they'd |
| 71 | +move to `metadata-keys.json` automatically); that's a large, `Content-Type`-blast-radius change left |
| 72 | +for a future major release. |
| 73 | + |
| 74 | +## `MetadataKeyValidator` — the registry-driven lint |
| 75 | +Classifies any key by reading the three registries above (no parser classes needed): |
| 76 | +`CLOSED` (in `metadata-keys.json` or `metadata-string-keys.json`), `OPEN` (under a registered |
| 77 | +passthrough prefix), `TEMPLATE` (a `<closed-key>:<lang>` lang-alt instance), or `UNKNOWN` — a typo, |
| 78 | +an unregistered namespace, or a key nobody declared. This is the payoff the registries exist for: a |
| 79 | +data-driven legitimacy check instead of a hand-coded regex. |
| 80 | + |
| 81 | +Together the files describe the key space of the scanned bundle: closed keys are enumerated and |
| 82 | +gated (Property-backed and legacy-String alike); open namespaces are enumerated by prefix and gated; |
| 83 | +templates are described by rule; and `MetadataCoverageTest` guarantees no scanned-bundle module is |
| 84 | +silently missed. Keys from the out-of-scope families above are excluded by design. |
0 commit comments