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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
package Torello.Browser.JsonAST;

import static Torello.Java.C.BRED;
import static Torello.Java.C.BCYAN;
import static Torello.Java.C.RESET;

import Torello.Java.StorageWriter;
import Torello.Java.ReadOnly.ReadOnlyList;
import Torello.Java.ReadOnly.ReadOnlyArrayList;

/**
 * After parsing both API Spec-Files, each and every Json Property namem {@code "ref$"} needs to 
 * have it's {@code String} converted into a genuine {@link TCE} instance; this process is 
 * called <B>'Linking'</B> and it is identical to the linkng that the Java Compiler {@code javac}
 * performs when converting variables &amp; fields inside source files into class files.
 * 
 * <EMBED CLASS='external-html' DATA-FILE-ID=LINKER>
 */
@Torello.JavaDoc.Annotations.StaticFunctional
@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="LINKER_JDHBI")
public class Linker
{
    private Linker() { }

    static void run(
            final StorageWriter sw,
            final API           browser,
            final API           js
        )
    {
        sw.println(BCYAN + "Run Linker on all Entities" + RESET);
        Helper$FindDomain.initialize(browser, js);

        // Primarily these two invocations are really quite crucial.
        // These two lines set the fields "reference" and "referenceArray" for all PPR instances.

        linkAllAPI(js);
        linkAllAPI(browser);

        sw.println(BRED + "DONE" + RESET + '\n');


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Setting a PPR's "Computed Type as String" (and "as Byte")
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // Of a "high importance" is remembering that the "Computed Type as String", and also the 
        // internal field "Computed Type as Byte" simply *MAY NOT BE SET* inside of class 'PPR'
        // *UNTIL* the fields 'PPR.reference' and 'PPR.referenceArray' have been set.
        // 
        // There are cases where the "Computed Type" will be something simple such as
        // 'java.lang.String',  However, there are also cases where the "Computed Type" of a PPR
        // (Method-[P]arameter, Method-[R]eturns, or Type-[P]roperty) will be a reference to a type
        // defined by CDP itself. 
        // 
        // The PPR's "Computed Type", when it is "STRING", could easily be converted to 
        // 'java.lang.String' when the PPR is constructed (and, therefore, done inside the
        // constructor).  Howver, the "Computed Type" is only *SOMETIMES* a simple or standard Java
        // type like 'String' or 'int'.  There are also situations where a PPR's computed type is 
        // some kind of actual Java POJO / Class.  (Maybe the "Computed Type" is
        // 'Network.AssociatedCookie', did you ever think of that one?  Or maybe the PPR is:
        // 'DOM.DetachedElementInfo')
        // 
        // In those cases, thee "Computed Type as a String" cannot be done until *BOTH* its fields 
        // 'PPR.reference' and 'PPR.referenceArray' have been set.
        // 
        // Note that the three lines directly above *THIS COMMENT* are doing exactly that!
        // They are setting the PPR.reference & PPR.referenceArray fields!
        // 
        // THUSLY... AND MOST IMPORTANTLY ... THESE THREE LINES (BELOW) *MUST* COME *AFTER* THE
        // PREVIOUS THREE LINES.
        // 
        // This concept has been encoded / hardcoded into an error-check & throw thingy that I added
        // into the methods PPR.setCTAS(), PPR.setReference(), and PPR.setReferenceArray().

        sw.println(BCYAN + "Generate CTAS for All PPR's" + RESET);
        PPR_CTAS.iterateSetCTAS(js);
        PPR_CTAS.iterateSetCTAS(browser);

        
        // NOTE: class TypeNode also has a "CTAS" ad a "CTAB" field.  However, with Type's, there 
        //       is a zero percent chance for a "type" to collapse into another CDP "type"
        // 
        // That means that the CTAS / CTAB can be computed inside of the constructor...
        // 
        // This is because they don't need to be "linked" in a way where the value needs to be
        // computed after all of the constructors have completed building the POJO's...
        // 
        // That meanns the CTAS / CTAB fields can actually be declared 'final' inside the class
        // definitions themselves.
        // 
        // That means "linking TypeNode's" doesn't actually happen.  Only PPR's need to go through
        // this literal hell.

        sw.println(BRED + "DONE" + RESET);
    }

    private static void linkAllAPI(final API api)
    { for (final Domain d : api.domains) linkDomain(d); }

