package ro.isdc.wro.http.support;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Responseible for resolving a content based on file name.
*
* @author Alex Objelean
*/
public class ContentTypeResolver {
private static final String DEFAULT_CONTENT_TYPE = "application/octet-stream";
private static final Logger LOG = LoggerFactory.getLogger(ContentTypeResolver.class);
private final static Map<String, String> defaultContentTypeMap = new HashMap<String, String>();
private final static Set<String> requiresCharset = new HashSet<String>();
static {
defaultContentTypeMap.put("txt", "text/plain");
defaultContentTypeMap.put("css", "text/css");
defaultContentTypeMap.put("html", "text/html");
defaultContentTypeMap.put("htm", "text/html");
defaultContentTypeMap.put("xml", "application/xml");
defaultContentTypeMap.put("xhtml", "application/xhtml+xml");
defaultContentTypeMap.put("js", "application/javascript");
defaultContentTypeMap.put("png", "image/png");
defaultContentTypeMap.put("gif", "image/gif");
defaultContentTypeMap.put("jpg", "image/jpeg");
defaultContentTypeMap.put("jpeg", "image/jpeg");
// font types
defaultContentTypeMap.put("eot", "application/vnd.ms-fontobject");
defaultContentTypeMap.put("otf", "application/x-font-opentype");
requiresCharset.add("text/css");
requiresCharset.add("text/html");
requiresCharset.add("text/plain");
requiresCharset.add("application/xml");
requiresCharset.add("application/xhtml+xml");
requiresCharset.add("application/javascript");
}
/**
* Returns a valid HTTP contentType's for a given filename. It first relies on the custom defaultContentTypeMap and if
* not found it will fall back to defaultFileTypeMap from javax.activation.FileTypeMap. Examples: - "somefile.css"
* resolves to "text/css". - "somefile.js.png" resolves to "image/png" - "/blah/index.html resolves to "text/html".
* <p/>
* The implementation uses reflection to load <code>javax.activation.FileTypeMap</code> class (available in jdk6) in
* order to be compatible with jdk5. If this class is not available, the default content type is returned.
*
* @param fileName
* with an filename extension
* @return contentType
*/
public static String get(final String fileName) {
final String extension = FilenameUtils.getExtension(fileName).toLowerCase();
if (defaultContentTypeMap.containsKey(extension)) {
return defaultContentTypeMap.get(extension);
}
try {
final Class<?> fileTypeMapClass = ClassLoader.getSystemClassLoader().loadClass("javax.activation.FileTypeMap");
LOG.debug("using {} to resolve contentType", fileTypeMapClass.getName());
final Object fileTypeMap = fileTypeMapClass.getMethod("getDefaultFileTypeMap").invoke(fileTypeMapClass);
return (String) fileTypeMapClass.getMethod("getContentType", String.class).invoke(fileTypeMap, fileName);
} catch (final Exception e) {
LOG.debug("FileTypeMap is not available (probably jdk5 is used). Exception {}, with message: {}", e.getClass(),
e.getMessage());
LOG.debug("Will use default content type: {} for fileName: {}", DEFAULT_CONTENT_TYPE, fileName);
}
return DEFAULT_CONTENT_TYPE;
}
/**
* Returns a valid HTTP contentType's for a given filename with charset. It first relies on the custom
* defaultContentTypeMap and if not found it will fall back to defaultFileTypeMap from javax.activation.FileTypeMap.
* Examples: - ("somefile.css", "UTF-8") resolves to "text/css"; charset=UTF-8". - ("somefile.js.png", "UTF-8")
* resolves to "image/png" - ("/blah/index.html, "UTF-8") resolves to "text/html; charset=8"
*
* @param fileName
* with an filename extension
* @param encoding
* which encoding to use
* @return contentType
*/
public static String get(final String fileName, final String encoding) {
final String contentType = get(fileName);
if (requiresCharset.contains(contentType)) {
return contentType + "; charset=" + encoding;
}
return contentType;
}
}