JaxpSetters.java

/*
 * 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
 *
 *      https://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 org.apache.commons.xml;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.stream.XMLInputFactory;
import javax.xml.transform.TransformerFactory;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.validation.ValidatorHandler;
import javax.xml.xpath.XPathFactory;

import org.xml.sax.XMLReader;

/**
 * Setter helpers shared by the bundled hardening providers.
 *
 * <p>Each overload wraps a single JAXP setter (feature, attribute or property) in a try/catch that translates any thrown exception into a
 * {@link HardeningException} whose message names the offending feature, attribute or property and the concrete factory class.</p>
 */
final class JaxpSetters {

    private static final String KIND_PROPERTY = "property";
    private static final String KIND_FEATURE = "feature";
    private static final String KIND_ATTRIBUTE = "attribute";

    /** Action that may throw any exception; used to share a single try/catch around every JAXP setter. */
    @FunctionalInterface
    private interface ThrowingAction {
        void run() throws Exception;
    }

    private static void apply(final Object factory, final String kind, final String name, final ThrowingAction action) {
        try {
            action.run();
        } catch (final Exception e) {
            throw new HardeningException("Failed to set " + kind + " '" + name + "' on " + factory.getClass().getName(), e);
        }
    }

    static void setAttribute(final DocumentBuilderFactory factory, final String attribute, final Object value) {
        apply(factory, KIND_ATTRIBUTE, attribute, () -> factory.setAttribute(attribute, value));
    }

    /**
     * Sets an attribute on a {@link DocumentBuilderFactory} and returns whether the implementation accepted it. Some implementations may reject certain
     * attributes, in which case this method will return {@code false}.
     *
     * @param factory   The target factory on which to set the attribute.
     * @param attribute The name of the attribute to set.
     * @param value     The value of the attribute to set.
     * @return {@code true} if the attribute was applied, {@code false} if the implementation rejected it.
     */
    static boolean trySetAttribute(final DocumentBuilderFactory factory, final String attribute, final Object value) {
        try {
            factory.setAttribute(attribute, value);
            return true;
        } catch (final Exception e) {
            return false;
        }
    }

    static void setOptionalAttribute(final DocumentBuilderFactory factory, final String attribute, final Object value) {
        trySetAttribute(factory, attribute, value);
    }

    static void setAttribute(final TransformerFactory factory, final String attribute, final Object value) {
        apply(factory, KIND_ATTRIBUTE, attribute, () -> factory.setAttribute(attribute, value));
    }

    static void setFeature(final DocumentBuilderFactory factory, final String feature, final boolean value) {
        apply(factory, KIND_FEATURE, feature, () -> factory.setFeature(feature, value));
    }

    static void setOptionalFeature(final DocumentBuilderFactory factory, final String feature, final boolean value) {
        try {
            factory.setFeature(feature, value);
        } catch (final Exception e) {
            // Ignored: the implementation does not recognize this feature.
        }
    }

    static void setFeature(final SAXParserFactory factory, final String feature, final boolean value) {
        apply(factory, KIND_FEATURE, feature, () -> factory.setFeature(feature, value));
    }

    static void setFeature(final TransformerFactory factory, final String feature, final boolean value) {
        apply(factory, KIND_FEATURE, feature, () -> factory.setFeature(feature, value));
    }

    static void setFeature(final XPathFactory factory, final String feature, final boolean value) {
        apply(factory, KIND_FEATURE, feature, () -> factory.setFeature(feature, value));
    }

    static void setFeature(final SchemaFactory factory, final String feature, final boolean value) {
        apply(factory, KIND_FEATURE, feature, () -> factory.setFeature(feature, value));
    }

    static void setFeature(final Validator validator, final String feature, final boolean value) {
        apply(validator, KIND_FEATURE, feature, () -> validator.setFeature(feature, value));
    }

    static void setFeature(final ValidatorHandler handler, final String feature, final boolean value) {
        apply(handler, KIND_FEATURE, feature, () -> handler.setFeature(feature, value));
    }

    static void setFeature(final XMLReader reader, final String feature, final boolean value) {
        apply(reader, KIND_FEATURE, feature, () -> reader.setFeature(feature, value));
    }

    static void setProperty(final XMLInputFactory factory, final String property, final Object value) {
        apply(factory, KIND_PROPERTY, property, () -> factory.setProperty(property, value));
    }

    static void setProperty(final SAXParser parser, final String property, final Object value) {
        apply(parser, KIND_PROPERTY, property, () -> parser.setProperty(property, value));
    }

    static void setProperty(final XMLReader reader, final String property, final Object value) {
        apply(reader, KIND_PROPERTY, property, () -> reader.setProperty(property, value));
    }

    static void setProperty(final SchemaFactory factory, final String property, final Object value) {
        apply(factory, KIND_PROPERTY, property, () -> factory.setProperty(property, value));
    }

    static void setProperty(final Validator validator, final String property, final Object value) {
        apply(validator, KIND_PROPERTY, property, () -> validator.setProperty(property, value));
    }

    static void setProperty(final ValidatorHandler handler, final String property, final Object value) {
        apply(handler, KIND_PROPERTY, property, () -> handler.setProperty(property, value));
    }

    private JaxpSetters() {
    }
}