/** * Copyright (C) 2012-2013 Selventa, Inc. * * This file is part of the OpenBEL Framework. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * The OpenBEL Framework is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with the OpenBEL Framework. If not, see <http://www.gnu.org/licenses/>. * * Additional Terms under LGPL v3: * * This license does not authorize you and you are prohibited from using the * name, trademarks, service marks, logos or similar indicia of Selventa, Inc., * or, in the discretion of other licensors or authors of the program, the * name, trademarks, service marks, logos or similar indicia of such authors or * licensors, in any marketing or advertising materials relating to your * distribution of the program or any covered product. This restriction does * not waive or limit your obligation to keep intact all copyright notices set * forth in the program as delivered to you. * * If you distribute the program in whole or in part, or any modified version * of the program, and you assume contractual liability to the recipient with * respect to the program or modified version, then you will indemnify the * authors and licensors of the program for any liabilities that these * contractual assumptions directly impose on those licensors and authors. */ package org.openbel.framework.common.xbel.parser; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; import static javax.xml.validation.SchemaFactory.newInstance; import static org.openbel.framework.common.BELUtilities.noItems; import static org.openbel.framework.common.Strings.UTF_8; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.List; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator; import org.openbel.framework.common.InvalidArgument; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; /** * Implements an XBEL validator. * <p> * This is nothing XBEL-specific about this class, apart from its name. * </p> * * @author Anthony Bargnesi {@code <abargnesi@selventa.com>} */ public class XBELValidator implements XMLValidator { private final Schema schema; /** * Creates an XBEL validator that processes the document's defined schema * to validate against. * * @throws SAXException Thrown if an error is found during processing the * specified schemas; the current API does not provide {@link ErrorHandler * SAX error handler} functionality */ public XBELValidator() throws SAXException { final SchemaFactory sf = newInstance(W3C_XML_SCHEMA_NS_URI); schema = sf.newSchema(); } /** * Creates a XBEL validator associated with any number of XML schemas. * * @param schemas Paths to XML schemas * @throws SAXException Thrown if an error is found during processing the * specified schemas; the current API does not provide {@link ErrorHandler * SAX error handler} functionality * @throws InvalidArgument Thrown if schemas is null or zero-length */ public XBELValidator(final String... schemas) throws SAXException { if (noItems(schemas)) throw new InvalidArgument("schemas is null or empty"); final List<Source> sources = new ArrayList<Source>(schemas.length); for (final String schema : schemas) { final Source s = new StreamSource(new File(schema)); sources.add(s); } final SchemaFactory sf = newInstance(W3C_XML_SCHEMA_NS_URI); schema = sf.newSchema(sources.toArray(new Source[0])); } /** * Creates a XBEL validator associated with any number of XML schemas. * * @param schemas XML schema files * @throws SAXException Thrown if an error is found during processing the * specified schemas; the current API does not provide {@link ErrorHandler * SAX error handler} functionality * @throws InvalidArgument Thrown if schemas is null or zero-length */ public XBELValidator(final File... schemas) throws SAXException { if (noItems(schemas)) throw new InvalidArgument("schemas is null or empty"); final List<Source> sources = new ArrayList<Source>(schemas.length); for (final File schema : schemas) { final Source s = new StreamSource(schema); sources.add(s); } final SchemaFactory sf = newInstance(W3C_XML_SCHEMA_NS_URI); schema = sf.newSchema(sources.toArray(new Source[0])); } /** * Creates a XBEL validator associated with any number of XML schemas. * * @param schemas {@link URL}s, the XML schema urls * @throws SAXException thrown if an error is found during processing the * specified schemas; the current API does not provide {@link ErrorHandler * SAX error handler} functionality * @throws URISyntaxException thrown if a {@link URL} in {@code schemas} has * invalid {@link URI} syntax * @throws IOException thrown if an i/o error occurs reading from the schema * url's {@link InputStream} * @throws InvalidArgument Thrown if schemas is null or zero-length */ public XBELValidator(final URL... schemas) throws SAXException, IOException, URISyntaxException { if (noItems(schemas)) throw new InvalidArgument("schemas is null or empty"); final List<Source> sources = new ArrayList<Source>(schemas.length); for (final URL schema : schemas) { if (schema == null) { throw new InvalidArgument("XBEL schemas contain a null element"); } InputStream is = schema.openStream(); URI uri = schema.toURI(); final Source s = new StreamSource(is, uri.toString()); sources.add(s); } final SchemaFactory sf = newInstance(W3C_XML_SCHEMA_NS_URI); schema = sf.newSchema(sources.toArray(new Source[0])); } /** * Creates a XBEL validator associated with any number of XML schemas. * * @param schemas XML schema sources * @throws SAXException Thrown if an error is found during processing the * specified schemas; the current API does not provide {@link ErrorHandler * SAX error handler} functionality * @throws InvalidArgument thrown if * <ul> * <li>{@code schemas} is null</li> * <li>{@code schemas} is a zero-length array</li> * </ul> */ public XBELValidator(final Source... schemas) throws SAXException { if (schemas == null || schemas.length == 0) throw new InvalidArgument("Invalid constructor argument schemas."); final SchemaFactory sf = newInstance(W3C_XML_SCHEMA_NS_URI); schema = sf.newSchema(schemas); } /** * {@inheritDoc} */ @Override public boolean isValid(final String s) { try { validate(s); } catch (SAXException e) { return false; } catch (IOException e) { return false; } return true; } /** * {@inheritDoc} */ @Override public boolean isValid(final File f) { try { validate(f); } catch (SAXException e) { return false; } catch (IOException e) { return false; } return true; } /** * {@inheritDoc} */ @Override public boolean isValid(final Source s) { try { validate(s); } catch (SAXException e) { return false; } catch (IOException e) { return false; } return true; } /** * {@inheritDoc} */ @Override public void validate(final String s) throws SAXException, IOException { final Source s2 = new StreamSource(new StringReader(s)); validate(s2, null); } /** * {@inheritDoc} */ @Override public List<SAXParseException> validateWithErrors(final String s) throws SAXException, IOException { final Source s2 = new StreamSource(new StringReader(s)); final Validator errorValidator = createNewErrorValidator(); errorValidator.validate(s2, null); return ((Handler) errorValidator.getErrorHandler()).exceptions; } /** * {@inheritDoc} */ @Override public void validate(final File f) throws SAXException, IOException { validate(utf8SourceForFile(f), null); } /** * {@inheritDoc} */ @Override public List<SAXParseException> validateWithErrors(final File f) throws SAXException, IOException { final Validator errorValidator = createNewErrorValidator(); errorValidator.validate(utf8SourceForFile(f), null); return ((Handler) errorValidator.getErrorHandler()).exceptions; } /** * {@inheritDoc} */ @Override public void validate(final Source s) throws SAXException, IOException { validate(s, null); } /** * {@inheritDoc} */ @Override public List<SAXParseException> validateWithErrors(final Source s) throws SAXException, IOException { final Validator errorValidator = createNewErrorValidator(); errorValidator.validate(s, null); return ((Handler) errorValidator.getErrorHandler()).exceptions; } /** * {@inheritDoc} */ @Override public void validate(final String s, final Result r) throws SAXException, IOException { final Source s2 = new StreamSource(new StringReader(s)); validate(s2, r); } /** * {@inheritDoc} */ @Override public void validate(final File f, final Result r) throws SAXException, IOException { validate(utf8SourceForFile(f), r); } /** * {@inheritDoc} */ @Override public void validate(final Source s, final Result r) throws SAXException, IOException { final Validator validator = createNewValidator(); validator.validate(s, r); } /** * Create a new instance of a {@link Validator} based on {@link #schema}. * * @return the new instance of {@link Validator} */ private Validator createNewValidator() { return schema.newValidator(); } /** * Create a new instance of a {@link Validator}, based on {@link #schema}, * that captures validation errors/warnings using {@link Handler}. * * @return the new instance of {@link Validator} that captures validation * errors/warnings */ private Validator createNewErrorValidator() { final Validator validator = schema.newValidator(); validator.setErrorHandler(new Handler()); return validator; } /** * Creates a UTF-8 {@link StreamSource} for a {@link File} by reading it * using the UTF-8 encoding. * * @param f {@link File} to read as UTF-8 * @return the {@link StreamSource} for <tt>f</tt> * @throws FileNotFoundException Thrown if <tt>f</tt> does not exist when * trying to read it * @throws UnsupportedEncodingException Thrown if <tt>UTF-8</tt> is not * supported */ private StreamSource utf8SourceForFile(final File f) throws FileNotFoundException, UnsupportedEncodingException { FileInputStream fis = new FileInputStream(f); Reader reader = new InputStreamReader(fis, UTF_8); return new StreamSource(reader); } private static class Handler implements ErrorHandler { private final List<SAXParseException> exceptions = new ArrayList<SAXParseException>(); /** * {@inheritDoc} */ @Override public void warning(SAXParseException exception) throws SAXException { exceptions.add(exception); } /** * {@inheritDoc} */ @Override public void error(SAXParseException exception) throws SAXException { exceptions.add(exception); } /** * {@inheritDoc} */ @Override public void fatalError(SAXParseException exception) throws SAXException { exceptions.add(exception); } } }