001package Torello.Browser.JsonAST;
002
003import static Torello.Java.C.BRED;
004import static Torello.Java.C.BCYAN;
005import static Torello.Java.C.RESET;
006
007import Torello.Java.StorageWriter;
008import Torello.Java.ReadOnly.ReadOnlyList;
009import Torello.Java.ReadOnly.ReadOnlyArrayList;
010
011/**
012 * After parsing both API Spec-Files, each and every Json Property namem {@code "ref$"} needs to 
013 * have it's {@code String} converted into a genuine {@link TCE} instance; this process is 
014 * called <B>'Linking'</B> and it is identical to the linkng that the Java Compiler {@code javac}
015 * performs when converting variables &amp; fields inside source files into class files.
016 * 
017 * <EMBED CLASS='external-html' DATA-FILE-ID=LINKER>
018 */
019@Torello.JavaDoc.Annotations.StaticFunctional
020@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="LINKER_JDHBI")
021public class Linker
022{
023    private Linker() { }
024
025    static void run(
026            final StorageWriter sw,
027            final API           browser,
028            final API           js
029        )
030    {
031        sw.println(BCYAN + "Run Linker on all Entities" + RESET);
032        Helper$FindDomain.initialize(browser, js);
033
034        // Primarily these two invocations are really quite crucial.
035        // These two lines set the fields "reference" and "referenceArray" for all PPR instances.
036
037        linkAllAPI(js);
038        linkAllAPI(browser);
039
040        sw.println(BRED + "DONE" + RESET + '\n');
041
042
043        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
044        // Setting a PPR's "Computed Type as String" (and "as Byte")
045        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
046        // 
047        // Of a "high importance" is remembering that the "Computed Type as String", and also the 
048        // internal field "Computed Type as Byte" simply *MAY NOT BE SET* inside of class 'PPR'
049        // *UNTIL* the fields 'PPR.reference' and 'PPR.referenceArray' have been set.
050        // 
051        // There are cases where the "Computed Type" will be something simple such as
052        // 'java.lang.String',  However, there are also cases where the "Computed Type" of a PPR
053        // (Method-[P]arameter, Method-[R]eturns, or Type-[P]roperty) will be a reference to a type
054        // defined by CDP itself. 
055        // 
056        // The PPR's "Computed Type", when it is "STRING", could easily be converted to 
057        // 'java.lang.String' when the PPR is constructed (and, therefore, done inside the
058        // constructor).  Howver, the "Computed Type" is only *SOMETIMES* a simple or standard Java
059        // type like 'String' or 'int'.  There are also situations where a PPR's computed type is 
060        // some kind of actual Java POJO / Class.  (Maybe the "Computed Type" is
061        // 'Network.AssociatedCookie', did you ever think of that one?  Or maybe the PPR is:
062        // 'DOM.DetachedElementInfo')
063        // 
064        // In those cases, thee "Computed Type as a String" cannot be done until *BOTH* its fields 
065        // 'PPR.reference' and 'PPR.referenceArray' have been set.
066        // 
067        // Note that the three lines directly above *THIS COMMENT* are doing exactly that!
068        // They are setting the PPR.reference & PPR.referenceArray fields!
069        // 
070        // THUSLY... AND MOST IMPORTANTLY ... THESE THREE LINES (BELOW) *MUST* COME *AFTER* THE
071        // PREVIOUS THREE LINES.
072        // 
073        // This concept has been encoded / hardcoded into an error-check & throw thingy that I added
074        // into the methods PPR.setCTAS(), PPR.setReference(), and PPR.setReferenceArray().
075
076        sw.println(BCYAN + "Generate CTAS for All PPR's" + RESET);
077        PPR_CTAS.iterateSetCTAS(js);
078        PPR_CTAS.iterateSetCTAS(browser);
079
080        
081        // NOTE: class TypeNode also has a "CTAS" ad a "CTAB" field.  However, with Type's, there 
082        //       is a zero percent chance for a "type" to collapse into another CDP "type"
083        // 
084        // That means that the CTAS / CTAB can be computed inside of the constructor...
085        // 
086        // This is because they don't need to be "linked" in a way where the value needs to be
087        // computed after all of the constructors have completed building the POJO's...
088        // 
089        // That meanns the CTAS / CTAB fields can actually be declared 'final' inside the class
090        // definitions themselves.
091        // 
092        // That means "linking TypeNode's" doesn't actually happen.  Only PPR's need to go through
093        // this literal hell.
094
095        sw.println(BRED + "DONE" + RESET);
096    }
097
098    private static void linkAllAPI(final API api)
099    { for (final Domain d : api.domains) linkDomain(d); }
100
101    private static void linkDomain(final Domain DOMAIN)
102    {
103        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
104        // The 'Domain.depAsStrs' ('dependencies' array).  Unused, but accounted for
105        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
106        // 
107        // Domain's MIGHT / POSSIBLY have a 'dependencies' String Array.
108        // If they do, convert the dependency as a STRING to a REFERENCE.
109        // 
110        // NOTE: this 'dependencies' is not used anywhere in the code of my Code-Generator
111        //       The only reason I'm "linking" this Domain.dependencies field is because it exists,
112        //       and perhaps one day me or somebody else will find a use for it.  For now, this is
113        //       extracted out of the Domain's JSON Definition, but it is totally ignored and 
114        //       unused!
115        // 
116        // Afterwards, there is some simple error checking.  It just ascertains whether or not the
117        // linker code above, actually succeded in linking all of the `Domain.depAsStrs` Strings
118        // from 'java.lang.String' into instances of 'JsonAST.Domain'
119        // 
120        // MOST IMPORTANT NOTE OF ALL:
121        //      This little loop and this little check are completely superfluous.  These fields
122        //      are not used anywhere by the AST (this package - Torello.Browser.JsonAST), nor are
123        //      they used by the Code Generator classes (Torello.Browser.CodeGenerator)
124        // 
125        //      Inside of the '.json' files which define this API, each Domain has a short little
126        //      list of "other domains" upon which "this domain" depends.  It is essentially 
127        //      identical to a class in 'java.util' - for instance 'java.util.ArrayList' doing an
128        //      import of somethign from 'java.util.regex' (or something like that)
129        // 
130        //      The only issue is, because Google Chrome and the Browser treat each "Domain" as a 
131        //      "separate Package" and because I place all domains from a single API into the same
132        //      exact Java Package - I neither know nor care what is in the field
133        //      'Domain.dependencies'
134        // 
135        //      Link it from a String to reference.  Get it over with.  Fix it and forget it.  
136        //      Perhaps one day this will be used, but that day is not today.
137
138        final ReadOnlyList<Domain> dependencies = new ReadOnlyArrayList<Domain>(
139            DOMAIN.depAsStrs,
140            (String s) -> Helper$FindDomain.find(s),
141            DOMAIN.depAsStrs.size()
142        );
143
144        for (int i=0; i < dependencies.size(); i++)
145            if (dependencies.get(i) == null)
146                throw new Error("Unknown Domain Dependency: " + DOMAIN.depAsStrs.get(i));
147
148        DOMAIN.setDependencies(dependencies);
149
150
151        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
152        // Now Iterate all of the TCE's in this Domain, and run the linker on each of them
153        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
154
155        for (final TypeNode type : DOMAIN.types)
156            if (type.properties != null)
157                for (final PPR field : type.properties)
158                    linkAllPPR(field);
159
160        for (final EventNode type : DOMAIN.events)
161            if (type.parameters != null)
162                for (final PPR field : type.parameters)
163                    linkAllPPR(field);
164
165        for (final CommandNode  command : DOMAIN.commands)
166        {
167            if (command.parameters != null)
168                for (final PPR param : command.parameters) linkAllPPR(param);
169
170            if (command.returns != null)
171                for (final PPR retVal : command.returns) linkAllPPR(retVal);
172
173            if (command.redirect != null) linkCommandNodeRedirect(command);
174        }
175    }
176
177
178    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
179    // CommandNode "redirect" (**COMPLETELY UNUSED** in my code - but maintained / accounted for)
180    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
181    // 
182    // IF YOU THINK THE "Domain.dependencies" field is completely ignored, unused and serves no
183    // purpose in my code.
184    // 
185    // You should try laying around and wondering  whether or not the "redirectDomain" has any 
186    // bearing on the Code Generator...  Guess what?  The answer is "NO!"
187    // 
188    // It is really *REALLY* easy to link this "Domain as a String" into a "Domain as a Reference"
189    // or "pointer".  So I go ahead and do that here.  Do I use it? ...  No way!
190
191    private static void linkCommandNodeRedirect(final CommandNode cn)
192    {
193        // Resolve 'redirect' from a java.lang.String, into a 'Domain' reference.
194        final Domain redirectDomain = Helper$FindDomain.find(cn.redirect);
195
196        // Error Checking
197        if (redirectDomain == null) throw new ASTError(
198            "Unknown redirect domain: [" + cn.redirect + "]\n" +
199            "For TCE:\n" + cn.toString() + '\n' +
200            "at:\n" +
201            cn.exceptionLocationSummary()
202        );
203
204        cn.setRedirectDomain(redirectDomain);
205    }
206
207
208    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
209    // link PPR
210    // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
211    // 
212    // THIS CODE IS, HOWEVER, IS THE PRIMARY PURPOSE OF THIS FILE - (see above, although this
213    // "setCTAS" is also really important)
214
215    private static void linkAllPPR(final PPR ppr)
216    { 
217        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
218        // HERE IT IS: PPR.setReference(...)
219        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
220
221        if (ppr.ref != null)
222            ppr.setReference(Helper$FindTypeNode.find(ppr, ppr.ref));
223
224
225        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
226        // HERE IT IS TOO: PPR.setReferenceArray(...)
227        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
228
229        if (ppr.refArray != null)
230            ppr.setReferenceArray(Helper$FindTypeNode.find(ppr, ppr.refArray));
231    }
232}
233