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