package org.apache.commons.digester3.binder;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
import static org.apache.commons.digester3.binder.BinderClassLoader.createBinderClassLoader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.validation.Schema;
import org.apache.commons.digester3.Digester;
import org.apache.commons.digester3.RuleSet;
import org.apache.commons.digester3.Rules;
import org.apache.commons.digester3.RulesBase;
import org.apache.commons.digester3.StackAction;
import org.apache.commons.digester3.Substitutor;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.Locator;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
This class manages the creation of Digester instances from digester rules modules.
/**
* This class manages the creation of Digester instances from digester rules modules.
*/
public final class DigesterLoader
{
The default head when reporting an errors list.
/**
* The default head when reporting an errors list.
*/
private static final String HEADING = "Digester creation errors:%n%n";
Creates a new DigesterLoader
instance given one or more RulesModule
instance. Params: - rulesModules – The modules containing the
Rule
binding
Returns: A new DigesterLoader
instance
/**
* Creates a new {@link DigesterLoader} instance given one or more {@link RulesModule} instance.
*
* @param rulesModules The modules containing the {@code Rule} binding
* @return A new {@link DigesterLoader} instance
*/
public static DigesterLoader newLoader( RulesModule... rulesModules )
{
if ( rulesModules == null || rulesModules.length == 0 )
{
throw new DigesterLoadingException( "At least one RulesModule has to be specified" );
}
return newLoader( Arrays.asList( rulesModules ) );
}
Creates a new DigesterLoader
instance given a collection of RulesModule
instance. Params: - rulesModules – The modules containing the
Rule
binding
Returns: A new DigesterLoader
instance
/**
* Creates a new {@link DigesterLoader} instance given a collection of {@link RulesModule} instance.
*
* @param rulesModules The modules containing the {@code Rule} binding
* @return A new {@link DigesterLoader} instance
*/
public static DigesterLoader newLoader( Iterable<RulesModule> rulesModules )
{
if ( rulesModules == null )
{
throw new DigesterLoadingException( "RulesModule has to be specified" );
}
return new DigesterLoader( rulesModules );
}
The concrete RulesBinder
implementation. /**
* The concrete {@link RulesBinder} implementation.
*/
private final DefaultRulesBinder rulesBinder = new DefaultRulesBinder();
The URLs of entityValidator that have been registered, keyed by the public
identifier that corresponds.
/**
* The URLs of entityValidator that have been registered, keyed by the public
* identifier that corresponds.
*/
private final Map<String, URL> entityValidator = new HashMap<String, URL>();
The SAXParserFactory to create new default Digester
instances. /**
* The SAXParserFactory to create new default {@link Digester} instances.
*/
private final SAXParserFactory factory = SAXParserFactory.newInstance();
private final Iterable<RulesModule> rulesModules;
The class loader to use for instantiating application objects.
If not specified, the context class loader, or the class loader
used to load Digester itself, is used, based on the value of the
useContextClassLoader
variable.
/**
* The class loader to use for instantiating application objects.
* If not specified, the context class loader, or the class loader
* used to load Digester itself, is used, based on the value of the
* <code>useContextClassLoader</code> variable.
*/
private BinderClassLoader classLoader;
An optional class that substitutes values in attributes and body text. This may be null and so a null check is
always required before use.
/**
* An optional class that substitutes values in attributes and body text. This may be null and so a null check is
* always required before use.
*/
private Substitutor substitutor;
The EntityResolver used by the SAX parser. By default it use this class
/**
* The EntityResolver used by the SAX parser. By default it use this class
*/
private EntityResolver entityResolver;
Object which will receive callbacks for every pop/push action on the default stack or named stacks.
/**
* Object which will receive callbacks for every pop/push action on the default stack or named stacks.
*/
private StackAction stackAction;
The executor service to run asynchronous parse method.
Since: 3.1
/**
* The executor service to run asynchronous parse method.
* @since 3.1
*/
private ExecutorService executorService;
The application-supplied error handler that is notified when parsing warnings, errors, or fatal errors occur.
Since: 3.2
/**
* The application-supplied error handler that is notified when parsing warnings, errors, or fatal errors occur.
* @since 3.2
*/
private ErrorHandler errorHandler = null;
The Locator associated with our parser.
Since: 3.2
/**
* The Locator associated with our parser.
* @since 3.2
*/
private Locator locator = null;
Creates a new DigesterLoader
instance given a collection of RulesModule
instance. Params: - rulesModules – The modules containing the
Rule
binding
/**
* Creates a new {@link DigesterLoader} instance given a collection of {@link RulesModule} instance.
*
* @param rulesModules The modules containing the {@code Rule} binding
*/
private DigesterLoader( Iterable<RulesModule> rulesModules )
{
this.rulesModules = rulesModules;
setUseContextClassLoader( true );
}
Determine whether to use the Context ClassLoader (the one found by
calling Thread.currentThread().getContextClassLoader()
)
to resolve/load classes that are defined in various rules. If not
using Context ClassLoader, then the class-loading defaults to
using the calling-class' ClassLoader.
Params: - useContextClassLoader – determines whether to use Context ClassLoader.
Returns: This loader instance, useful to chain methods.
/**
* Determine whether to use the Context ClassLoader (the one found by
* calling <code>Thread.currentThread().getContextClassLoader()</code>)
* to resolve/load classes that are defined in various rules. If not
* using Context ClassLoader, then the class-loading defaults to
* using the calling-class' ClassLoader.
*
* @param useContextClassLoader determines whether to use Context ClassLoader.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setUseContextClassLoader( boolean useContextClassLoader )
{
if ( useContextClassLoader )
{
setClassLoader( Thread.currentThread().getContextClassLoader() );
}
else
{
setClassLoader( getClass().getClassLoader() );
}
return this;
}
Set the class loader to be used for instantiating application objects when required.
Params: - classLoader – the class loader to be used for instantiating application objects when required.
Returns: This loader instance, useful to chain methods.
/**
* Set the class loader to be used for instantiating application objects when required.
*
* @param classLoader the class loader to be used for instantiating application objects when required.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setClassLoader( ClassLoader classLoader )
{
if ( classLoader == null )
{
throw new IllegalArgumentException( "Parameter 'classLoader' cannot be null" );
}
this.classLoader = createBinderClassLoader( classLoader );
return this;
}
Sets the Substitutor
to be used to convert attributes and body text.
Params: - substitutor – the Substitutor to be used to convert attributes and body text
or null if not substitution of these values is to be performed.
Returns: This loader instance, useful to chain methods.
/**
* Sets the <code>Substitutor</code> to be used to convert attributes and body text.
*
* @param substitutor the Substitutor to be used to convert attributes and body text
* or null if not substitution of these values is to be performed.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setSubstitutor( Substitutor substitutor )
{
this.substitutor = substitutor;
return this;
}
Set the "namespace aware" flag for parsers we create.
Params: - namespaceAware – The new "namespace aware" flag
Returns: This loader instance, useful to chain methods.
/**
* Set the "namespace aware" flag for parsers we create.
*
* @param namespaceAware The new "namespace aware" flag
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setNamespaceAware( boolean namespaceAware )
{
factory.setNamespaceAware( namespaceAware );
return this;
}
Return the "namespace aware" flag for parsers we create.
Returns: true, if the "namespace aware" flag for parsers we create, false otherwise.
/**
* Return the "namespace aware" flag for parsers we create.
*
* @return true, if the "namespace aware" flag for parsers we create, false otherwise.
*/
public boolean isNamespaceAware()
{
return factory.isNamespaceAware();
}
Set the XInclude-aware flag for parsers we create. This additionally
requires namespace-awareness.
Params: - xIncludeAware – The new XInclude-aware flag
See Also: Returns: This loader instance, useful to chain methods.
/**
* Set the XInclude-aware flag for parsers we create. This additionally
* requires namespace-awareness.
*
* @param xIncludeAware The new XInclude-aware flag
* @return This loader instance, useful to chain methods.
* @see #setNamespaceAware(boolean)
*/
public DigesterLoader setXIncludeAware( boolean xIncludeAware )
{
factory.setXIncludeAware( xIncludeAware );
return this;
}
Return the XInclude-aware flag for parsers we create;
Returns: true, if the XInclude-aware flag for parsers we create is set,
false otherwise
/**
* Return the XInclude-aware flag for parsers we create;
*
* @return true, if the XInclude-aware flag for parsers we create is set,
* false otherwise
*/
public boolean isXIncludeAware()
{
return factory.isXIncludeAware();
}
Set the validating parser flag.
Params: - validating – The new validating parser flag.
Returns: This loader instance, useful to chain methods.
/**
* Set the validating parser flag.
*
* @param validating The new validating parser flag.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setValidating( boolean validating )
{
factory.setValidating( validating );
return this;
}
Return the validating parser flag.
Returns: true, if the validating parser flag is set, false otherwise
/**
* Return the validating parser flag.
*
* @return true, if the validating parser flag is set, false otherwise
*/
public boolean isValidating()
{
return this.factory.isValidating();
}
Set the XML Schema to be used when parsing.
Params: - schema – The
Schema
instance to use.
Returns: This loader instance, useful to chain methods.
/**
* Set the XML Schema to be used when parsing.
*
* @param schema The {@link Schema} instance to use.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setSchema( Schema schema )
{
factory.setSchema( schema );
return this;
}
Register the specified DTD URL for the specified public identifier.
This must be called before the first call to parse()
.
Digester
contains an internal EntityResolver
implementation. This maps PUBLICID
's to URLs
(from which the resource will be loaded). A common use case for this
method is to register local URLs (possibly computed at runtime by a
classloader) for DTDs. This allows the performance advantage of using
a local version without having to ensure every SYSTEM
URI on every processed xml document is local. This implementation provides only basic functionality. If more sophisticated features are required, using setEntityResolver(EntityResolver)
to set a custom resolver is recommended.
Note: This method will have no effect when a custom
EntityResolver
has been set. (Setting a custom
EntityResolver
overrides the internal implementation.)
Params: - publicId – Public identifier of the DTD to be resolved
- entityURL – The URL to use for reading this DTD
Returns: This loader instance, useful to chain methods.
/**
* <p>Register the specified DTD URL for the specified public identifier.
* This must be called before the first call to <code>parse()</code>.
* </p><p>
* <code>Digester</code> contains an internal <code>EntityResolver</code>
* implementation. This maps <code>PUBLICID</code>'s to URLs
* (from which the resource will be loaded). A common use case for this
* method is to register local URLs (possibly computed at runtime by a
* classloader) for DTDs. This allows the performance advantage of using
* a local version without having to ensure every <code>SYSTEM</code>
* URI on every processed xml document is local. This implementation provides
* only basic functionality. If more sophisticated features are required,
* using {@link #setEntityResolver(EntityResolver)} to set a custom resolver is recommended.
* </p><p>
* <strong>Note:</strong> This method will have no effect when a custom
* <code>EntityResolver</code> has been set. (Setting a custom
* <code>EntityResolver</code> overrides the internal implementation.)
* </p>
* @param publicId Public identifier of the DTD to be resolved
* @param entityURL The URL to use for reading this DTD
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader register( String publicId, URL entityURL )
{
entityValidator.put( publicId, entityURL );
return this;
}
Convenience method that registers the string version of an entity URL
instead of a URL version.
Params: - publicId – Public identifier of the entity to be resolved
- entityURL – The URL to use for reading this entity
Returns: This loader instance, useful to chain methods.
/**
* <p>Convenience method that registers the string version of an entity URL
* instead of a URL version.</p>
*
* @param publicId Public identifier of the entity to be resolved
* @param entityURL The URL to use for reading this entity
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader register( String publicId, String entityURL )
{
try
{
return register( publicId, new URL( entityURL ) );
}
catch ( MalformedURLException e )
{
throw new IllegalArgumentException( "Malformed URL '" + entityURL + "' : " + e.getMessage() );
}
}
Return the set of DTD URL registrations, keyed by public identifier.
Returns: the set of DTD URL registrations.
/**
* Return the set of DTD URL registrations, keyed by public identifier.
*
* @return the set of DTD URL registrations.
*/
public Map<String, URL> getRegistrations()
{
return Collections.unmodifiableMap( this.entityValidator );
}
Set the EntityResolver
used by SAX when resolving public id and system id. This must be called
before the first call to parse()
.
Params: - entityResolver – a class that implement the
EntityResolver
interface.
Returns: This loader instance, useful to chain methods.
/**
* Set the <code>EntityResolver</code> used by SAX when resolving public id and system id. This must be called
* before the first call to <code>parse()</code>.
*
* @param entityResolver a class that implement the <code>EntityResolver</code> interface.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setEntityResolver( EntityResolver entityResolver )
{
this.entityResolver = entityResolver;
return this;
}
Sets the Object which will receive callbacks for every pop/push action on the default stack or named stacks.
Params: - stackAction – the Object which will receive callbacks for every pop/push action on the default stack
or named stacks.
Returns: This loader instance, useful to chain methods.
/**
* Sets the Object which will receive callbacks for every pop/push action on the default stack or named stacks.
*
* @param stackAction the Object which will receive callbacks for every pop/push action on the default stack
* or named stacks.
* @return This loader instance, useful to chain methods.
*/
public DigesterLoader setStackAction( StackAction stackAction )
{
this.stackAction = stackAction;
return this;
}
Returns the executor service used to run asynchronous parse method.
Returns: the executor service used to run asynchronous parse method Since: 3.1
/**
* Returns the executor service used to run asynchronous parse method.
*
* @return the executor service used to run asynchronous parse method
* @since 3.1
*/
public ExecutorService getExecutorService()
{
return executorService;
}
Sets the executor service to run asynchronous parse method.
Params: - executorService – the executor service to run asynchronous parse method
Returns: This loader instance, useful to chain methods. Since: 3.1
/**
* Sets the executor service to run asynchronous parse method.
*
* @param executorService the executor service to run asynchronous parse method
* @return This loader instance, useful to chain methods.
* @since 3.1
*/
public DigesterLoader setExecutorService( ExecutorService executorService )
{
this.executorService = executorService;
return this;
}
Return the error handler for this Digester.
Returns: the error handler for this Digester. Since: 3.2
/**
* Return the error handler for this Digester.
*
* @return the error handler for this Digester.
* @since 3.2
*/
public ErrorHandler getErrorHandler()
{
return ( this.errorHandler );
}
Set the error handler for this Digester.
Params: - errorHandler – The new error handler
Returns: This loader instance, useful to chain methods. Since: 3.2
/**
* Set the error handler for this Digester.
*
* @param errorHandler The new error handler
* @return This loader instance, useful to chain methods.
* @since 3.2
*/
public DigesterLoader setErrorHandler( ErrorHandler errorHandler )
{
this.errorHandler = errorHandler;
return this;
}
Gets the document locator associated with our parser.
Returns: the Locator supplied by the document parser Since: 3.2
/**
* Gets the document locator associated with our parser.
*
* @return the Locator supplied by the document parser
* @since 3.2
*/
public Locator getDocumentLocator()
{
return locator;
}
Sets the document locator associated with our parser.
Params: - locator – the document locator associated with our parser.
Returns: This loader instance, useful to chain methods. Since: 3.2
/**
* Sets the document locator associated with our parser.
*
* @param locator the document locator associated with our parser.
* @return This loader instance, useful to chain methods.
* @since 3.2
*/
public DigesterLoader setDocumentLocator( Locator locator )
{
this.locator = locator;
return this;
}
Returns: a new Digester
instance
/**
* Creates a new {@link Digester} instance that relies on the default {@link Rules} implementation.
*
* @return a new {@link Digester} instance
*/
public Digester newDigester()
{
return this.newDigester( new RulesBase() );
}
Params: - rules – The custom user define
Rules
implementation
Returns: a new Digester
instance
/**
* Creates a new {@link Digester} instance that relies on the custom user define {@link Rules} implementation
*
* @param rules The custom user define {@link Rules} implementation
* @return a new {@link Digester} instance
*/
public Digester newDigester( Rules rules )
{
try
{
return this.newDigester( this.factory.newSAXParser(), rules );
}
catch ( ParserConfigurationException e )
{
throw new DigesterLoadingException( "SAX Parser misconfigured", e );
}
catch ( SAXException e )
{
throw new DigesterLoadingException( "An error occurred while initializing the SAX Parser", e );
}
}
Creates a new Digester
instance that relies on the given SAXParser
and the default Rules
implementation. Params: - parser – the user defined
SAXParser
Returns: a new Digester
instance
/**
* Creates a new {@link Digester} instance that relies on the given {@code SAXParser}
* and the default {@link Rules} implementation.
*
* @param parser the user defined {@code SAXParser}
* @return a new {@link Digester} instance
*/
public Digester newDigester( SAXParser parser )
{
return newDigester( parser, new RulesBase() );
}
Creates a new Digester
instance that relies on the given SAXParser
and custom user define Rules
implementation. Params: - parser – The user defined
SAXParser
- rules – The custom user define
Rules
implementation
Returns: a new Digester
instance
/**
* Creates a new {@link Digester} instance that relies on the given {@code SAXParser}
* and custom user define {@link Rules} implementation.
*
* @param parser The user defined {@code SAXParser}
* @param rules The custom user define {@link Rules} implementation
* @return a new {@link Digester} instance
*/
public Digester newDigester( SAXParser parser, Rules rules )
{
if ( parser == null )
{
throw new DigesterLoadingException( "SAXParser must be not null" );
}
try
{
return this.newDigester( parser.getXMLReader(), rules );
}
catch ( SAXException e )
{
throw new DigesterLoadingException( "An error occurred while creating the XML Reader", e );
}
}
Creates a new XMLReader
instance that relies on the given XMLReader
and the default Rules
implementation. Params: - reader – The user defined
XMLReader
Returns: a new Digester
instance
/**
* Creates a new {@link XMLReader} instance that relies on the given {@code XMLReader}
* and the default {@link Rules} implementation.
*
* @param reader The user defined {@code XMLReader}
* @return a new {@link Digester} instance
*/
public Digester newDigester( XMLReader reader )
{
return this.newDigester( reader, new RulesBase() );
}
Creates a new XMLReader
instance that relies on the given XMLReader
and custom user define Rules
implementation. Params: - reader – The user defined
XMLReader
- rules – The custom user define
Rules
implementation
Returns: a new Digester
instance
/**
* Creates a new {@link XMLReader} instance that relies on the given {@code XMLReader}
* and custom user define {@link Rules} implementation.
*
* @param reader The user defined {@code XMLReader}
* @param rules The custom user define {@link Rules} implementation
* @return a new {@link Digester} instance
*/
public Digester newDigester( XMLReader reader, Rules rules )
{
if ( reader == null )
{
throw new DigesterLoadingException( "XMLReader must be not null" );
}
if ( rules == null )
{
throw new DigesterLoadingException( "Impossible to create a new Digester with null Rules" );
}
Digester digester = new Digester( reader );
// the ClassLoader adapter is no needed anymore
digester.setClassLoader( classLoader.getAdaptedClassLoader() );
digester.setRules( rules );
digester.setSubstitutor( substitutor );
digester.registerAll( entityValidator );
digester.setEntityResolver( entityResolver );
digester.setStackAction( stackAction );
digester.setNamespaceAware( isNamespaceAware() );
digester.setExecutorService( executorService );
digester.setErrorHandler( errorHandler );
digester.setDocumentLocator( locator );
addRules( digester );
return digester;
}
Add rules to an already created Digester instance, analyzing the digester annotations in the target class.
Params: - digester – the Digester instance reference.
/**
* Add rules to an already created Digester instance, analyzing the digester annotations in the target class.
*
* @param digester the Digester instance reference.
*/
public void addRules( final Digester digester )
{
RuleSet ruleSet = createRuleSet();
ruleSet.addRuleInstances( digester );
}
Creates a new RuleSet
instance based on the current configuration. Returns: A new RuleSet
instance based on the current configuration.
/**
* Creates a new {@link RuleSet} instance based on the current configuration.
*
* @return A new {@link RuleSet} instance based on the current configuration.
*/
public RuleSet createRuleSet()
{
if ( classLoader != rulesBinder.getContextClassLoader() )
{
rulesBinder.initialize( classLoader );
for ( RulesModule rulesModule : rulesModules )
{
rulesModule.configure( rulesBinder );
}
}
if ( rulesBinder.hasError() )
{
Formatter fmt = new Formatter().format( HEADING );
int index = 1;
for ( ErrorMessage errorMessage : rulesBinder.getErrors() )
{
fmt.format( "%s) %s%n", index++, errorMessage.getMessage() );
Throwable cause = errorMessage.getCause();
if ( cause != null )
{
StringWriter writer = new StringWriter();
cause.printStackTrace( new PrintWriter( writer ) );
fmt.format( "Caused by: %s", writer.getBuffer() );
}
fmt.format( "%n" );
}
if ( rulesBinder.errorsSize() == 1 )
{
fmt.format( "1 error" );
}
else
{
fmt.format( "%s errors", rulesBinder.errorsSize() );
}
throw new DigesterLoadingException( fmt.toString() );
}
return rulesBinder.getFromBinderRuleSet();
}
}