Coverage Report - com.jcabi.http.response.XmlResponse
 
Classes in this File Line Coverage Branch Coverage Complexity
XmlResponse
65%
13/20
8%
1/12
1.429
 
 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.Request;
 34  
 import com.jcabi.http.Response;
 35  
 import com.jcabi.immutable.ArrayMap;
 36  
 import com.jcabi.matchers.XhtmlMatchers;
 37  
 import com.jcabi.xml.XML;
 38  
 import com.jcabi.xml.XMLDocument;
 39  
 import com.jcabi.xml.XPathContext;
 40  
 import java.net.URI;
 41  
 import java.util.Map;
 42  
 import javax.xml.namespace.NamespaceContext;
 43  
 import lombok.EqualsAndHashCode;
 44  
 
 45  
 /**
 46  
  * XML response.
 47  
  *
 48  
  * <p>This response decorator is able to parse HTTP response body as
 49  
  * an XML document and manipulate with it afterwords, for example:
 50  
  *
 51  
  * <pre> String name = new JdkRequest("http://my.example.com")
 52  
  *   .header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)
 53  
  *   .fetch()
 54  
  *   .as(XmlResponse.class)
 55  
  *   .assertXPath("/user/name")
 56  
  *   .xml()
 57  
  *   .xpath("/user/name/text()")
 58  
  *   .get(0);</pre>
 59  
  *
 60  
  * <p>In <a href="http://en.wikipedia.org/wiki/HATEOAS">HATEOAS</a>
 61  
  * responses it is convenient to use this decorator's
 62  
  * method {@link #rel(String)}
 63  
  * in order to follow the link provided in {@code &lt;link&gt;} XML element,
 64  
  * for example:
 65  
  *
 66  
  * <pre> String data = new JdkRequest("http://my.example.com")
 67  
  *   .header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)
 68  
  *   .fetch()
 69  
  *   .as(XmlResponse.class)
 70  
  *   .rel("/user/links/link[@rel='next']/@href")
 71  
  *   .fetch()
 72  
  *   .body();</pre>
 73  
  *
 74  
  * <p>The class is immutable and thread-safe.
 75  
  *
 76  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 77  
  * @version $Id: 9441436bbd0e41473e29063e0be22884f666a2c3 $
 78  
  * @since 0.8
 79  
  */
 80  6
 @Immutable
 81  0
 @EqualsAndHashCode(callSuper = true)
 82  
 public final class XmlResponse extends AbstractResponse {
 83  
 
 84  
     /**
 85  
      * Map of namespaces.
 86  
      */
 87  
     private final transient ArrayMap<String, String> namespaces;
 88  
 
 89  
     /**
 90  
      * Public ctor.
 91  
      * @param resp Response
 92  
      */
 93  
     public XmlResponse(final Response resp) {
 94  8
         this(resp, new ArrayMap<String, String>());
 95  8
     }
 96  
 
 97  
     /**
 98  
      * Public ctor.
 99  
      * @param resp Response
 100  
      * @param map Map of namespaces
 101  
      */
 102  
     private XmlResponse(final Response resp,
 103  
         final ArrayMap<String, String> map) {
 104  9
         super(resp);
 105  9
         this.namespaces = map;
 106  9
     }
 107  
 
 108  
     /**
 109  
      * Get XML body.
 110  
      * @return XML body
 111  
      */
 112  
     public XML xml() {
 113  3
         return new XMLDocument(this.body()).merge(this.context());
 114  
     }
 115  
 
 116  
     /**
 117  
      * Register this new namespace.
 118  
      * @param prefix Prefix to use
 119  
      * @param uri Namespace URI
 120  
      * @return This object
 121  
      */
 122  
     public XmlResponse registerNs(final String prefix, final String uri) {
 123  1
         return new XmlResponse(this, this.namespaces.with(prefix, uri));
 124  
     }
 125  
 
 126  
     /**
 127  
      * Verifies HTTP response body XHTML/XML content against XPath query,
 128  
      * and throws {@link AssertionError} in case of mismatch.
 129  
      * @param xpath Query to use
 130  
      * @return This object
 131  
      */
 132  
     public XmlResponse assertXPath(final String xpath) {
 133  6
         final String body = this.body();
 134  6
         if (!XhtmlMatchers.hasXPath(xpath, this.context()).matches(body)) {
 135  0
             throw new AssertionError(
 136  
                 String.format(
 137  
                     "XML doesn't contain required XPath '%s':%n%s",
 138  
                     xpath, body
 139  
                 )
 140  
             );
 141  
         }
 142  0
         return this;
 143  
     }
 144  
 
 145  
     /**
 146  
      * Follow XML link.
 147  
      * @param query XPath query to fetch new URI
 148  
      * @return New request
 149  
      */
 150  
     public Request rel(final String query) {
 151  0
         this.assertXPath(query);
 152  0
         return new RestResponse(this).jump(
 153  
             URI.create(this.xml().xpath(query).get(0))
 154  
         );
 155  
     }
 156  
 
 157  
     /**
 158  
      * Create XPath context.
 159  
      * @return Context
 160  
      */
 161  
     private NamespaceContext context() {
 162  6
         XPathContext context = new XPathContext();
 163  
         for (final Map.Entry<String, String> entry
 164  6
             : this.namespaces.entrySet()) {
 165  0
             context = context.add(entry.getKey(), entry.getValue());
 166  0
         }
 167  6
         return context;
 168  
     }
 169  
 
 170  
 }