/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package fedora.utilities;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import org.apache.log4j.Logger;
/**
* <tt>MimeTypeUtils</tt> provides the
* {@link #fileExtensionForMIMEType fileExtensionForMIMEType()} method, which
* converts a MIME type to a file extension. That method uses a traditional
* <tt>mime.types</tt> files, similar to the file shipped with with web
* servers such as Apache. It looks for a suitable file in the following
* locations:
* <ol>
* <li> First, it looks for the file <tt>.mime.types</tt> in the user's home
* directory.
* <li> Next, it looks for <tt>mime.types</tt> (no leading ".") in all the
* directories in the CLASSPATH
* </ol>
* <p>
* It loads all the matching files it finds; the first mapping found for a given
* MIME type is the one that is used. The files are only loaded once within a
* given running Java VM.
* </p>
*
* <p>This class is derived from org.clapper.util.misc.MIMETypeUtil, originally
* authored by Brian M. Clapper and made available under a BSD-style license.</p>
*
* @author Brian M. Clapper
* @author Edwin Shin *
* @version $Id$
*/
public class MimeTypeUtils {
/**
* Default MIME type, when a MIME type cannot be determined from a file's
* extension.
*
* @see #MIMETypeForFile
* @see #MIMETypeForFileName
*/
public static final String DEFAULT_MIME_TYPE = "application/octet-stream";
/** Logger for this class. */
private static final Logger LOG = Logger.getLogger(MimeTypeUtils.class);
/**
* Table for converting MIME type strings to file extensions. The table is
* initialized the first time fileExtensionForMIMEType() is called.
*/
private static Map<String, String> mimeTypeToExtensionMap = null;
/**
* Resource bundle containing MIME type mappings
*/
private static final String MIME_MAPPINGS_BUNDLE =
"fedora.server.resources.MIMETypes";
private MimeTypeUtils() {
// Can't be instantiated
}
/**
* Get an appropriate extension for a MIME type.
*
* @param mimeType
* the String MIME type
* @return the appropriate file name extension, or a default extension if
* not found. The extension will not have the leading "." character.
*/
public static String fileExtensionForMIMEType(String mimeType) {
loadMappings();
String ext = (String) mimeTypeToExtensionMap.get(mimeType);
if (ext == null) ext = "dat";
return ext;
}
/**
* Load the MIME type mappings into memory.
*/
private static synchronized void loadMappings() {
if (mimeTypeToExtensionMap != null) return;
mimeTypeToExtensionMap = new HashMap<String, String>();
// First, check the user's home directory.
String fileSep = System.getProperty("file.separator");
StringBuffer buf = new StringBuffer();
buf.append(System.getProperty("user.home"));
buf.append(fileSep);
buf.append(".mime.types");
loadMIMETypesFile(buf.toString());
// Now, check every directory in the classpath.
String pathSep = System.getProperty("path.separator");
String[] pathComponents = pathSep.split(" ");
int i;
for (i = 0; i < pathComponents.length; i++) {
buf.setLength(0);
buf.append(pathComponents[i]);
buf.append(fileSep);
buf.append("mime.types");
loadMIMETypesFile(buf.toString());
}
// Finally, load the resource bundle.
ResourceBundle bundle = ResourceBundle.getBundle(MIME_MAPPINGS_BUNDLE);
for (Enumeration<?> e = bundle.getKeys(); e.hasMoreElements();) {
String type = (String) e.nextElement();
try {
String[] extensions = bundle.getString(type).split(" ");
if (mimeTypeToExtensionMap.get(type) == null) {
LOG.debug("Internal: " + type + " -> \"" + extensions[0]
+ "\"");
mimeTypeToExtensionMap.put(type, extensions[0]);
}
}
catch (MissingResourceException ex) {
LOG.error("While reading internal bundle \""
+ MIME_MAPPINGS_BUNDLE
+ "\", got unexpected error on key \"" + type
+ "\"",
ex);
}
}
}
/**
* Attempt to load a MIME types file. Throws no exceptions.
*
* @param path
* path to the file
* @param map
* map to load
*/
private static void loadMIMETypesFile(String path) {
try {
File f = new File(path);
LOG.debug("Attempting to load MIME types file \"" + path + "\"");
if (!(f.exists() && f.isFile()))
LOG.debug("Regular file \"" + path + "\" does not exist.");
else {
LineNumberReader r = new LineNumberReader(new FileReader(f));
String line;
while ((line = r.readLine()) != null) {
line = line.trim();
if ((line.length() == 0) || (line.startsWith("#")))
continue;
String[] fields = line.split(" ");
// Skip lines without at least two tokens.
if (fields.length < 2) continue;
// Special case: Scan the extensions, and make sure we
// have at least one valid extension. Some .mime.types
// files have entries like this:
//
// mime/type desc="xxx" exts="jnlp"
//
// We don't handle those.
List<String> extensions = new ArrayList<String>();
for (int i = 1; i < fields.length; i++) {
if (fields[i].indexOf('=') != -1) continue;
if (fields[i].indexOf('"') != -1) continue;
// Treat as valid. Remove any leading "."
if (fields[i].startsWith(".")) {
if (fields[i].length() == 1) continue;
fields[i] = fields[i].substring(1);
}
extensions.add(fields[i]);
}
if (extensions.size() == 0) continue;
// If the MIME type doesn't have a "/", skip it
String mimeType = fields[0];
String extension;
if (mimeType.indexOf('/') == -1) continue;
// The first field is the preferred extension. Keep any
// existing mapping for the MIME type.
if (mimeTypeToExtensionMap.get(mimeType) == null) {
extension = (String) extensions.get(0);
LOG.debug("File \"" + path + "\": " + mimeType
+ " -> \"" + extension + "\"");
mimeTypeToExtensionMap.put(mimeType, extension);
}
}
r.close();
}
} catch (IOException ex) {
LOG.debug("Error reading \"" + path + "\"", ex);
}
}
}