package com.google.sitebricks.rendering.resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.atomic.AtomicReference;
import net.jcip.annotations.ThreadSafe;
import com.google.common.collect.MapMaker;
import com.google.common.io.CharStreams;
import com.google.inject.Singleton;
import com.google.sitebricks.Export;
import com.google.sitebricks.Renderable;
import com.google.sitebricks.Respond;
/**
* @author Dhanji R. Prasanna (dhanji@gmail com)
*/
@ThreadSafe
@Singleton
class ClasspathResourcesService implements ResourcesService {
private final Map<String, Resource> resources = new MapMaker().makeMap();
private static final AtomicReference<Map<String, String>> mimes =
new AtomicReference<Map<String, String>>();
private static final String DEFAULT_MIME = "__defaultMimeType";
public ClasspathResourcesService() {
if (null == mimes.get()) {
final Properties properties = new Properties();
try {
properties.load(
ClasspathResourcesService.class.getResourceAsStream("mimetypes.properties"));
} catch (IOException e) {
throw new ResourceLoadingException("Can't find mimetypes.properties", e);
}
//noinspection unchecked
mimes.compareAndSet(null, (Map) properties);
}
}
public void add(Class<?> clazz, Export export) {
resources.put(export.at(), new Resource(export, clazz));
}
public Respond serve(String uri) {
final Resource resource = resources.get(uri);
//nothing registered
if (null == resource) {
return null;
}
//load and render resource to responder
return new StaticResourceRespond(resource);
}
static String mimeOf(String file) {
final Map<String, String> mimeTypes = mimes.get();
for (Map.Entry<String, String> mime : mimeTypes.entrySet()) {
if (file.matches(mime.getKey()))
return mime.getValue();
}
//no match, use the default?
return mimeTypes.get(DEFAULT_MIME);
}
private static class Resource {
private final Export export;
private final Class<?> clazz;
private final String mimeType;
private Resource(Export export, Class<?> clazz) {
this.export = export;
this.clazz = clazz;
this.mimeType = mimeOf(export.resource());
}
public String toString() {
return new StringBuilder()
.append("Resource {")
.append("export=")
.append(export)
.append(", class=")
.append(clazz).append('}')
.toString();
}
}
private static class StaticResourceRespond implements Respond {
private final Resource resource;
public StaticResourceRespond(Resource resource) {
this.resource = resource;
}
public String getContentType() {
return resource.mimeType;
}
@Override
public String toString() {
//load and render
List list;
try {
final InputStream stream = resource.clazz.getResourceAsStream(resource.export.resource());
if (null == stream)
throw new ResourceLoadingException(
"Couldn't find static resource (did you spell it right?) specified by: "
+ resource);
list = CharStreams.readLines(new InputStreamReader(stream));
} catch (IOException e) {
throw new ResourceLoadingException(
"Error loading static resource specified by: " + resource, e);
}
StringBuilder buffer = new StringBuilder();
for (Object o : list) {
buffer.append((String) o);
}
return buffer.toString();
}
public void write(String text) {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public HtmlTagBuilder withHtml() {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public void write(char c) {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public void chew() {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public void writeToHead(String text) {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public void require(String requireString) {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public void redirect(String to) {
throw new UnsupportedOperationException("Static resource responders can't be written to");
}
public String getRedirect() {
return null;
}
public Renderable include(String argument) {
return null;
}
public String getHead() {
return null;
}
@Override
public void clear() {
}
@Override public Object pageObject() {
return null;
}
@Override
public List<String> getErrors() {
return null;
}
@Override
public void setErrors(List<String> errors) {
}
}
}