001package Torello.Browser.BrowserAPI;
002
003import java.util.*;
004import javax.json.*;
005import javax.json.stream.*;
006import java.io.*;
007
008import java.lang.reflect.Method;
009import java.lang.reflect.Parameter;
010import java.util.function.Function;
011
012import Torello.Browser.BrowserEvent;
013import Torello.Browser.JavaScriptAPI.*;
014import Torello.Browser.helper.*;
015
016import Torello.Java.Additional.*;
017import Torello.Java.JSON.*;
018
019import static Torello.Java.JSON.JFlag.*;
020
021import Torello.Java.StrCmpr;
022import Torello.JavaDoc.StaticFunctional;
023import Torello.JavaDoc.JDHeaderBackgroundImg;
024import Torello.JavaDoc.Excuse;
025
026/**
027 * <SPAN CLASS=COPIEDJDK><B>Input/Output operations for streams produced by DevTools.</B></SPAN>
028 * 
029 * <EMBED CLASS='external-html' DATA-FILE-ID=CODE_GEN_NOTE>
030 */
031@StaticFunctional(Excused={"counter"}, Excuses={Excuse.CONFIGURATION})
032@JDHeaderBackgroundImg(EmbedTagFileID="WOOD_PLANK_NOTE")
033public class IO
034{
035    // ********************************************************************************************
036    // ********************************************************************************************
037    // Class Header Stuff
038    // ********************************************************************************************
039    // ********************************************************************************************
040
041
042    // No Pubic Constructors
043    private IO () { }
044
045    // These two Vector's are used by all the "Methods" exported by this class.  java.lang.reflect
046    // is used to generate the JSON String's.  It saves thousands of lines of Auto-Generated Code.
047    private static final Map<String, Vector<String>>    parameterNames = new HashMap<>();
048    private static final Map<String, Vector<Class<?>>>  parameterTypes = new HashMap<>();
049
050    // Some Methods do not take any parameters - for instance all the "enable()" and "disable()"
051    // I simply could not get ride of RAW-TYPES and UNCHECKED warnings... so there are now,
052    // offically, two empty-vectors.  One for String's, and the other for Classes.
053
054    private static final Vector<String>     EMPTY_VEC_STR = new Vector<>();
055    private static final Vector<Class<?>>   EMPTY_VEC_CLASS = new Vector<>();
056
057    static
058    {
059        for (Method m : IO.class.getMethods())
060        {
061            // This doesn't work!  The parameter names are all "arg0" ... "argN"
062            // It works for java.lang.reflect.Field, BUT NOT java.lang.reflect.Parameter!
063            //
064            // Vector<String> parameterNamesList = new Vector<>(); -- NOPE!
065
066            Vector<Class<?>> parameterTypesList = new Vector<>();
067        
068            for (Parameter p : m.getParameters()) parameterTypesList.add(p.getType());
069
070            parameterTypes.put(
071                m.getName(),
072                (parameterTypesList.size() > 0) ? parameterTypesList : EMPTY_VEC_CLASS
073            );
074        }
075    }
076
077    static
078    {
079        Vector<String> v = null;
080
081        v = new Vector<String>(1);
082        parameterNames.put("close", v);
083        Collections.addAll(v, new String[]
084        { "handle", });
085
086        v = new Vector<String>(3);
087        parameterNames.put("read", v);
088        Collections.addAll(v, new String[]
089        { "handle", "offset", "size", });
090
091        v = new Vector<String>(1);
092        parameterNames.put("resolveBlob", v);
093        Collections.addAll(v, new String[]
094        { "objectId", });
095    }
096
097
098    // ********************************************************************************************
099    // ********************************************************************************************
100    // Types - Static Inner Classes
101    // ********************************************************************************************
102    // ********************************************************************************************
103
104    // public static class StreamHandle => String
105    
106    
107    // Counter for keeping the WebSocket Request ID's distinct.
108    private static int counter = 1;
109    
110    /**
111     * Close the stream, discard any temporary backing storage.
112     * 
113     * @param handle Handle of the stream to close.
114     * 
115     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
116     * {@link Ret0}&gt;</CODE>
117     *
118     * <BR /><BR />This {@code Script} instance must be <B STYLE='color:red'>executed</B> before the
119     * browser receives the invocation-request.
120     *
121     * <BR /><BR />This Browser-Function <I>does not have</I> a return-value.  You may choose to
122     * <B STYLE='color: red'>await</B> the {@link Promise}{@code <JsonObject,} {@link Ret0}
123     * {@code >} to ensure the Browser Function has run to completion.
124     */
125    public static Script<String, JsonObject, Ret0> close(String handle)
126    {
127        // Exception-Check(s) to ensure that if any parameters which are not declared as
128        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
129        
130        if (handle == null) THROWS.throwNPE("handle");
131        
132        final int       webSocketID = 25000000 + counter++;
133        final boolean[] optionals   = { false, };
134        
135        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
136        String requestJSON = WriteJSON.get(
137            parameterTypes.get("close"),
138            parameterNames.get("close"),
139            optionals, webSocketID,
140            "IO.close",
141            handle
142        );
143        
144        // This Remote Command does not have a Return-Value.
145        return new Script<>
146            (webSocketID, requestJSON, VOID_RETURN.NoReturnValues);
147    }
148    
149    /**
150     * Read a chunk of the stream
151     * 
152     * @param handle Handle of the stream to read.
153     * 
154     * @param offset 
155     * Seek to the specified offset before reading (if not specified, proceed with offset
156     * following the last read). Some types of streams may only support sequential reads.
157     * <BR /><B CLASS=Opt>OPTIONAL</B>
158     * 
159     * @param size Maximum number of bytes to read (left upon the agent discretion if not specified).
160     * <BR /><B CLASS=Opt>OPTIONAL</B>
161     * 
162     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
163     * {@link Ret3}&gt;</CODE>
164     *
165     * <BR /><BR />This {@link Script} may be <B STYLE='color:red'>executed</B> (using 
166     * {@link Script#exec()}), and a {@link Promise} returned.
167     *
168     * <BR /><BR />When the {@code Promise} is <B STYLE='color: red'>awaited</B>
169     * (using {@link Promise#await()}), the {@code Ret3} will subsequently
170     * be returned from that call.
171     * 
172     * <BR /><BR />The <B STYLE='color: red'>returned</B> values are encapsulated
173     * in an instance of <B>{@link Ret3}</B>
174     *
175     * <BR /><BR /><UL CLASS=JDUL>
176     * <LI><CODE><B>Ret3.a:</B> Boolean (<B>base64Encoded</B>)</CODE>
177     *     <BR />Set if the data is base64-encoded
178     *     <BR /><BR /></LI>
179     * <LI><CODE><B>Ret3.b:</B> String (<B>data</B>)</CODE>
180     *     <BR />Data that were read.
181     *     <BR /><BR /></LI>
182     * <LI><CODE><B>Ret3.c:</B> Boolean (<B>eof</B>)</CODE>
183     *     <BR />Set if the end-of-file condition occurred while reading.
184     *     </LI>
185     * </UL>
186     */
187    public static Script<String, JsonObject, Ret3<Boolean, String, Boolean>> read
188        (String handle, Integer offset, Integer size)
189    {
190        // Exception-Check(s) to ensure that if any parameters which are not declared as
191        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
192        
193        if (handle == null) THROWS.throwNPE("handle");
194        
195        final int       webSocketID = 25001000 + counter++;
196        final boolean[] optionals   = { false, true, true, };
197        
198        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
199        String requestJSON = WriteJSON.get(
200            parameterTypes.get("read"),
201            parameterNames.get("read"),
202            optionals, webSocketID,
203            "IO.read",
204            handle, offset, size
205        );
206        
207        // 'JSON Binding' ... Converts Browser Response-JSON into Java-Type 'Ret3'
208        Function<JsonObject, Ret3<Boolean, String, Boolean>>
209            responseProcessor = (JsonObject jo) -> new Ret3<>(
210                ReadBoxedJSON.getBoolean(jo, "base64Encoded", true),
211                ReadJSON.getString(jo, "data", false, true),
212                ReadBoxedJSON.getBoolean(jo, "eof", true)
213            );
214        
215        return new Script<>(webSocketID, requestJSON, responseProcessor);
216    }
217    
218    /**
219     * Return UUID of Blob object specified by a remote object id.
220     * 
221     * @param objectId Object id of a Blob object wrapper.
222     * 
223     * @return An instance of <CODE>{@link Script}&lt;String, {@link JsonObject},
224     * String&gt;</CODE>
225     * 
226     * <BR /><BR />This <B>script</B> may be <B STYLE='color: red'>executed</B>, using
227     * {@link Script#exec()}, and afterwards, a {@link Promise}<CODE>&lt;JsonObject,
228     * String&gt;</CODE> will be returned.
229     *
230     * <BR /><BR />Finally, the <B>{@code Promise}</B> may be <B STYLE='color: red'>awaited</B>,
231     * using {@link Promise#await()}, <I>and the returned result of this Browser Function may
232      * may be retrieved.</I>
233     *
234     * <BR /><BR />This Browser Function <B STYLE='color: red'>returns</B>
235     * <BR /><BR /><UL CLASS=JDUL>
236     * <LI><CODE>String (<B>uuid</B></CODE>)
237     *     <BR />UUID of the specified Blob.
238     * </LI>
239     * </UL> */
240    public static Script<String, JsonObject, String> resolveBlob(String objectId)
241    {
242        // Exception-Check(s) to ensure that if any parameters which are not declared as
243        // 'Optional', but have a 'null' value anyway, that a NullPointerException shall throw.
244        
245        if (objectId == null) THROWS.throwNPE("objectId");
246        
247        final int       webSocketID = 25002000 + counter++;
248        final boolean[] optionals   = { false, };
249        
250        // Convert Method Parameters into JSON.  Build the JSON Request-Object (as a String)
251        String requestJSON = WriteJSON.get(
252            parameterTypes.get("resolveBlob"),
253            parameterNames.get("resolveBlob"),
254            optionals, webSocketID,
255            "IO.resolveBlob",
256            objectId
257        );
258        
259        // 'JSON Binding' ... Converts Browser Response-JSON to 'String'
260        Function<JsonObject, String> responseProcessor = (JsonObject jo) ->
261            ReadJSON.getString(jo, "uuid", false, true);
262        
263        return new Script<>(webSocketID, requestJSON, responseProcessor);
264    }
265    
266}