001: /*
002: * @(#)MimeTable.java 1.34 06/10/10
003: *
004: * Copyright 1990-2006 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program 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 version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: *
026: */
027:
028: package sun.net.www;
029:
030: import java.io.*;
031: import java.util.Calendar;
032: import java.net.FileNameMap;
033: import java.util.Hashtable;
034: import java.util.Enumeration;
035: import java.util.Properties;
036: import java.util.StringTokenizer;
037:
038: public class MimeTable implements FileNameMap {
039: /** Keyed by content type, returns MimeEntries */
040: private Hashtable entries = new Hashtable();
041:
042: /** Keyed by file extension (with the .), returns MimeEntries */
043: private Hashtable extensionMap = new Hashtable();
044:
045: // Will be reset if in the platform-specific data file
046: private static String tempFileTemplate;
047:
048: static {
049: java.security.AccessController
050: .doPrivileged(new java.security.PrivilegedAction() {
051: public Object run() {
052: tempFileTemplate = System.getProperty(
053: "content.types.temp.file.template",
054: "/tmp/%s");
055:
056: mailcapLocations = new String[] {
057: System.getProperty("user.mailcap"),
058: System.getProperty("user.home")
059: + "/.mailcap",
060: "/etc/mailcap",
061: "/usr/etc/mailcap",
062: "/usr/local/etc/mailcap",
063: System.getProperty("hotjava.home",
064: "/usr/local/hotjava")
065: + "/lib/mailcap", };
066: return null;
067: }
068: });
069: }
070:
071: private static final String filePreamble = "sun.net.www MIME content-types table";
072: private static final String fileMagic = "#" + filePreamble;
073: private static MimeTable defaultInstance = null;
074:
075: MimeTable() {
076: load();
077: }
078:
079: /**
080: * Get the single instance of this class. First use will load the
081: * table from a data file.
082: */
083: public static MimeTable getDefaultTable() {
084: if (defaultInstance == null) {
085: java.security.AccessController
086: .doPrivileged(new java.security.PrivilegedAction() {
087: public Object run() {
088: defaultInstance = new MimeTable();
089: java.net.URLConnection
090: .setFileNameMap(defaultInstance);
091: return null;
092: }
093: });
094: }
095:
096: return defaultInstance;
097: }
098:
099: /**
100: *
101: */
102: public static FileNameMap loadTable() {
103: MimeTable mt = getDefaultTable();
104: return (FileNameMap) mt;
105: }
106:
107: public synchronized int getSize() {
108: return entries.size();
109: }
110:
111: public synchronized String getContentTypeFor(String fileName) {
112: MimeEntry entry = findByFileName(fileName);
113: if (entry != null) {
114: return entry.getType();
115: } else {
116: return null;
117: }
118: }
119:
120: public synchronized void add(MimeEntry m) {
121: entries.put(m.getType(), m);
122:
123: String exts[] = m.getExtensions();
124: if (exts == null) {
125: return;
126: }
127:
128: for (int i = 0; i < exts.length; i++) {
129: extensionMap.put(exts[i], m);
130: }
131: }
132:
133: public synchronized MimeEntry remove(String type) {
134: MimeEntry entry = (MimeEntry) entries.get(type);
135: return remove(entry);
136: }
137:
138: public synchronized MimeEntry remove(MimeEntry entry) {
139: String[] extensionKeys = entry.getExtensions();
140: if (extensionKeys != null) {
141: for (int i = 0; i < extensionKeys.length; i++) {
142: extensionMap.remove(extensionKeys[i]);
143: }
144: }
145:
146: return (MimeEntry) entries.remove(entry.getType());
147: }
148:
149: public synchronized MimeEntry find(String type) {
150: MimeEntry entry = (MimeEntry) entries.get(type);
151: if (entry == null) {
152: // try a wildcard lookup
153: Enumeration e = entries.elements();
154: while (e.hasMoreElements()) {
155: MimeEntry wild = (MimeEntry) e.nextElement();
156: if (wild.matches(type)) {
157: return wild;
158: }
159: }
160: }
161:
162: return entry;
163: }
164:
165: /**
166: * Locate a MimeEntry by the file extension that has been associated
167: * with it. Parses general file names, and URLs.
168: */
169: public MimeEntry findByFileName(String fname) {
170: String ext = "";
171:
172: int i = fname.lastIndexOf('#');
173:
174: if (i > 0) {
175: fname = fname.substring(0, i - 1);
176: }
177:
178: i = fname.lastIndexOf('.');
179: // NOTE: OS specific delimters appear here
180: i = Math.max(i, fname.lastIndexOf('/'));
181: i = Math.max(i, fname.lastIndexOf('?'));
182:
183: if (i != -1 && fname.charAt(i) == '.') {
184: ext = fname.substring(i).toLowerCase();
185: }
186:
187: return findByExt(ext);
188: }
189:
190: /**
191: * Locate a MimeEntry by the file extension that has been associated
192: * with it.
193: */
194: public synchronized MimeEntry findByExt(String fileExtension) {
195: return (MimeEntry) extensionMap.get(fileExtension);
196: }
197:
198: public synchronized MimeEntry findByDescription(String description) {
199: Enumeration e = elements();
200: while (e.hasMoreElements()) {
201: MimeEntry entry = (MimeEntry) e.nextElement();
202: if (description.equals(entry.getDescription())) {
203: return entry;
204: }
205: }
206:
207: // We failed, now try treating description as type
208: return find(description);
209: }
210:
211: String getTempFileTemplate() {
212: return tempFileTemplate;
213: }
214:
215: public synchronized Enumeration elements() {
216: return entries.elements();
217: }
218:
219: // For backward compatibility -- mailcap format files
220: // This is not currently used, but may in the future when we add ability
221: // to read BOTH the properties format and the mailcap format.
222: protected static String[] mailcapLocations;
223:
224: public synchronized void load() {
225: Properties entries = new Properties();
226: File file = null;
227: try {
228: InputStream is;
229: // First try to load the user-specific table, if it exists
230: String userTablePath = System
231: .getProperty("content.types.user.table");
232: if (userTablePath != null) {
233: file = new File(userTablePath);
234: if (!file.exists()) {
235: // No user-table, try to load the default built-in table.
236: file = new File(System.getProperty("java.home")
237: + File.separator + "lib" + File.separator
238: + "content-types.properties");
239: }
240: } else {
241: // No user table, try to load the default built-in table.
242: file = new File(System.getProperty("java.home")
243: + File.separator + "lib" + File.separator
244: + "content-types.properties");
245: }
246:
247: is = new BufferedInputStream(new FileInputStream(file));
248: entries.load(is);
249: is.close();
250: } catch (IOException e) {
251: System.err
252: .println("Warning: default mime table not found: "
253: + file.getPath());
254: return;
255: }
256: parse(entries);
257: }
258:
259: void parse(Properties entries) {
260: // first, strip out the platform-specific temp file template
261: String tempFileTemplate = (String) entries
262: .get("temp.file.template");
263: if (tempFileTemplate != null) {
264: entries.remove("temp.file.template");
265: this .tempFileTemplate = tempFileTemplate;
266: }
267:
268: // now, parse the mime-type spec's
269: Enumeration types = entries.propertyNames();
270: while (types.hasMoreElements()) {
271: String type = (String) types.nextElement();
272: String attrs = entries.getProperty(type);
273: parse(type, attrs);
274: }
275: }
276:
277: //
278: // Table format:
279: //
280: // <entry> ::= <table_tag> | <type_entry>
281: //
282: // <table_tag> ::= <table_format_version> | <temp_file_template>
283: //
284: // <type_entry> ::= <type_subtype_pair> '=' <type_attrs_list>
285: //
286: // <type_subtype_pair> ::= <type> '/' <subtype>
287: //
288: // <type_attrs_list> ::= <attr_value_pair> [ ';' <attr_value_pair> ]*
289: // | [ <attr_value_pair> ]+
290: //
291: // <attr_value_pair> ::= <attr_name> '=' <attr_value>
292: //
293: // <attr_name> ::= 'description' | 'action' | 'application'
294: // | 'file_extensions' | 'icon'
295: //
296: // <attr_value> ::= <legal_char>*
297: //
298: // Embedded ';' in an <attr_value> are quoted with leading '\' .
299: //
300: // Interpretation of <attr_value> depends on the <attr_name> it is
301: // associated with.
302: //
303:
304: void parse(String type, String attrs) {
305: MimeEntry newEntry = new MimeEntry(type);
306:
307: // should handle embedded ';' and '|' and literal '"'
308: StringTokenizer tokenizer = new StringTokenizer(attrs, ";");
309: while (tokenizer.hasMoreTokens()) {
310: String pair = tokenizer.nextToken();
311: parse(pair, newEntry);
312: }
313:
314: add(newEntry);
315: }
316:
317: void parse(String pair, MimeEntry entry) {
318: // should add exception handling...
319: String name = null;
320: String value = null;
321:
322: boolean gotName = false;
323: StringTokenizer tokenizer = new StringTokenizer(pair, "=");
324: while (tokenizer.hasMoreTokens()) {
325: if (gotName) {
326: value = tokenizer.nextToken().trim();
327: } else {
328: name = tokenizer.nextToken().trim();
329: gotName = true;
330: }
331: }
332:
333: fill(entry, name, value);
334: }
335:
336: void fill(MimeEntry entry, String name, String value) {
337: if ("description".equalsIgnoreCase(name)) {
338: entry.setDescription(value);
339: } else if ("action".equalsIgnoreCase(name)) {
340: entry.setAction(getActionCode(value));
341: } else if ("application".equalsIgnoreCase(name)) {
342: entry.setCommand(value);
343: } else if ("icon".equalsIgnoreCase(name)) {
344: entry.setImageFileName(value);
345: } else if ("file_extensions".equalsIgnoreCase(name)) {
346: entry.setExtensions(value);
347: }
348:
349: // else illegal name exception
350: }
351:
352: String[] getExtensions(String list) {
353: StringTokenizer tokenizer = new StringTokenizer(list, ",");
354: int n = tokenizer.countTokens();
355: String[] extensions = new String[n];
356: for (int i = 0; i < n; i++) {
357: extensions[i] = tokenizer.nextToken();
358: }
359:
360: return extensions;
361: }
362:
363: int getActionCode(String action) {
364: for (int i = 0; i < MimeEntry.actionKeywords.length; i++) {
365: if (action.equalsIgnoreCase(MimeEntry.actionKeywords[i])) {
366: return i;
367: }
368: }
369:
370: return MimeEntry.UNKNOWN;
371: }
372:
373: public synchronized boolean save(String filename) {
374: if (filename == null) {
375: filename = System.getProperty("user.home" + File.separator
376: + "lib" + File.separator
377: + "content-types.properties");
378: }
379:
380: return saveAsProperties(new File(filename));
381: }
382:
383: public Properties getAsProperties() {
384: Properties properties = new Properties();
385: Enumeration e = elements();
386: while (e.hasMoreElements()) {
387: MimeEntry entry = (MimeEntry) e.nextElement();
388: properties.put(entry.getType(), entry.toProperty());
389: }
390:
391: return properties;
392: }
393:
394: protected boolean saveAsProperties(File file) {
395: FileOutputStream os = null;
396: try {
397: os = new FileOutputStream(file);
398: Properties properties = getAsProperties();
399: properties.put("temp.file.template", tempFileTemplate);
400: String tag;
401: String user = System.getProperty("user.name");
402: if (user != null) {
403: tag = "; customized for " + user;
404: properties.store(os, filePreamble + tag);
405: } else {
406: properties.store(os, filePreamble);
407: }
408: } catch (IOException e) {
409: e.printStackTrace();
410: return false;
411: } finally {
412: if (os != null) {
413: try {
414: os.close();
415: } catch (IOException e) {
416: }
417: }
418: }
419:
420: return true;
421: }
422: /*
423: * Debugging utilities
424: *
425: public void list(PrintStream out) {
426: Enumeration keys = entries.keys();
427: while (keys.hasMoreElements()) {
428: String key = (String)keys.nextElement();
429: MimeEntry entry = (MimeEntry)entries.get(key);
430: out.println(key + ": " + entry);
431: }
432: }
433:
434: public static void main(String[] args) {
435: MimeTable testTable = MimeTable.getDefaultTable();
436:
437: Enumeration e = testTable.elements();
438: while (e.hasMoreElements()) {
439: MimeEntry entry = (MimeEntry)e.nextElement();
440: System.out.println(entry);
441: }
442:
443: testTable.save(File.separator + "tmp" +
444: File.separator + "mime_table.save");
445: }
446: */
447: }
|