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><java.home></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 }