Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1c56262 authored by Tobias Thierer's avatar Tobias Thierer Committed by android-build-merger
Browse files

Make MimeMap final and introduce MimeMap.Builder. am: fd9657d0

am: 821f77fc

Change-Id: I1225c7975ae3d2d42a0a144b46aef4290d0c5fa2
parents effe42d0 821f77fc
Loading
Loading
Loading
Loading
+11 −127
Original line number Diff line number Diff line
@@ -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;

/**
@@ -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;
@@ -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);
    }
}