blob: 03b685df644ef185e563e75c0043d5fa0b5f6c18 [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.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.regex.Pattern;
/**
* 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));
}
private static final Pattern SPLIT_PATTERN = Pattern.compile("\\s+");
/**
* 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();
parseTypes(builder, true, resourceSupplier, "mime.types");
parseTypes(builder, true, resourceSupplier, "android.mime.types");
parseTypes(builder, false, resourceSupplier, "vendor.mime.types");
return builder.build();
}
private static void parseTypes(MimeMap.Builder builder, boolean allowOverwrite,
Function<String, InputStream> resourceSupplier, String resourceName) {
try (InputStream inputStream = Objects.requireNonNull(resourceSupplier.apply(resourceName));
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
String line;
while ((line = reader.readLine()) != null) {
int commentPos = line.indexOf('#');
if (commentPos >= 0) {
line = line.substring(0, commentPos);
}
line = line.trim();
if (line.isEmpty()) {
continue;
}
List<String> specs = Arrays.asList(SPLIT_PATTERN.split(line));
if (!allowOverwrite) {
// Pretend that the mimeType and each file extension listed in the line
// carries a "?" prefix, which means that it can add new mappings but
// not modify existing mappings (putIfAbsent() semantics).
specs = ensurePrefix("?", specs);
}
builder.put(specs.get(0), specs.subList(1, specs.size()));
}
} catch (IOException | RuntimeException e) {
throw new RuntimeException("Failed to parse " + resourceName, e);
}
}
private static List<String> ensurePrefix(String prefix, List<String> strings) {
List<String> result = new ArrayList<>(strings.size());
for (String s : strings) {
if (!s.startsWith(prefix)) {
s = prefix + s;
}
result.add(s);
}
return result;
}
}