AbstractSingleResolutionBuilder.java

package fr.sii.ogham.core.builder.resolution;

import static java.util.Arrays.asList;

import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.configuration.ConfigurationValueBuilderHelper;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.fluent.AbstractParent;
import fr.sii.ogham.core.resource.resolver.RelativeResolver;
import fr.sii.ogham.core.resource.resolver.RelativisableResourceResolver;
import fr.sii.ogham.core.resource.resolver.ResourceResolver;

/**
 * Base implementation to handle lookup prefix configuration. Path prefix/suffix
 * configuration is also partially managed. As path prefix/suffix for string
 * resolution has no sense, this base class doesn't implement
 * {@link PrefixSuffixBuilder}.
 * 
 * @author Aurélien Baudet
 *
 * @param <MYSELF>
 *            The type of this instance. This is needed to have the right return
 *            type for fluent chaining with inheritance
 * @param <P>
 *            the type of the parent builder (when calling {@link #and()}
 *            method)
 */
@SuppressWarnings("squid:S00119")
public abstract class AbstractSingleResolutionBuilder<MYSELF extends AbstractSingleResolutionBuilder<MYSELF, P>, P> extends AbstractParent<P> implements Builder<ResourceResolver> {
	private static final Logger LOG = LoggerFactory.getLogger(AbstractSingleResolutionBuilder.class);

	protected final BuildContext buildContext;
	protected final List<String> lookups;
	protected final ConfigurationValueBuilderHelper<MYSELF, String> pathPrefixValueBuilder;
	protected final ConfigurationValueBuilderHelper<MYSELF, String> pathSuffixValueBuilder;
	protected final MYSELF myself;

	@SuppressWarnings("unchecked")
	protected AbstractSingleResolutionBuilder(Class<?> selfType, P parent, BuildContext buildContext) {
		super(parent);
		myself = (MYSELF) selfType.cast(this);
		this.buildContext = buildContext;
		lookups = new ArrayList<>();
		pathPrefixValueBuilder = buildContext.newConfigurationValueBuilder(myself, String.class);
		pathSuffixValueBuilder = buildContext.newConfigurationValueBuilder(myself, String.class);
	}

	/**
	 * Configure lookup prefix. For example:
	 * 
	 * <pre>
	 * .classpath().lookup("classpath:");
	 * 
	 * // path prefixed by classpath: matches 
	 * // then classpath resolver is used
	 * resourceResolver.getResource("classpath:foo/bar.html");
	 * // path is not prefixed (or using another prefix) doesn't match 
	 * // then classpath resolver is not used
	 * resourceResolver.getResource("foo/bar.html");
	 * </pre>
	 * 
	 * <p>
	 * Several lookups can be provided:
	 * 
	 * <pre>
	 * .classpath().lookup("classpath:", "cp:");
	 * </pre>
	 * 
	 * If a path starts with one of the prefix ("classpath:" or "cp:"), the
	 * corresponding resolver (classpath resolver in this example) is used to
	 * resolve the file.
	 * 
	 * <p>
	 * Lookup may be empty meaning that if the path has no prefix, the
	 * corresponding resolver is use as default. For example:
	 * 
	 * <pre>
	 * .classpath().lookup("");
	 * </pre>
	 * 
	 * If the path is "foo/bar.html" is provided then the classpath resolver is
	 * used.
	 * 
	 * @param prefix
	 *            one or several prefixes that indicates which resolver to use
	 *            according to path
	 * @return this instance for fluent chaining
	 */
	public MYSELF lookup(String... prefix) {
		this.lookups.addAll(asList(prefix));
		return myself;
	}

	/**
	 * Configure lookup prefix. For example:
	 * 
	 * <pre>
	 * .classpath().lookup("classpath:");
	 * 
	 * // path prefixed by classpath: matches 
	 * // then classpath resolver is used
	 * resourceResolver.getResource("classpath:foo/bar.html");
	 * // path is not prefixed (or using another prefix) doesn't match 
	 * // then classpath resolver is not used
	 * resourceResolver.getResource("foo/bar.html");
	 * </pre>
	 * 
	 * <p>
	 * Several lookups can be provided:
	 * 
	 * <pre>
	 * .classpath().lookup("classpath:", "cp:");
	 * </pre>
	 * 
	 * If a path starts with one of the prefix ("classpath:" or "cp:"), the
	 * corresponding resolver (classpath resolver in this example) is used to
	 * resolve the file.
	 * 
	 * <p>
	 * Lookup may be empty meaning that if the path has no prefix, the
	 * corresponding resolver is use as default. For example:
	 * 
	 * <pre>
	 * .classpath().lookup("");
	 * </pre>
	 * 
	 * If the path is "foo/bar.html" is provided then the classpath resolver is
	 * used.
	 * 
	 * @param prefix
	 *            one or several prefixes that indicates which resolver to use
	 *            according to path
	 * @return this instance for fluent chaining
	 */
	public MYSELF lookup(List<String> prefix) {
		this.lookups.addAll(prefix);
		return myself;
	}

	@Override
	public ResourceResolver build() {
		ResourceResolver resolver = buildContext.register(createResolver());
		if (!(resolver instanceof RelativisableResourceResolver)) {
			return resolver;
		}
		String resolvedPathPrefix = pathPrefixValueBuilder.getValue("");
		String resolvedPathSuffix = pathSuffixValueBuilder.getValue("");
		if (!resolvedPathPrefix.isEmpty() || !resolvedPathSuffix.isEmpty()) {
			LOG.debug("Using parentPath {} and extension {} for resource resolution", resolvedPathPrefix, resolvedPathSuffix);
			resolver = buildContext.register(new RelativeResolver((RelativisableResourceResolver) resolver, resolvedPathPrefix, resolvedPathSuffix));
		}
		return resolver;
	}

	protected abstract ResourceResolver createResolver();

	/**
	 * Provide the list of registered lookups
	 * 
	 * @return the list of lookups
	 */
	public List<String> getLookups() {
		return lookups;
	}
}