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 }