source: trunk/autoquest-htmlmonitor/src/main/java/de/ugoe/cs/autoquest/htmlmonitor/HtmlMonitorServlet.java @ 1233

Last change on this file since 1233 was 1233, checked in by pharms, 11 years ago
  • improved logging
  • corrected handling of onchange events
File size: 31.4 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.htmlmonitor;
16
17import java.io.BufferedReader;
18import java.io.FileNotFoundException;
19import java.io.IOException;
20import java.io.InputStream;
21import java.io.InputStreamReader;
22import java.io.PrintWriter;
23import java.net.MalformedURLException;
24import java.net.URL;
25import java.util.ArrayList;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29
30import javax.servlet.ServletException;
31import javax.servlet.http.HttpServletRequest;
32import javax.servlet.http.HttpServletResponse;
33
34import org.json.simple.JSONArray;
35import org.json.simple.JSONObject;
36import org.json.simple.JSONValue;
37import org.json.simple.parser.ParseException;
38import org.mortbay.jetty.servlet.DefaultServlet;
39
40import de.ugoe.cs.util.FileTools;
41import de.ugoe.cs.util.console.Console;
42
43/**
44 * <p>
45 * the servlet deployed in the web server that receives all client messages and returns the client
46 * java script. The messages are parsed, validated, and forwarded to the provided message listener.
47 * If a message is not valid, it is discarded. If an event in a message is not valid, it is
48 * discarded. Messages are only received via the POST HTTP method. The GET HTTP method is only
49 * implemented for returning the client java script.
50 * </p>
51 *
52 * @author Patrick Harms
53 */
54class HtmlMonitorServlet extends DefaultServlet {
55
56    /**  */
57    private static final long serialVersionUID = 1L;
58   
59    /**  */
60    private static final boolean DO_TRACE = false;
61   
62    /**
63     * <p>
64     * Name and path of the robot filter.
65     * </p>
66     */
67    private static final String ROBOTFILTERFILE = "data/robots/robotfilter.txt";
68
69    /**
70     * <p>
71     * Field that contains a regular expression that matches all robots
72     * contained in {@link #ROBOTFILTERFILE}.
73     * </p>
74     */
75    private String robotRegex = null;
76
77    /**
78     * the message listener to forward received messages to.
79     */
80    private transient HtmlGUIElementManager guiElementManager = new HtmlGUIElementManager();
81
82    /**
83     * the message listener to forward received messages to.
84     */
85    private transient HtmlMonitorMessageListener messageListener;
86
87    /**
88     * <p>
89     * initializes the servlet with the message listener to which all events shall be forwarded
90     * </p>
91     *
92     * @param messageListener the message listener that shall receive all client events
93     */
94    HtmlMonitorServlet(HtmlMonitorMessageListener messageListener) {
95        this.messageListener = messageListener;
96        try {
97            loadRobotRegex();
98        }
99        catch (Exception e) {
100            Console.println
101                ("robot filtering disabled: could not parse robot filter file " + ROBOTFILTERFILE);
102        }
103    }
104
105    /**
106     * this implements handling of doGet. For this servlet this means that
107     * the autoquest-htmlmonitor.js will be delivered to the instance
108     * which sent the get request.
109     *
110     *
111     *  (non-Javadoc)
112     * @see org.mortbay.jetty.servlet.DefaultServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
113     */
114    @Override
115    protected void doGet(HttpServletRequest request, HttpServletResponse response)
116        throws ServletException, IOException
117    {
118        if ((request.getPathInfo() != null) &&
119            (request.getPathInfo().endsWith("/script/autoquest-htmlmonitor.js")))
120        {
121            BufferedReader reader = null;
122           
123            try {
124                InputStream script = this.getClass().getClassLoader().getResourceAsStream
125                     ("autoquest-htmlmonitor.js");
126               
127                if (script == null) {
128                    Console.printerrln("could not read autoquest-htmlmonitor.js from classpath");
129                }
130                else {
131                    reader = new BufferedReader(new InputStreamReader(script, "UTF-8"));
132                    PrintWriter output = response.getWriter();
133                    String line;
134                   
135                    while ((line = reader.readLine()) != null) {
136                        output.println(line);
137                    }
138                   
139                    output.close();
140                }
141            }
142            catch (Exception e) {
143                Console.printerrln("could not read autoquest-htmlmonitor.js from classpath: " + e);
144                Console.logException(e);
145            }
146            finally {
147                if (reader != null) {
148                    reader.close();
149                }
150            }
151        }
152    }
153
154    /**
155     * this implements handling of doPost. For this servlet this means that
156     * the data from the post request will be parsed and validated.
157     *
158     * (non-Javadoc)
159     * @see org.mortbay.jetty.servlet.DefaultServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
160     */
161    @Override
162    protected void doPost(HttpServletRequest request, HttpServletResponse response)
163        throws ServletException, IOException
164    {
165        Object value = null;
166        try {
167            //InputStream requestInputStream = dumpStreamContent(request.getInputStream());
168            InputStream requestInputStream = request.getInputStream();
169
170            value = JSONValue.parseWithException
171                (new InputStreamReader(requestInputStream, "UTF-8"));
172           
173            if (!(value instanceof JSONObject)) {
174                Console.printerrln("incoming data is not of the expected type --> discarding it");
175            }
176            else {
177                handleJSONObject((JSONObject) value);
178            }
179        }
180        catch (ParseException e) {
181            Console.printerrln
182                ("could not parse incoming data --> discarding it (" + e.toString() + ")");
183        }
184       
185        // this must be done to prevent firefox from notifying the "element not found" error
186        response.setContentType("text/plain");
187        response.getOutputStream().write(' ');
188    }
189
190    /**
191     * <p>
192     * processes a received JSON object and validates it. If the message is ok, it is forwarded
193     * to the message listener
194     * </p>
195     *
196     * @param object the JSON object that contains a client message
197     */
198    private void handleJSONObject(JSONObject object) {
199        if (DO_TRACE) {
200            dumpJSONObject(object, "");
201        }
202       
203        JSONObject message = assertValue(object, "message", JSONObject.class);
204       
205        if (message == null) {
206            Console.printerrln("incoming data is no valid message --> discarding it");
207        }
208        else {
209            HtmlClientInfos clientInfos = extractClientInfos(message);
210
211            if (clientInfos == null) {
212                Console.printerrln
213                    ("incoming message does not contain valid client infos --> discarding it");
214            }
215            else if (isRobot(clientInfos.getUserAgent())) {
216                Console.printerrln
217                    ("ignoring robot " + clientInfos.getUserAgent());
218            }
219            else {
220                HtmlGUIElement guiStructure = extractHtmlPageElements(message, clientInfos);
221                HtmlEvent[] events = extractHtmlEvents(message, clientInfos);
222               
223                if (events == null) {
224                    Console.printerrln
225                        ("incoming message does not contain valid events --> discarding it");
226                }
227                else {
228                    messageListener.handleMessage(clientInfos, guiStructure, events);
229                    Console.println
230                        ("handled message of " + clientInfos.getClientId() + " (" +
231                         clientInfos.getWebAppId() + "; " + events.length + " events)");
232                }
233            }
234        }
235    }
236
237    /**
238     * <p>
239     * tries to extract the client infos out of the received JSON object. If this is not fully
240     * possible, an appropriate message is dumped and the whole message is discarded (the method
241     * return null).
242     * </p>
243     *
244     * @param message the message to parse the client infos from
245     *
246     * @return the client infos, if the message is valid in this respect, or null if not
247     */
248    private HtmlClientInfos extractClientInfos(JSONObject message) {
249        HtmlClientInfos clientInfos = null;
250       
251        JSONObject infos = assertValue(message, "clientInfos", JSONObject.class);
252       
253        if (infos != null) {
254            String clientId = assertValue((JSONObject) infos, "clientId", String.class);
255            String userAgent = assertValue((JSONObject) infos, "userAgent", String.class);
256            URL url = assertValue((JSONObject) infos, "url", URL.class);
257            String title = assertValue((JSONObject) infos, "title", String.class);
258           
259            if (clientId == null) {
260                Console.printerrln("client infos do not contain a valid client id");
261            }
262            else if (userAgent == null) {
263                Console.printerrln("client infos do not contain a valid user agent");
264            }
265            else if (url == null) {
266                Console.printerrln("client infos do not contain a valid URL");
267            }
268            else if (title == null) {
269                Console.printerrln("client infos do not contain a valid title");
270            }
271            else {
272                clientInfos = new HtmlClientInfos(clientId, userAgent, url, title);
273            }
274        }
275       
276        return clientInfos;
277    }
278
279    /**
280     * <p>
281     * tries to extract the events out of the received JSON object. If this is not fully
282     * possible, an appropriate message is dumped and the errorprone event is discarded. If no
283     * valid event is found, the whole message is discarded.
284     * </p>
285     *
286     * @param object      the message to parse the events from
287     * @param clientInfos the infos about the client that send the events
288     * 
289     * @return the valid events stored in the message, or null if there are none
290     */
291    private HtmlEvent[] extractHtmlEvents(JSONObject object, HtmlClientInfos clientInfos) {
292        List<HtmlEvent> events = null;
293       
294        JSONArray eventArray = assertValue(object, "events", JSONArray.class);
295       
296        if (eventArray != null) {
297            events = new ArrayList<HtmlEvent>();
298           
299            HtmlServer server = getServerElement(clientInfos);
300            HtmlDocument document = getPageElementRepresentingWebPage(clientInfos, server);
301
302            for (int i = 0; i < eventArray.size(); i++) {
303                Object eventObj = eventArray.get(i);
304                if (!(eventObj instanceof JSONObject)) {
305                    Console.printerrln("event number " + (i + 1) + " is not a valid event object");
306                }
307                else {
308                    Long time = assertValue(((JSONObject) eventObj), "time", Long.class);
309                    String domPath = assertValue(((JSONObject) eventObj), "path", String.class);
310                    String eventType =
311                        assertValue(((JSONObject) eventObj), "eventType", String.class);
312                    Integer[] coordinates =
313                        assertValue(((JSONObject) eventObj), "coordinates", Integer[].class);
314                    Integer key = assertValue(((JSONObject) eventObj), "key", Integer.class);
315                    Integer[] scrollPosition =
316                        assertValue(((JSONObject) eventObj), "scrollPosition", Integer[].class);
317                    String selectedValue =
318                            assertValue(((JSONObject) eventObj), "selectedValue", String.class);
319                   
320                    if (eventType == null) {
321                        Console.printerrln("event number " + (i + 1) + " has no valid event type");
322                    }
323                    else if (time == null) {
324                        Console.printerrln(eventType + " event has no valid timestamp");
325                    }
326                    else if (domPath == null) {
327                        Console.printerrln(eventType + " event has no valid DOM path");
328                    }
329                    else if ((coordinates != null) && (coordinates.length != 2)) {
330                        Console.printerrln(eventType + " event has no valid coordinates");
331                    }
332                    else if (checkEventParameterCombinations
333                                (eventType, coordinates, key, scrollPosition, selectedValue, domPath))
334                    {
335                        HtmlPageElement target =
336                            guiElementManager.getPageElement(document, domPath);
337                       
338                        if (target != null) {
339                            events.add(new HtmlEvent(clientInfos, time, target, eventType,
340                                                     coordinates, key, scrollPosition,
341                                                     selectedValue));
342                        }
343                        else {
344                            events.add(new HtmlEvent(clientInfos, time, document, domPath,
345                                                     eventType, coordinates, key, scrollPosition,
346                                                     selectedValue));
347                        }
348                    }
349                    else {
350                        Console.printerrln(eventType + " event has no valid parameter combination");
351                    }
352                }
353            }
354           
355        }
356       
357        if ((events != null) && (events.size() > 0)) {
358            return events.toArray(new HtmlEvent[events.size()]);
359        }
360        else {
361            return null;
362        }
363    }
364
365    /**
366     * <p>
367     * extracts the GUI structure from the provided JSON object.
368     * </p>
369     *
370     * @param object      the JSON object to extract the GUI structure from
371     * @param clientInfos infos about the client who send the data
372     *
373     * @return the GUI structure extracted from the JSON object of which the root node is a
374     *         representation of the server of the HTML page that was observed
375     */
376    private HtmlServer extractHtmlPageElements(JSONObject      object,
377                                               HtmlClientInfos clientInfos)
378    {
379        HtmlServer server = getServerElement(clientInfos);
380        HtmlDocument document = getPageElementRepresentingWebPage(clientInfos, server);
381
382        JSONObject jsonPageElement = assertValue(object, "guiModel", JSONObject.class);
383        document.addChild(convert(jsonPageElement, document, null));
384       
385        return server;
386    }
387
388    /**
389     * <p>
390     * instantiates an element of the GUI structure representing the server of the observed
391     * web page
392     * </p>
393     *
394     * @param clientInfos infos about the client who send the data
395     *
396     * @return as described
397     */
398    private HtmlServer getServerElement(HtmlClientInfos clientInfos) {
399        String host = clientInfos.getUrl().getHost();
400        int port = 80;
401       
402        if (clientInfos.getUrl().getPort() > -1) {
403            port = clientInfos.getUrl().getPort();
404        }
405       
406        return guiElementManager.createHtmlServer(host, port);
407    }
408
409    /**
410     * <p>
411     * instantiates an element of the GUI structure representing the observed web page. Adds
412     * this element to the provided server as child.
413     * </p>
414     *
415     * @param clientInfos infos about the client who send the data
416     * @param server      the server on which the page represented by the return value resists
417     *
418     * @return as described
419     */
420    private HtmlDocument getPageElementRepresentingWebPage(HtmlClientInfos clientInfos,
421                                                           HtmlServer      server)
422    {
423        String path = clientInfos.getUrl().getPath();
424        String query = null;
425       
426        if (clientInfos.getUrl().getQuery() != null) {
427            query = "?" + clientInfos.getUrl().getQuery();
428        }
429       
430        HtmlDocument document = guiElementManager.createHtmlDocument
431            (server, path, query, clientInfos.getTitle());
432       
433        server.addChild(document);
434       
435        return document;
436    }
437
438    /**
439     * <p>
440     * converts a JSON object representing an HTML page element to an HTML page element. Calls
441     * itself recursively to also convert the children of the element, if any.
442     * </p>
443     *
444     * @param jsonPageElement the JSON object to be converted
445     * @param document        the document to which the page element belongs
446     * @param parent          the parent page element of the converted element, of null, if none
447     *                        is present. In this case the document is considered the parent
448     *                        element.
449     *                       
450     * @return as described.
451     */
452    private HtmlPageElement convert(JSONObject      jsonPageElement,
453                                    HtmlDocument    document,
454                                    HtmlPageElement parent)
455    {
456        HtmlPageElement result = null;
457
458        if (jsonPageElement != null) {
459            String tagName = assertValue(jsonPageElement, "tagName", String.class);
460            String htmlid = assertValue(jsonPageElement, "htmlId", String.class);
461            Integer index = assertValue(jsonPageElement, "index", Integer.class);
462
463            result = guiElementManager.createHtmlPageElement
464                (document, parent, tagName, htmlid, index);
465
466            JSONArray childElements = assertValue(jsonPageElement, "children", JSONArray.class);
467           
468            if (childElements != null) {
469                Object jsonChild;
470
471                for (int i = 0; i < childElements.size(); i++) {
472                    jsonChild = childElements.get(i);
473                    if (!(jsonChild instanceof JSONObject)) {
474                        Console.printerrln("child " + (i + 1) + " of HTML page element " + tagName +
475                                           " is no valid HTML page element");
476                    }
477                    else {
478                        result.addChild(convert((JSONObject) jsonChild, document, result));
479                    }
480                }
481            }
482           
483        }
484       
485        return result;   
486    }
487
488    /**
489     * <p>
490     * validates if for the given event type the parameter combination of coordinates, key,
491     * scroll position, and selected value is valid. As an example, an onclick event should
492     * usually not have an associated scroll position.
493     * </p>
494     *
495     * @param eventType      the type of the event
496     * @param coordinates    the coordinates of the event
497     * @param key            the key of the event
498     * @param scrollPosition the scroll position of the event
499     * @param selectedValue  the value selected through a specific event
500     * @param domPath            the path through the DOM of the document of the HTML element on which
501     *                       the event was executed
502     *
503     * @return true, if the combination of the parameters is valid, false else
504     */
505    private boolean checkEventParameterCombinations(String    eventType,
506                                                    Integer[] coordinates,
507                                                    Integer   key,
508                                                    Integer[] scrollPosition,
509                                                    String    selectedValue,
510                                                    String    domPath)
511    {
512        boolean result = false;
513       
514        if ("onscroll".equals(eventType)) {
515            result =
516                (coordinates == null) && (key == null) &&
517                (scrollPosition != null) && (selectedValue == null);
518        }
519        else if ("onclick".equals(eventType) || "ondblclick".equals(eventType)) {
520            result =
521                (coordinates != null) && (key == null) &&
522                (scrollPosition == null) && (selectedValue == null);
523        }
524        else if ("onchange".equals(eventType)) {
525            // onchange events may have and may also not have a selected value. In the second
526            // case it means, that null was selected
527            result = (coordinates == null) && (key == null) && (scrollPosition == null);
528        }
529        else if ("onkeypress".equals(eventType) || "onkeydown".equals(eventType) ||
530                 "onkeyup".equals(eventType))
531        {
532            result =
533                (coordinates == null) && (key != null) &&
534                (scrollPosition == null) && (selectedValue == null);
535        }
536        else if ("onfocus".equals(eventType) || "onmouseout".equals(eventType) ||
537                 "onmousemove".equals(eventType) || "onload".equals(eventType) ||
538                 "onunload".equals(eventType) || "onbeforeunload".equals(eventType) ||
539                 "onpagehide".equals(eventType) || "onpageshow".equals(eventType) ||
540                 "onabort".equals(eventType) || "onsubmit".equals(eventType) ||
541                 "onplaying".equals(eventType) || "onpause".equals(eventType) ||
542                 "ontimeupdate".equals(eventType) || "onerror".equals(eventType) ||
543                 "onundo".equals(eventType) || "onreset".equals(eventType) ||
544                 "onselect".equals(eventType))
545        {
546            result =
547                (coordinates == null) && (key == null) &&
548                (scrollPosition == null) && (selectedValue == null);
549        }
550        else {
551            Console.printerrln("'" + eventType + "' is not a valid event type");
552        }
553       
554        if (!result) {
555            Console.printerrln(eventType + " event has invalid parameters");
556            Console.printerrln("coordinates: " + coordinates);
557            Console.printerrln("key: " + key);
558            Console.printerrln("scrollPosition: " + scrollPosition);
559            Console.printerrln("selectedValue: " + selectedValue);
560        }
561       
562        return result;
563    }
564
565    /**
566     * <p>
567     * converts a value in the provided object matching the provided key to the provided type. If
568     * there is no value with the key or if the value can not be transformed to the provided type,
569     * the method returns null.
570     * </p>
571     *
572     * @param object the object to read the value from
573     * @param key    the key of the value
574     * @param clazz  the type to which the value should be transformed
575     *
576     * @return the value or null if either the value does not exist or if it can not be transformed
577     *         to the expected type
578     */
579    @SuppressWarnings("unchecked")
580    private <T> T assertValue(JSONObject object, String key, Class<T> clazz) {
581        Object value = object.get(key);
582        T result = null;
583       
584        if (clazz.isInstance(value)) {
585            result = (T) value;
586        }
587        else if (value instanceof String) {
588            if (URL.class.equals(clazz)) {
589                try {
590                    result = (T) new URL((String) value);
591                }
592                catch (MalformedURLException e) {
593                    e.printStackTrace();
594                    Console.printerrln("retrieved malformed URL for key '" + key + "': " + value +
595                                       " (" + e.toString() + ")");
596                }
597            }
598            else if ((int.class.equals(clazz)) || (Integer.class.equals(clazz))) {
599                try {
600                    result = (T) Integer.valueOf(Integer.parseInt((String) value));
601                }
602                catch (NumberFormatException e) {
603                    Console.printerrln
604                        ("retrieved malformed integer for key '" + key + "': " + value);
605                }
606            }
607            else if ((long.class.equals(clazz)) || (Long.class.equals(clazz))) {
608                try {
609                    result = (T) Long.valueOf(Long.parseLong((String) value));
610                }
611                catch (NumberFormatException e) {
612                    Console.printerrln
613                        ("retrieved malformed long for key '" + key + "': " + value);
614                }
615            }
616        }
617        else if (value instanceof Long) {
618            if ((int.class.equals(clazz)) || (Integer.class.equals(clazz))) {
619                result = (T) (Integer) ((Long) value).intValue();
620            }
621        }
622        else if (value instanceof JSONArray) {
623            if ((int[].class.equals(clazz)) || (Integer[].class.equals(clazz))) {
624                Integer[] resultArray = new Integer[((JSONArray) value).size()];
625                boolean allCouldBeParsed = true;
626               
627                for (int i = 0; i < ((JSONArray) value).size(); i++) {
628                    try {
629                        if (((JSONArray) value).get(i) instanceof Long) {
630                            resultArray[i] = (int) (long) (Long) ((JSONArray) value).get(i);
631                        }
632                        else if (((JSONArray) value).get(i) instanceof String) {
633                            try {
634                                resultArray[i] =
635                                    (int) Long.parseLong((String) ((JSONArray) value).get(i));
636                            }
637                            catch (NumberFormatException e) {
638                                Console.printerrln
639                                    ("retrieved malformed integer array for key '" + key + "': " +
640                                     value);
641                       
642                                allCouldBeParsed = false;
643                                break;
644                            }
645                        }
646                        else {
647                            Console.printerrln
648                                ("can not handle type of value in expected integer array '" + key +
649                                 "': " + value);
650                        }
651                    }
652                    catch (ClassCastException e) {
653                        e.printStackTrace();
654                        Console.printerrln("expected integer array for key '" + key +
655                                           "' but it was something else: " + value);
656                       
657                        allCouldBeParsed = false;
658                        break;
659                    }
660                }
661               
662                if (allCouldBeParsed) {
663                    result = (T) resultArray;
664                }
665            }
666        }
667       
668        return result;
669    }
670
671    /**
672     * <p>
673     * Checks whether an agent is a robot.
674     * </p>
675     *
676     * @param agent
677     *            agent that is checked
678     * @return true, if the agent is a robot; false otherwise
679     */
680    private boolean isRobot(String agent) {
681        return agent.matches(robotRegex);
682    }
683
684    /**
685     * <p>
686     * Reads {@link #ROBOTFILTERFILE} and creates a regular expression that
687     * matches all the robots defined in the file. The regular expression is
688     * stored in the field {@link #robotRegex}.
689     * </p>
690     *
691     * @throws IOException
692     *             thrown if there is a problem reading the robot filter
693     * @throws FileNotFoundException
694     *             thrown if the robot filter is not found
695     */
696    private void loadRobotRegex() throws IOException, FileNotFoundException {
697        String[] lines = FileTools.getLinesFromFile(ROBOTFILTERFILE);
698        StringBuilder regex = new StringBuilder();
699        for (int i = 0; i < lines.length; i++) {
700            regex.append("(.*" + lines[i] + ".*)");
701            if (i != lines.length - 1) {
702                regex.append('|');
703            }
704        }
705        robotRegex = regex.toString();
706    }
707
708    /**
709     * <p>
710     * convenience method for dumping the content of a stream and returning a new stream
711     * containing the same data.
712     * </p>
713     *
714     * @param inputStream the stream to be dumped and copied
715     * @return the copy of the stream
716     *
717     * @throws IOException if the stream can not be read
718     */
719/*    private InputStream dumpStreamContent(ServletInputStream inputStream) throws IOException {
720        List<Byte> bytes = new ArrayList<Byte>();
721        int buf;
722       
723        while ((buf = inputStream.read()) >= 0) {
724            bytes.add((byte) buf);
725        }
726       
727        byte[] byteArray = new byte[bytes.size()];
728        for (int i = 0; i < bytes.size(); i++) {
729            byteArray[i] = bytes.get(i);
730        }
731       
732        System.out.println(new String(byteArray, "UTF-8"));
733       
734        return new ByteArrayInputStream(byteArray);
735    }*/
736
737    /**
738     * <p>
739     * convenience method for dumping an object to std out. If the object is a JSON object, it is
740     * deeply analyzed and its internal structure is dumped as well.
741     * </p>
742     *
743     * @param object the object to dump
744     * @param indent the indentation to be used.
745     */
746    private void dumpJSONObject(Object object, String indent) {
747        if (object instanceof JSONArray) {
748            boolean arrayContainsJSONObjects = false;
749            for (Object arrayElem : (JSONArray) object) {
750                if (arrayElem instanceof JSONObject) {
751                    arrayContainsJSONObjects = true;
752                    break;
753                }               
754            }
755           
756            if (arrayContainsJSONObjects) {
757                System.out.println();
758                System.out.print(indent);
759                System.out.println('[');
760                System.out.print(indent);
761                System.out.print(' ');
762            }
763            else {
764                System.out.print(' ');
765                System.out.print('[');
766            }
767           
768            int index = 0;
769            for (Object arrayElem : (JSONArray) object) {
770                if (index++ > 0) {
771                    System.out.print(",");
772                    if (arrayContainsJSONObjects) {
773                        System.out.println();
774                        System.out.print(indent);
775                    }
776
777                    System.out.print(' ');
778                }
779
780                dumpJSONObject(arrayElem, indent + "  ");
781            }
782           
783            if (arrayContainsJSONObjects) {
784                System.out.println();
785                System.out.print(indent);
786            }
787           
788            System.out.print(']');
789        }
790        else if (object instanceof JSONObject) {
791            System.out.println(" {");
792           
793            @SuppressWarnings("unchecked")
794            Set<Map.Entry<?,?>> entrySet = ((JSONObject) object).entrySet();
795           
796            int index = 0;
797            for (Map.Entry<?,?> entry : entrySet) {
798                if (index++ > 0) {
799                    System.out.println(",");
800                }
801                System.out.print(indent);
802                System.out.print("  \"");
803                System.out.print(entry.getKey());
804                System.out.print("\":");
805                dumpJSONObject(entry.getValue(), indent + "  ");
806            }
807           
808            System.out.println();
809            System.out.print(indent);
810            System.out.print('}');
811        }
812        else {
813            System.out.print('"');
814            System.out.print(object);
815            System.out.print('"');
816        }
817    }
818
819}
Note: See TracBrowser for help on using the repository browser.