ExtensionMappingVariantResolver.java

package fr.sii.ogham.template.common.adapter;

import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

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

import fr.sii.ogham.core.exception.resource.ResourceResolutionException;
import fr.sii.ogham.core.message.capability.HasVariant;
import fr.sii.ogham.core.message.content.TemplateContent;
import fr.sii.ogham.core.message.content.Variant;
import fr.sii.ogham.core.resource.path.ResolvedPath;
import fr.sii.ogham.core.resource.path.ResourcePath;
import fr.sii.ogham.core.resource.path.UnresolvedPath;
import fr.sii.ogham.core.resource.resolver.ResourceResolver;
import fr.sii.ogham.template.exception.UnknownVariantException;
import fr.sii.ogham.template.exception.VariantResolutionException;

/**
 * Simple implementation that maps a variant instance to an extension.
 * 
 * @author Aurélien Baudet
 *
 */
public class ExtensionMappingVariantResolver implements VariantResolver, CanProvidePossiblePaths {
	private static final Logger LOG = LoggerFactory.getLogger(ExtensionMappingVariantResolver.class);

	private final ResourceResolver resourceResolver;
	private final Map<Variant, List<String>> mapping;

	public ExtensionMappingVariantResolver(ResourceResolver resourceResolver) {
		this(resourceResolver, new HashMap<>());
	}

	public ExtensionMappingVariantResolver(ResourceResolver resourceResolver, Map<Variant, List<String>> mapping) {
		super();
		this.resourceResolver = resourceResolver;
		this.mapping = mapping;
	}

	@Override
	public ResourcePath getRealPath(TemplateContent template) throws VariantResolutionException {
		String originalPath = template.getPath().getOriginalPath();
		if (!(template instanceof HasVariant)) {
			return resourceResolver.resolve(new UnresolvedPath(originalPath));
		}

		Variant variant = ((HasVariant) template).getVariant();
		List<String> extensions = mapping.get(variant);
		if (extensions == null) {
			throw new UnknownVariantException("Failed to resolve template due to unknown variant/extension", template.getPath(), template.getContext(), variant);
		}

		return resolvePath(template, extensions);
	}

	@Override
	public boolean variantExists(TemplateContent template) {
		if (!(template instanceof HasVariant)) {
			return false;
		}

		List<String> extensions = mapping.get(((HasVariant) template).getVariant());
		if (extensions == null) {
			return false;
		}

		return resolvePath(template, extensions) != null;
	}

	@Override
	public List<ResourcePath> getPossiblePaths(TemplateContent template) {
		if (!(template instanceof HasVariant)) {
			return emptyList();
		}

		Variant variant = ((HasVariant) template).getVariant();
		List<String> extensions = mapping.get(variant);
		if (extensions == null) {
			return emptyList();
		}

		// @formatter:off
		return extensions.stream()
				.map(extension -> resolveVariantPath(template.getPath().getOriginalPath(), extension))
				.filter(Objects::nonNull)
				.collect(toList());
		// @formatter:on
	}

	/**
	 * Register a mapping between a variant and an extension.
	 * 
	 * <p>
	 * If a variant is already registered, the new extension is appended to the
	 * list of extensions associated to the variant. The registration order of
	 * extensions for a variant is important.
	 * 
	 * @param variant
	 *            the variant
	 * @param extension
	 *            the extension to associate with the variant
	 * @return this instance for fluent chaining
	 */
	public ExtensionMappingVariantResolver register(Variant variant, String extension) {
		List<String> extensions = mapping.computeIfAbsent(variant, k -> new ArrayList<>());
		String normalized = extension.startsWith(".") ? extension : ("." + extension);
		extensions.add(normalized);
		return this;
	}

	private ResourcePath resolvePath(TemplateContent template, List<String> extensions) {
		ResourcePath templatePath = template.getPath();
		for (String extension : extensions) {
			try {
				ResourcePath path = resolveVariantPath(template.getPath().getOriginalPath(), extension);
				resourceResolver.getResource(path);
				return path;
			} catch (ResourceResolutionException e) {
				LOG.trace("template {}.{} not found", templatePath, extension, e);
			}
		}
		return null;
	}

	private ResourcePath resolveVariantPath(String originalPath, String extension) {
		// if extension already explicitly set, try without adding the extension
		// provided by variant
		ResolvedPath path = useExplicitExtensionIfSameAsVariant(originalPath, extension);
		if (path != null) {
			return path;
		}
		return resourceResolver.resolve(new UnresolvedPath(originalPath + extension));
	}

	private ResolvedPath useExplicitExtensionIfSameAsVariant(String originalPath, String extension) {
		if (originalPath.endsWith(extension)) {
			return resourceResolver.resolve(new UnresolvedPath(originalPath));
		}
		return null;
	}

}