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