1 /*
   2  * Copyright (c) 1997, 2012, 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      */
  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 
  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      */
  68     public MimeTypeFile() {
  69     }
  70 
  71     /**
  72      * get the MimeTypeEntry based on the file extension
  73      */
  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      */
  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      */
 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      */
 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      */
 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      */
 227     public LineTokenizer(String str) {
 228         currentPosition = 0;
 229         this.str = str;
 230         maxPosition = str.length();
 231     }
 232 
 233     /**
 234      * Skips white space.
 235      */
 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      */
 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      */
 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 
 314     public void pushToken(String token) {
 315         stack.addElement(token);
 316     }
 317 }