Java tutorial
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. */ public final class DigesterLoader { /** * The default head when reporting an errors list. */ private static final String HEADING = "Digester creation errors:%n%n"; /** * 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 {@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 {@link RulesBinder} implementation. */ private final DefaultRulesBinder rulesBinder = new DefaultRulesBinder(); /** * 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 {@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 * <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. */ private Substitutor substitutor; /** * 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. */ private StackAction stackAction; /** * 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 */ private ErrorHandler errorHandler = null; /** * The Locator associated with our parser. * @since 3.2 */ private Locator locator = null; /** * 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 <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. * * @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 <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. * * @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. * * @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. * * @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; * * @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. * * @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. * * @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. * * @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; } /** * <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; } /** * <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. * * @return the set of DTD URL registrations. */ public Map<String, URL> getRegistrations() { return Collections.unmodifiableMap(this.entityValidator); } /** * 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. * * @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. * * @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. * * @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. * * @return the error handler for this Digester. * @since 3.2 */ public ErrorHandler getErrorHandler() { return (this.errorHandler); } /** * 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. * * @return the Locator supplied by the document parser * @since 3.2 */ public Locator getDocumentLocator() { return locator; } /** * 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; } /** * 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()); } /** * 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 {@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 {@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 {@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 {@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. * * @param digester the Digester instance reference. */ public void addRules(final Digester digester) { RuleSet ruleSet = createRuleSet(); ruleSet.addRuleInstances(digester); } /** * 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(); } }