/*
 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package sun.nio.fs;

import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

File type detector that uses a file extension to look up its MIME type based on a mime.types file.
/** * File type detector that uses a file extension to look up its MIME type * based on a mime.types file. */
class MimeTypesFileTypeDetector extends AbstractFileTypeDetector { // path to mime.types file private final Path mimeTypesFile; // map of extension to MIME type private Map<String,String> mimeTypeMap; // set to true when file loaded private volatile boolean loaded; public MimeTypesFileTypeDetector(Path filePath) { mimeTypesFile = filePath; } @Override protected String implProbeContentType(Path path) { Path fn = path.getFileName(); if (fn == null) return null; // no file name String ext = getExtension(fn.toString()); if (ext.isEmpty()) return null; // no extension loadMimeTypes(); if (mimeTypeMap == null || mimeTypeMap.isEmpty()) return null; // Case-sensitive search String mimeType; do { mimeType = mimeTypeMap.get(ext); if (mimeType == null) ext = getExtension(ext); } while (mimeType == null && !ext.isEmpty()); return mimeType; }
Parse the mime types file, and store the type-extension mappings into mimeTypeMap. The mime types file is not loaded until the first probe to achieve the lazy initialization. It adopts double-checked locking optimization to reduce the locking overhead.
/** * Parse the mime types file, and store the type-extension mappings into * mimeTypeMap. The mime types file is not loaded until the first probe * to achieve the lazy initialization. It adopts double-checked locking * optimization to reduce the locking overhead. */
private void loadMimeTypes() { if (!loaded) { synchronized (this) { if (!loaded) { List<String> lines = AccessController.doPrivileged( new PrivilegedAction<>() { @Override public List<String> run() { try { return Files.readAllLines(mimeTypesFile, Charset.defaultCharset()); } catch (IOException ignore) { return Collections.emptyList(); } } }); mimeTypeMap = new HashMap<>(lines.size()); String entry = ""; for (String line : lines) { entry += line; if (entry.endsWith("\\")) { entry = entry.substring(0, entry.length() - 1); continue; } parseMimeEntry(entry); entry = ""; } if (!entry.isEmpty()) { parseMimeEntry(entry); } loaded = true; } } } }
Parse a mime-types entry, which can have the following formats. 1) Simple space-delimited format image/jpeg jpeg jpg jpe JPG 2) Netscape key-value pair format type=application/x-java-jnlp-file desc="Java Web Start" exts="jnlp" or type=text/html exts=htm,html
/** * Parse a mime-types entry, which can have the following formats. * 1) Simple space-delimited format * image/jpeg jpeg jpg jpe JPG * * 2) Netscape key-value pair format * type=application/x-java-jnlp-file desc="Java Web Start" exts="jnlp" * or * type=text/html exts=htm,html */
private void parseMimeEntry(String entry) { entry = entry.trim(); if (entry.isEmpty() || entry.charAt(0) == '#') return; entry = entry.replaceAll("\\s*#.*", ""); int equalIdx = entry.indexOf('='); if (equalIdx > 0) { // Parse a mime-types command having the key-value pair format final String TYPEEQUAL = "type="; String typeRegex = "\\b" + TYPEEQUAL + "(\"\\p{Graph}+?/\\p{Graph}+?\"|\\p{Graph}+/\\p{Graph}+\\b)"; Pattern typePattern = Pattern.compile(typeRegex); Matcher typeMatcher = typePattern.matcher(entry); if (typeMatcher.find()) { String type = typeMatcher.group().substring(TYPEEQUAL.length()); if (type.charAt(0) == '"') { type = type.substring(1, type.length() - 1); } final String EXTEQUAL = "exts="; String extRegex = "\\b" + EXTEQUAL + "(\"[\\p{Graph}\\p{Blank}]+?\"|\\p{Graph}+\\b)"; Pattern extPattern = Pattern.compile(extRegex); Matcher extMatcher = extPattern.matcher(entry); if (extMatcher.find()) { String exts = extMatcher.group().substring(EXTEQUAL.length()); if (exts.charAt(0) == '"') { exts = exts.substring(1, exts.length() - 1); } String[] extList = exts.split("[\\p{Blank}\\p{Punct}]+"); for (String ext : extList) { putIfAbsent(ext, type); } } } } else { // Parse a mime-types command having the space-delimited format String[] elements = entry.split("\\s+"); int i = 1; while (i < elements.length) { putIfAbsent(elements[i++], elements[0]); } } } private void putIfAbsent(String key, String value) { if (key != null && !key.isEmpty() && value != null && !value.isEmpty() && !mimeTypeMap.containsKey(key)) { mimeTypeMap.put(key, value); } } }