eu.medsea.mimeutil.detector.ExtensionMimeDetector.java Source code

Java tutorial

Introduction

Here is the source code for eu.medsea.mimeutil.detector.ExtensionMimeDetector.java

Source

/*
 * Copyright 2007-2009 Medsea Business Solutions S.L.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package eu.medsea.mimeutil.detector;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import eu.medsea.mimeutil.MimeException;
import eu.medsea.mimeutil.MimeType;
import eu.medsea.mimeutil.MimeUtil;
import eu.medsea.mimeutil.TextMimeDetector;

/**
 * <p>
 * The extension mime mappings are loaded in the following way.
 * <ol>
 * <li>Load the properties file from the mime utility jar named <code>eu.medsea.mimeutil.mime-types.properties</code>.</li>
 * <li>Locate and load a file named <code>.mime-types.properties</code> from the users home directory if one exists.</li>
 * <li>Locate and load a file named <code>mime-types.properties</code> from the classpath if one exists</li>
 * <li>locate and load a file named by the JVM property <code>mime-mappings</code> i.e. <code>-Dmime-mappings=../my-mime-types.properties</code></li>
 * </ol>
 * Each property file loaded will add to the list of extensions understood by MimeUtil. If there is a clash of extension names then the last one loaded wins, i.e they are not adative, this makes it
 * possible to completely change the mime types associated to a file extension declared in previously loaded property files. The extensions are also case sensitive meaning that bat, bAt, BAT and Bat
 * can all be recognised individually. If however, no match is found using case sensitive matching then it will perform an insensitive match by lower casing the extension of the file to be matched
 * first.
 * </p>
 * <p>
 * Fortunately, we have compiled a relatively large list of mappings into a java properties file from information gleaned from many sites on the Internet. This file resides in the
 * eu.medsea.util.mime-types.properties file and is not guaranteed to be correct or contain all the known mappings for a file extension type. This is not a complete or exhaustive list as that would
 * have proven too difficult to compile for this project. So instead we give you the opportunity to extend and override these mappings for yourself as defined above. Obviously, to use this method you
 * don't actually need a file object, you just need a file name with an extension. Also, if you have given or renamed a file using a different extension than the one that it would normally be
 * associated with then this mapping will return the wrong mime-type and if the file has no extension at all, such as Make, then it's not going to be possible to determine a mime type using this
 * technique
 * </p>
 * <p>
 * We acquired many mappings from many different sources on the net for the extension mappings. The internal list is quite large and there can be many associated mime types. These may not match what
 * you are expecting so you can add the mapping you want to change to your own property file following the rules above. If you provide a mapping for an extension then any previously loaded mappings
 * will be removed and only the mappings you define will be returned. This can be used to map certain extensions that are incorrectly returned for our environment defined in the internal property
 * file.
 * </p>
 * <p>
 * If we have not provided a mapping for a file extension that you know the mime type for you can add this to your custom property files so that a correct mime type is returned for you.
 * <p>
 * <p>
 * We use the <code>application/directory</code> mime type to identify directories. Even though this is not an official mime type it seems to be well accepted on the net as an unofficial mime type so
 * we thought it was OK for us to use as well.
 * </p>
 * <p>
 * This class is auto loaded by MimeUtil as it has an entry in the file called MimeDetectors. MimeUtil reads this file at startup and calls Class.forName() on each entry found. This mean the
 * MimeDetector must have a no arg constructor.
 * </p>
 * @author Steven McArdle.
 */
public class ExtensionMimeDetector extends MimeDetector {

    //private static Logger log = LoggerFactory.getLogger(ExtensionMimeDetector.class);
    private static Log log = LogFactory.getLog(ExtensionMimeDetector.class);

    // Extension MimeTypes
    private static Map extMimeTypes;

    public ExtensionMimeDetector() {
        ExtensionMimeDetector.initMimeTypes();
    }

    public String getDescription() {
        return "Get the mime types of file extensions";
    }

    /**
     * Get the mime type of a file using extension mappings. The file path
     * can be a relative or absolute path or can refer to a completely non-existent file as
     * only the extension is important here.
     * @param file points to a file or directory. May not actually exist
     * @return collection of the matched mime types.
     * @throws MimeException if errors occur.
     */
    public Collection getMimeTypesFile(final File file) throws MimeException {
        return getMimeTypesFileName(file.getName());
    }

    /**
     * Get the mime type of a URL using extension mappings. Only the extension is important here.
     * @param url is a valid URL
     * @return collection of the matched mime types.
     * @throws MimeException if errors occur.
     */
    public Collection getMimeTypesURL(final URL url) throws MimeException {
        return getMimeTypesFileName(url.getPath());
    }

    /**
     * Get the mime type of a file name using file name extension mappings. The file name path
     * can be a relative or absolute path or can refer to a completely non-existent file as
     * only the extension is important here.
     * @param fileName points to a file or directory. May not actually exist
     * @return collection of the matched mime types.
     * @throws MimeException if errors occur.
     */
    public Collection getMimeTypesFileName(final String fileName) throws MimeException {
        Collection mimeTypes = new HashSet();

        String fileExtension = MimeUtil.getExtension(fileName);
        while (fileExtension.length() != 0) {
            String types = null;
            // First try case sensitive
            types = (String) extMimeTypes.get(fileExtension);
            if (types != null) {
                String[] mimeTypeArray = types.split(",");
                for (int i = 0; i < mimeTypeArray.length; i++) {
                    mimeTypes.add(new MimeType(mimeTypeArray[i]));
                }
                return mimeTypes;
            }
            if (mimeTypes.isEmpty()) {
                // Failed to find case insensitive extension so lets try again with
                // lowercase
                types = (String) extMimeTypes.get(fileExtension.toLowerCase());
                if (types != null) {
                    String[] mimeTypeArray = types.split(",");
                    for (int i = 0; i < mimeTypeArray.length; i++) {
                        mimeTypes.add(new MimeType(mimeTypeArray[i]));
                    }
                    return mimeTypes;
                }
            }
            fileExtension = MimeUtil.getExtension(fileExtension);
        }
        return mimeTypes;
    }

