ResourceResolutionBuilderHelper.java
package fr.sii.ogham.core.builder.resolution;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import fr.sii.ogham.core.builder.Builder;
import fr.sii.ogham.core.builder.context.BuildContext;
import fr.sii.ogham.core.builder.env.EnvironmentBuilder;
import fr.sii.ogham.core.resource.resolver.ResourceResolver;
/**
* Resource resolution is used many times. This implementation helps configure
* resource resolution.
*
* @author Aurélien Baudet
*
* @param <FLUENT>
* The type of the helped instance. This is needed to have the right
* return type for fluent chaining
*/
@SuppressWarnings("squid:S00119")
public class ResourceResolutionBuilderHelper<FLUENT extends ResourceResolutionBuilder<FLUENT>> implements ResourceResolutionBuilder<FLUENT> {
private final BuildContext buildContext;
private ClassPathResolutionBuilder<FLUENT> classPath;
private FileResolutionBuilder<FLUENT> file;
private StringResolutionBuilder<FLUENT> string;
private List<ResourceResolver> customResolvers;
private FLUENT fluent;
/**
* Initializes the helper with the fluent instance and the
* {@link EnvironmentBuilder}. The fluent instance is used for chaining. It
* indicates which type is returned. The {@link EnvironmentBuilder} is used
* by sub-builders ( {@link ClassPathResolutionBuilder} and
* {@link FileResolutionBuilder}) to evaluate properties when their build
* methods are called.
*
* @param fluent
* the instance used for chaining calls
* @param buildContext
* for property resolution
*/
public ResourceResolutionBuilderHelper(FLUENT fluent, BuildContext buildContext) {
super();
this.fluent = fluent;
this.buildContext = buildContext;
customResolvers = new ArrayList<>();
}
@Override
public ClassPathResolutionBuilder<FLUENT> classpath() {
if (classPath == null) {
classPath = new ClassPathResolutionBuilder<>(fluent, buildContext);
}
return classPath;
}
@Override
public FileResolutionBuilder<FLUENT> file() {
if (file == null) {
file = new FileResolutionBuilder<>(fluent, buildContext);
}
return file;
}
@Override
public StringResolutionBuilder<FLUENT> string() {
if (string == null) {
string = new StringResolutionBuilder<>(fluent, buildContext);
}
return string;
}
@Override
public FLUENT resolver(ResourceResolver resolver) {
customResolvers.add(resolver);
return fluent;
}
/**
* For each kind of lookup, stores the list of registered lookups.
*
* @return map of lookups indexed by lookup type.
*/
public Map<String, List<String>> getAllLookups() {
Map<String, List<String>> all = new HashMap<>();
if (string != null) {
all.put("string", string.getLookups());
}
if (file != null) {
all.put("file", file.getLookups());
}
if (classPath != null) {
all.put("classpath", classPath.getLookups());
}
return all;
}
/**
* Build the list of resource resolvers.
*
* <p>
* The list is ordered to ensure that empty string lookup is always the last
* registered.
* </p>
*
* <p>
* If some custom resolvers are registered, they are used before default
* ones in the order they were registered.
* </p>
*
* @return the list of resource resolvers
*/
public List<ResourceResolver> buildResolvers() {
List<ResourceResolver> resolvers = new ArrayList<>();
resolvers.addAll(customResolvers);
// ensure that default lookup is always the last registered
List<ResolverHelper> helpers = new ArrayList<>();
if (classPath != null) {
helpers.add(new ResolverHelper(classPath.getLookups(), classPath));
}
if (file != null) {
helpers.add(new ResolverHelper(file.getLookups(), file));
}
if (string != null) {
helpers.add(new ResolverHelper(string.getLookups(), string));
}
Collections.sort(helpers, new PrefixComparator());
for (ResolverHelper helper : helpers) {
helper.register(resolvers);
}
return resolvers;
}
/**
* Order prefixes in order to ensure that empty string lookup is registered
* at the end.
*
* @author Aurélien Baudet
*
*/
private static class PrefixComparator implements Comparator<ResolverHelper> {
@Override
public int compare(ResolverHelper o1, ResolverHelper o2) {
StringBuilder concat1 = new StringBuilder();
for (String prefix : o1.getPrefixes()) {
if (prefix.isEmpty()) {
return 1;
}
concat1.append(prefix);
}
StringBuilder concat2 = new StringBuilder();
for (String prefix : o2.getPrefixes()) {
if (prefix.isEmpty()) {
return -1;
}
concat2.append(prefix);
}
return concat1.toString().compareTo(concat2.toString());
}
}
private static class ResolverHelper {
private final List<String> prefixes;
private final Builder<ResourceResolver> builder;
public ResolverHelper(List<String> prefixes, Builder<ResourceResolver> builder) {
super();
this.prefixes = prefixes;
this.builder = builder;
}
public void register(List<ResourceResolver> resolvers) {
resolvers.add(builder.build());
}
public List<String> getPrefixes() {
return prefixes;
}
}
}