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; } }