Home | History | Annotate | Download | only in webkit
      1 /*
      2  * Copyright (C) 2007 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.webkit;
     18 
     19 import android.annotation.Nullable;
     20 import android.text.TextUtils;
     21 
     22 import libcore.net.MimeUtils;
     23 
     24 import java.util.regex.Pattern;
     25 
     26 /**
     27  * Two-way map that maps MIME-types to file extensions and vice versa.
     28  *
     29  * <p>See also {@link java.net.URLConnection#guessContentTypeFromName}
     30  * and {@link java.net.URLConnection#guessContentTypeFromStream}. This
     31  * class and {@code URLConnection} share the same MIME-type database.
     32  */
     33 public class MimeTypeMap {
     34     private static final MimeTypeMap sMimeTypeMap = new MimeTypeMap();
     35 
     36     private MimeTypeMap() {
     37     }
     38 
     39     /**
     40      * Returns the file extension or an empty string if there is no
     41      * extension. This method is a convenience method for obtaining the
     42      * extension of a url and has undefined results for other Strings.
     43      * @param url
     44      * @return The file extension of the given url.
     45      */
     46     public static String getFileExtensionFromUrl(String url) {
     47         if (!TextUtils.isEmpty(url)) {
     48             int fragment = url.lastIndexOf('#');
     49             if (fragment > 0) {
     50                 url = url.substring(0, fragment);
     51             }
     52 
     53             int query = url.lastIndexOf('?');
     54             if (query > 0) {
     55                 url = url.substring(0, query);
     56             }
     57 
     58             int filenamePos = url.lastIndexOf('/');
     59             String filename =
     60                 0 <= filenamePos ? url.substring(filenamePos + 1) : url;
     61 
     62             // if the filename contains special characters, we don't
     63             // consider it valid for our matching purposes:
     64             if (!filename.isEmpty() &&
     65                 Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) {
     66                 int dotPos = filename.lastIndexOf('.');
     67                 if (0 <= dotPos) {
     68                     return filename.substring(dotPos + 1);
     69                 }
     70             }
     71         }
     72 
     73         return "";
     74     }
     75 
     76     /**
     77      * Return {@code true} if the given MIME type has an entry in the map.
     78      * @param mimeType A MIME type (i.e. text/plain)
     79      * @return {@code true} if there is a mimeType entry in the map.
     80      */
     81     public boolean hasMimeType(String mimeType) {
     82         return MimeUtils.hasMimeType(mimeType);
     83     }
     84 
     85     /**
     86      * Return the MIME type for the given extension.
     87      * @param extension A file extension without the leading '.'
     88      * @return The MIME type for the given extension or {@code null} if there is none.
     89      */
     90     @Nullable
     91     public String getMimeTypeFromExtension(String extension) {
     92         return MimeUtils.guessMimeTypeFromExtension(extension);
     93     }
     94 
     95     // Static method called by jni.
     96     private static String mimeTypeFromExtension(String extension) {
     97         return MimeUtils.guessMimeTypeFromExtension(extension);
     98     }
     99 
    100     /**
    101      * Return {@code true} if the given extension has a registered MIME type.
    102      * @param extension A file extension without the leading '.'
    103      * @return {@code true} if there is an extension entry in the map.
    104      */
    105     public boolean hasExtension(String extension) {
    106         return MimeUtils.hasExtension(extension);
    107     }
    108 
    109     /**
    110      * Return the registered extension for the given MIME type. Note that some
    111      * MIME types map to multiple extensions. This call will return the most
    112      * common extension for the given MIME type.
    113      * @param mimeType A MIME type (i.e. text/plain)
    114      * @return The extension for the given MIME type or {@code null} if there is none.
    115      */
    116     @Nullable
    117     public String getExtensionFromMimeType(String mimeType) {
    118         return MimeUtils.guessExtensionFromMimeType(mimeType);
    119     }
    120 
    121     /**
    122      * If the given MIME type is {@code null}, or one of the "generic" types (text/plain
    123      * or application/octet-stream) map it to a type that Android can deal with.
    124      * If the given type is not generic, return it unchanged.
    125      *
    126      * @param mimeType MIME type provided by the server.
    127      * @param url URL of the data being loaded.
    128      * @param contentDisposition Content-disposition header given by the server.
    129      * @return The MIME type that should be used for this data.
    130      */
    131     /* package */ String remapGenericMimeType(@Nullable String mimeType, String url,
    132             String contentDisposition) {
    133         // If we have one of "generic" MIME types, try to deduce
    134         // the right MIME type from the file extension (if any):
    135         if ("text/plain".equals(mimeType) ||
    136                 "application/octet-stream".equals(mimeType)) {
    137 
    138             // for attachment, use the filename in the Content-Disposition
    139             // to guess the mimetype
    140             String filename = null;
    141             if (contentDisposition != null) {
    142                 filename = URLUtil.parseContentDisposition(contentDisposition);
    143             }
    144             if (filename != null) {
    145                 url = filename;
    146             }
    147             String extension = getFileExtensionFromUrl(url);
    148             String newMimeType = getMimeTypeFromExtension(extension);
    149             if (newMimeType != null) {
    150                 mimeType = newMimeType;
    151             }
    152         } else if ("text/vnd.wap.wml".equals(mimeType)) {
    153             // As we don't support wml, render it as plain text
    154             mimeType = "text/plain";
    155         } else {
    156             // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
    157             // subtypes are used interchangeably. So treat them the same.
    158             if ("application/vnd.wap.xhtml+xml".equals(mimeType)) {
    159                 mimeType = "application/xhtml+xml";
    160             }
    161         }
    162         return mimeType;
    163     }
    164 
    165     /**
    166      * Get the singleton instance of MimeTypeMap.
    167      * @return The singleton instance of the MIME-type map.
    168      */
    169     public static MimeTypeMap getSingleton() {
    170         return sMimeTypeMap;
    171     }
    172 }
    173