    private static void linkDomain(final Domain DOMAIN)
    {
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // The 'Domain.depAsStrs' ('dependencies' array).  Unused, but accounted for
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // Domain's MIGHT / POSSIBLY have a 'dependencies' String Array.
        // If they do, convert the dependency as a STRING to a REFERENCE.
        // 
        // NOTE: this 'dependencies' is not used anywhere in the code of my Code-Generator
        //       The only reason I'm "linking" this Domain.dependencies field is because it exists,
        //       and perhaps one day me or somebody else will find a use for it.  For now, this is
        //       extracted out of the Domain's JSON Definition, but it is totally ignored and 
        //       unused!
        // 
        // Afterwards, there is some simple error checking.  It just ascertains whether or not the
        // linker code above, actually succeded in linking all of the `Domain.depAsStrs` Strings
        // from 'java.lang.String' into instances of 'JsonAST.Domain'
        // 
        // MOST IMPORTANT NOTE OF ALL:
        //      This little loop and this little check are completely superfluous.  These fields
        //      are not used anywhere by the AST (this package - Torello.Browser.JsonAST), nor are
        //      they used by the Code Generator classes (Torello.Browser.CodeGenerator)
        // 
        //      Inside of the '.json' files which define this API, each Domain has a short little
        //      list of "other domains" upon which "this domain" depends.  It is essentially 
        //      identical to a class in 'java.util' - for instance 'java.util.ArrayList' doing an
        //      import of somethign from 'java.util.regex' (or something like that)
        // 
        //      The only issue is, because Google Chrome and the Browser treat each "Domain" as a 
        //      "separate Package" and because I place all domains from a single API into the same
        //      exact Java Package - I neither know nor care what is in the field
        //      'Domain.dependencies'
        // 
        //      Link it from a String to reference.  Get it over with.  Fix it and forget it.  
        //      Perhaps one day this will be used, but that day is not today.

        final ReadOnlyList<Domain> dependencies = new ReadOnlyArrayList<Domain>(
            DOMAIN.depAsStrs,
            (String s) -> Helper$FindDomain.find(s),
            DOMAIN.depAsStrs.size()
        );

        for (int i=0; i < dependencies.size(); i++)
            if (dependencies.get(i) == null)
                throw new Error("Unknown Domain Dependency: " + DOMAIN.depAsStrs.get(i));

        DOMAIN.setDependencies(dependencies);


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Now Iterate all of the TCE's in this Domain, and run the linker on each of them
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        for (final TypeNode type : DOMAIN.types)
            if (type.properties != null)
                for (final PPR field : type.properties)
                    linkAllPPR(field);

        for (final EventNode type : DOMAIN.events)
            if (type.parameters != null)
                for (final PPR field : type.parameters)
                    linkAllPPR(field);

        for (final CommandNode  command : DOMAIN.commands)
        {
            if (command.parameters != null)
                for (final PPR param : command.parameters) linkAllPPR(param);

            if (command.returns != null)
                for (final PPR retVal : command.returns) linkAllPPR(retVal);

            if (command.redirect != null) linkCommandNodeRedirect(command);
        }
    }


    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // CommandNode "redirect" (**COMPLETELY UNUSED** in my code - but maintained / accounted for)
    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // 
    // IF YOU THINK THE "Domain.dependencies" field is completely ignored, unused and serves no
    // purpose in my code.
    // 
    // You should try laying around and wondering  whether or not the "redirectDomain" has any 
    // bearing on the Code Generator...  Guess what?  The answer is "NO!"
    // 
    // It is really *REALLY* easy to link this "Domain as a String" into a "Domain as a Reference"
    // or "pointer".  So I go ahead and do that here.  Do I use it? ...  No way!

    private static void linkCommandNodeRedirect(final CommandNode cn)
    {
        // Resolve 'redirect' from a java.lang.String, into a 'Domain' reference.
        final Domain redirectDomain = Helper$FindDomain.find(cn.redirect);

        // Error Checking
        if (redirectDomain == null) throw new ASTError(
            "Unknown redirect domain: [" + cn.redirect + "]\n" +
            "For TCE:\n" + cn.toString() + '\n' +
            "at:\n" +
            cn.exceptionLocationSummary()
        );

        cn.setRedirectDomain(redirectDomain);
    }


    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // link PPR
    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
    // 
    // THIS CODE IS, HOWEVER, IS THE PRIMARY PURPOSE OF THIS FILE - (see above, although this
    // "setCTAS" is also really important)

    private static void linkAllPPR(final PPR ppr)
    { 
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // HERE IT IS: PPR.setReference(...)
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (ppr.ref != null)
            ppr.setReference(Helper$FindTypeNode.find(ppr, ppr.ref));


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // HERE IT IS TOO: PPR.setReferenceArray(...)
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        if (ppr.refArray != null)
            ppr.setReferenceArray(Helper$FindTypeNode.find(ppr, ppr.refArray));
    }
}