001/* MimeTypesFileTypeMap.java -- A file type map using mime.types. 002 Copyright (C) 2004 Free Software Foundation, Inc. 003 004This file is part of GNU Classpath. 005 006GNU Classpath is free software; you can redistribute it and/or modify 007it under the terms of the GNU General Public License as published by 008the Free Software Foundation; either version 2, or (at your option) 009any later version. 010 011GNU Classpath is distributed in the hope that it will be useful, but 012WITHOUT ANY WARRANTY; without even the implied warranty of 013MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014General Public License for more details. 015 016You should have received a copy of the GNU General Public License 017along with GNU Classpath; see the file COPYING. If not, write to the 018Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 01902110-1301 USA. 020 021Linking this library statically or dynamically with other modules is 022making a combined work based on this library. Thus, the terms and 023conditions of the GNU General Public License cover the whole 024combination. 025 026As a special exception, the copyright holders of this library give you 027permission to link this library with independent modules to produce an 028executable, regardless of the license terms of these independent 029modules, and to copy and distribute the resulting executable under 030terms of your choice, provided that you also meet, for each linked 031independent module, the terms and conditions of the license of that 032module. An independent module is a module which is not derived from 033or based on this library. If you modify this library, you may extend 034this exception to your version of the library, but you are not 035obligated to do so. If you do not wish to do so, delete this 036exception statement from your version. */ 037 038package javax.activation; 039 040import gnu.java.lang.CPStringBuilder; 041 042import java.io.BufferedReader; 043import java.io.File; 044import java.io.FileReader; 045import java.io.InputStream; 046import java.io.InputStreamReader; 047import java.io.IOException; 048import java.io.Reader; 049import java.io.StringReader; 050import java.net.URL; 051import java.util.ArrayList; 052import java.util.Enumeration; 053import java.util.HashMap; 054import java.util.Iterator; 055import java.util.List; 056import 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 */ 073public 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} 440