A superset of WebObjects' .api format adding per-element and per-binding documentation, binding types, directionality, and value interpretation — enough to render an element's API in an editor. Proposal stage; in use here. The element reference renders entirely from .apiext files.
The current/old .api declares an element's bindings — names, a few flags, cross-binding validation — and little to nothing about what a binding means or it's typing. .apiext adds that as a strict superset: existing .api files validate unchanged, and the new information is additive. The WO runtime does not read .api; only tooling does (WOLips, Parslips), so a successor format is constrained only by "don't break existing files".
Same skeleton as .api (<wodefinitions> / <wo> / <binding> / <validation>); extensions in green:
<wodefinitions> <wo class="WOCheckBox" wocomponentcontent="false" passthrough="true"> <doc><![CDATA[A checkbox. Use **either** `checked` or `value`+`selection`.]]></doc> <binding name="checked"> <pull><type interpretation="truthy">java.lang.Object</type></pull> <push><type>java.lang.Boolean</type></push> <doc>The checked state.</doc> </binding> <validation message="A WOCheckBox uses either 'checked' or 'value', not both."> <bound name="checked"/> <bound name="value"/> </validation> </wo> </wodefinitions>
Each element and binding may carry a <doc>. Bodies use an injection-safe Markdown subset (inline `code`, **bold**, *italic*, links, fenced code blocks); a plain consumer can show the raw text.
A binding declares one or more <type> — a fully-qualified Java class (or, later, a value-set name). Multiple types mean "accepts any of these" (e.g. updateContainerID: String or List). Intended for validation, not only display.
<pull> / <push>A binding's type can differ by direction: a checkbox's checked pulls any value (read as a boolean) and pushes back a Boolean. The type lives inside a direction block; the presence of <pull>/<push> declares the direction (no separate direction= attribute).
<pull> → read.<push> → written back.Only type is direction-specific; name, doc, and required stay on the binding. A <type> is always inside a direction block, never a direct child of <binding>.
interpretation="truthy"Some bindings read a value by a coercion rule, not by type. WOConditional.condition accepts anything but reads it as a boolean (null, 0, false → false). The interpretation attribute names that rule without changing the type: <type interpretation="truthy">java.lang.Object</type>. The type remains the validatable constraint (Object); the interpretation is shown as a qualifier, Object (truthy).
The format describes a binding's contract, not the runtime. A type that is only determined at runtime is declared as its widest honest type. Example: WOTextField.value pushes a String, Number, or NSTimestamp depending on the bound formatter (anything, for a custom one), so its push type is declared Object, with the detail in <doc>.
| Capability | Status |
|---|---|
| per-element / per-binding doc (Markdown) | present |
| typed bindings (multi-type) | present (declared; enforcement is future work) |
| required, cross-binding validation | present (verbatim from .api) |
| directionality (pull / push) | present |
| value interpretation (truthiness) | present |
| default values, deprecation, value-sets, unknown-binding policy | not yet |
Validates as a superset of WO's DTD (including WebObjectsDefinitions.xml). The format spec — grammar (apiext.dtd), field-by-field reference, examples, and status — lives in its own repository: github.com/undur/apiext-format.