Classfile API proposal to integrate basic print functionality directly to ClassModel and MethodModel
Adam Sotona
adam.sotona at oracle.com
Tue Jul 26 08:19:40 UTC 2022
We are building structured documents with focus on visual aspect, which makes them human readable. Such documents are very complex trees with zillions of combinations of various branch joints.
These six classes (records) represent careful selection of such joints, so their combination (hopefully) covers all our needs for visually appearing class printing in three major structured text formats.
Asymmetry of the joins exactly delimits permitted combinations. I didn’t validate it by test yet, however I expect any possible combination of Value, ValueList, Mapping, BlockMapping, BlockList and Comment will always render into valid (and nicely styled) document in all three formats.
I’ve been evaluating another two approaches:
1. Separate data from formatting , so data can be hold in Map-of-Maps, while each mapping will be attributed by a single Enum describing the formatting. However I found no simple way how to attribute LinkedHashMap (without implementing custom keys, values, entries or the whole Map). And also generic Map<String, Object> is not very specific API, the whole code restrictions would have to move to Javadoc. Also implementation of each Printer will become a nightmare.
2. Produce generic data as Map-of-Maps without any formatting attributes and match it by each printer with specific “style” descriptor (matching by keys). However beside the same problems as in #1 it also ignores the fact the data must be produce with formatting in mind, or the printers code will be one big hell and there will be no way how to cover the combinations by tests.
I’m not sure how using generics can reduce actual number of 6 classes (records).
Actual switch expression in each printer have exactly 6 cases to handle. When using generics we would need additional API and sub-switch to determine differences.
Theoretically we can make the tree a bit more square and add an Enum parameter to determine difference between Mapping, BlockMapping, ValueList and BlockList (for example public record Mapping(String key, ShapeEnum shape, List<Printables>)), however it will more than double possible combinations of the joins. Each combination must be covered in each printer or to exclude in runtime and document in Javadoc and provided with a test.
To be more specific about the actual classes:
Mapping accepts only fragments, because it must render as single line and for example in XML it renders as single element with attributes.
It can hold single values or list of values, where the list is rendered into a single attribute value.
It cannot hold another Mapping because we cannot embed XML element into an attribute (so that is we it implements printable).
Rendering multiple elements on a single line is still valid XML document, however far from human readability.
Mapping can represent a Fragment in JSON and in YAML, however XML throws an axe into that possibility.
BlockMapping is the most powerful (multi-line indenting) joint, able to nest and render correctly another BlockMapping as well as any other Printable or Fragment.
ValueList is restricted to leaf values (Strings, quoted String, numbers), because it is simple in all three formats. Construction of generic List of Fragments will require printers to render tons of other joint combinations, which we simply do not need.
BlockList makes sense only in combination with BlockMappings. We would like to avoid BlockList of BlockLists as multi-level unnamed lists do not make much sense. Also rendering lists with any other joints is useless. If you have list of classes, list of methods, or list of fields – it does not make sense to put any other Fragment, BlockList or Comment in between them. Theoretically we can replace BlockList with ListOfBlockMaps(String key, String mapsKey, List<List<Printable>> printables), however that does not make anything easier nor smaller.
From: Brian Goetz <brian.goetz at oracle.com>
Date: Monday, 25 July 2022 19:59
To: Adam Sotona <adam.sotona at oracle.com>
Cc: classfile-api-dev at openjdk.org <classfile-api-dev at openjdk.org>
Subject: Re: Classfile API proposal to integrate basic print functionality directly to ClassModel and MethodModel
If "block" means multi-line, and non-block means single-line, I'm confused as to why we have
public record Mapping(String key, List<Fragment> fragments) implements Printable {}
but
public record BlockMapping(String key, List<Printable> printables) implements Printable {}
where one has fragments and the other only has Printables. Similarly, we have
public record ValueList(String key, List<ConstantDesc> values) implements Fragment {}
public record BlockList(String key, List<BlockMapping> blockMappings) implements Printable {}
where again the List element differs. Is there no way to make the block/non-block orthogonal to the payload type? That would allow us to replace the two BlockXxx(...) with different payloads, with a Block<T> wrapper as a formatting hint.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/classfile-api-dev/attachments/20220726/6c172f0b/attachment-0001.htm>
More information about the classfile-api-dev
mailing list