All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.helger.schematron.pure.SchematronResourcePure Maven / Gradle / Ivy

There is a newer version: 5.6.5
Show newest version
/**
 * Copyright (C) 2014-2020 Philip Helger (www.helger.com)
 * philip[at]helger[dot]com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.helger.schematron.pure;

import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import javax.xml.xpath.XPathFunctionResolver;
import javax.xml.xpath.XPathVariableResolver;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.EntityResolver;

import com.helger.commons.ValueEnforcer;
import com.helger.commons.annotation.Nonempty;
import com.helger.commons.error.SingleError;
import com.helger.commons.io.resource.ClassPathResource;
import com.helger.commons.io.resource.FileSystemResource;
import com.helger.commons.io.resource.IReadableResource;
import com.helger.commons.io.resource.URLResource;
import com.helger.commons.io.resource.inmemory.AbstractMemoryReadableResource;
import com.helger.commons.io.resource.inmemory.ReadableResourceByteArray;
import com.helger.commons.io.resource.inmemory.ReadableResourceInputStream;
import com.helger.commons.location.SimpleLocation;
import com.helger.commons.state.EValidity;
import com.helger.schematron.AbstractSchematronResource;
import com.helger.schematron.CSchematron;
import com.helger.schematron.SchematronDebug;
import com.helger.schematron.SchematronException;
import com.helger.schematron.pure.bound.IPSBoundSchema;
import com.helger.schematron.pure.bound.PSBoundSchemaCache;
import com.helger.schematron.pure.bound.PSBoundSchemaCacheKey;
import com.helger.schematron.pure.errorhandler.DoNothingPSErrorHandler;
import com.helger.schematron.pure.errorhandler.IPSErrorHandler;
import com.helger.schematron.pure.exchange.PSWriter;
import com.helger.schematron.pure.model.PSSchema;
import com.helger.schematron.pure.validation.IPSValidationHandler;
import com.helger.schematron.svrl.SVRLMarshaller;
import com.helger.schematron.svrl.jaxb.SchematronOutputType;
import com.helger.schematron.xpath.IXPathConfig;
import com.helger.schematron.xpath.XPathConfig;
import com.helger.schematron.xpath.XPathConfigBuilder;
import com.helger.xml.serialize.write.XMLWriterSettings;

/**
 * A Schematron resource that is not XSLT based but using the pure (native Java)
 * implementation. This class itself is not thread safe, but the underlying
 * cache is thread safe. So once you configured this object fully (with all the
 * setter), it can be considered thread safe.
* Important: This class can only handle XPath expressions but no * XSLT functions in Schematron asserts and reports! If your Schematrons use * XSLT functionality you're better off using the * {@link com.helger.schematron.xslt.SchematronResourceSCH} or * {@link com.helger.schematron.xslt.SchematronResourceXSLT} classes instead! * * @author Philip Helger */ @NotThreadSafe public class SchematronResourcePure extends AbstractSchematronResource { private static final Logger LOGGER = LoggerFactory.getLogger (SchematronResourcePure.class); private String m_sPhase; private IPSErrorHandler m_aErrorHandler; private IPSValidationHandler m_aCustomValidationHandler; private IXPathConfig m_aXPathConfig = XPathConfigBuilder.DEFAULT; // Status var private IPSBoundSchema m_aBoundSchema; public SchematronResourcePure (@Nonnull final IReadableResource aResource) { this (aResource, (String) null, (IPSErrorHandler) null, CSchematron.DEFAULT_ALLOW_DEPRECATED_NAMESPACES); } public SchematronResourcePure (@Nonnull final IReadableResource aResource, final boolean bLenient) { this (aResource, (String) null, (IPSErrorHandler) null, bLenient); } public SchematronResourcePure (@Nonnull final IReadableResource aResource, @Nullable final String sPhase, @Nullable final IPSErrorHandler aErrorHandler) { this (aResource, sPhase, aErrorHandler, CSchematron.DEFAULT_ALLOW_DEPRECATED_NAMESPACES); } public SchematronResourcePure (@Nonnull final IReadableResource aResource, @Nullable final String sPhase, @Nullable final IPSErrorHandler aErrorHandler, final boolean bLenient) { super (aResource); setPhase (sPhase); setErrorHandler (aErrorHandler); setLenient (bLenient); } /** * @return The phase to be used. May be null. */ @Nullable public final String getPhase () { return m_sPhase; } /** * Set the Schematron phase to be evaluated. Changing the phase will result in * a newly bound schema! * * @param sPhase * The name of the phase to use. May be null which means * all phases. * @return this */ @Nonnull public final SchematronResourcePure setPhase (@Nullable final String sPhase) { if (m_aBoundSchema != null) throw new IllegalStateException ("Schematron was already bound and can therefore not be altered!"); m_sPhase = sPhase; return this; } /** * @return The error handler to be used to bind the schema. May be * null. */ @Nullable public final IPSErrorHandler getErrorHandler () { return m_aErrorHandler; } /** * Set the error handler to be used during binding. * * @param aErrorHandler * The error handler. May be null. * @return this */ @Nonnull public final SchematronResourcePure setErrorHandler (@Nullable final IPSErrorHandler aErrorHandler) { if (m_aBoundSchema != null) throw new IllegalStateException ("Schematron was already bound and can therefore not be altered!"); m_aErrorHandler = aErrorHandler; return this; } /** * @return The custom validation handler to be used to bind the schema. May be * null. * @since 5.3.0 */ @Nullable public final IPSValidationHandler getCustomValidationHandler () { return m_aCustomValidationHandler; } /** * Set the custom validation handler to be used during binding. * * @param aCustomValidationHandler * The validation handler. May be null. * @return this * @since 5.3.0 */ @Nonnull public final SchematronResourcePure setCustomValidationHandler (@Nullable final IPSValidationHandler aCustomValidationHandler) { if (m_aBoundSchema != null) throw new IllegalStateException ("Schematron was already bound and can therefore not be altered!"); m_aCustomValidationHandler = aCustomValidationHandler; return this; } /** * @return The variable resolver to be used. May be null. */ @Nullable public final XPathVariableResolver getVariableResolver () { return m_aXPathConfig.getXPathVariableResolver (); } /** * @return The function resolver to be used. May be null. */ @Nullable public final XPathFunctionResolver getFunctionResolver () { return m_aXPathConfig.getXPathFunctionResolver (); } /** * Set the {@link XPathConfig} to be used in the XPath statements. This can * only be set before the Schematron is bound. If it is already bound an * exception is thrown to indicate the unnecessity of the call. * * @param aXPathConfig * The xpath config to set. May be null. * @return this */ @Nonnull public final SchematronResourcePure setXPathConfig (@Nonnull final IXPathConfig aXPathConfig) { ValueEnforcer.notNull (aXPathConfig, "XPathConfig"); if (m_aBoundSchema != null) throw new IllegalStateException ("Schematron was already bound and can therefore not be altered!"); m_aXPathConfig = aXPathConfig; return this; } /** * Set the XML entity resolver to be used when reading the Schematron or the * XML to be validated. This can only be set before the Schematron is bound. * If it is already bound an exception is thrown to indicate the unnecessity * of the call. * * @param aEntityResolver * The entity resolver to set. May be null. * @return this * @since 4.1.1 */ @Nonnull public SchematronResourcePure setEntityResolver (@Nullable final EntityResolver aEntityResolver) { if (m_aBoundSchema != null) throw new IllegalStateException ("Schematron was already bound and can therefore not be altered!"); internalSetEntityResolver (aEntityResolver); return this; } @Nonnull protected IPSBoundSchema createBoundSchema () { final IReadableResource aResource = getResource (); final PSBoundSchemaCacheKey aCacheKey = new PSBoundSchemaCacheKey (aResource, m_sPhase, m_aErrorHandler, m_aCustomValidationHandler, m_aXPathConfig, getEntityResolver (), isLenient ()); if (aResource instanceof AbstractMemoryReadableResource || !isUseCache ()) { // No need to cache anything for memory resources try { return aCacheKey.createBoundSchema (); } catch (final SchematronException ex) { // Convert to runtime exception throw new IllegalStateException ("Failed to bind Schematron", ex); } } // Resolve from cache - inside the cacheKey the reading and binding // happens return PSBoundSchemaCache.getInstance ().getFromCache (aCacheKey); } /** * Get the cached bound schema or create a new one. * * @return The bound schema. Never null. */ @Nonnull public IPSBoundSchema getOrCreateBoundSchema () { if (m_aBoundSchema == null) try { m_aBoundSchema = createBoundSchema (); } catch (final RuntimeException ex) { if (m_aErrorHandler != null) m_aErrorHandler.handleError (SingleError.builderError () .setErrorLocation (new SimpleLocation (getResource ().getPath ())) .setErrorText ("Error creating bound schema") .setLinkedException (ex) .build ()); throw ex; } return m_aBoundSchema; } public boolean isValidSchematron () { // Use the provided error handler (if any) try { final IPSErrorHandler aErrorHandler = m_aErrorHandler != null ? m_aErrorHandler : new DoNothingPSErrorHandler (); return getOrCreateBoundSchema ().getOriginalSchema ().isValid (aErrorHandler); } catch (final RuntimeException ex) { // May happen when XPath errors are contained return false; } } /** * Use the internal error handler to validate all elements in the schematron. * It tries to catch as many errors as possible. */ public void validateCompletely () { // Use the provided error handler (if any) final IPSErrorHandler aErrorHandler = m_aErrorHandler != null ? m_aErrorHandler : new DoNothingPSErrorHandler (); validateCompletely (aErrorHandler); } /** * Use the provided error handler to validate all elements in the schematron. * It tries to catch as many errors as possible. * * @param aErrorHandler * The error handler to use. May not be null. */ public void validateCompletely (@Nonnull final IPSErrorHandler aErrorHandler) { ValueEnforcer.notNull (aErrorHandler, "ErrorHandler"); try { getOrCreateBoundSchema ().getOriginalSchema ().validateCompletely (aErrorHandler); } catch (final RuntimeException ex) { // May happen when XPath errors are contained } } @Nonnull public EValidity getSchematronValidity (@Nonnull final Node aXMLNode, @Nullable final String sBaseURI) throws Exception { ValueEnforcer.notNull (aXMLNode, "XMLNode"); if (!isValidSchematron ()) return EValidity.INVALID; return getOrCreateBoundSchema ().validatePartially (aXMLNode, sBaseURI); } /** * The main method to convert a node to an SVRL document. * * @param aXMLNode * The source node to be validated. May not be null. * @param sBaseURI * Base URI of the XML document to be validated. May be * null. * @return The SVRL document. Never null. * @throws SchematronException * in case of a sever error validating the schema */ @Nonnull public SchematronOutputType applySchematronValidationToSVRL (@Nonnull final Node aXMLNode, @Nullable final String sBaseURI) throws SchematronException { ValueEnforcer.notNull (aXMLNode, "XMLNode"); final SchematronOutputType aSOT = getOrCreateBoundSchema ().validateComplete (aXMLNode, sBaseURI); // Debug print the created SVRL document if (SchematronDebug.isShowCreatedSVRL ()) LOGGER.info ("Created SVRL:\n" + new SVRLMarshaller (false).getAsString (aSOT)); return aSOT; } @Nullable public Document applySchematronValidation (@Nonnull final Node aXMLNode, @Nullable final String sBaseURI) throws Exception { ValueEnforcer.notNull (aXMLNode, "XMLNode"); final SchematronOutputType aSO = applySchematronValidationToSVRL (aXMLNode, sBaseURI); return new SVRLMarshaller ().getAsDocument (aSO); } /** * Create a new {@link SchematronResourcePure} from a Classpath Schematron * rules * * @param sSCHPath * The classpath relative path to the Schematron rules. * @return Never null. */ @Nonnull public static SchematronResourcePure fromClassPath (@Nonnull @Nonempty final String sSCHPath) { return new SchematronResourcePure (new ClassPathResource (sSCHPath)); } /** * Create a new {@link SchematronResourcePure} from file system Schematron * rules * * @param sSCHPath * The file system path to the Schematron rules. * @return Never null. */ @Nonnull public static SchematronResourcePure fromFile (@Nonnull @Nonempty final String sSCHPath) { return new SchematronResourcePure (new FileSystemResource (sSCHPath)); } /** * Create a new {@link SchematronResourcePure} from file system Schematron * rules * * @param aSCHFile * The file system path to the Schematron rules. * @return Never null. */ @Nonnull public static SchematronResourcePure fromFile (@Nonnull final File aSCHFile) { return new SchematronResourcePure (new FileSystemResource (aSCHFile)); } /** * Create a new {@link SchematronResourcePure} from Schematron rules provided * at a URL * * @param sSCHURL * The URL to the Schematron rules. May neither be null * nor empty. * @return Never null. * @throws MalformedURLException * In case an invalid URL is provided */ @Nonnull public static SchematronResourcePure fromURL (@Nonnull @Nonempty final String sSCHURL) throws MalformedURLException { return new SchematronResourcePure (new URLResource (sSCHURL)); } /** * Create a new {@link SchematronResourcePure} from Schematron rules provided * at a URL * * @param aSCHURL * The URL to the Schematron rules. May not be null. * @return Never null. */ @Nonnull public static SchematronResourcePure fromURL (@Nonnull final URL aSCHURL) { return new SchematronResourcePure (new URLResource (aSCHURL)); } /** * Create a new {@link SchematronResourcePure} from Schematron rules provided * by an arbitrary {@link InputStream}.
* Important: in this case, no include resolution will be performed!! * * @param aSchematronIS * The {@link InputStream} to read the Schematron rules from. May not * be null. * @return Never null. */ @Nonnull public static SchematronResourcePure fromInputStream (@Nonnull final InputStream aSchematronIS) { return new SchematronResourcePure (new ReadableResourceInputStream (aSchematronIS)); } /** * Create a new {@link SchematronResourcePure} from Schematron rules provided * by an arbitrary byte array.
* Important: in this case, no include resolution will be performed!! * * @param aSchematron * The byte array representing the Schematron. May not be * null. * @return Never null. */ @Nonnull public static SchematronResourcePure fromByteArray (@Nonnull final byte [] aSchematron) { return new SchematronResourcePure (new ReadableResourceByteArray (aSchematron)); } /** * Create a new {@link SchematronResourcePure} from Schematron rules provided * by an arbitrary String.
* Important: in this case, no include resolution will be performed!! * * @param sSchematron * The String representing the Schematron. May not be null * . * @param aCharset * The charset to be used to convert the String to a byte array. * @return Never null. */ @Nonnull public static SchematronResourcePure fromString (@Nonnull final String sSchematron, @Nonnull final Charset aCharset) { return fromByteArray (sSchematron.getBytes (aCharset)); } /** * Create a new {@link SchematronResourcePure} from Schematron rules provided * by a domain model.
* Important: in this case, no include resolution will be performed!! * * @param aSchematron * The Schematron model to be used. May not be null . * @return Never null. */ @Nonnull public static SchematronResourcePure fromSchema (@Nonnull final PSSchema aSchematron) { return fromString (new PSWriter ().getXMLString (aSchematron), XMLWriterSettings.DEFAULT_XML_CHARSET_OBJ); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy