package pl.matisoft.soy.template;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import pl.matisoft.soy.config.SoyViewConfigDefaults;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import javax.annotation.concurrent.ThreadSafe;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* An implementation that will recursively search (and resolve)
* for soy files based on provided templatesLocation path
* @author: Brandon Zeeb
*/
@ParametersAreNonnullByDefault
@ThreadSafe
public class ClasspathTemplateFilesResolver implements TemplateFilesResolver, InitializingBean {
private static final Logger logger = LoggerFactory.getLogger(ClasspathTemplateFilesResolver.class);
/** spring resource string that points to a root path, in which soy templates are located */
private String templatesLocation = null;
private boolean recursive = true;
private boolean hotReloadMode = SoyViewConfigDefaults.DEFAULT_HOT_RELOAD_MODE;
private PathMatchingResourcePatternResolver resolver;
private ResourceLoader resourceLoader = new DefaultResourceLoader(getClass().getClassLoader());
/** friendly */ Collection<URL> cachedFiles;
private String filesExtension = SoyViewConfigDefaults.DEFAULT_FILES_EXTENSION;
@Override
public void afterPropertiesSet() {
Preconditions.checkArgument(resourceLoader != null, "A ResourceLoader is expected to have been provided.");
resolver = new PathMatchingResourcePatternResolver(resourceLoader);
}
@Override
public Collection<URL> resolve() throws IOException {
Preconditions.checkNotNull(templatesLocation, "templatesLocation cannot be null!");
return Collections.unmodifiableCollection(handleResolution());
}
@Override
public Optional<URL> resolve(final @Nullable String templateFileName) throws IOException {
if (templateFileName == null) {
return Optional.absent();
}
final Collection<URL> files = handleResolution();
final String normalizedFileName = normalizeTemplateName(templateFileName);
final URL templateFile = Iterables.find(files, new Predicate<URL>() {
@Override
public boolean apply(final URL url) {
final String fileName = url.getFile();
final File file = new File(fileName);
return file.toURI().toString().endsWith(normalizedFileName);
}
}, null);
return Optional.fromNullable(templateFile);
}
protected Collection<URL> handleResolution() throws IOException {
Preconditions.checkNotNull(templatesLocation, "templatesLocation cannot be null!");
if (hotReloadMode) {
final Collection<URL> files = resolveSoyResources(templatesLocation);
logger.debug("Debug on - resolved files: {}", files.size());
return Collections.unmodifiableCollection(files);
}
// no debug
// double check locking to ensure we don't block threads to test if cached files is null
if (cachedFiles == null) {
synchronized (this) {
if (cachedFiles == null) {
final Collection<URL> files = resolveSoyResources(templatesLocation);
logger.debug("templates location: {}", templatesLocation);
logger.debug("Using cache resolve, debug off, urls: {}", files.size());
cachedFiles = Collections.unmodifiableCollection(files);
}
}
}
return cachedFiles;
}
private Collection<URL> resolveSoyResources(final String templatesLocation) throws IOException {
List<URL> urls = new LinkedList<URL>();
final String search = templateSearchStrings(templatesLocation);
final Resource[] resources = resolver.getResources(search);
for (Resource r : resources) {
urls.add(r.getURL());
}
return urls;
}
private String normalizeTemplateName(final String templateFileName) {
String normalizedTemplateName = templateFileName;
if (!templateFileName.endsWith(dotWithExtension())) {
normalizedTemplateName = templateFileName + dotWithExtension();
}
return normalizedTemplateName;
}
private String templateSearchStrings(String resource) {
if (recursive) {
return resource + "/**/*" + dotWithExtension();
}
return resource + "/*" + dotWithExtension();
}
private String dotWithExtension() {
return "." + filesExtension;
}
public void setTemplatesLocation(String templatesLocation) {
this.templatesLocation = templatesLocation;
}
public void setRecursive(boolean recursive) {
this.recursive = recursive;
}
public void setHotReloadMode(boolean hotReloadMode) {
this.hotReloadMode = hotReloadMode;
}
public String getTemplatesLocation() {
return templatesLocation;
}
public boolean isRecursive() {
return recursive;
}
public boolean isHotReloadMode() {
return hotReloadMode;
}
public String getFilesExtension() {
return filesExtension;
}
public void setFilesExtension(String filesExtension) {
this.filesExtension = filesExtension;
}
}