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