Coverage Report - com.jcabi.matchers.JaxbConverter
 
Classes in this File Line Coverage Branch Coverage Complexity
JaxbConverter
73%
25/34
50%
5/10
3
 
 1  
 /**
 2  
  * Copyright (c) 2011-2014, 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.matchers;
 31  
 
 32  
 import java.io.StringWriter;
 33  
 import javax.xml.bind.JAXBContext;
 34  
 import javax.xml.bind.JAXBElement;
 35  
 import javax.xml.bind.JAXBException;
 36  
 import javax.xml.bind.JAXBIntrospector;
 37  
 import javax.xml.bind.Marshaller;
 38  
 import javax.xml.bind.annotation.XmlType;
 39  
 import javax.xml.namespace.QName;
 40  
 import javax.xml.transform.Source;
 41  
 import lombok.EqualsAndHashCode;
 42  
 import lombok.ToString;
 43  
 
 44  
 /**
 45  
  * JAXB-empowered object to XML converting utility.
 46  
  *
 47  
  * <p>The object has to be annotated with JAXB annotations
 48  
  * in order to be convertable. Let's consider an example JAXB-annotated class:
 49  
  *
 50  
  * <pre> import javax.xml.bind.annotation.XmlAccessType;
 51  
  * import javax.xml.bind.annotation.XmlAccessorType;
 52  
  * import javax.xml.bind.annotation.XmlElement;
 53  
  * import javax.xml.bind.annotation.XmlRootElement;
 54  
  * &#64;XmlRootElement(name = "employee")
 55  
  * &#64;XmlAccessorType(XmlAccessType.NONE)
 56  
  * public class Employee {
 57  
  *   &#64;XmlElement(name = "name")
 58  
  *   public String getName() {
 59  
  *     return "John Doe";
 60  
  *   }
 61  
  * }</pre>
 62  
  *
 63  
  * <p>Now you want to test how it works with real data after convertion
 64  
  * to XML (in a unit test):
 65  
  *
 66  
  * <pre> import com.jcabi.matchers.JaxbConverter;
 67  
  * import com.jcabi.matchers.XhtmlMatchers;
 68  
  * import org.junit.Assert;
 69  
  * import org.junit.Test;
 70  
  * public final class EmployeeTest {
 71  
  *   &#64;Test
 72  
  *   public void testObjectToXmlConversion() throws Exception {
 73  
  *     final Object object = new Employee();
 74  
  *     Assert.assertThat(
 75  
  *       JaxbConverter.the(object),
 76  
  *       XhtmlMatchers.hasXPath("/employee/name[.='John Doe']")
 77  
  *     );
 78  
  *   }
 79  
  * }</pre>
 80  
  *
 81  
  * @author Yegor Bugayenko (yegor@tpc2.com)
 82  
  * @version $Id$
 83  
  */
 84  0
 @ToString
 85  0
 @EqualsAndHashCode
 86  
 public final class JaxbConverter {
 87  
 
 88  
     /**
 89  
      * Private ctor, to avoid direct instantiation of the class.
 90  
      */
 91  0
     private JaxbConverter() {
 92  
         // intentionally empty
 93  0
     }
 94  
 
 95  
     /**
 96  
      * Convert an object to XML.
 97  
      *
 98  
      * <p>The method will throw {@link AssertionError} if marshalling of
 99  
      * provided object fails for some reason.
 100  
      *
 101  
      * <p>The name of the method is motivated by
 102  
      * <a href="http://code.google.com/p/xml-matchers/">xmlatchers</a> project
 103  
      * and their {@code XmlMatchers.the(String)} method. Looks like this name
 104  
      * is short enough and convenient for unit tests.
 105  
      *
 106  
      * @param object The object to convert
 107  
      * @param deps Dependencies that we should take into account
 108  
      * @return DOM source/document
 109  
      * @throws JAXBException If an exception occurs inside
 110  
      */
 111  
     public static Source the(final Object object, final Class<?>... deps)
 112  
         throws JAXBException {
 113  6
         final Class<?>[] classes = new Class<?>[deps.length + 1];
 114  6
         classes[0] = object.getClass();
 115  6
         System.arraycopy(deps, 0, classes, 1, deps.length);
 116  
         final JAXBContext ctx;
 117  
         try {
 118  6
             ctx = JAXBContext.newInstance(classes);
 119  0
         } catch (final JAXBException ex) {
 120  0
             throw new IllegalArgumentException(ex);
 121  6
         }
 122  6
         final JAXBIntrospector intro = ctx.createJAXBIntrospector();
 123  6
         Object subject = object;
 124  6
         if (intro.getElementName(object) == null) {
 125  
             @SuppressWarnings("unchecked")
 126  3
             final Class<Object> type = (Class<Object>) object.getClass();
 127  3
             subject = new JAXBElement<Object>(
 128  
                 JaxbConverter.qname(object),
 129  
                 type,
 130  
                 object
 131  
             );
 132  
         }
 133  6
         final Marshaller mrsh = JaxbConverter.marshaller(ctx);
 134  6
         final StringWriter writer = new StringWriter();
 135  
         try {
 136  6
             mrsh.marshal(subject, writer);
 137  0
         } catch (final JAXBException ex) {
 138  0
             throw new AssertionError(ex);
 139  6
         }
 140  6
         final String xml = writer.toString();
 141  6
         return new StringSource(xml);
 142  
     }
 143  
 
 144  
     /**
 145  
      * Create marshaller.
 146  
      * @param ctx The context
 147  
      * @return Marshaller
 148  
      * @throws JAXBException If an exception occurs inside
 149  
      */
 150  
     private static Marshaller marshaller(final JAXBContext ctx)
 151  
         throws JAXBException {
 152  6
         final Marshaller mrsh = ctx.createMarshaller();
 153  6
         mrsh.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
 154  6
         return mrsh;
 155  
     }
 156  
 
 157  
     /**
 158  
      * Get type name, if XmlType annotation is present (exception otherwise).
 159  
      * @param obj The object
 160  
      * @return Qualified name
 161  
      * @see XmlElement#namespace()
 162  
      */
 163  
     private static QName qname(final Object obj) {
 164  3
         final XmlType type = XmlType.class.cast(
 165  
             obj.getClass().getAnnotation(XmlType.class)
 166  
         );
 167  3
         if (type == null) {
 168  0
             throw new AssertionError(
 169  
                 String.format(
 170  
                     // @checkstyle LineLength (1 line)
 171  
                     "@XmlType or @XmlRootElement annotation required at %s",
 172  
                     obj.getClass().getName()
 173  
                 )
 174  
             );
 175  
         }
 176  
         final QName qname;
 177  3
         if ("##default".equals(type.namespace())) {
 178  1
             qname = new QName(type.name());
 179  
         } else {
 180  2
             qname = new QName(type.namespace(), type.name());
 181  
         }
 182  3
         return qname;
 183  
     }
 184  
 
 185  
 }