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.regex.Pattern;
210f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughesimport libcore.net.MimeUtils;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Two-way map that maps MIME-types to file extensions and vice versa.
250f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes *
260f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes * <p>See also {@link java.net.URLConnection#guessContentTypeFromName}
270f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes * and {@link java.net.URLConnection#guessContentTypeFromStream}. This
280f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes * class and {@code URLConnection} share the same MIME-type database.
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
309f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scottpublic class MimeTypeMap {
310f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes    private static final MimeTypeMap sMimeTypeMap = new MimeTypeMap();
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private MimeTypeMap() {
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the file extension or an empty string iff there is no
389f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * extension. This method is a convenience method for obtaining the
399f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * extension of a url and has undefined results for other Strings.
409f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param url
419f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @return The file extension of the given url.
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static String getFileExtensionFromUrl(String url) {
440f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        if (!TextUtils.isEmpty(url)) {
451870015932895244e668a85fb4100632ac949cc3Ben Murdoch            int fragment = url.lastIndexOf('#');
461870015932895244e668a85fb4100632ac949cc3Ben Murdoch            if (fragment > 0) {
471870015932895244e668a85fb4100632ac949cc3Ben Murdoch                url = url.substring(0, fragment);
481870015932895244e668a85fb4100632ac949cc3Ben Murdoch            }
491870015932895244e668a85fb4100632ac949cc3Ben Murdoch
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int query = url.lastIndexOf('?');
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (query > 0) {
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                url = url.substring(0, query);
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
541870015932895244e668a85fb4100632ac949cc3Ben Murdoch
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            int filenamePos = url.lastIndexOf('/');
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String filename =
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                0 <= filenamePos ? url.substring(filenamePos + 1) : url;
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // if the filename contains special characters, we don't
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // consider it valid for our matching purposes:
610f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes            if (!filename.isEmpty() &&
62f19670ac8637e80a09aed3183072f9ec989028c9Melanie Clements                Pattern.matches("[a-zA-Z_0-9\\.\\-\\(\\)\\%]+", filename)) {
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int dotPos = filename.lastIndexOf('.');
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (0 <= dotPos) {
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return filename.substring(dotPos + 1);
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return "";
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
749f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return true if the given MIME type has an entry in the map.
759f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param mimeType A MIME type (i.e. text/plain)
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True iff there is a mimeType entry in the map.
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean hasMimeType(String mimeType) {
790f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.hasMimeType(mimeType);
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
839f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return the MIME type for the given extension.
849f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param extension A file extension without the leading '.'
859f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @return The MIME type for the given extension or null iff there is none.
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String getMimeTypeFromExtension(String extension) {
880f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.guessMimeTypeFromExtension(extension);
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
91021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott    // Static method called by jni.
92021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott    private static String mimeTypeFromExtension(String extension) {
930f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.guessMimeTypeFromExtension(extension);
94021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott    }
95021b7b443fa4bb24ec73d0c09cf019c14eba67bcPatrick Scott
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
979f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return true if the given extension has a registered MIME type.
989f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param extension A file extension without the leading '.'
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True iff there is an extension entry in the map.
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public boolean hasExtension(String extension) {
1020f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.hasExtension(extension);
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1069f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Return the registered extension for the given MIME type. Note that some
1079f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * MIME types map to multiple extensions. This call will return the most
1089f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * common extension for the given MIME type.
1099f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @param mimeType A MIME type (i.e. text/plain)
1109f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * @return The extension for the given MIME type or null iff there is none.
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public String getExtensionFromMimeType(String mimeType) {
1130f11696164e1dadd2d33dddeca418f719f26bc10Elliott Hughes        return MimeUtils.guessExtensionFromMimeType(mimeType);
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
117d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * If the given MIME type is null, or one of the "generic" types (text/plain
118d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * or application/octet-stream) map it to a type that Android can deal with.
119d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * If the given type is not generic, return it unchanged.
120d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     *
121d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @param mimeType MIME type provided by the server.
122d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @param url URL of the data being loaded.
123d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @param contentDisposition Content-disposition header given by the server.
124d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     * @return The MIME type that should be used for this data.
125d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick     */
126d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick    /* package */ String remapGenericMimeType(String mimeType, String url,
127d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String contentDisposition) {
128d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        // If we have one of "generic" MIME types, try to deduce
129d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        // the right MIME type from the file extension (if any):
130d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        if ("text/plain".equals(mimeType) ||
131d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                "application/octet-stream".equals(mimeType)) {
132d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick
133d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // for attachment, use the filename in the Content-Disposition
134d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // to guess the mimetype
135d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String filename = null;
136d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if (contentDisposition != null) {
137d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                filename = URLUtil.parseContentDisposition(contentDisposition);
138d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
139d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if (filename != null) {
140d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                url = filename;
141d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
142d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String extension = getFileExtensionFromUrl(url);
143d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            String newMimeType = getMimeTypeFromExtension(extension);
144d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if (newMimeType != null) {
145d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                mimeType = newMimeType;
146d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
147d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        } else if ("text/vnd.wap.wml".equals(mimeType)) {
148d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // As we don't support wml, render it as plain text
149d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            mimeType = "text/plain";
150d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        } else {
151d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // It seems that xhtml+xml and vnd.wap.xhtml+xml mime
152d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            // subtypes are used interchangeably. So treat them the same.
153d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            if ("application/vnd.wap.xhtml+xml".equals(mimeType)) {
154d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick                mimeType = "application/xhtml+xml";
155d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick            }
156d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        }
157d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick        return mimeType;
158d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick    }
159d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick
160d77f9b7b3901a93521068ffda53200d3a4233de7Iain Merrick    /**
1619f61732e9b5c146d95a0b157da806d58948f81a5Patrick Scott     * Get the singleton instance of MimeTypeMap.
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The singleton instance of the MIME-type map.
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static MimeTypeMap getSingleton() {
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sMimeTypeMap;
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
168