001    package org.apache.fulcrum.mimetype.util;
002    
003    
004    /*
005     * Licensed to the Apache Software Foundation (ASF) under one
006     * or more contributor license agreements.  See the NOTICE file
007     * distributed with this work for additional information
008     * regarding copyright ownership.  The ASF licenses this file
009     * to you under the Apache License, Version 2.0 (the
010     * "License"); you may not use this file except in compliance
011     * with the License.  You may obtain a copy of the License at
012     *
013     *   http://www.apache.org/licenses/LICENSE-2.0
014     *
015     * Unless required by applicable law or agreed to in writing,
016     * software distributed under the License is distributed on an
017     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
018     * KIND, either express or implied.  See the License for the
019     * specific language governing permissions and limitations
020     * under the License.
021     */
022    
023    
024    import java.io.File;
025    import java.io.InputStream;
026    import java.io.IOException;
027    
028    /**
029     * This class maintains a set of mappers defining mappings
030     * between MIME types and the corresponding file name extensions.
031     * The mappings are defined as lines formed by a MIME type name
032     * followed by a list of extensions separated by a whitespace.
033     * The definitions can be listed in MIME type files located in user's
034     * home directory, Java home directory or the current class jar.
035     * In addition, this class maintains static default mappings
036     * and constructors support application specific mappings.
037     *
038     * @author <a href="mailto:ilkka.priha@simsoft.fi">Ilkka Priha</a>
039     * @author Daniel Rall
040     * @version $Id: MimeTypeMap.java 826489 2009-10-18 18:54:59Z tv $
041     */
042    public class MimeTypeMap
043    {
044        /**
045         * The default MIME type when nothing else is applicable.
046         */
047        public static final MimeType DEFAULT_MIMETYPE =
048            MimeType.APPLICATION_OCTET_STREAM;
049    
050        /**
051         * The default MIME type as a string.
052         */
053        public static final String DEFAULT_TYPE = DEFAULT_MIMETYPE.toString();
054    
055        /**
056         * The name for MIME type mapper resources.
057         */
058        public static final String MIMETYPE_RESOURCE = "mime.types";
059    
060        /**
061         * Common MIME type extensions.
062         */
063        public static final String EXT_HTML = "html";
064        public static final String EXT_HTM = "htm";
065        public static final String EXT_WML = "wml";
066        public static final String EXT_HDML = "hdml";
067        public static final String EXT_HDM = "hdm";
068        public static final String EXT_CHTML = "chtml";
069        public static final String EXT_TEXT = "txt";
070        public static final String EXT_GIF = "gif";
071        public static final String EXT_JPEG = "jpeg";
072        public static final String EXT_JPG = "jpg";
073        public static final String EXT_WBMP = "wbmp";
074    
075        /**
076         * Priorities of available mappers.
077         */
078        private static final int MAP_PROG = 0;
079        private static final int MAP_HOME = 1;
080        private static final int MAP_SYS = 2;
081        private static final int MAP_JAR = 3;
082        private static final int MAP_COM = 4;
083    
084        /**
085         * A common MIME type mapper.
086         */
087        private static MimeTypeMapper commonMapper = new MimeTypeMapper();
088        static
089        {
090            commonMapper.setContentType(
091                MimeType.TEXT_HTML.toString() + " " + EXT_HTML + " " + EXT_HTM);
092            commonMapper.setContentType(
093                MimeType.TEXT_WML.toString() + " " + EXT_WML);
094            commonMapper.setContentType(
095                MimeType.TEXT_HDML.toString() + " " + EXT_HDML + " " + EXT_HDM);
096            commonMapper.setContentType(
097                MimeType.TEXT_CHTML.toString() + " " + EXT_CHTML);
098            commonMapper.setContentType(
099                MimeType.TEXT_PLAIN.toString() + " " + EXT_TEXT);
100            commonMapper.setContentType(
101                MimeType.IMAGE_GIF.toString() + " " + EXT_GIF);
102            commonMapper.setContentType(
103                MimeType.IMAGE_JPEG.toString() + " " + EXT_JPEG + " " + EXT_JPG);
104            commonMapper.setContentType(
105                MimeType.IMAGE_WBMP.toString() + " " + EXT_WBMP);
106        }
107    
108        /**
109         * An array of available MIME type mappers.
110         */
111        private MimeTypeMapper mappers[] = new MimeTypeMapper[5];
112    
113        /**
114         * Loads mappings from a file path.
115         *
116         * @param path a file path.
117         * @return the mappings.
118         * @throws IOException for an incorrect file.
119         */
120        protected static MimeTypeMapper loadPath(String path)
121            throws IOException
122        {
123            return new MimeTypeMapper(path);
124        }
125    
126        /**
127         * Loads mappings from a resource.
128         *
129         * @param name a resource name.
130         * @return the mappings.
131         */
132        protected static MimeTypeMapper loadResource(String name)
133        {
134            InputStream input = MimeTypeMap.class.getResourceAsStream(name);
135            if (input != null)
136            {
137                try
138                {
139                    return new MimeTypeMapper(input);
140                }
141                catch (IOException x)
142                {
143                    return null;
144                }
145            }
146            else
147            {
148                return null;
149            }
150        }
151    
152        /**
153         * Constructs a new MIME type map with default mappers.
154         */
155        public MimeTypeMap()
156        {
157            String path;
158            try
159            {
160                // Check whether the user directory contains mappings.
161                path = System.getProperty("user.home");
162                if (path != null)
163                {
164                    path = path + File.separator + MIMETYPE_RESOURCE;
165                    mappers[MAP_HOME] = loadPath(path);
166                }
167            }
168            catch (IOException x)
169            {
170                // ignore
171            }
172    
173            try
174            {
175                // Check whether the system directory contains mappings.
176                path = System.getProperty("java.home") +
177                    File.separator + "lib" + File.separator + MIMETYPE_RESOURCE;
178                mappers[MAP_SYS] = loadPath(path);
179            }
180            catch (IOException x)
181            {
182                // ignore
183            }
184    
185            // Check whether the current class jar contains mappings.
186            mappers[MAP_JAR] = loadResource("/META-INF/" + MIMETYPE_RESOURCE);
187    
188            // Set the common mapper to have the lowest priority.
189            mappers[MAP_COM] = commonMapper;
190        }
191    
192        /**
193         * Contructs a MIME type map read from a stream.
194         *
195         * @param input an input stream.
196         * @throws IOException for an incorrect stream.
197         */
198        public MimeTypeMap(InputStream input)
199            throws IOException
200        {
201            this();
202            mappers[MAP_PROG] = new MimeTypeMapper(input);
203        }
204    
205        /**
206         * Contructs a MIME type map read from a file.
207         *
208         * @param file an input file.
209         * @throws IOException for an incorrect input file.
210         */
211        public MimeTypeMap(File file)
212            throws IOException
213        {
214            this();
215            mappers[MAP_PROG] = new MimeTypeMapper(file);
216        }
217    
218        /**
219         * Contructs a MIME type map read from a file path.
220         *
221         * @param path an input file path.
222         * @throws IOException for an incorrect input file.
223         */
224        public MimeTypeMap(String path)
225            throws IOException
226        {
227            this();
228            mappers[MAP_PROG] = new MimeTypeMapper(path);
229        }
230    
231        /**
232         * Sets a MIME content type mapping to extensions.
233         *
234         * @param spec a MIME type extension specification to set.
235         */
236        public synchronized void setContentType(String spec)
237        {
238            if (mappers[MAP_PROG] == null)
239            {
240                mappers[MAP_PROG] = new MimeTypeMapper();
241            }
242            mappers[MAP_PROG].setContentType(spec);
243        }
244    
245        /**
246         * Gets the MIME content type for a file as a string.
247         *
248         * @param file The file to look up a MIME type mapping for.
249         * @return The MIME type, or {@link #DEFAULT_TYPE} if unmapped.
250         */
251        public String getContentType(File file)
252        {
253            return getContentType(file.getName());
254        }
255    
256        /**
257         * Gets the MIME content type for a named file as a string.
258         *
259         * @param fileName The name of the file to look up a MIME type
260         * mapping for.
261         * @return The MIME type, or {@link #DEFAULT_TYPE} if unmapped.
262         */
263        public String getContentType(String fileName)
264        {
265            return getContentType(fileName, DEFAULT_TYPE);
266        }
267    
268        /**
269         * Gets the MIME content type for a file name extension as a string.
270         *
271         * @param fileName The name of the file to look up a MIME type
272         * mapping for.
273         * @param def The default MIME type to use if no mapping exists.
274         * @return The MIME type, or <code>def</code> if unmapped.
275         */
276        public String getContentType(String fileName, String def)
277        {
278            String ext = parseFileExtension(fileName);
279            if (ext != null)
280            {
281                String mimeType;
282                MimeTypeMapper mapper;
283                for (int i = 0; i < mappers.length; i++)
284                {
285                    mapper = mappers[i];
286                    if (mapper != null)
287                    {
288                        mimeType = mapper.getContentType(ext);
289                        if (mimeType != null)
290                        {
291                            return mimeType;
292                        }
293                    }
294                }
295            }
296            return def;
297        }
298    
299        /**
300         * @param fileName The name of the file to parse the extension
301         * from.  Must be non-<code>null</code>.
302         * @return The file extension parsed from <code>fileName</code>
303         * (if present), or <code>null</code> if not found.
304         */
305        private static String parseFileExtension(String fileName)
306        {
307            int i = fileName.lastIndexOf('.');
308            return (i >= 0 && i + 2 < fileName.length() ?
309                    fileName.substring(i + 1) : null);
310        }
311    
312        /**
313         * Gets the MIME content type for a file.
314         *
315         * @param file the file.
316         * @return the MIME type.
317         */
318        public MimeType getMimeContentType(File file)
319        {
320            try
321            {
322                return new MimeType(getContentType(file));
323            }
324            catch (Exception x)
325            {
326                return DEFAULT_MIMETYPE;
327            }
328        }
329    
330        /**
331         * Gets the MIME content type for a named file.
332         *
333         * @param name the name of the file.
334         * @return the MIME type.
335         */
336        public MimeType getMimeContentType(String name)
337        {
338            try
339            {
340                return new MimeType(getContentType(name));
341            }
342            catch (Exception x)
343            {
344                return DEFAULT_MIMETYPE;
345            }
346        }
347    
348        /**
349         * Gets the MIME content type for a file name extension.
350         *
351         * @param ext the file name extension.
352         * @param def the default type if none is found.
353         * @return the MIME type.
354         */
355        public MimeType getMimeContentType(String ext,
356                                           String def)
357        {
358            try
359            {
360                return new MimeType(getContentType(ext,def));
361            }
362            catch (Exception x)
363            {
364                return DEFAULT_MIMETYPE;
365            }
366        }
367    
368        /**
369         * Gets the default file name extension for a MIME type.
370         * Note that the mappers are called in the reverse order.
371         *
372         * @param type the MIME type as a string.
373         * @return the file name extension or null.
374         */
375        public String getDefaultExtension(String type)
376        {
377            String ext;
378            MimeTypeMapper mapper;
379            int i = type.indexOf(';');
380            if (i >= 0)
381            {
382                type = type.substring(0,i);
383            }
384            type = type.trim();
385            for (i = mappers.length - 1; i >= 0; i--)
386            {
387                mapper = mappers[i];
388                if (mapper != null)
389                {
390                    ext = mapper.getExtension(type);
391                    if (ext != null)
392                    {
393                        return ext;
394                    }
395                }
396            }
397            return null;
398        }
399    
400        /**
401         * Gets the default file name extension for a MIME type.
402         * Note that the mappers are called in the reverse order.
403         *
404         * @param mime the MIME type.
405         * @return the file name extension or null.
406         */
407        public String getDefaultExtension(MimeType mime)
408        {
409            return getDefaultExtension(mime.getTypes());
410        }
411    
412        /**
413         * Sets a common MIME content type mapping to extensions.
414         *
415         * @param spec a MIME type extension specification to set.
416         */
417        protected synchronized void setCommonContentType(String spec)
418        {
419            mappers[MAP_COM].setContentType(spec);
420        }
421    }