001    /* MimeTypesFileTypeMap.java -- A file type map using mime.types.
002       Copyright (C) 2004 Free Software Foundation, Inc.
003    
004    This file is part of GNU Classpath.
005    
006    GNU Classpath is free software; you can redistribute it and/or modify
007    it under the terms of the GNU General Public License as published by
008    the Free Software Foundation; either version 2, or (at your option)
009    any later version.
010    
011    GNU Classpath is distributed in the hope that it will be useful, but
012    WITHOUT ANY WARRANTY; without even the implied warranty of
013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014    General Public License for more details.
015    
016    You should have received a copy of the GNU General Public License
017    along with GNU Classpath; see the file COPYING.  If not, write to the
018    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
019    02110-1301 USA.
020    
021    Linking this library statically or dynamically with other modules is
022    making a combined work based on this library.  Thus, the terms and
023    conditions of the GNU General Public License cover the whole
024    combination.
025    
026    As a special exception, the copyright holders of this library give you
027    permission to link this library with independent modules to produce an
028    executable, regardless of the license terms of these independent
029    modules, and to copy and distribute the resulting executable under
030    terms of your choice, provided that you also meet, for each linked
031    independent module, the terms and conditions of the license of that
032    module.  An independent module is a module which is not derived from
033    or based on this library.  If you modify this library, you may extend
034    this exception to your version of the library, but you are not
035    obligated to do so.  If you do not wish to do so, delete this
036    exception statement from your version. */
037    
038    package javax.activation;
039    
040    import gnu.java.lang.CPStringBuilder;
041    
042    import java.io.BufferedReader;
043    import java.io.File;
044    import java.io.FileReader;
045    import java.io.InputStream;
046    import java.io.InputStreamReader;
047    import java.io.IOException;
048    import java.io.Reader;
049    import java.io.StringReader;
050    import java.net.URL;
051    import java.util.ArrayList;
052    import java.util.Enumeration;
053    import java.util.HashMap;
054    import java.util.Iterator;
055    import java.util.List;
056    import java.util.Map;
057    
058    /**
059     * Implementation of FileTypeMap that uses the <tt>mime.types</tt> format.
060     * File entries are searched for in the following locations and order:
061     * <ol>
062     * <li>Programmatically added entries to this instance</li>
063     * <li>The file <tt>.mime.types</tt> in the user's home directory</li>
064     * <li>The file <i>&lt;java.home&gt;</i><tt>/lib/mime.types</tt></li>
065     * <li>The resource <tt>META-INF/mime.types</tt></li>
066     * <li>The resource <tt>META-INF/mimetypes.default</tt> in the JAF
067     * distribution</li>
068     * </ol>
069     *
070     * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
071     * @version 1.1
072     */
073    public class MimetypesFileTypeMap
074      extends FileTypeMap
075    {
076    
077      private static final int PROG = 0;
078      private static final int HOME = 1;
079      private static final int SYS = 2;
080      private static final int JAR = 3;
081      private static final int DEF = 4;
082      private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
083      private static boolean debug = false;
084      static
085      {
086        try
087          {
088            String d = System.getProperty("javax.activation.debug");
089            debug = Boolean.valueOf(d).booleanValue();
090          }
091        catch (SecurityException e)
092          {
093          }
094      }
095    
096      private Map<String,String>[] mimetypes;
097    
098      /**
099       * Default constructor.
100       */
101      public MimetypesFileTypeMap()
102      {
103        init(null);
104      }
105    
106      /**
107       * Constructor specifying a filename.
108       * @param mimeTypeFileName the name of the file to read mime.types
109       * entries from
110       */
111      public MimetypesFileTypeMap(String mimeTypeFileName)
112        throws IOException
113      {
114        Reader in = null;
115        try
116          {
117            in = new FileReader(mimeTypeFileName);
118            init(in);
119          }
120        finally
121          {
122            if (in != null)
123              {
124                in.close();
125              }
126          }
127      }
128    
129      /**
130       * Constructor specifying an input stream.
131       * @param is the input stream to read mime.types entries from
132       */
133      public MimetypesFileTypeMap(InputStream is)
134      {
135        init(new InputStreamReader(is));
136      }
137    
138      private void init(Reader in)
139      {
140        mimetypes = new Map[5];
141        for (int i = 0; i < mimetypes.length; i++)
142          {
143            mimetypes[i] = new HashMap<String,String>();
144          }
145        if (in != null)
146          {
147            if (debug)
148              {
149                System.out.println("MimetypesFileTypeMap: load PROG");
150              }
151            try
152              {
153                parse(mimetypes[PROG], in);
154              }
155            catch (IOException e)
156              {
157              }
158          }
159    
160        if (debug)
161          {
162            System.out.println("MimetypesFileTypeMap: load HOME");
163          }
164        try
165          {
166            String home = System.getProperty("user.home");
167            if (home != null)
168              {
169                parseFile(mimetypes[HOME], new CPStringBuilder(home)
170                          .append(File.separatorChar)
171                          .append(".mime.types")
172                          .toString());
173              }
174          }
175        catch (SecurityException e)
176          {
177          }
178    
179        if (debug)
180          {
181            System.out.println("MimetypesFileTypeMap: load SYS");
182          }
183        try
184          {
185            parseFile(mimetypes[SYS],
186                      new CPStringBuilder(System.getProperty("java.home"))
187                      .append(File.separatorChar)
188                      .append("lib")
189                      .append(File.separatorChar)
190                      .append("mime.types")
191                      .toString());
192          }
193        catch (SecurityException e)
194          {
195          }
196        if (debug)
197          {
198            System.out.println("MimetypesFileTypeMap: load JAR");
199          }
200        List<URL> systemResources = getSystemResources("META-INF/mime.types");
201        int len = systemResources.size();
202        if (len > 0)
203          {
204            for (int i = 0; i < len ; i++)
205              {
206                Reader urlIn = null;
207                URL url = (URL)systemResources.get(i);
208                try
209                  {
210                    urlIn = new InputStreamReader(url.openStream());
211                    parse(mimetypes[JAR], urlIn);
212                  }
213                catch (IOException e)
214                  {
215                  }
216                finally
217                  {
218                    if (urlIn != null)
219                      {
220                        try
221                          {
222                            urlIn.close();
223                          }
224                        catch (IOException e)
225                          {
226                          }
227                      }
228                  }
229              }
230          }
231        else
232          {
233            parseResource(mimetypes[JAR], "/META-INF/mime.types");
234          }
235    
236        if (debug)
237          {
238            System.out.println("MimetypesFileTypeMap: load DEF");
239          }
240        parseResource(mimetypes[DEF], "/META-INF/mimetypes.default");
241      }
242    
243      /**
244       * Adds entries prorammatically to the registry.
245       * @param mime_types a mime.types formatted entries string
246       */
247      public synchronized void addMimeTypes(String mime_types)
248      {
249        if (debug)
250          {
251            System.out.println("MimetypesFileTypeMap: add to PROG");
252          }
253        try
254          {
255            parse(mimetypes[PROG], new StringReader(mime_types));
256          }
257        catch (IOException e)
258          {
259          }
260      }
261    
262      /**
263       * Returns the MIME content type of the file.
264       * This calls <code>getContentType(f.getName())</code>.
265       * @param f the file
266       */
267      public String getContentType(File f)
268      {
269        return getContentType(f.getName());
270      }
271    
272      /**
273       * Returns the MIME type based on the given filename.
274       * If no entry is found, returns "application/octet-stream".
275       * @param filename the filename
276       */
277      public synchronized String getContentType(String filename)
278      {
279        int di = filename.lastIndexOf('.');
280        if (di < 0)
281          {
282            return DEFAULT_MIME_TYPE;
283          }
284        String tail = filename.substring(di + 1);
285        if (tail.length() < 1)
286          {
287            return DEFAULT_MIME_TYPE;
288          }
289        for (int i = 0; i < mimetypes.length; i++)
290          {
291            String mimeType = (String)mimetypes[i].get(tail);
292            if (mimeType != null)
293              {
294                return mimeType;
295              }
296          }
297        return DEFAULT_MIME_TYPE;
298      }
299    
300      private void parseFile(Map<String,String> mimetypes, String filename)
301      {
302        Reader in = null;
303        try
304          {
305            in = new FileReader(filename);
306            parse(mimetypes, in);
307          }
308        catch (IOException e)
309          {
310          }
311        finally
312          {
313            if (in != null)
314              {
315                try
316                  {
317                    in.close();
318                  }
319                catch (IOException e)
320                  {
321                  }
322              }
323          }
324      }
325    
326      private void parseResource(Map<String,String> mimetypes, String name)
327      {
328        Reader in = null;
329        try
330          {
331            InputStream is = getClass().getResourceAsStream(name);
332            if (is != null)
333              {
334                in = new InputStreamReader(is);
335                parse(mimetypes, in);
336              }
337          }
338        catch (IOException e)
339          {
340          }
341        finally
342          {
343            if (in != null)
344              {
345                try
346                  {
347                    in.close();
348                  }
349                catch (IOException e)
350                  {
351                  }
352              }
353          }
354      }
355    
356      private void parse(Map<String,String> mimetypes, Reader in)
357        throws IOException
358      {
359        BufferedReader br = new BufferedReader(in);
360        CPStringBuilder buf = null;
361        for (String line = br.readLine(); line != null; line = br.readLine())
362          {
363            line = line.trim();
364            int len = line.length();
365            if (len == 0 || line.charAt(0) == '#')
366              {
367                continue; // Empty line / comment
368              }
369            if (line.charAt(len - 1) == '\\')
370              {
371                if (buf == null)
372                  {
373                    buf = new CPStringBuilder();
374                  }
375                buf.append(line.substring(0, len - 1));
376              }
377            else if (buf != null)
378              {
379                buf.append(line);
380                parseEntry(mimetypes, buf.toString());
381                buf = null;
382              }
383            else
384              {
385                parseEntry(mimetypes, line);
386              }
387          }
388      }
389    
390      private void parseEntry(Map<String,String> mimetypes,
391                              String line)
392      {
393        // Tokenize
394        String mimeType = null;
395        char[] chars = line.toCharArray();
396        int len = chars.length;
397        CPStringBuilder buffer = new CPStringBuilder();
398        for (int i = 0; i < len; i++)
399          {
400            char c = chars[i];
401            if (Character.isWhitespace(c))
402              {
403                if (mimeType == null)
404                  {
405                    mimeType = buffer.toString();
406                  }
407                else if (buffer.length() > 0)
408                  {
409                    mimetypes.put(buffer.toString(), mimeType);
410                  }
411                buffer.setLength(0);
412              }
413            else
414              buffer.append(c);
415          }
416        if (buffer.length() > 0)
417          {
418            mimetypes.put(buffer.toString(), mimeType);
419          }
420      }
421    
422      // -- Utility methods --
423    
424      private List<URL> getSystemResources(String name)
425      {
426        List<URL> acc = new ArrayList<URL>();
427        try
428          {
429            for (Enumeration<URL> i = ClassLoader.getSystemResources(name);
430                 i.hasMoreElements(); )
431              acc.add(i.nextElement());
432          }
433        catch (IOException e)
434          {
435          }
436        return acc;
437      }
438    
439    }