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
package Torello.Java.Additional;

import Torello.Java.StrCSV;
import Torello.Java.StrIndent;
import Torello.Java.StringParse;

class MultiTypeToString 
{
    private static final String[] fieldNames2To5 =
    { "a", "b", "c", "d", "e" };

    private static final String[] fieldNames6To8 =
    { "a1", "b2", "c3", "d4", "e5", "f6", "g7", "h8" };

    @SuppressWarnings("rawtypes")
    public static String run(final Object[] fields, final int n)
    {
        // All MultiType / Tuple implementations return their fields as an array.
        // Object[] fields = asArray();
        // 
        // whatever subclass this instance is, this is actually just the number of fields
        // final int n = n();

        // Tells the output-printing mechanism when/if one of the fields is an array.
        final boolean[] isArray = new boolean[n];

        // These will hold the "Simple Name's" of each class/type in the previous array.
        final String[] types = new String[n];


        // The fields are named 'a ... e', unless N is 6, 7, or 8.  In that case the fields are
        // named 'a1 ... h8'

        final String[] FIELD_NAMES = (n < 6) ? fieldNames2To5 : fieldNames6To8;

        // This will hold the returned java.lang.String that is provided by this method call.
        final StringBuilder sb = new StringBuilder();

        // Simple Loop Variables
        int i=0, maxLen=0;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Loop merely retrieves the TYPE/CLASS of each field as a STRING (and if it is an array)
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        for (final Object field : fields)
        {
            // If the field is non-null, retrieving the class is easy, otherwise, it isn't-possible
            // because of GENERIC-ERASURE

            if (field != null)
            {
                final Class c   = field.getClass();
                types[i]        = c.getSimpleName();
                isArray[i]      = c.isArray();
            }

            else
            {
                types[i]    = "<GENERIC-ERASURE>";  // Smoke 'em if you got 'em
                isArray[i]  = false;                // DUMMY-VALUE
            }


            // These String's are pretty-printed with right-space-pad.  This computes the padding.
            if (types[i].length() > maxLen) maxLen = types[i].length();

            i++;
        }

        // Formatting: the '+2' adds two space-characters to the output.  These spaces occur
        // *AFTER* the Type/Class is printed to the output.

        maxLen += 2;

        i=0;
        for (final Object field : fields)
        {
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // This print's the NAME & TYPE of the Field - For Example: "Ret6.a1: String"
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            final String line = 
                "Ret" + n + "." + FIELD_NAMES[i] + ":  " +
                StringParse.rightSpacePad(types[i], maxLen);

            sb.append(line);


            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // This simply prints the VALUE of the Field.  For arrays, "extra-care is provided"
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

            if (field != null)
            {
                final String s = isArray[i]
                    ? toArrayString(field, types[i])
                    : field.toString();

                if (s.indexOf('\n') != -1)
                    sb.append('\n' + StrIndent.indent(s, 4));

                else if (line.length() + s.length() > 70)
                    sb.append('\n' + StrIndent.indent(s, 4));

                else
                    sb.append(s);
            }

            else
                sb.append("null");

            sb.append('\n');
            i++;
        }

        return sb.toString();
    }


    // This is a big thing that prints array on a "Best Efforts" case.
    // This is both "The Whole Value" of the 'toString' method, and also the problem-issue

    private static String toArrayString(final Object o, String classAsStr)
    {
        final int pos   = classAsStr.indexOf("[");
        final int c     = StringParse.countCharacters(classAsStr, '[');

        classAsStr = classAsStr.substring(0, pos);

        if (c == 1) switch (classAsStr)
        {
            case "byte"     : return StrCSV.toCSV((byte[])      o, null, 70);
            case "short"    : return StrCSV.toCSV((short[])     o, null, 70);
            case "int"      : return StrCSV.toCSV((int[])       o, null, 70);
            case "long"     : return StrCSV.toCSV((long[])      o, null, 70);
            case "float"    : return StrCSV.toCSV((float[])     o, null, 70);
            case "double"   : return StrCSV.toCSV((double[])    o, null, 70);
            case "char"     : return StrCSV.toCSV((char[])      o, null, 70);
            case "boolean"  : return StrCSV.toCSV((boolean[])   o, null, 70);
            default         : return StrCSV.toCSV((Object[]) o, false, true, 70);
        }

        if (c == 2) switch (classAsStr)
        {
            case "byte"     : return StrCSV.toCSV((byte[][])    o, null, null, true, 70, 4);
            case "short"    : return StrCSV.toCSV((short[][])   o, null, null, true, 70, 4);
            case "int"      : return StrCSV.toCSV((int[][])     o, null, null, true, 70, 4);
            case "long"     : return StrCSV.toCSV((long[][])    o, null, null, true, 70, 4);
            case "float"    : return StrCSV.toCSV((float[][])   o, null, null, true, 70, 4);
            case "double"   : return StrCSV.toCSV((double[][])  o, null, null, true, 70, 4);
            case "char"     : return StrCSV.toCSV((char[][])    o, null, null, true, 70, 4);
            case "boolean"  : return StrCSV.toCSV((boolean[][]) o, null, null, true, 70, 4);
            default         : return StrCSV.toCSV((Object[][])  o, null, null, true, 70, 4);
        }

        return "<" + c + "> dimensional array, \"toString\" not provided";
    }
}