package com.github.fge.grappa.transform; import com.github.fge.grappa.Grappa; import com.github.fge.grappa.parsers.BaseParser; import com.github.fge.grappa.transform.load.ClassLoaderList; import com.google.common.annotations.Beta; import java.lang.reflect.Constructor; /** * A factory to generate parser instances * * <p>The main difference with {@link Grappa#createParser(Class, Object...)} is * that this class can build from other classloaders than the defaults (see * {@link ClassLoaderList}).</p> */ @Beta public final class ParserFactory { private final ClassLoaderList loaderList; public static Builder newBuilder() { return new Builder(); } private ParserFactory(final Builder builder) { loaderList = builder.loaderListBuilder.build(); } public <V, P extends BaseParser<V>> P getParser(final Class<P> parserClass, final Object... arguments) throws Exception { // TODO: generics usage may be improved here final ParserGenerator<V, P> generator = new ParserGenerator<>( parserClass, loaderList); final Class<? extends P> c = generator.transformParser(); final Constructor<?> constructor = findConstructor(c, arguments); return (P) constructor.newInstance(arguments); } private static Constructor<?> findConstructor(final Class<?> c, final Object[] arguments) { Class<?>[] argumentTypes; for (final Constructor<?> constructor : c.getConstructors()) { argumentTypes = constructor.getParameterTypes(); if (argumentsMatch(arguments, argumentTypes)) return constructor; } throw new ParserTransformException("No constructor found for " + c + " and the given " + arguments.length + " arguments"); } private static boolean argumentsMatch(final Object[] arguments, final Class<?>[] argumentTypes) { final int len = argumentTypes.length; if (len != arguments.length) return false; Object argument; Class<?> argumentType; for (int index = 0; index < len; index++) { argument = arguments[index]; argumentType = argumentTypes[index]; /* * If the argument is not null, check whether its class is exactly, * or a subtype of, the parameter class at the same index */ if (argument != null && !argumentType.isAssignableFrom(argument.getClass())) return false; /* * If it is null, anything goes... Except if the type is a primitive */ if (argument == null && argumentType.isPrimitive()) return false; } /* * All arguments checked, compatible types; this is a match */ return true; } public static final class Builder { private final ClassLoaderList.Builder loaderListBuilder = ClassLoaderList.newBuilder(); private Builder() { } public Builder addClassLoader(final ClassLoader loader) { loaderListBuilder.addClassLoader(loader); return this; } public ParserFactory build() { return new ParserFactory(this); } } }