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

hm.binkley.xml.reflect.XMLFuzzy Maven / Gradle / Ivy

There is a newer version: 6
Show newest version
package hm.binkley.xml.reflect;

import hm.binkley.util.Converter;
import org.w3c.dom.Node;

import javax.annotation.Nonnull;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import static java.lang.String.format;
import static java.lang.reflect.Proxy.newProxyInstance;
import static java.util.Arrays.asList;

/**
 * {@code RedoXMLBeam} needs documentation.
 *
 * @author B. K. Oxley (binkley)
 * @todo Needs documentation.
 * @todo Replace run-time conversion with annotation processor to fix at compile time
 * @see XMLBeam
 * @see Little Fuzzy
 */
public final class XMLFuzzy
        implements InvocationHandler {
    private static final XPath xpath = XPathFactory.newInstance().newXPath();
    private static final Map expressions = new ConcurrentHashMap<>();

    private final Node node;
    private final Converter converter;

    public static final class Factory {
        private final Converter converter;

        public Factory(final Converter converter) {
            this.converter = converter;
        }

        public  T of(@Nonnull final Class itf, @Nonnull final Node node) {
            return XMLFuzzy.of(itf, node, converter);
        }
    }

    public static  T of(@Nonnull final Class itf, @Nonnull final Node node,
            @Nonnull final Converter converter) {
        return itf.cast(newProxyInstance(itf.getClassLoader(), new Class[]{itf},
                new XMLFuzzy(node, converter)));
    }

    private XMLFuzzy(final Node node, final Converter converter) {
        this.node = node;
        this.converter = converter;
    }

    @Override
    public Object invoke(final Object proxy, final Method method, final Object[] args)
            throws Throwable {
        return converter.convert(method.getReturnType(), expressions.
                computeIfAbsent(method, XMLFuzzy::compile).
                evaluate(node));
    }

    private static XPathExpression compile(@Nonnull final Method method) {
        final String expression = asList(method.getAnnotations()).stream().
                filter(From.class::isInstance).
                map(From.class::cast).
                findFirst().
                orElseThrow(() -> new MissingAnnotation(method)).
                value();
        try {
            return xpath.compile(expression);
        } catch (final XPathExpressionException e) {
            throw new BadXPath(method, expression, e);
        }
    }

    public static final class MissingAnnotation
            extends RuntimeException {
        private MissingAnnotation(final Method method) {
            super(format("Missing @XMLFuzzy(xpath) annotation: %s", method));
        }
    }

    public static final class BadXPath
            extends RuntimeException {
        private BadXPath(final Method method, final String expression,
                final XPathExpressionException e) {
            super(format("Bad @XMLFuzzy(xpath) annotation on '%s': %s: %s", method, expression,
                    e.getMessage()));
            setStackTrace(e.getStackTrace());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy