Loading mime/java/android/content/type/MimeMapImpl.java +11 −127 Original line number Diff line number Diff line Loading @@ -21,10 +21,8 @@ import libcore.net.MimeMap; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** Loading @@ -36,96 +34,27 @@ import java.util.regex.Pattern; * * @hide */ public class MimeMapImpl extends MimeMap { public class MimeMapImpl { /** * Creates and returns a new {@link MimeMapImpl} instance that implements. * Android's default mapping between MIME types and extensions. */ public static MimeMapImpl createDefaultInstance() { public static MimeMap createDefaultInstance() { return parseFromResources("/mime.types", "/android.mime.types"); } private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+"); /** * Note: These maps only contain lowercase keys/values, regarded as the * {@link #toLowerCase(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 final Map<String, String> mMimeTypeToExtension; private final Map<String, String> mExtensionToMimeType; public MimeMapImpl(Map<String, String> mimeTypeToExtension, Map<String, String> extensionToMimeType) { this.mMimeTypeToExtension = new HashMap<>(mimeTypeToExtension); for (Map.Entry<String, String> entry : mimeTypeToExtension.entrySet()) { checkValidMimeType(entry.getKey()); checkValidExtension(entry.getValue()); } this.mExtensionToMimeType = new HashMap<>(extensionToMimeType); for (Map.Entry<String, String> entry : extensionToMimeType.entrySet()) { checkValidExtension(entry.getKey()); checkValidMimeType(entry.getValue()); } } private static void checkValidMimeType(String s) { if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) { throw new IllegalArgumentException("Invalid MIME type: " + s); } } private static void checkValidExtension(String s) { if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) { throw new IllegalArgumentException("Invalid extension: " + s); } } static MimeMapImpl parseFromResources(String... resourceNames) { Map<String, String> mimeTypeToExtension = new HashMap<>(); Map<String, String> extensionToMimeType = new HashMap<>(); static MimeMap parseFromResources(String... resourceNames) { MimeMap.Builder builder = MimeMap.builder(); for (String resourceName : resourceNames) { parseTypes(mimeTypeToExtension, extensionToMimeType, resourceName); parseTypes(builder, resourceName); } return new MimeMapImpl(mimeTypeToExtension, extensionToMimeType); return builder.build(); } /** * An element of a *mime.types file: A MIME type or an extension, with an optional * prefix of "?" (if not overriding an earlier value). */ private static class Element { public final boolean keepExisting; public final String s; Element(boolean keepExisting, String value) { this.keepExisting = keepExisting; this.s = toLowerCase(value); if (value.isEmpty()) { throw new IllegalArgumentException(); } } public String toString() { return keepExisting ? ("?" + s) : s; } } private static String maybePut(Map<String, String> map, Element keyElement, String value) { if (keyElement.keepExisting) { return map.putIfAbsent(keyElement.s, value); } else { return map.put(keyElement.s, value); } } private static void parseTypes(Map<String, String> mimeTypeToExtension, Map<String, String> extensionToMimeType, String resource) { private static void parseTypes(MimeMap.Builder builder, String resource) { try (BufferedReader r = new BufferedReader( new InputStreamReader(MimeMapImpl.class.getResourceAsStream(resource)))) { String line; Loading @@ -135,60 +64,15 @@ public class MimeMapImpl extends MimeMap { line = line.substring(0, commentPos); } line = line.trim(); // The first time a MIME type is encountered it is mapped to the first extension // listed in its line. The first time an extension is encountered it is mapped // to the MIME type. // // When encountering a previously seen MIME type or extension, then by default // the later ones override earlier mappings (put() semantics); however if a MIME // type or extension is prefixed with '?' then any earlier mapping _from_ that // MIME type / extension is kept (putIfAbsent() semantics). final String[] split = SPLIT_PATTERN.split(line); if (split.length <= 1) { // Need mimeType + at least one extension to make a mapping. // "mime.types" files may also contain lines with just a mimeType without // an extension but we skip them as they provide no mapping info. if (line.isEmpty()) { continue; } List<Element> lineElements = new ArrayList<>(split.length); for (String s : split) { boolean keepExisting = s.startsWith("?"); if (keepExisting) { s = s.substring(1); } if (s.isEmpty()) { throw new IllegalArgumentException("Invalid entry in '" + line + "'"); } lineElements.add(new Element(keepExisting, s)); } // MIME type -> first extension (one mapping) // This will override any earlier mapping from this MIME type to another // extension, unless this MIME type was prefixed with '?'. Element mimeElement = lineElements.get(0); List<Element> extensionElements = lineElements.subList(1, lineElements.size()); String firstExtension = extensionElements.get(0).s; maybePut(mimeTypeToExtension, mimeElement, firstExtension); // extension -> MIME type (one or more mappings). // This will override any earlier mapping from this extension to another // MIME type, unless this extension was prefixed with '?'. for (Element extensionElement : extensionElements) { maybePut(extensionToMimeType, extensionElement, mimeElement.s); } List<String> specs = Arrays.asList(SPLIT_PATTERN.split(line)); builder.put(specs.get(0), specs.subList(1, specs.size())); } } catch (IOException | RuntimeException e) { throw new RuntimeException("Failed to parse " + resource, e); } } @Override protected String guessExtensionFromLowerCaseMimeType(String mimeType) { return mMimeTypeToExtension.get(mimeType); } @Override protected String guessMimeTypeFromLowerCaseExtension(String extension) { return mExtensionToMimeType.get(extension); } } Loading
mime/java/android/content/type/MimeMapImpl.java +11 −127 Original line number Diff line number Diff line Loading @@ -21,10 +21,8 @@ import libcore.net.MimeMap; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.HashMap; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** Loading @@ -36,96 +34,27 @@ import java.util.regex.Pattern; * * @hide */ public class MimeMapImpl extends MimeMap { public class MimeMapImpl { /** * Creates and returns a new {@link MimeMapImpl} instance that implements. * Android's default mapping between MIME types and extensions. */ public static MimeMapImpl createDefaultInstance() { public static MimeMap createDefaultInstance() { return parseFromResources("/mime.types", "/android.mime.types"); } private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+"); /** * Note: These maps only contain lowercase keys/values, regarded as the * {@link #toLowerCase(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 final Map<String, String> mMimeTypeToExtension; private final Map<String, String> mExtensionToMimeType; public MimeMapImpl(Map<String, String> mimeTypeToExtension, Map<String, String> extensionToMimeType) { this.mMimeTypeToExtension = new HashMap<>(mimeTypeToExtension); for (Map.Entry<String, String> entry : mimeTypeToExtension.entrySet()) { checkValidMimeType(entry.getKey()); checkValidExtension(entry.getValue()); } this.mExtensionToMimeType = new HashMap<>(extensionToMimeType); for (Map.Entry<String, String> entry : extensionToMimeType.entrySet()) { checkValidExtension(entry.getKey()); checkValidMimeType(entry.getValue()); } } private static void checkValidMimeType(String s) { if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) { throw new IllegalArgumentException("Invalid MIME type: " + s); } } private static void checkValidExtension(String s) { if (MimeMap.isNullOrEmpty(s) || !s.equals(MimeMap.toLowerCase(s))) { throw new IllegalArgumentException("Invalid extension: " + s); } } static MimeMapImpl parseFromResources(String... resourceNames) { Map<String, String> mimeTypeToExtension = new HashMap<>(); Map<String, String> extensionToMimeType = new HashMap<>(); static MimeMap parseFromResources(String... resourceNames) { MimeMap.Builder builder = MimeMap.builder(); for (String resourceName : resourceNames) { parseTypes(mimeTypeToExtension, extensionToMimeType, resourceName); parseTypes(builder, resourceName); } return new MimeMapImpl(mimeTypeToExtension, extensionToMimeType); return builder.build(); } /** * An element of a *mime.types file: A MIME type or an extension, with an optional * prefix of "?" (if not overriding an earlier value). */ private static class Element { public final boolean keepExisting; public final String s; Element(boolean keepExisting, String value) { this.keepExisting = keepExisting; this.s = toLowerCase(value); if (value.isEmpty()) { throw new IllegalArgumentException(); } } public String toString() { return keepExisting ? ("?" + s) : s; } } private static String maybePut(Map<String, String> map, Element keyElement, String value) { if (keyElement.keepExisting) { return map.putIfAbsent(keyElement.s, value); } else { return map.put(keyElement.s, value); } } private static void parseTypes(Map<String, String> mimeTypeToExtension, Map<String, String> extensionToMimeType, String resource) { private static void parseTypes(MimeMap.Builder builder, String resource) { try (BufferedReader r = new BufferedReader( new InputStreamReader(MimeMapImpl.class.getResourceAsStream(resource)))) { String line; Loading @@ -135,60 +64,15 @@ public class MimeMapImpl extends MimeMap { line = line.substring(0, commentPos); } line = line.trim(); // The first time a MIME type is encountered it is mapped to the first extension // listed in its line. The first time an extension is encountered it is mapped // to the MIME type. // // When encountering a previously seen MIME type or extension, then by default // the later ones override earlier mappings (put() semantics); however if a MIME // type or extension is prefixed with '?' then any earlier mapping _from_ that // MIME type / extension is kept (putIfAbsent() semantics). final String[] split = SPLIT_PATTERN.split(line); if (split.length <= 1) { // Need mimeType + at least one extension to make a mapping. // "mime.types" files may also contain lines with just a mimeType without // an extension but we skip them as they provide no mapping info. if (line.isEmpty()) { continue; } List<Element> lineElements = new ArrayList<>(split.length); for (String s : split) { boolean keepExisting = s.startsWith("?"); if (keepExisting) { s = s.substring(1); } if (s.isEmpty()) { throw new IllegalArgumentException("Invalid entry in '" + line + "'"); } lineElements.add(new Element(keepExisting, s)); } // MIME type -> first extension (one mapping) // This will override any earlier mapping from this MIME type to another // extension, unless this MIME type was prefixed with '?'. Element mimeElement = lineElements.get(0); List<Element> extensionElements = lineElements.subList(1, lineElements.size()); String firstExtension = extensionElements.get(0).s; maybePut(mimeTypeToExtension, mimeElement, firstExtension); // extension -> MIME type (one or more mappings). // This will override any earlier mapping from this extension to another // MIME type, unless this extension was prefixed with '?'. for (Element extensionElement : extensionElements) { maybePut(extensionToMimeType, extensionElement, mimeElement.s); } List<String> specs = Arrays.asList(SPLIT_PATTERN.split(line)); builder.put(specs.get(0), specs.subList(1, specs.size())); } } catch (IOException | RuntimeException e) { throw new RuntimeException("Failed to parse " + resource, e); } } @Override protected String guessExtensionFromLowerCaseMimeType(String mimeType) { return mMimeTypeToExtension.get(mimeType); } @Override protected String guessMimeTypeFromLowerCaseExtension(String extension) { return mExtensionToMimeType.get(extension); } }