blob: 8cd0147c12439f101c3cf57f1ec2bc66a5d8ff0a [file] [log] [blame]
/*
* Copyright (C) 2010 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package libcore.net;
import dalvik.annotation.compat.UnsupportedAppUsage;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Pattern;
/**
* Utilities for dealing with MIME types.
* Used to implement java.net.URLConnection and android.webkit.MimeTypeMap.
* @hide
*/
@libcore.api.CorePlatformApi
public final class MimeUtils {
private static final Pattern splitPattern = Pattern.compile("\\s+");
/**
* Note: These maps only contain lowercase keys/values, regarded as the
* {@link #canonicalize(String) canonical form}.
*
* <p>This is the case for both extensions and MIME types. The mime.types
* data file contains examples of mixed-case MIME types, but some applications
* use the lowercase version of these same types. RFC 2045 section 2 states
* that MIME types are case insensitive.
*/
private static final Map<String, String> mimeTypeToExtensionMap = new HashMap<>();
private static final Map<String, String> extensionToMimeTypeMap = new HashMap<>();
static {
parseTypes("mime.types");
parseTypes("android.mime.types");
}
private static void parseTypes(String resource) {
try (BufferedReader r = new BufferedReader(
new InputStreamReader(MimeUtils.class.getResourceAsStream(resource)))) {
String line;
while ((line = r.readLine()) != null) {
int commentPos = line.indexOf('#');
if (commentPos >= 0) {
line = line.substring(0, commentPos);
}
line = line.trim();
if (line.equals("")) {
continue;
}
final String[] split = splitPattern.split(line);
final String mimeType = canonicalize(split[0]);
if (!allowedInMap(mimeType)) {
throw new IllegalArgumentException(
"Invalid mimeType " + mimeType + " in: " + line);
}
for (int i = 1; i < split.length; i++) {
String extension = canonicalize(split[i]);
if (!allowedInMap(extension)) {
throw new IllegalArgumentException(
"Invalid extension " + extension + " in: " + line);
}
// Normally the first MIME type definition wins, and the
// last extension definition wins. However, a file can
// override a MIME type definition by adding the "!" suffix
// to an extension.
if (extension.endsWith("!")) {
extension = extension.substring(0, extension.length() - 1);
// Overriding MIME definition wins
mimeTypeToExtensionMap.put(mimeType, extension);
} else {
// First MIME definition wins
if (!mimeTypeToExtensionMap.containsKey(mimeType)) {
mimeTypeToExtensionMap.put(mimeType, extension);
}
}
// Last extension definition wins
extensionToMimeTypeMap.put(extension, mimeType);
}
}
} catch (IOException e) {
throw new RuntimeException("Failed to parse " + resource, e);
}
}
private MimeUtils() {
}
/**
* Returns the canonical (lowercase) form of the given extension or MIME type.
*/
private static String canonicalize(String s) {
return s.toLowerCase(Locale.ROOT);
}
/**
* Checks whether the given extension or MIME type might be valid and
* therefore may appear in the mimeType <-> extension maps.
*/
private static boolean allowedInMap(String s) {
return s != null && !s.isEmpty();
}
/**
* Returns true if the given case insensitive MIME type has an entry in the map.
* @param mimeType A MIME type (i.e. text/plain)
* @return True if a extension has been registered for
* the given case insensitive MIME type.
*/
@libcore.api.CorePlatformApi
public static boolean hasMimeType(String mimeType) {
return (guessExtensionFromMimeType(mimeType) != null);
}
/**
* Returns the MIME type for the given case insensitive file extension.
* @param extension A file extension without the leading '.'
* @return The MIME type has been registered for
* the given case insensitive file extension or null if there is none.
*/
@UnsupportedAppUsage
@libcore.api.CorePlatformApi
public static String guessMimeTypeFromExtension(String extension) {
if (!allowedInMap(extension)) {
return null;
}
extension = canonicalize(extension);
return extensionToMimeTypeMap.get(extension);
}
/**
* Returns true if the given case insensitive extension has a registered MIME type.
* @param extension A file extension without the leading '.'
* @return True if a MIME type has been registered for
* the given case insensitive file extension.
*/
@libcore.api.CorePlatformApi
public static boolean hasExtension(String extension) {
return (guessMimeTypeFromExtension(extension) != null);
}
/**
* Returns the registered extension for the given case insensitive MIME type. Note that some
* MIME types map to multiple extensions. This call will return the most
* common extension for the given MIME type.
* @param mimeType A MIME type (i.e. text/plain)
* @return The extension has been registered for
* the given case insensitive MIME type or null if there is none.
*/
@UnsupportedAppUsage
@libcore.api.CorePlatformApi
public static String guessExtensionFromMimeType(String mimeType) {
if (!allowedInMap(mimeType)) {
return null;
}
mimeType = canonicalize(mimeType);
return mimeTypeToExtensionMap.get(mimeType);
}
}