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

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