Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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.content.type; |
| 18 | |
Tobias Thierer | 8edd837 | 2019-10-03 13:43:21 +0100 | [diff] [blame] | 19 | import libcore.content.type.MimeMap; |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 20 | |
| 21 | import java.io.BufferedReader; |
| 22 | import java.io.IOException; |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 23 | import java.io.InputStream; |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 24 | import java.io.InputStreamReader; |
Tobias Thierer | 3243491 | 2019-09-27 19:22:39 +0100 | [diff] [blame] | 25 | import java.util.ArrayList; |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 26 | import java.util.List; |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 27 | import java.util.Objects; |
| 28 | import java.util.function.Function; |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 29 | |
| 30 | /** |
Tobias Thierer | af0cef9 | 2019-09-27 17:08:49 +0100 | [diff] [blame] | 31 | * Creates the framework default {@link MimeMap}, a bidirectional mapping |
| 32 | * between MIME types and file extensions. |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 33 | * |
| 34 | * This default mapping is loaded from data files that start with some mappings |
| 35 | * recognized by IANA plus some custom extensions and overrides. |
| 36 | * |
| 37 | * @hide |
| 38 | */ |
Tobias Thierer | af0cef9 | 2019-09-27 17:08:49 +0100 | [diff] [blame] | 39 | public class DefaultMimeMapFactory { |
| 40 | |
| 41 | private DefaultMimeMapFactory() { |
| 42 | } |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 43 | |
| 44 | /** |
Tobias Thierer | af0cef9 | 2019-09-27 17:08:49 +0100 | [diff] [blame] | 45 | * Creates and returns a new {@link MimeMap} instance that implements. |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 46 | * Android's default mapping between MIME types and extensions. |
| 47 | */ |
Tobias Thierer | af0cef9 | 2019-09-27 17:08:49 +0100 | [diff] [blame] | 48 | public static MimeMap create() { |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 49 | Class c = DefaultMimeMapFactory.class; |
| 50 | // The resources are placed into the res/ path by the "mimemap-res.jar" genrule. |
| 51 | return create(resourceName -> c.getResourceAsStream("/res/" + resourceName)); |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 52 | } |
| 53 | |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 54 | /** |
| 55 | * Creates a {@link MimeMap} instance whose resources are loaded from the |
| 56 | * InputStreams looked up in {@code resourceSupplier}. |
| 57 | * |
| 58 | * @hide |
| 59 | */ |
| 60 | public static MimeMap create(Function<String, InputStream> resourceSupplier) { |
| 61 | MimeMap.Builder builder = MimeMap.builder(); |
Tobias Thierer | 8e1c1f4 | 2019-10-08 16:27:35 +0100 | [diff] [blame] | 62 | // The files loaded here must be in minimized format with lines of the |
| 63 | // form "mime/type ext1 ext2 ext3", i.e. no comments, no empty lines, no |
| 64 | // leading/trailing whitespace and with a single space between entries on |
| 65 | // each line. See http://b/142267887 |
| 66 | // |
| 67 | // Note: the order here matters - later entries can overwrite earlier ones |
| 68 | // (except that vendor.mime.types entries are prefixed with '?' which makes |
| 69 | // them never overwrite). |
| 70 | parseTypes(builder, resourceSupplier, "debian.mime.types"); |
| 71 | parseTypes(builder, resourceSupplier, "android.mime.types"); |
| 72 | parseTypes(builder, resourceSupplier, "vendor.mime.types"); |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 73 | return builder.build(); |
| 74 | } |
| 75 | |
Tobias Thierer | 8e1c1f4 | 2019-10-08 16:27:35 +0100 | [diff] [blame] | 76 | private static void parseTypes(MimeMap.Builder builder, |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 77 | Function<String, InputStream> resourceSupplier, String resourceName) { |
| 78 | try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName)); |
| 79 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 80 | String line; |
Tobias Thierer | 8e1c1f4 | 2019-10-08 16:27:35 +0100 | [diff] [blame] | 81 | List<String> specs = new ArrayList<>(10); // re-use for each line |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 82 | while ((line = reader.readLine()) != null) { |
Tobias Thierer | 8e1c1f4 | 2019-10-08 16:27:35 +0100 | [diff] [blame] | 83 | specs.clear(); |
| 84 | // Lines are of the form "mimeSpec extSpec extSpec[...]" with a single space |
| 85 | // separating them and no leading/trailing spaces and no empty lines. |
| 86 | int startIdx = 0; |
| 87 | do { |
| 88 | int endIdx = line.indexOf(' ', startIdx); |
| 89 | if (endIdx < 0) { |
| 90 | endIdx = line.length(); |
| 91 | } |
| 92 | String spec = line.substring(startIdx, endIdx); |
| 93 | if (spec.isEmpty()) { |
| 94 | throw new IllegalArgumentException("Malformed line: " + line); |
| 95 | } |
| 96 | specs.add(spec); |
| 97 | startIdx = endIdx + 1; // skip over the space |
| 98 | } while (startIdx < line.length()); |
Tobias Thierer | fd9657d | 2019-08-20 15:23:34 +0100 | [diff] [blame] | 99 | builder.put(specs.get(0), specs.subList(1, specs.size())); |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 100 | } |
| 101 | } catch (IOException | RuntimeException e) { |
Tobias Thierer | adeea59 | 2019-09-27 20:27:29 +0100 | [diff] [blame] | 102 | throw new RuntimeException("Failed to parse " + resourceName, e); |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 103 | } |
| 104 | } |
| 105 | |
Tobias Thierer | 878c77b | 2019-08-18 15:19:45 +0100 | [diff] [blame] | 106 | } |