blob: 11d20d4d6c807f0c28f80557d23597440fa86c5c [file] [log] [blame]
/*
* Copyright (C) 2019 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 android.content.type;
import libcore.content.type.MimeMap;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
/**
* Creates the framework default {@link MimeMap}, a bidirectional mapping
* between MIME types and file extensions.
*
* This default mapping is loaded from data files that start with some mappings
* recognized by IANA plus some custom extensions and overrides.
*
* @hide
*/
public class DefaultMimeMapFactory {
private DefaultMimeMapFactory() {
}
/**
* Creates and returns a new {@link MimeMap} instance that implements.
* Android's default mapping between MIME types and extensions.
*/
public static MimeMap create() {
Class c = DefaultMimeMapFactory.class;
// The resources are placed into the res/ path by the "mimemap-res.jar" genrule.
return create(resourceName -> c.getResourceAsStream("/res/" + resourceName));
}
/**
* Creates a {@link MimeMap} instance whose resources are loaded from the
* InputStreams looked up in {@code resourceSupplier}.
*
* @hide
*/
public static MimeMap create(Function<String, InputStream> resourceSupplier) {
MimeMap.Builder builder = MimeMap.builder();
// The files loaded here must be in minimized format with lines of the
// form "mime/type ext1 ext2 ext3", i.e. no comments, no empty lines, no
// leading/trailing whitespace and with a single space between entries on
// each line. See http://b/142267887
//
// Note: the order here matters - later entries can overwrite earlier ones
// (except that vendor.mime.types entries are prefixed with '?' which makes
// them never overwrite).
parseTypes(builder, resourceSupplier, "debian.mime.types");
parseTypes(builder, resourceSupplier, "android.mime.types");
parseTypes(builder, resourceSupplier, "vendor.mime.types");
return builder.build();
}
private static void parseTypes(MimeMap.Builder builder,
Function<String, InputStream> resourceSupplier, String resourceName) {
try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName));
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
List<String> specs = new ArrayList<>(10); // re-use for each line
while ((line = reader.readLine()) != null) {
specs.clear();
// Lines are of the form "mimeSpec extSpec extSpec[...]" with a single space
// separating them and no leading/trailing spaces and no empty lines.
int startIdx = 0;
do {
int endIdx = line.indexOf(' ', startIdx);
if (endIdx < 0) {
endIdx = line.length();
}
String spec = line.substring(startIdx, endIdx);
if (spec.isEmpty()) {
throw new IllegalArgumentException("Malformed line: " + line);
}
specs.add(spec);
startIdx = endIdx + 1; // skip over the space
} while (startIdx < line.length());
builder.put(specs.get(0), specs.subList(1, specs.size()));
}
} catch (IOException | RuntimeException e) {
throw new RuntimeException("Failed to parse " + resourceName, e);
}
}
}