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