1 /*
2  * Copyright (c) 1997, 2003, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package com.sun.activation.registries;
27 
28 import java.io.*;
29 import java.util.*;
30 
31 public class MimeTypeFile {
32     private String fname = null;
33     private Hashtable type_hash = new Hashtable();
34 
35     /**
36      * The construtor that takes a filename as an argument.
37      *
38      * @param new_fname The file name of the mime types file.
39      */
MimeTypeFile(String new_fname)40     public MimeTypeFile(String new_fname) throws IOException {
41         File mime_file = null;
42         FileReader fr = null;
43 
44         fname = new_fname; // remember the file name
45 
46         mime_file = new File(fname); // get a file object
47 
48         fr = new FileReader(mime_file);
49 
50         try {
51             parse(new BufferedReader(fr));
52         } finally {
53             try {
54                 fr.close(); // close it
55             } catch (IOException e) {
56                 // ignore it
57             }
58         }
59     }
60 
MimeTypeFile(InputStream is)61     public MimeTypeFile(InputStream is) throws IOException {
62         parse(new BufferedReader(new InputStreamReader(is, "iso-8859-1")));
63     }
64 
65     /**
66      * Creates an empty DB.
67      */
MimeTypeFile()68     public MimeTypeFile() {
69     }
70 
71     /**
72      * get the MimeTypeEntry based on the file extension
73      */
getMimeTypeEntry(String file_ext)74     public MimeTypeEntry getMimeTypeEntry(String file_ext) {
75         return (MimeTypeEntry)type_hash.get((Object)file_ext);
76     }
77 
78     /**
79      * Get the MIME type string corresponding to the file extension.
80      */
getMIMETypeString(String file_ext)81     public String getMIMETypeString(String file_ext) {
82         MimeTypeEntry entry = this.getMimeTypeEntry(file_ext);
83 
84         if (entry != null)
85             return entry.getMIMEType();
86         else
87             return null;
88     }
89 
90     /**
91      * Appends string of entries to the types registry, must be valid
92      * .mime.types format.
93      * A mime.types entry is one of two forms:
94      *
95      *  type/subtype    ext1 ext2 ...
96      * or
97      *  type=type/subtype desc="description of type" exts=ext1,ext2,...
98      *
99      * Example:
100      * # this is a test
101      * audio/basic            au
102      * text/plain             txt text
103      * type=application/postscript exts=ps,eps
104      */
appendToRegistry(String mime_types)105     public void appendToRegistry(String mime_types) {
106         try {
107             parse(new BufferedReader(new StringReader(mime_types)));
108         } catch (IOException ex) {
109             // can't happen
110         }
111     }
112 
113     /**
114      * Parse a stream of mime.types entries.
115      */
parse(BufferedReader buf_reader)116     private void parse(BufferedReader buf_reader) throws IOException {
117         String line = null, prev = null;
118 
119         while ((line = buf_reader.readLine()) != null) {
120             if (prev == null)
121                 prev = line;
122             else
123                 prev += line;
124             int end = prev.length();
125             if (prev.length() > 0 && prev.charAt(end - 1) == '\\') {
126                 prev = prev.substring(0, end - 1);
127                 continue;
128             }
129             this.parseEntry(prev);
130             prev = null;
131         }
132         if (prev != null)
133             this.parseEntry(prev);
134     }
135 
136     /**
137      * Parse single mime.types entry.
138      */
parseEntry(String line)139     private void parseEntry(String line) {
140         String mime_type = null;
141         String file_ext = null;
142         line = line.trim();
143 
144         if (line.length() == 0) // empty line...
145             return; // BAIL!
146 
147         // check to see if this is a comment line?
148         if (line.charAt(0) == '#')
149             return; // then we are done!
150 
151         // is it a new format line or old format?
152         if (line.indexOf('=') > 0) {
153             // new format
154             LineTokenizer lt = new LineTokenizer(line);
155             while (lt.hasMoreTokens()) {
156                 String name = lt.nextToken();
157                 String value = null;
158                 if (lt.hasMoreTokens() && lt.nextToken().equals("=") &&
159                                                         lt.hasMoreTokens())
160                     value = lt.nextToken();
161                 if (value == null) {
162                     if (LogSupport.isLoggable())
163                         LogSupport.log("Bad .mime.types entry: " + line);
164                     return;
165                 }
166                 if (name.equals("type"))
167                     mime_type = value;
168                 else if (name.equals("exts")) {
169                     StringTokenizer st = new StringTokenizer(value, ",");
170                     while (st.hasMoreTokens()) {
171                         file_ext = st.nextToken();
172                         MimeTypeEntry entry =
173                                 new MimeTypeEntry(mime_type, file_ext);
174                         type_hash.put(file_ext, entry);
175                         if (LogSupport.isLoggable())
176                             LogSupport.log("Added: " + entry.toString());
177                     }
178                 }
179             }
180         } else {
181             // old format
182             // count the tokens
183             StringTokenizer strtok = new StringTokenizer(line);
184             int num_tok = strtok.countTokens();
185 
186             if (num_tok == 0) // empty line
187                 return;
188 
189             mime_type = strtok.nextToken(); // get the MIME type
190 
191             while (strtok.hasMoreTokens()) {
192                 MimeTypeEntry entry = null;
193 
194                 file_ext = strtok.nextToken();
195                 entry = new MimeTypeEntry(mime_type, file_ext);
196                 type_hash.put(file_ext, entry);
197                 if (LogSupport.isLoggable())
198                     LogSupport.log("Added: " + entry.toString());
199             }
200         }
201     }
202 
203     // for debugging
204     /*
205     public static void main(String[] argv) throws Exception {
206         MimeTypeFile mf = new MimeTypeFile(argv[0]);
207         System.out.println("ext " + argv[1] + " type " +
208                                                 mf.getMIMETypeString(argv[1]));
209         System.exit(0);
210     }
211     */
212 }
213 
214 class LineTokenizer {
215     private int currentPosition;
216     private int maxPosition;
217     private String str;
218     private Vector stack = new Vector();
219     private static final String singles = "=";  // single character tokens
220 
221     /**
222      * Constructs a tokenizer for the specified string.
223      * <p>
224      *
225      * @param   str            a string to be parsed.
226      */
LineTokenizer(String str)227     public LineTokenizer(String str) {
228         currentPosition = 0;
229         this.str = str;
230         maxPosition = str.length();
231     }
232 
233     /**
234      * Skips white space.
235      */
skipWhiteSpace()236     private void skipWhiteSpace() {
237         while ((currentPosition < maxPosition) &&
238                Character.isWhitespace(str.charAt(currentPosition))) {
239             currentPosition++;
240         }
241     }
242 
243     /**
244      * Tests if there are more tokens available from this tokenizer's string.
245      *
246      * @return  <code>true</code> if there are more tokens available from this
247      *          tokenizer's string; <code>false</code> otherwise.
248      */
hasMoreTokens()249     public boolean hasMoreTokens() {
250         if (stack.size() > 0)
251             return true;
252         skipWhiteSpace();
253         return (currentPosition < maxPosition);
254     }
255 
256     /**
257      * Returns the next token from this tokenizer.
258      *
259      * @return     the next token from this tokenizer.
260      * @exception  NoSuchElementException  if there are no more tokens in this
261      *               tokenizer's string.
262      */
nextToken()263     public String nextToken() {
264         int size = stack.size();
265         if (size > 0) {
266             String t = (String)stack.elementAt(size - 1);
267             stack.removeElementAt(size - 1);
268             return t;
269         }
270         skipWhiteSpace();
271 
272         if (currentPosition >= maxPosition) {
273             throw new NoSuchElementException();
274         }
275 
276         int start = currentPosition;
277         char c = str.charAt(start);
278         if (c == '"') {
279             currentPosition++;
280             boolean filter = false;
281             while (currentPosition < maxPosition) {
282                 c = str.charAt(currentPosition++);
283                 if (c == '\\') {
284                     currentPosition++;
285                     filter = true;
286                 } else if (c == '"') {
287                     String s;
288 
289                     if (filter) {
290                         StringBuffer sb = new StringBuffer();
291                         for (int i = start + 1; i < currentPosition - 1; i++) {
292                             c = str.charAt(i);
293                             if (c != '\\')
294                                 sb.append(c);
295                         }
296                         s = sb.toString();
297                     } else
298                         s = str.substring(start + 1, currentPosition - 1);
299                     return s;
300                 }
301             }
302         } else if (singles.indexOf(c) >= 0) {
303             currentPosition++;
304         } else {
305             while ((currentPosition < maxPosition) &&
306                    singles.indexOf(str.charAt(currentPosition)) < 0 &&
307                    !Character.isWhitespace(str.charAt(currentPosition))) {
308                 currentPosition++;
309             }
310         }
311         return str.substring(start, currentPosition);
312     }
313 
pushToken(String token)314     public void pushToken(String token) {
315         stack.addElement(token);
316     }
317 }
318