19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.webkit;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
190f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughesimport android.text.TextUtils;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.regex.Pattern;
220f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughesimport libcore.net.MimeUtils;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Two-way map that maps MIME-types to file extensions and vice versa.
260f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes *
270f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes * <p>See also {@link java.net.URLConnection#guessContentTypeFromName}
280f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes * and {@link java.net.URLConnection#guessContentTypeFromStream}. This
290f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes * class and {@code URLConnection} share the same MIME-type database.
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
319f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scottpublic class MimeTypeMap {
320f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes    private static final MimeTypeMap sMimeTypeMap = new MimeTypeMap();
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MimeTypeMap() {
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the file extension or an empty string iff there is no
399f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * extension. This method is a convenience method for obtaining the
409f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * extension of a url and has undefined results for other Strings.
419f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param url
429f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @return The file extension of the given url.
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String getFileExtensionFromUrl(String url) {
450f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        if (!TextUtils.isEmpty(url)) {
461870015932895244e668a85fb4100632ac949cc3Ben Murdoch            int fragment = url.lastIndexOf('#');
471870015932895244e668a85fb4100632ac949cc3Ben Murdoch            if (fragment > 0) {
481870015932895244e668a85fb4100632ac949cc3Ben Murdoch                url = url.substring(0, fragment);
491870015932895244e668a85fb4100632ac949cc3Ben Murdoch            }
501870015932895244e668a85fb4100632ac949cc3Ben Murdoch
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int query = url.lastIndexOf('?');
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (query > 0) {
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                url = url.substring(0, query);
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
551870015932895244e668a85fb4100632ac949cc3Ben Murdoch
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int filenamePos = url.lastIndexOf('/');
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String filename =
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                0 <= filenamePos ? url.substring(filenamePos + 1) : url;
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // if the filename contains special characters, we don't
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // consider it valid for our matching purposes:
620f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes            if (!filename.isEmpty() &&
63f19670ac8637e80a09aed3183072f9ec989028c9Melanie Clements                Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) {
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int dotPos = filename.lastIndexOf('.');
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (0 <= dotPos) {
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return filename.substring(dotPos + 1);
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return "";
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
759f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return true if the given MIME type has an entry in the map.
769f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param mimeType A MIME type (i.e. text/plain)
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True iff there is a mimeType entry in the map.
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean hasMimeType(String mimeType) {
800f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.hasMimeType(mimeType);
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
849f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return the MIME type for the given extension.
859f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param extension A file extension without the leading '.'
869f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @return The MIME type for the given extension or null iff there is none.
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String getMimeTypeFromExtension(String extension) {
890f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.guessMimeTypeFromExtension(extension);
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
92021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott    // Static method called by jni.
93021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott    private static String mimeTypeFromExtension(String extension) {
940f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.guessMimeTypeFromExtension(extension);
95021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott    }
96021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
989f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return true if the given extension has a registered MIME type.
999f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param extension A file extension without the leading '.'
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True iff there is an extension entry in the map.
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean hasExtension(String extension) {
1030f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.hasExtension(extension);
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1079f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return the registered extension for the given MIME type. Note that some
1089f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * MIME types map to multiple extensions. This call will return the most
1099f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * common extension for the given MIME type.
1109f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param mimeType A MIME type (i.e. text/plain)
1119f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @return The extension for the given MIME type or null iff there is none.
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String getExtensionFromMimeType(String mimeType) {
1140f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.guessExtensionFromMimeType(mimeType);
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
118d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * If the given MIME type is null, or one of the "generic" types (text/plain
119d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * or application/octet-stream) map it to a type that Android can deal with.
120d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * If the given type is not generic, return it unchanged.
121d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     *
122d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @param mimeType MIME type provided by the server.
123d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @param url URL of the data being loaded.
124d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @param contentDisposition Content-disposition header given by the server.
125d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @return The MIME type that should be used for this data.
126d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     */
127d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick    /* package */ String remapGenericMimeType(String mimeType, String url,
128d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String contentDisposition) {
129d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        // If we have one of "generic" MIME types, try to deduce
130d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        // the right MIME type from the file extension (if any):
131d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        if ("text/plain".equals(mimeType) ||
132d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                "application/octet-stream".equals(mimeType)) {
133d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick
134d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // for attachment, use the filename in the Content-Disposition
135d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // to guess the mimetype
136d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String filename = null;
137d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if (contentDisposition != null) {
138d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                filename = URLUtil.parseContentDisposition(contentDisposition);
139d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
140d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if (filename != null) {
141d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                url = filename;
142d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
143d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String extension = getFileExtensionFromUrl(url);
144d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String newMimeType = getMimeTypeFromExtension(extension);
145d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if (newMimeType != null) {
146d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                mimeType = newMimeType;
147d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
148d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        } else if ("text/vnd.wap.wml".equals(mimeType)) {
149d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // As we don't support wml, render it as plain text
150d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            mimeType = "text/plain";
151d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        } else {
152d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
153d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // subtypes are used interchangeably. So treat them the same.
154d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if ("application/vnd.wap.xhtml+xml".equals(mimeType)) {
155d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                mimeType = "application/xhtml+xml";
156d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
157d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        }
158d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        return mimeType;
159d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick    }
160d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick
161d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick    /**
1629f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Get the singleton instance of MimeTypeMap.
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The singleton instance of the MIME-type map.
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static MimeTypeMap getSingleton() {
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sMimeTypeMap;
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
169