001package Torello.Browser;
002
003import Torello.JavaDoc.Annotations.IntoHTMLTable;
004import static Torello.JavaDoc.Annotations.IntoHTMLTable.Background.BlueDither;
005import static Torello.JavaDoc.Annotations.IntoHTMLTable.Background.GreenDither;
006
007import Torello.Browser.JsonAST.PPR;
008import Torello.Browser.JsonAST.TypeNode;
009import Torello.Java.UnreachableError;
010
011import Torello.Java.ReadOnly.ReadOnlyMap;
012import Torello.Java.ReadOnly.ROHashMapBuilder;
013import Torello.Java.ReadOnly.ROTreeMapBuilder;
014
015import java.lang.reflect.Field;
016
017import java.util.function.Predicate;
018import java.util.regex.Pattern;
019
020// Needed by JavaDoc only (all of the types are used in JavaDoc)
021import javax.json.*;
022
023
024/**
025 * 💡 Primarily, this class is used by the Code Generator to produce the classes that comprise the
026 * Java Browser-Automation Library.  Generating Java Code to interface with a
027 * <B STYLE='color:red;'>"Google Compliant Browser"</B> isn't exactly rocket science, but rather it
028 * is just very tedious and time consuming.  When the Code Generator emits a class - that class is
029 * nothing more than a "wrapper" around a "Web Sockets Implementation" for Google Chromes CDP
030 * (Chrome DevTools Protocol).
031 * 
032 * <BR /><BR />
033 * 🧠 The reality is that the entire CDP library is based on nothing more than two JSON files that 
034 * Google has left on a particular Git-Hub Page.  You may search Google for the CDP JSON 
035 * specification files, and it will produce a nice page  for you.  Those JSON files are converted
036 * <B STYLE='color:red;'><I>piece by piece</I></B> from "Json Strings" into actual Java Types.  An
037 * "AST Tree" is built from the JSON file, and then that AST is passed to a Code Generator that
038 * "emits" Java Files.
039 * 
040 * <BR /><BR />
041 * <B STYLE='color:red'><I>100% of the files in the BrowserAPI &amp; JavaScriptAPI</I></B> were 
042 * produced, themselves, by a Code Generator package that is not actually part of the public 
043 * Torello Java-HTML JAR Distribution Library.
044 * 
045 * <BR /><BR />
046 * 📌 This class does nothing more than map a {@code java.lang.String} into a {@code byte} primitive
047 * constant.  Why?  Implementing a Code Generator that contains quite a few switch statements which
048 * "switch" on a Java {@code 'byte'} is much nicer to look at &amp; read than one which is
049 * repeatedly "switching" on {@code java.lang.String's}.
050 * 
051 * <BR /><BR />
052 * 🚫 It's quite simple really!  Only downside (for you, the user) is that I don't actually expose 
053 * or provide the Code Generator's Java source code.  Sorry, it's just not a useful thing for
054 * anyone except me!  (Trust me, you really wouldn't care...)
055 */
056@Torello.JavaDoc.Annotations.JDHeaderBackgroundImg(EmbedTagFileID="INTERNAL_USE_JDHBI")
057public class CDPTypes 
058{
059    private CDPTypes() { }
060
061    /**
062     * <BR>CDP In-Browser Type: An actual Data-Object, defined for use by the browser
063     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonObject}
064     * <BR>Java Type:   Any one of the scores of Data-Classes inside the Browser &amp; Java-Script
065     *                  API packages.  (These are always a 'nested class' of any domain-class)
066     * <BR>Note: A type that is a "CDP Type" *must* be one of type, event or command-return-types 
067     */
068    @IntoHTMLTable(title="Indicates an In-Browser 'Data-Class'", background=BlueDither)
069    public static final byte CDP_TYPE = 1;
070
071    /**
072     * <BR>CDP In-Browser Type: An array of Browser Types / Objects
073     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
074     * <BR>Java Type: An array of a 'nested-class' data-object
075     */
076    @IntoHTMLTable(title="An Array of any one In-Browser 'Data-Class'", background=GreenDither)
077    public static final byte CDP_TYPE_ARRAY_1D = CDP_TYPE + 1;
078
079    /**
080     * <BR>CDP In-Browser Type: Unknown, Any
081     * <BR>Web-Sockets Transmisson Layer: The Json-Type Raw-Json
082     * <BR>Java Type: {@link JsonObject}
083     * <BR>Note:    There are *VERY FEW* types which are defined as 'Any' or (unknown) 'Object' in
084     *              Google's CDP Spec-Files.  When it used, you (the user) get to receive the "Raw
085     *              JSON Object" that was transmitted via Web-Socket.
086     */
087    @IntoHTMLTable(title="Unspecified Browser Type - Accepts Any Type!", background=BlueDither)
088    public static final byte RAW_JSON_VALUE = CDP_TYPE_ARRAY_1D + 1;
089
090
091    // ********************************************************************************************
092    // ********************************************************************************************
093    // Some Basic-Types
094    // ********************************************************************************************
095    // ********************************************************************************************
096
097
098    /**
099     * <BR>CDP In-Browser Type: Integer
100     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonNumber}
101     * <BR>Java Type: Primitive {@code 'int'}
102     */
103    @IntoHTMLTable(title="Required, Non-Nullable 'int'", background=GreenDither)
104    public static final byte PRIMITIVE_INT = RAW_JSON_VALUE + 1;
105
106    /**
107     * <BR>CDP In-Browser Type: Integer
108     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonNumber}
109     * <BR>Java Type: Boxed-Type {@code java.lang.Integer}
110     */
111    @IntoHTMLTable(title="Nullabele / Optional Integer", background=BlueDither)
112    public static final byte BOXED_INTEGER = PRIMITIVE_INT + 1;
113
114    /**
115     * <BR>CDP In-Browser Type: Boolean
116     * <BR>Web-Sockets Transmisson Layer: Either {@link JsonValue#TRUE} or {@link JsonValue#FALSE}
117     * <BR>Java Type: Primitive {@code 'boolean'}
118     */
119    @IntoHTMLTable(title="Required, Non-Nullable boolean", background=GreenDither)
120    public static final byte PRIMITIVE_BOOLEAN = BOXED_INTEGER + 1;
121
122    /**
123     * <BR>CDP In-Browser Type: Boolean
124     * <BR>Web-Sockets Transmisson Layer: Either {@link JsonValue#TRUE} or {@link JsonValue#FALSE}
125     * <BR>Java Type: Boxed-Type {@code java.lang.Boolean}
126     */
127    @IntoHTMLTable(title="Optional / Nullable boolean", background=BlueDither)
128    public static final byte BOXED_BOOLEAN = PRIMITIVE_BOOLEAN + 1;
129
130    /**
131     * <BR>CDP In-Browser Type: String
132     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonString}
133     * <BR>Java Type: A {@code java.lang.String}
134     */
135    @IntoHTMLTable(title="String of Characters", background=GreenDither)
136    public static final byte STRING = BOXED_BOOLEAN + 1;
137
138    /**
139     * <BR>CDP In-Browser Type: Unspecified numeric type
140     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonNumber}
141     * <BR>Java Type: Any concrete sub-class of {@code java.lang.Number}
142     */
143    @IntoHTMLTable(title="Unspecified Numeric Type", background=BlueDither)
144    public static final byte NUMBER = STRING + 1;
145
146
147    // ********************************************************************************************
148    // ********************************************************************************************
149    // One-Dimensional Array-Types
150    // ********************************************************************************************
151    // ********************************************************************************************
152
153
154    /**
155     * <BR>CDP In-Browser Type: One dimensional integer array
156     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
157     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that input
158     * {@link JsonArray} instances have only valid integers, and no
159     * {@link JsonValue#NULL Json Null's}
160     * <BR>Java Type: Primitive {@code int[]}-Array
161     */
162    @IntoHTMLTable(title="Type: int[]-Array", background=GreenDither)
163    public static final byte INT_ARRAY_1D = NUMBER + 1;
164
165    /**
166     * <BR>CDP In-Browser Type: One dimensional boolean array
167     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
168     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that input
169     * {@link JsonArray} instances contain only {@link JsonValue#TRUE} or {@link JsonValue#FALSE},
170     * without any {@link JsonValue#NULL Json Null's}
171     * <BR>Java Type: Primitive {@code boolean[]}-Array
172     */
173    @IntoHTMLTable(title="Type: boolean[]-Array", background=BlueDither)
174    public static final byte BOOLEAN_ARRAY_1D = INT_ARRAY_1D + 1;
175
176    /**
177     * <BR>CDP In-Browser Type: One dimensional string array
178     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
179     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that the {@link JsonArray}
180     * have only valid {@link JsonString} instances
181     * <BR>Java Type: A {@code java.lang.String[]}
182     */
183    @IntoHTMLTable(title="Type: String[]-Array", background=GreenDither)
184    public static final byte STRING_ARRAY_1D = BOOLEAN_ARRAY_1D + 1;
185
186    /**
187     * <BR>CDP In-Browser Type: One dimensional numeric array, of an unspecified numeric type
188     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
189     * <BR>Java Type: A {@code Number[]} (may contain any concrete {@code java.lang.Number} subclass)
190     */
191    @IntoHTMLTable(title="Type: Number[]-Array", background=BlueDither)
192    public static final byte NUMBER_ARRAY_1D = STRING_ARRAY_1D + 1;
193
194
195    // ********************************************************************************************
196    // ********************************************************************************************
197    // Two-Dimensional Array-Types
198    // ********************************************************************************************
199    // ********************************************************************************************
200
201
202    /**
203     * <BR>CDP In-Browser Type: Two-dimensional integer array
204     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
205     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that the {@link JsonArray}
206     * be a valid 2-D array of arrays.
207     * <BR>Java Type: Primitive, two-dimensioinal {@code int[][]}-Array
208     */
209    @IntoHTMLTable(title="Type: 2-D int[][]-Array", background=GreenDither)
210    public static final byte INT_ARRAY_2D = NUMBER_ARRAY_1D + 1;
211
212    /**
213     * <BR>CDP In-Browser Type: Two dimensional boolean array
214     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
215     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that the {@link JsonArray}
216     * be a valid 2-D array of arrays.
217     * <BR>Java Type: Primitive, two-dimensioinal {@code boolean[][]}-Array
218     */
219    @IntoHTMLTable(title="Type: 2-D boolean[][]-Array", background=BlueDither)
220    public static final byte BOOLEAN_ARRAY_2D = INT_ARRAY_2D + 1;
221
222    /**
223     * <BR>CDP In-Browser Type: Two dimensional string array
224     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
225     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that the {@link JsonArray}
226     * be a valid 2-D array of arrays.
227     * <BR>Java Type: Two-dimensional {@code String[][]}-Array
228     */
229    @IntoHTMLTable(title="Type: 2-D String[][]-Array", background=GreenDither)
230    public static final byte STRING_ARRAY_2D = BOOLEAN_ARRAY_2D + 1;
231
232    /**
233     * <BR>CDP In-Browser Type: Two dimensional numeric array, of an unspecified numeric type
234     * <BR>Web-Sockets Transmisson Layer: The Json-Type {@link JsonArray}
235     * <BR>Expectation: It is <B STYLE='color:red;'><I>expected</I></B> that the {@link JsonArray}
236     * be a valid 2-D array of arrays.
237     * <BR>Java Type: Two-dimensional {@code Number[][]}-Array
238     */
239    @IntoHTMLTable(title="Type: 2-D Number[][] Array", background=BlueDither)
240    public static final byte NUMBER_ARRAY_2D = STRING_ARRAY_2D + 1;
241
242
243    // ********************************************************************************************
244    // ********************************************************************************************
245    // Byte to "Name as a Java String"  (A simple "ReadOnlyMap" of Byte -> String)
246    // ********************************************************************************************
247    // ********************************************************************************************
248
249
250    /**
251     * The "switch" from a number to string is done using this map.  When a Code Generator "emits"
252     * or "outputs" (when it does its thing, and generates Java Source Code), it literally just 
253     * goes through the AST and using lots of {@code java.lang.StringBuilder} to build strings.
254     * 
255     * <BR /><BR />
256     * The {@code String's} which are produced, are saved directly to {@code '.java'} files, and
257     * eventually those {@code '.java'} files are compiled using the standard {@code 'javac'} 
258     * compiler stage of the Torello Build-Tool, and turned into the {@code '.class'} files offered
259     * by the Java-HTML JAR Library.
260     * 
261     * <BR /><BR /><DIV CLASS=JDHint>
262     * Click on the "View Hilited Source" arrow-button '⮫' (directly above) to see how this map
263     * is loaded, using reflection.
264     * 
265     * <BR /><BR />
266     * This particular field does nothing more than map one of the {@code 'byte'} constants, such
267     * as {@link #PRIMITIVE_INT} or {@link #NUMBER_ARRAY_1D} to the Java {@code 'String'} name of 
268     * that constant - {@code "PRIMITIVE_INT"} or {@code "NUMBER_ARRAY_1D"}.
269     * </DIV>
270     * 
271     * <BR /><DIV CLASS=JDHintAlt>
272     * If you ask "Why?" - again, the classes inside {@code 'BrowserAPI'} and
273     * {@code 'JavaScriptAPI'} have source code that was emitted,
274     * <B STYLE='color:red'><I>automatically</B></I>, and "emitting" Java Code such as 
275     * {@code "CDPTypes.PRIMTIVIE_INT"} requires this little map field. 
276     * 
277     * <BR ><BR />
278     * It is that simple, but unfortunately, the Java Code Generator which produces the classes in
279     * this API is not part of the public-API, and therefore its source code cannot be viewed.  It
280     * mostly "really boring" stuff.  The code generator isn't included in the Java-HTML
281     * {@code '.jar'} because it doesn't serve any purpose, other than generating the
282     * {@code '.java'} files inside {@code 'BrowserAPI'} and {@code 'JavaScriptAPI'}. 
283     * </DIV>
284     */
285    public static final ReadOnlyMap<Byte, String> names;
286
287    static
288    {
289        final Field[]                           fArr    = CDPTypes.class.getFields();
290        final ROHashMapBuilder<Byte, String>    rohmb   = new ROHashMapBuilder<>();
291
292        try 
293        {
294            for (final Field f : fArr)
295
296                // Only save the static-byte fields within this class!  Remember, there are a few
297                // fields which are not actually "byte constants".  Skip any field which isn't one
298                // of the byte constants at the top of this class.
299                // 
300                // Map-Key: The actual byte-value of the constant (1, 2, 3....)
301                // Map-Value:   The actual name of the constant "PRIMITIVE_INT",
302                //              "BOOLEAN_ARRAY_1D" etc.
303
304                if (f.getType() == byte.class)
305                    rohmb.put((byte) f.getInt(null), f.getName());
306        }
307
308
309        // This should just never happen, because I only declare "public byte" fields.  All fields
310        // which are "byte" are "public", so IllegalAccessError should be, theoretically, perfectly
311        // impossible!
312        // 
313        // NOTE: 'IllegalAccessException' is a checked exception, and "UnreachableError" inherits
314        //       java.lang.Error, and is therefore, unchecked.
315
316        catch (IllegalAccessException iae) { throw new UnreachableError(iae); }
317
318        names = rohmb.build();
319    }
320
321
322    // ********************************************************************************************
323    // ********************************************************************************************
324    // Java-String to "Byte"
325    // ********************************************************************************************
326    // ********************************************************************************************
327
328
329    // This is used inside of the two methods, below.  It serves no purpose whatsoever outside of
330    // this class.  This class is largely an "internal use only", or a "non-public facing API" 
331    // class.  The only problems with Java's keyword "public" is that sometimes there are classes
332    // which have to be "public", due to the fact that their methods & fields are used outside of
333    // the package in which they are defined.
334    // 
335    // This class is used over & over outside of the package 'Torello.Browser' - where it is 
336    // defined.  However, actual end-users, themselves, ought never find a need for using it!
337
338    private static final ReadOnlyMap<String, Byte> bytes;
339
340    static
341    {
342        final ROTreeMapBuilder<String, Byte> rotmb = new ROTreeMapBuilder<>();
343
344        rotmb.put("JsonValue",      RAW_JSON_VALUE);
345        rotmb.put("int",            PRIMITIVE_INT);
346        rotmb.put("Integer",        BOXED_INTEGER);
347        rotmb.put("boolean",        PRIMITIVE_BOOLEAN);
348        rotmb.put("Boolean",        BOXED_BOOLEAN);
349        rotmb.put("String",         STRING);
350        rotmb.put("Number",         NUMBER);
351
352        rotmb.put("int[]",          INT_ARRAY_1D);
353        rotmb.put("boolean[]",      BOOLEAN_ARRAY_1D);
354        rotmb.put("String[]",       STRING_ARRAY_1D);
355        rotmb.put("Number[]",       NUMBER_ARRAY_1D);
356
357        rotmb.put("int[][]",        INT_ARRAY_2D);
358        rotmb.put("boolean[][]",    BOOLEAN_ARRAY_2D);
359        rotmb.put("String[][]",     STRING_ARRAY_2D);
360        rotmb.put("Number[][]",     NUMBER_ARRAY_2D);
361
362        bytes = rotmb.build();
363    }
364
365
366    // CD types obey this rule - all it says is that things like "Accessibility.AXNode" is a 
367    // match.  Any class-name (as a string) which is a "nested class" of some other class will
368    // produce a "match" by this predicate.
369
370    private static final Predicate<String> CDP_TYPE_NAME_PREDICATE =
371        Pattern.compile("\\w+\\.\\w+").asPredicate();
372
373
374    /**
375     * Converts the strings created inside the Json-AST package into a {@code 'byte'} constant
376     * 
377     * <BR /><BR /><DIV CLASS=JDHint>
378     * Note that there is one minor "exceptional case" that is handled inside of this method.
379     * Just look at the test for {@code FilterEntry[]}, below, to see how it is handled.  It's a
380     * minor nuisance inside of the CDP specs.
381     * </DIV>
382     * 
383     * <BR /><BR /><DIV CLASS=JDHintAlt>
384     * This class is used only once, throughout all of the Java-HTML {@code '.jar'}.  It may be 
385     * found inside the AST node for "types" (see class {@link TypeNode}).  Click on the
386     * {@code TypeNode} link, and then click the "View Hilited Source" to see this method's use.
387     * 
388     * <BR /><BR />
389     * It may found inside the package-private constructor for that class.
390     * </DIV>
391     * 
392     * @param CTAS The "Computed Type as a String"
393     * 
394     * @return A {@code 'byte'} constant which represents that type.  If the type is an actual
395     * and reified browser object type, then the {@code 'byte'} constants {@link #CDP_TYPE} or
396     * {@link #CDP_TYPE_ARRAY_1D} are returned instead.
397     * 
398     * @throws UnrecognizedCTASError
399     * <EMBED CLASS='external-html' DATA-FILE-ID=CDPTypes.UnrecognizedCTASError>
400     * 
401     * @see TypeNode
402     */
403    public static byte ctasToByte(final String CTAS)
404    {
405        final Byte ret = bytes.get(CTAS);
406
407        if (ret != null)                                return ret.byteValue();
408        else if (CTAS.equals("FilterEntry[]"))          return CDP_TYPE_ARRAY_1D;
409        else if (CDP_TYPE_NAME_PREDICATE.test(CTAS))    return CDP_TYPE;
410        else                                            throw new UnrecognizedCTASError(CTAS);
411    }
412
413    /**
414     * Converts the strings created inside the Json-AST package into a {@code 'byte'} constant
415     * 
416     * <BR /><BR /><DIV CLASS=JDHint>
417     * This class is used only once throughout all of the Java-HTML {@code '.jar'}.  It may be 
418     * found inside the AST node for "Parameters, Properties &amp; Return-Values" ({@link PPR}).
419     * Click the PPR link, and then click the "View Hilited Source" to see this method's use.
420     * 
421     * <BR /><BR />
422     * It may be found inside the package-private method named "setCTAS(String)"
423     * </DIV>
424     * 
425     * @param CTAS The "Computed Type as a String"
426     * @param refNonNull indicates whether the {@link PPR#reference()} returns null
427     * @param refArrayNonNull indicates whether the {@link PPR#referenceArray()} returns null
428     * 
429     * @return A {@code 'byte'} constant which represents that type.  If the type is an actual
430     * and reified browser object type, then the {@code 'byte'} constants {@link #CDP_TYPE} or
431     * {@link #CDP_TYPE_ARRAY_1D} are returned instead.
432     * 
433     * @throws UnrecognizedCTASError
434     * <EMBED CLASS='external-html' DATA-FILE-ID=CDPTypes.UnrecognizedCTASError>
435     * 
436     * @see PPR
437     */
438    public static byte ctasToByte
439        (final String CTAS, final boolean refNonNull, final boolean refArrayNonNull)
440    {
441        final Byte ret = bytes.get(CTAS);
442
443        if (ret != null)            return ret.byteValue();
444        else if (refNonNull)        return CDP_TYPE;
445        else if (refArrayNonNull)   return CDP_TYPE_ARRAY_1D;
446        else                        throw new UnrecognizedCTASError(CTAS);
447    }
448
449
450    // ********************************************************************************************
451    // ********************************************************************************************
452    // Byte to Java Type-As-String
453    // ********************************************************************************************
454    // ********************************************************************************************
455
456
457    /** Switches from a {@code byte} constant to the Java-Type, as a {@code java.lang.String} */
458    public static final ReadOnlyMap<Byte, String> types;
459
460    static
461    {
462        final ROTreeMapBuilder<Byte, String> rotmb = new ROTreeMapBuilder<>();
463
464        rotmb.put(RAW_JSON_VALUE,       "JsonValue");
465        rotmb.put(PRIMITIVE_INT,        "int");
466        rotmb.put(BOXED_INTEGER,        "Integer");
467        rotmb.put(PRIMITIVE_BOOLEAN,    "boolean");
468        rotmb.put(BOXED_BOOLEAN,        "Boolean");
469        rotmb.put(STRING,               "String");
470        rotmb.put(NUMBER,               "Number");
471
472        rotmb.put(INT_ARRAY_1D,         "int[]");
473        rotmb.put(BOOLEAN_ARRAY_1D,     "boolean[]");
474        rotmb.put(STRING_ARRAY_1D,      "String[]");
475        rotmb.put(NUMBER_ARRAY_1D,      "Number[]");
476
477        rotmb.put(INT_ARRAY_2D,         "int[][]");
478        rotmb.put(BOOLEAN_ARRAY_2D,     "boolean[][]");
479        rotmb.put(STRING_ARRAY_2D,      "String[][]");
480        rotmb.put(NUMBER_ARRAY_2D,      "Number[][]");
481
482        types = rotmb.build();
483    }
484}