Coverage Report - com.jcabi.http.response.XmlResponse
 
Classes in this File Line Coverage Branch Coverage Complexity
XmlResponse
66%
12/18
10%
1/10
1.143
 
 1  
 /**
 2  
  * Copyright (c) 2011-2015, 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  
 import org.hamcrest.MatcherAssert;
 45  
 
 46  
 /**
 47  
  * XML response.
 48  
  *
 49  
  * <p>This response decorator is able to parse HTTP response body as
 50  
  * an XML document and manipulate with it afterwords, for example:
 51  
  *
 52  
  * <pre> String name = new JdkRequest("http://my.example.com")
 53  
  *   .header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)
 54  
  *   .fetch()
 55  
  *   .as(XmlResponse.class)
 56  
  *   .assertXPath("/user/name")
 57  
  *   .xml()
 58  
  *   .xpath("/user/name/text()")
 59  
  *   .get(0);</pre>
 60  
  *
 61  
  * <p>In <a href="http://en.wikipedia.org/wiki/HATEOAS">HATEOAS</a>
 62  
  * responses it is convenient to use this decorator's
 63  
  * method {@link #rel(String)}
 64  
  * in order to follow the link provided in {@code &lt;link&gt;} XML element,
 65  
  * for example:
 66  
  *
 67  
  * <pre> String data = new JdkRequest("http://my.example.com")
 68  
  *   .header(HttpHeaders.ACCEPT, MediaType.TEXT_XML)
 69  
  *   .fetch()
 70  
  *   .as(XmlResponse.class)
 71  
  *   .rel("/user/links/link[@rel='next']/@href")
 72  
  *   .fetch()
 73  
  *   .body();</pre>
 74  
  *
 75  
  * <p>The class is immutable and thread-safe.
 76  
  *
 77  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 78  
  * @version $Id: ea68841aba5aa3153c80ab2517ad472da2c28dd4 $
 79  
  * @since 0.8
 80  
  */
 81  12
 @Immutable
 82  0
 @EqualsAndHashCode(callSuper = true)
 83  
 public final class XmlResponse extends AbstractResponse {
 84  
 
 85  
     /**
 86  
      * Map of namespaces.
 87  
      */
 88  
     private final transient ArrayMap<String, String> namespaces;
 89  
 
 90  
     /**
 91  
      * Public ctor.
 92  
      * @param resp Response
 93  
      */
 94  
     public XmlResponse(final Response resp) {
 95  9
         this(resp, new ArrayMap<String, String>());
 96  9
     }
 97  
 
 98  
     /**
 99  
      * Public ctor.
 100  
      * @param resp Response
 101  
      * @param map Map of namespaces
 102  
      */
 103  
     private XmlResponse(final Response resp,
 104  
         final ArrayMap<String, String> map) {
 105  10
         super(resp);
 106  10
         this.namespaces = map;
 107  10
     }
 108  
 
 109  
     /**
 110  
      * Get XML body.
 111  
      * @return XML body
 112  
      */
 113  
     public XML xml() {
 114  3
         return new XMLDocument(this.body()).merge(this.context());
 115  
     }
 116  
 
 117  
     /**
 118  
      * Register this new namespace.
 119  
      * @param prefix Prefix to use
 120  
      * @param uri Namespace URI
 121  
      * @return This object
 122  
      */
 123  
     public XmlResponse registerNs(final String prefix, final String uri) {
 124  1
         return new XmlResponse(this, this.namespaces.with(prefix, uri));
 125  
     }
 126  
 
 127  
     /**
 128  
      * Verifies HTTP response body XHTML/XML content against XPath query,
 129  
      * and throws {@link AssertionError} in case of mismatch.
 130  
      * @param xpath Query to use
 131  
      * @return This object
 132  
      */
 133  
     public XmlResponse assertXPath(final String xpath) {
 134  6
         MatcherAssert.assertThat(
 135  
             String.format(
 136  
                 "XML doesn't contain required XPath '%s':%n%s",
 137  
                 xpath, this.body()
 138  
             ),
 139  
             this.body(),
 140  
             XhtmlMatchers.hasXPath(xpath, this.context())
 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  
 }