1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
package Torello.Browser;

import Torello.Java.ReadOnly.ReadOnlyList;
import Torello.Java.StringParse;
import Torello.Java.UnreachableError;

import java.util.function.IntFunction;

class ParamRec
{
    // ********************************************************************************************
    // ********************************************************************************************
    // Fields
    // ********************************************************************************************
    // ********************************************************************************************


    // Maximum width used when wrapping the generated diagnostic text.
    private static final int MAX_LINE_LINE = 100;

    // Accumulates the text generated during one complete toString pass.
    final StringBuilder sb = new StringBuilder();

    // The object currently being printed; either a BaseType<?> or an AbstractBuilder<?> subclass.
    final Object THIS;

    // Descriptor metadata for either class fields or command/type builder parameters.
    final AbstractDescriptor descriptor;


    // Retrieves an actual value using an index
    // BaseType         => value of a field (the 'ith' field)
    // CommandBuilders  => value assigned to a method parameter (the 'ith' parameter)
    // TypeBuilder      => value assigned to a field (the 'ith' field)

    final IntFunction<Object> getValue;


    // Number of descriptor entries:
    // BaseType         => number of fields,
    // CommandBuilders  => number of method parameters
    // TypeBuilder      => number of field-assignments

    final int NUM;


    // BaseType         => BaseType<Instance>.getClass()
    // CommandBuilders  => CommandBuilder.class
    // TypeBuilder      => TypeBuilder.class

    final Class<?> clazz;

    // Current indentation depth, normally increased in multiples of four spaces.
    final int CURRENT_INDENT;

    // Padding width used to align printed field or parameter names.
    final int NAME_PADDING;

    // Remaining first-line width passed to StrPrint.wrapToIndentationPlus().
    final int VAL_PRINT_LEN;

    // Line width available after current indentation.
    final int MAX_LINE_LEN;

    // Cached indentation string: StringParse.nChars(' ', CURRENT_INDENT).
    final String INDENT;


    // Header printed above this block:
    // BaseType input will print:       RunTime.ObjectPreview
    // TypeBuilder input will print:    TypeBuilder for RunTime.ObjectPreview
    // CommandBuilder input will print: CommandBuilder for RunTime.evaluate

    final String printedName;


    // ********************************************************************************************
    // ********************************************************************************************
    // Constructors
    // ********************************************************************************************
    // ********************************************************************************************


    ParamRec(final BaseType<?> bt)
    { this(bt, true, bt.getDescriptor(), 4); }

    ParamRec(final AbstractBuilder<?> ab)
    { this(ab, false, ab.descriptor, 4); }

    ParamRec(final BaseType<?> bt, final ParamRec rec)
    { this(bt, true, bt.getDescriptor(), rec.CURRENT_INDENT + 4); }

    private ParamRec(
            final Object                THIS,
            final boolean               baseType_OR_AbstractBuilder,
            final AbstractDescriptor    descriptor,
            final int                   CURRENT_INDENT
        )
    {
        this.THIS           = THIS;
        this.descriptor     = descriptor;
        this.NUM            = descriptor.names.size();
        this.clazz          = THIS.getClass();
        this.CURRENT_INDENT = CURRENT_INDENT;
        this.NAME_PADDING   = getMaxNameLen(descriptor.names);
        this.VAL_PRINT_LEN  = MAX_LINE_LINE - this.NAME_PADDING - CURRENT_INDENT;
        this.MAX_LINE_LEN   = MAX_LINE_LINE - CURRENT_INDENT;
        this.INDENT         = StringParse.nChars(' ', CURRENT_INDENT);


        // I spent at least 3 hours on this string stuff today.  These comments are essentialy
        // a summary of what I did today.  They are really good, though!  Thanks Chat-GPT!
        // 
        // "printedName" is the human-readable header shown at the top of the
        // generated diagnostic block.
        //
        // BaseType instances print using their nested CDP type name:
        //
        //     RunTime.ObjectPreview
        //
        // Builder instances print using:
        //
        //     TypeBuilder for RunTime.ObjectPreview
        //     CommandBuilder for RunTime.evaluate
        //
        // This distinction exists purely for diagnostics/debugging readability.

        this.printedName = baseType_OR_AbstractBuilder
            ? CDPToString.className(this.clazz)
            : THIS.getClass().getSimpleName() + " for " + descriptor.domain + "." + descriptor.name;


        // The "getValue" function-pointer abstracts the only meaningful behavioral
        // difference between:
        //
        //     BaseType<?>
        //     AbstractBuilder<?>
        //
        // BaseType values are retrieved reflectively from public fields.
        //
        // Builder values are retrieved directly from the internal assignments[] array.
        //
        // The remainder of the formatting pipeline is identical for both pathways.

        this.getValue = baseType_OR_AbstractBuilder
            ? this::getValueBaseType
            : this::getValueAbstractDescriptor;
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Helper
    // ********************************************************************************************
    // ********************************************************************************************


    private Object getValueBaseType(final int i)
    {
        try
            { return this.clazz.getField(this.descriptor.names.get(i)).get(this.THIS); }

        catch (NoSuchFieldException e)
            { throw new UnreachableError(); }

        catch (IllegalAccessException e)
            { throw new UnreachableError(); }
    }

    @SuppressWarnings("unchecked")
    private Object getValueAbstractDescriptor(final int i)
    { return ((AbstractBuilder) this.THIS).assignments[i]; }

    private static int getMaxNameLen(final ReadOnlyList<String> names)
    {
        int maxNameLen  = 0;

        for (final String name : names)
            if (name.length() > maxNameLen)
                maxNameLen = name.length();

        return maxNameLen + 2; // ": ".length() => 2
    }

}