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 MimeTypeMap()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 */ getFileExtensionFromUrl(String url)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 */ hasMimeType(String mimeType)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 getMimeTypeFromExtension(String extension)91 public String getMimeTypeFromExtension(String extension) { 92 return MimeUtils.guessMimeTypeFromExtension(extension); 93 } 94 95 // Static method called by jni. mimeTypeFromExtension(String extension)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 */ hasExtension(String extension)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 getExtensionFromMimeType(String mimeType)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 */ remapGenericMimeType(@ullable String mimeType, String url, String contentDisposition)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 */ getSingleton()169 public static MimeTypeMap getSingleton() { 170 return sMimeTypeMap; 171 } 172 } 173