    /*
     * This loads the mime-types.properties files that define mime types based
     * on file extensions using the following load sequence 1. Loads the
     * property file from the mime utility jar named
     * eu.medsea.mime.mime-types.properties. 2. Locates and loads a file named
     * .mime-types.properties from the users home directory if one exists. 3.
     * Locates and loads a file named mime-types.properties from the classpath
     * if one exists 4. locates and loads a file named by the JVM property
     * mime-mappings i.e. -Dmime-mappings=../my-mime-types.properties
     */
    private static void initMimeTypes() {
        InputStream is = null;
        extMimeTypes = new Properties();
        try {
            // Load the file extension mappings from the internal property file and
            // then
            // from the custom property files if they can be found
            try {
                // Load the default supplied mime types
                is = MimeUtil.class.getClassLoader()
                        .getResourceAsStream("eu/medsea/mimeutil/mime-types.properties");
                if (is != null) {
                    ((Properties) extMimeTypes).load(is);
                }
            } catch (Exception e) {
                // log the error but don't throw the exception up the stack
                log.error("Error loading internal mime-types.properties", e);
            } finally {
                is = closeStream(is);
            }

            // Load any .mime-types.properties from the users home directory
            try {
                File f = new File(System.getProperty("user.home") + File.separator + ".mime-types.properties");
                if (f.exists()) {
                    is = new FileInputStream(f);
                    if (is != null) {
                        log.debug("Found a custom .mime-types.properties file in the users home directory.");
                        Properties props = new Properties();
                        props.load(is);
                        if (props.size() > 0) {
                            extMimeTypes.putAll(props);
                        }
                        log.debug("Successfully parsed .mime-types.properties from users home directory.");
                    }
                }
            } catch (Exception e) {
                log.error("Failed to parse .magic.mime file from users home directory. File will be ignored.", e);
            } finally {
                is = closeStream(is);
            }

            // Load any classpath provided mime types that either extend or
            // override the default mime type entries. Could also be in jar files.
            // Get an enumeration of all files on the classpath with this name. They could be in jar files as well
            try {
                Enumeration e = MimeUtil.class.getClassLoader().getResources("mime-types.properties");
                while (e.hasMoreElements()) {
                    URL url = (URL) e.nextElement();
                    if (log.isDebugEnabled()) {
                        log.debug("Found custom mime-types.properties file on the classpath [" + url + "].");
                    }
                    Properties props = new Properties();
                    try {
                        is = url.openStream();
                        if (is != null) {
                            props.load(is);
                            if (props.size() > 0) {
                                extMimeTypes.putAll(props);
                                if (log.isDebugEnabled()) {
                                    log.debug("Successfully loaded custome mime-type.properties file [" + url
                                            + "] from classpath.");
                                }
                            }
                        }
                    } catch (Exception ex) {
                        log.error("Failed while loading custom mime-type.properties file [" + url
                                + "] from classpath. File will be ignored.");
                    }
                }
            } catch (Exception e) {
                log.error(
                        "Problem while processing mime-types.properties files(s) from classpath. Files will be ignored.",
                        e);
            } finally {
                is = closeStream(is);
            }

            try {
                // Load any mime extension mappings file defined with the JVM
                // property -Dmime-mappings=../my/custom/mappings.properties
                String fname = System.getProperty("mime-mappings");
                if (fname != null && fname.length() != 0) {
                    is = new FileInputStream(fname);
                    if (is != null) {
                        if (log.isDebugEnabled()) {
                            log.debug(
                                    "Found a custom mime-mappings property defined by the property -Dmime-mappings ["
                                            + System.getProperty("mime-mappings") + "].");
                        }
                        Properties props = new Properties();
                        props.load(is);
                        if (props.size() > 0) {
                            extMimeTypes.putAll(props);
                        }
                        log.debug("Successfully loaded the mime mappings file from property -Dmime-mappings ["
                                + System.getProperty("mime-mappings") + "].");
                    }
                }
            } catch (Exception ex) {
                log.error("Failed to load the mime-mappings file defined by the property -Dmime-mappings ["
                        + System.getProperty("mime-mappings") + "].");
            } finally {
                is = closeStream(is);
            }
        } finally {
            // Load the mime types into the known mime types map of MimeUtil
            Iterator it = extMimeTypes.values().iterator();
            while (it.hasNext()) {
                String[] types = ((String) it.next()).split(",");
                for (int i = 0; i < types.length; i++) {
                    MimeUtil.addKnownMimeType(types[i]);
                }
            }
        }
    }

    /**
     * This method is required by the abstract MimeDetector class. As we do not support extension mapping of streams
     * we just throw an {@link UnsupportedOperationException}. This ensures that the getMimeTypes(...) methods ignore this
     * method. We could also have just returned an empty collection.
     */
    public Collection getMimeTypesInputStream(InputStream in) throws UnsupportedOperationException {
        throw new UnsupportedOperationException("This MimeDetector does not support detection from streams.");
    }

    /**
     * This method is required by the abstract MimeDetector class. As we do not support extension mapping of byte arrays
     * we just throw an {@link UnsupportedOperationException}. This ensures that the getMimeTypes(...) methods ignore this
     * method. We could also have just returned an empty collection.
     */
    public Collection getMimeTypesByteArray(byte[] data) throws UnsupportedOperationException {
        throw new UnsupportedOperationException("This MimeDetector does not support detection from byte arrays.");
    }
}