Coverage Report - com.jcabi.xml.XSDDocument
 
Classes in this File Line Coverage Branch Coverage Complexity
XSDDocument
55%
20/36
0%
0/12
1.692
XSDDocument$ValidationHandler
77%
7/9
N/A
1.692
 
 1  
 /**
 2  
  * Copyright (c) 2012-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.xml;
 31  
 
 32  
 import com.jcabi.log.Logger;
 33  
 import java.io.IOException;
 34  
 import java.io.InputStream;
 35  
 import java.io.StringReader;
 36  
 import java.net.URI;
 37  
 import java.net.URL;
 38  
 import java.util.Collection;
 39  
 import java.util.concurrent.CopyOnWriteArrayList;
 40  
 import javax.xml.XMLConstants;
 41  
 import javax.xml.transform.Source;
 42  
 import javax.xml.transform.stream.StreamSource;
 43  
 import javax.xml.validation.Schema;
 44  
 import javax.xml.validation.SchemaFactory;
 45  
 import javax.xml.validation.Validator;
 46  
 import lombok.EqualsAndHashCode;
 47  
 import org.xml.sax.ErrorHandler;
 48  
 import org.xml.sax.SAXException;
 49  
 import org.xml.sax.SAXParseException;
 50  
 
 51  
 /**
 52  
  * Implementation of {@link XSD}.
 53  
  *
 54  
  * <p>Objects of this class are immutable and thread-safe.
 55  
  *
 56  
  * @author Yegor Bugayenko (yegor@teamed.io)
 57  
  * @version $Id: 0fc5670d929cb6f6f8ce51038a17ae56ac6dacbb $
 58  
  * @since 0.5
 59  
  */
 60  0
 @EqualsAndHashCode(of = "xsd")
 61  
 public final class XSDDocument implements XSD {
 62  
 
 63  
     /**
 64  
      * XSD document.
 65  
      */
 66  
     private final transient String xsd;
 67  
 
 68  
     /**
 69  
      * Public ctor, from XSD as a source.
 70  
      * @param src XSD document body
 71  
      */
 72  
     public XSDDocument(final XML src) {
 73  0
         this(src.toString());
 74  0
     }
 75  
 
 76  
     /**
 77  
      * Public ctor, from XSD as a string.
 78  
      * @param src XSD document body
 79  
      */
 80  12
     public XSDDocument(final String src) {
 81  12
         this.xsd = src;
 82  12
     }
 83  
 
 84  
     /**
 85  
      * Public ctor, from URL.
 86  
      * @param url Location of document
 87  
      * @throws IOException If fails to read
 88  
      * @since 0.7.4
 89  
      */
 90  
     public XSDDocument(final URL url) throws IOException {
 91  1
         this(new TextResource(url).toString());
 92  1
     }
 93  
 
 94  
     /**
 95  
      * Public ctor, from URI.
 96  
      * @param uri Location of document
 97  
      * @throws IOException If fails to read
 98  
      * @since 0.15
 99  
      */
 100  
     public XSDDocument(final URI uri) throws IOException {
 101  0
         this(new TextResource(uri).toString());
 102  0
     }
 103  
 
 104  
     /**
 105  
      * Public ctor, from XSD as an input stream.
 106  
      * @param stream XSD input stream
 107  
      */
 108  
     public XSDDocument(final InputStream stream) {
 109  1
         this(new TextResource(stream).toString());
 110  1
     }
 111  
 
 112  
     /**
 113  
      * Make an instance of XSD schema without I/O exceptions.
 114  
      *
 115  
      * <p>This factory method is useful when you need to create
 116  
      * an instance of XSD schema as a static final variable. In this
 117  
      * case you can't catch an exception but this method can help, for example:
 118  
      *
 119  
      * <pre> class Foo {
 120  
      *   private static final XSD SCHEMA = XSDDocument.make(
 121  
      *     Foo.class.getResourceAsStream("my-schema.xsd")
 122  
      *   );
 123  
      * }</pre>
 124  
      *
 125  
      * @param stream Input stream
 126  
      * @return XSD schema
 127  
      */
 128  
     public static XSD make(final InputStream stream) {
 129  0
         return new XSDDocument(stream);
 130  
     }
 131  
 
 132  
     /**
 133  
      * Make an instance of XSD schema without I/O exceptions.
 134  
      * @param url URL with content
 135  
      * @return XSD schema
 136  
      * @see #make(InputStream)
 137  
      * @since 0.7.4
 138  
      */
 139  
     public static XSD make(final URL url) {
 140  
         try {
 141  0
             return new XSDDocument(url);
 142  0
         } catch (final IOException ex) {
 143  0
             throw new IllegalStateException(ex);
 144  
         }
 145  
     }
 146  
 
 147  
     @Override
 148  
     public String toString() {
 149  0
         return new XMLDocument(this.xsd).toString();
 150  
     }
 151  
 
 152  
     @Override
 153  
     public Collection<SAXParseException> validate(final Source xml) {
 154  
         final Schema schema;
 155  
         try {
 156  71
             synchronized (XSDDocument.class) {
 157  71
                 schema = SchemaFactory
 158  
                     .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI)
 159  
                     .newSchema(new StreamSource(new StringReader(this.xsd)));
 160  71
             }
 161  0
         } catch (final SAXException ex) {
 162  0
             throw new IllegalStateException(
 163  
                 String.format("failed to create XSD schema from %s", this.xsd),
 164  
                 ex
 165  
             );
 166  71
         }
 167  71
         final Collection<SAXParseException> errors =
 168  
             new CopyOnWriteArrayList<SAXParseException>();
 169  71
         final Validator validator = schema.newValidator();
 170  71
         validator.setErrorHandler(new ValidationHandler(errors));
 171  
         try {
 172  71
             synchronized (XSDDocument.class) {
 173  71
                 validator.validate(xml);
 174  71
             }
 175  0
         } catch (final SAXException ex) {
 176  0
             throw new IllegalStateException(ex);
 177  0
         } catch (final IOException ex) {
 178  0
             throw new IllegalStateException(ex);
 179  71
         }
 180  71
         Logger.debug(
 181  
             this, "%s detected %d error(s)",
 182  
             schema.getClass().getName(), errors.size()
 183  
         );
 184  71
         return errors;
 185  
     }
 186  
 
 187  
     /**
 188  
      * Validation error handler.
 189  
      */
 190  
     static final class ValidationHandler implements ErrorHandler {
 191  
         /**
 192  
          * Errors.
 193  
          */
 194  
         private final transient Collection<SAXParseException> errors;
 195  
         /**
 196  
          * Constructor.
 197  
          * @param errs Collection of errors
 198  
          */
 199  77
         ValidationHandler(final Collection<SAXParseException> errs) {
 200  77
             this.errors = errs;
 201  77
         }
 202  
         @Override
 203  
         public void warning(final SAXParseException error) {
 204  5
             this.errors.add(error);
 205  5
         }
 206  
         @Override
 207  
         public void error(final SAXParseException error) {
 208  890
             this.errors.add(error);
 209  890
         }
 210  
         @Override
 211  
         public void fatalError(final SAXParseException error) {
 212  0
             this.errors.add(error);
 213  0
         }
 214  
     }
 215  
 
 216  
 }