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