Coverage Report - com.jcabi.http.response.JsonResponse
 
Classes in this File Line Coverage Branch Coverage Complexity
JsonResponse
72%
13/18
20%
2/10
2.444
JsonResponse$VerboseReader
86%
13/15
N/A
2.444
 
 1  
 /**
 2  
  * Copyright (c) 2011-2017, jcabi.com
 3  
  * All rights reserved.
 4  
  *
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions
 7  
  * are met: 1) Redistributions of source code must retain the above
 8  
  * copyright notice, this list of conditions and the following
 9  
  * disclaimer. 2) Redistributions in binary form must reproduce the above
 10  
  * copyright notice, this list of conditions and the following
 11  
  * disclaimer in the documentation and/or other materials provided
 12  
  * with the distribution. 3) Neither the name of the jcabi.com nor
 13  
  * the names of its contributors may be used to endorse or promote
 14  
  * products derived from this software without specific prior written
 15  
  * permission.
 16  
  *
 17  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  
  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
 19  
  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 20  
  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
 21  
  * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 22  
  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 23  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 24  
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 25  
  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 26  
  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 27  
  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 28  
  * OF THE POSSIBILITY OF SUCH DAMAGE.
 29  
  */
 30  
 package com.jcabi.http.response;
 31  
 
 32  
 import com.jcabi.aspects.Immutable;
 33  
 import com.jcabi.http.Response;
 34  
 import java.io.StringReader;
 35  
 import java.io.UnsupportedEncodingException;
 36  
 import java.util.regex.Matcher;
 37  
 import java.util.regex.Pattern;
 38  
 import javax.json.Json;
 39  
 import javax.json.JsonArray;
 40  
 import javax.json.JsonObject;
 41  
 import javax.json.JsonReader;
 42  
 import javax.json.JsonStructure;
 43  
 import javax.json.stream.JsonParsingException;
 44  
 import lombok.EqualsAndHashCode;
 45  
 
 46  
 /**
 47  
  * JSON response.
 48  
  *
 49  
  * <p>This response decorator is able to parse HTTP response body as
 50  
  * a JSON document and manipulate with it afterwords, for example:
 51  
  *
 52  
  * <pre> String name = new JdkRequest("http://my.example.com")
 53  
  *   .header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON)
 54  
  *   .fetch()
 55  
  *   .as(JsonResponse.class)
 56  
  *   .json()
 57  
  *   .readObject()
 58  
  *   .getString("name");</pre>
 59  
  *
 60  
  * <p>The class is immutable and thread-safe.
 61  
  *
 62  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 63  
  * @version $Id: 2d1bb024cec199d41ad240c83456be45e5678523 $
 64  
  * @since 0.8
 65  
  */
 66  0
 @Immutable
 67  0
 @EqualsAndHashCode(callSuper = true)
 68  
 public final class JsonResponse extends AbstractResponse {
 69  
 
 70  
     /**
 71  
      * Pattern matching non-ASCII characters, to escape them before parsing.
 72  
      */
 73  1
     private static final Pattern CONTROL = Pattern.compile(
 74  
         "[\u0000-\u0008\u000e-\u001f\u007f-\uffff]"
 75  
     );
 76  
 
 77  
     /**
 78  
      * Public ctor.
 79  
      * @param resp Response
 80  
      */
 81  
     public JsonResponse(final Response resp) {
 82  5
         super(resp);
 83  5
     }
 84  
 
 85  
     /**
 86  
      * Verifies the JSON data against the element identifier argument,
 87  
      * and throws {@link AssertionError} in case of mismatch.
 88  
      * @param element Element in the JSON data of this object
 89  
      * @return This object
 90  
      */
 91  
     public JsonResponse assertJson(final String element) {
 92  0
         throw new UnsupportedOperationException(
 93  
             // @checkstyle LineLength (1 line)
 94  
             "assertJson() is not implemented yet, since we are not sure which JSON query standard to use"
 95  
         );
 96  
     }
 97  
 
 98  
     /**
 99  
      * Read body as JSON.
 100  
      * @return Json reader
 101  
      */
 102  
     public JsonReader json() {
 103  6
         final byte[] body = this.binary();
 104  
         final String json;
 105  
         try {
 106  6
             json = new String(body, "UTF-8");
 107  0
         } catch (final UnsupportedEncodingException ex) {
 108  0
             throw new IllegalStateException(ex);
 109  6
         }
 110  6
         return new JsonResponse.VerboseReader(
 111  
             Json.createReader(
 112  
                 new StringReader(
 113  
                     JsonResponse.escape(json)
 114  
                 )
 115  
             ),
 116  
             json
 117  
         );
 118  
     }
 119  
 
 120  
     /**
 121  
      * Escape control characters in JSON parsing.
 122  
      *
 123  
      * @param input The input JSON string
 124  
      * @return Escaped JSON
 125  
      * @see <a href="http://tools.ietf.org/html/rfc4627">RFC 4627</a>
 126  
      */
 127  
     private static String escape(final CharSequence input) {
 128  6
         final Matcher matcher = JsonResponse.CONTROL.matcher(input);
 129  6
         final StringBuffer escaped = new StringBuffer(input.length());
 130  11
         while (matcher.find()) {
 131  5
             matcher.appendReplacement(
 132  
                 escaped,
 133  
                 String.format("\\\\u%04X", (int) matcher.group().charAt(0))
 134  
             );
 135  
         }
 136  6
         matcher.appendTail(escaped);
 137  6
         return escaped.toString();
 138  
     }
 139  
 
 140  
     /**
 141  
      * Verbose reader.
 142  
      */
 143  
     private static final class VerboseReader implements JsonReader {
 144  
         /**
 145  
          * Original reader.
 146  
          */
 147  
         private final transient JsonReader origin;
 148  
         /**
 149  
          * JSON body.
 150  
          */
 151  
         private final transient String json;
 152  
         /**
 153  
          * Ctor.
 154  
          * @param reader Original reader
 155  
          * @param body JSON body
 156  
          */
 157  6
         VerboseReader(final JsonReader reader, final String body) {
 158  6
             this.origin = reader;
 159  6
             this.json = body;
 160  6
         }
 161  
         @Override
 162  
         public JsonObject readObject() {
 163  
             try {
 164  4
                 return this.origin.readObject();
 165  1
             } catch (final JsonParsingException ex) {
 166  1
                 throw new JsonParsingException(
 167  
                     this.json, ex, ex.getLocation()
 168  
                 );
 169  
             }
 170  
         }
 171  
         @Override
 172  
         public JsonArray readArray() {
 173  
             try {
 174  1
                 return this.origin.readArray();
 175  1
             } catch (final JsonParsingException ex) {
 176  1
                 throw new JsonParsingException(
 177  
                     this.json, ex, ex.getLocation()
 178  
                 );
 179  
             }
 180  
         }
 181  
         @Override
 182  
         public JsonStructure read() {
 183  
             try {
 184  1
                 return this.origin.read();
 185  1
             } catch (final JsonParsingException ex) {
 186  1
                 throw new JsonParsingException(
 187  
                     this.json, ex, ex.getLocation()
 188  
                 );
 189  
             }
 190  
         }
 191  
         @Override
 192  
         public void close() {
 193  0
             this.origin.close();
 194  0
         }
 195  
     }
 196  
 
 197  
 }