/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.sun.activation.registries;
import java.io.*;
import java.util.*;
public class
MailcapFile {
/**
* A Map indexed by MIME type (string) that references
* a Map of commands for each type. The comand Map
* is indexed by the command name and references a List of
* class names (strings) for each command.
*/
private
Map type_hash = new
HashMap();
/**
* Another Map like above, but for fallback entries.
*/
private
Map fallback_hash = new
HashMap();
/**
* A Map indexed by MIME type (string) that references
* a List of native commands (string) corresponding to the type.
*/
private
Map native_commands = new
HashMap();
private static boolean
addReverse = false;
static {
try {
addReverse =
Boolean.
getBoolean("javax.activation.addreverse");
} catch (
Throwable t) {
// ignore any errors
}
}
/**
* The constructor that takes a filename as an argument.
*
* @param new_fname The file name of the mailcap file.
*/
public
MailcapFile(
String new_fname) throws
IOException {
if (
LogSupport.
isLoggable())
LogSupport.
log("new MailcapFile: file " +
new_fname);
FileReader reader = null;
try {
reader = new
FileReader(
new_fname);
parse(new
BufferedReader(
reader));
} finally {
if (
reader != null) {
try {
reader.
close();
} catch (
IOException ex) { }
}
}
}
/**
* The constructor that takes an input stream as an argument.
*
* @param is the input stream
*/
public
MailcapFile(
InputStream is) throws
IOException {
if (
LogSupport.
isLoggable())
LogSupport.
log("new MailcapFile: InputStream");
parse(new
BufferedReader(new
InputStreamReader(
is, "iso-8859-1")));
}
/**
* Mailcap file default constructor.
*/
public
MailcapFile() {
if (
LogSupport.
isLoggable())
LogSupport.
log("new MailcapFile: default");
}
/**
* Get the Map of MailcapEntries based on the MIME type.
*
* <p>
* <strong>Semantics:</strong> First check for the literal mime type,
* if that fails looks for wildcard <type>/\* and return that. Return the
* list of all that hit.
*/
public
Map getMailcapList(
String mime_type) {
Map search_result = null;
Map wildcard_result = null;
// first try the literal
search_result = (
Map)
type_hash.
get(
mime_type);
// ok, now try the wildcard
int
separator =
mime_type.
indexOf('/');
String subtype =
mime_type.
substring(
separator + 1);
if (!
subtype.
equals("*")) {
String type =
mime_type.
substring(0,
separator + 1) + "*";
wildcard_result = (
Map)
type_hash.
get(
type);
if (
wildcard_result != null) { // damn, we have to merge!!!
if (
search_result != null)
search_result =
mergeResults(
search_result,
wildcard_result);
else
search_result =
wildcard_result;
}
}
return
search_result;
}
/**
* Get the Map of fallback MailcapEntries based on the MIME type.
*
* <p>
* <strong>Semantics:</strong> First check for the literal mime type,
* if that fails looks for wildcard <type>/\* and return that. Return the
* list of all that hit.
*/
public
Map getMailcapFallbackList(
String mime_type) {
Map search_result = null;
Map wildcard_result = null;
// first try the literal
search_result = (
Map)
fallback_hash.
get(
mime_type);
// ok, now try the wildcard
int
separator =
mime_type.
indexOf('/');
String subtype =
mime_type.
substring(
separator + 1);
if (!
subtype.
equals("*")) {
String type =
mime_type.
substring(0,
separator + 1) + "*";
wildcard_result = (
Map)
fallback_hash.
get(
type);
if (
wildcard_result != null) { // damn, we have to merge!!!
if (
search_result != null)
search_result =
mergeResults(
search_result,
wildcard_result);
else
search_result =
wildcard_result;
}
}
return
search_result;
}
/**
* Return all the MIME types known to this mailcap file.
*/
public
String[]
getMimeTypes() {
Set types = new
HashSet(
type_hash.
keySet());
types.
addAll(
fallback_hash.
keySet());
types.
addAll(
native_commands.
keySet());
String[]
mts = new
String[
types.
size()];
mts = (
String[])
types.
toArray(
mts);
return
mts;
}
/**
* Return all the native comands for the given MIME type.
*/
public
String[]
getNativeCommands(
String mime_type) {
String[]
cmds = null;
List v =
(
List)
native_commands.
get(
mime_type.
toLowerCase(
Locale.
ENGLISH));
if (
v != null) {
cmds = new
String[
v.
size()];
cmds = (
String[])
v.
toArray(
cmds);
}
return
cmds;
}
/**
* Merge the first hash into the second.
* This merge will only effect the hashtable that is
* returned, we don't want to touch the one passed in since
* its integrity must be maintained.
*/
private
Map mergeResults(
Map first,
Map second) {
Iterator verb_enum =
second.
keySet().
iterator();
Map clonedHash = new
HashMap(
first);
// iterate through the verbs in the second map
while (
verb_enum.
hasNext()) {
String verb = (
String)
verb_enum.
next();
List cmdVector = (
List)
clonedHash.
get(
verb);
if (
cmdVector == null) {
clonedHash.
put(
verb,
second.
get(
verb));
} else {
// merge the two
List oldV = (
List)
second.
get(
verb);
cmdVector = new
ArrayList(
cmdVector);
cmdVector.
addAll(
oldV);
clonedHash.
put(
verb,
cmdVector);
}
}
return
clonedHash;
}
/**
* appendToMailcap: Append to this Mailcap DB, use the mailcap
* format:
* Comment == "# <i>comment string</i>
* Entry == "mimetype; javabeanclass<nl>
*
* Example:
* # this is a comment
* image/gif jaf.viewers.ImageViewer
*/
public void
appendToMailcap(
String mail_cap) {
if (
LogSupport.
isLoggable())
LogSupport.
log("appendToMailcap: " +
mail_cap);
try {
parse(new
StringReader(
mail_cap));
} catch (
IOException ex) {
// can't happen
}
}
/**
* parse file into a hash table of MC Type Entry Obj
*/
private void
parse(
Reader reader) throws
IOException {
BufferedReader buf_reader = new
BufferedReader(
reader);
String line = null;
String continued = null;
while ((
line =
buf_reader.
readLine()) != null) {
// LogSupport.log("parsing line: " + line);
line =
line.
trim();
try {
if (
line.
charAt(0) == '#')
continue;
if (
line.
charAt(
line.
length() - 1) == '\\') {
if (
continued != null)
continued +=
line.
substring(0,
line.
length() - 1);
else
continued =
line.
substring(0,
line.
length() - 1);
} else if (
continued != null) {
// handle the two strings
continued =
continued +
line;
// LogSupport.log("parse: " + continued);
try {
parseLine(
continued);
} catch (
MailcapParseException e) {
//e.printStackTrace();
}
continued = null;
}
else {
// LogSupport.log("parse: " + line);
try {
parseLine(
line);
// LogSupport.log("hash.size = " + type_hash.size());
} catch (
MailcapParseException e) {
//e.printStackTrace();
}
}
} catch (
StringIndexOutOfBoundsException e) {}
}
}
/**
* A routine to parse individual entries in a Mailcap file.
*
* Note that this routine does not handle line continuations.
* They should have been handled prior to calling this routine.
*/
protected void
parseLine(
String mailcapEntry)
throws
MailcapParseException,
IOException {
MailcapTokenizer tokenizer = new
MailcapTokenizer(
mailcapEntry);
tokenizer.
setIsAutoquoting(false);
if (
LogSupport.
isLoggable())
LogSupport.
log("parse: " +
mailcapEntry);
// parse the primary type
int
currentToken =
tokenizer.
nextToken();
if (
currentToken !=
MailcapTokenizer.
STRING_TOKEN) {
reportParseError(
MailcapTokenizer.
STRING_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
String primaryType =
tokenizer.
getCurrentTokenValue().
toLowerCase(
Locale.
ENGLISH);
String subType = "*";
// parse the '/' between primary and sub
// if it's not present that's ok, we just don't have a subtype
currentToken =
tokenizer.
nextToken();
if ((
currentToken !=
MailcapTokenizer.
SLASH_TOKEN) &&
(
currentToken !=
MailcapTokenizer.
SEMICOLON_TOKEN)) {
reportParseError(
MailcapTokenizer.
SLASH_TOKEN,
MailcapTokenizer.
SEMICOLON_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
// only need to look for a sub type if we got a '/'
if (
currentToken ==
MailcapTokenizer.
SLASH_TOKEN) {
// parse the sub type
currentToken =
tokenizer.
nextToken();
if (
currentToken !=
MailcapTokenizer.
STRING_TOKEN) {
reportParseError(
MailcapTokenizer.
STRING_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
subType =
tokenizer.
getCurrentTokenValue().
toLowerCase(
Locale.
ENGLISH);
// get the next token to simplify the next step
currentToken =
tokenizer.
nextToken();
}
String mimeType =
primaryType + "/" +
subType;
if (
LogSupport.
isLoggable())
LogSupport.
log(" Type: " +
mimeType);
// now setup the commands hashtable
Map commands = new
LinkedHashMap(); // keep commands in order found
// parse the ';' that separates the type from the parameters
if (
currentToken !=
MailcapTokenizer.
SEMICOLON_TOKEN) {
reportParseError(
MailcapTokenizer.
SEMICOLON_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
// eat it
// parse the required view command
tokenizer.
setIsAutoquoting(true);
currentToken =
tokenizer.
nextToken();
tokenizer.
setIsAutoquoting(false);
if ((
currentToken !=
MailcapTokenizer.
STRING_TOKEN) &&
(
currentToken !=
MailcapTokenizer.
SEMICOLON_TOKEN)) {
reportParseError(
MailcapTokenizer.
STRING_TOKEN,
MailcapTokenizer.
SEMICOLON_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
if (
currentToken ==
MailcapTokenizer.
STRING_TOKEN) {
// have a native comand, save the entire mailcap entry
//String nativeCommand = tokenizer.getCurrentTokenValue();
List v = (
List)
native_commands.
get(
mimeType);
if (
v == null) {
v = new
ArrayList();
v.
add(
mailcapEntry);
native_commands.
put(
mimeType,
v);
} else {
// XXX - check for duplicates?
v.
add(
mailcapEntry);
}
}
// only have to get the next token if the current one isn't a ';'
if (
currentToken !=
MailcapTokenizer.
SEMICOLON_TOKEN) {
currentToken =
tokenizer.
nextToken();
}
// look for a ';' which will indicate whether
// a parameter list is present or not
if (
currentToken ==
MailcapTokenizer.
SEMICOLON_TOKEN) {
boolean
isFallback = false;
do {
// eat the ';'
// parse the parameter name
currentToken =
tokenizer.
nextToken();
if (
currentToken !=
MailcapTokenizer.
STRING_TOKEN) {
reportParseError(
MailcapTokenizer.
STRING_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
String paramName =
tokenizer.
getCurrentTokenValue().
toLowerCase(
Locale.
ENGLISH);
// parse the '=' which separates the name from the value
currentToken =
tokenizer.
nextToken();
if ((
currentToken !=
MailcapTokenizer.
EQUALS_TOKEN) &&
(
currentToken !=
MailcapTokenizer.
SEMICOLON_TOKEN) &&
(
currentToken !=
MailcapTokenizer.
EOI_TOKEN)) {
reportParseError(
MailcapTokenizer.
EQUALS_TOKEN,
MailcapTokenizer.
SEMICOLON_TOKEN,
MailcapTokenizer.
EOI_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
// we only have a useful command if it is named
if (
currentToken ==
MailcapTokenizer.
EQUALS_TOKEN) {
// eat it
// parse the parameter value (which is autoquoted)
tokenizer.
setIsAutoquoting(true);
currentToken =
tokenizer.
nextToken();
tokenizer.
setIsAutoquoting(false);
if (
currentToken !=
MailcapTokenizer.
STRING_TOKEN) {
reportParseError(
MailcapTokenizer.
STRING_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
String paramValue =
tokenizer.
getCurrentTokenValue();
// add the class to the list iff it is one we care about
if (
paramName.
startsWith("x-java-")) {
String commandName =
paramName.
substring(7);
// 7 == "x-java-".length
if (
commandName.
equals("fallback-entry") &&
paramValue.
equalsIgnoreCase("true")) {
isFallback = true;
} else {
// setup the class entry list
if (
LogSupport.
isLoggable())
LogSupport.
log(" Command: " +
commandName +
", Class: " +
paramValue);
List classes = (
List)
commands.
get(
commandName);
if (
classes == null) {
classes = new
ArrayList();
commands.
put(
commandName,
classes);
}
if (
addReverse)
classes.
add(0,
paramValue);
else
classes.
add(
paramValue);
}
}
// set up the next iteration
currentToken =
tokenizer.
nextToken();
}
} while (
currentToken ==
MailcapTokenizer.
SEMICOLON_TOKEN);
Map masterHash =
isFallback ?
fallback_hash :
type_hash;
Map curcommands =
(
Map)
masterHash.
get(
mimeType);
if (
curcommands == null) {
masterHash.
put(
mimeType,
commands);
} else {
if (
LogSupport.
isLoggable())
LogSupport.
log("Merging commands for type " +
mimeType);
// have to merge current and new commands
// first, merge list of classes for commands already known
Iterator cn =
curcommands.
keySet().
iterator();
while (
cn.
hasNext()) {
String cmdName = (
String)
cn.
next();
List ccv = (
List)
curcommands.
get(
cmdName);
List cv = (
List)
commands.
get(
cmdName);
if (
cv == null)
continue;
// add everything in cv to ccv, if it's not already there
Iterator cvn =
cv.
iterator();
while (
cvn.
hasNext()) {
String clazz = (
String)
cvn.
next();
if (!
ccv.
contains(
clazz))
if (
addReverse)
ccv.
add(0,
clazz);
else
ccv.
add(
clazz);
}
}
// now, add commands not previously known
cn =
commands.
keySet().
iterator();
while (
cn.
hasNext()) {
String cmdName = (
String)
cn.
next();
if (
curcommands.
containsKey(
cmdName))
continue;
List cv = (
List)
commands.
get(
cmdName);
curcommands.
put(
cmdName,
cv);
}
}
} else if (
currentToken !=
MailcapTokenizer.
EOI_TOKEN) {
reportParseError(
MailcapTokenizer.
EOI_TOKEN,
MailcapTokenizer.
SEMICOLON_TOKEN,
currentToken,
tokenizer.
getCurrentTokenValue());
}
}
protected static void
reportParseError(int
expectedToken, int
actualToken,
String actualTokenValue) throws
MailcapParseException {
throw new
MailcapParseException("Encountered a " +
MailcapTokenizer.
nameForToken(
actualToken) + " token (" +
actualTokenValue + ") while expecting a " +
MailcapTokenizer.
nameForToken(
expectedToken) + " token.");
}
protected static void
reportParseError(int
expectedToken,
int
otherExpectedToken, int
actualToken,
String actualTokenValue)
throws
MailcapParseException {
throw new
MailcapParseException("Encountered a " +
MailcapTokenizer.
nameForToken(
actualToken) + " token (" +
actualTokenValue + ") while expecting a " +
MailcapTokenizer.
nameForToken(
expectedToken) + " or a " +
MailcapTokenizer.
nameForToken(
otherExpectedToken) + " token.");
}
protected static void
reportParseError(int
expectedToken,
int
otherExpectedToken, int
anotherExpectedToken, int
actualToken,
String actualTokenValue) throws
MailcapParseException {
if (
LogSupport.
isLoggable())
LogSupport.
log("PARSE ERROR: " + "Encountered a " +
MailcapTokenizer.
nameForToken(
actualToken) + " token (" +
actualTokenValue + ") while expecting a " +
MailcapTokenizer.
nameForToken(
expectedToken) + ", a " +
MailcapTokenizer.
nameForToken(
otherExpectedToken) + ", or a " +
MailcapTokenizer.
nameForToken(
anotherExpectedToken) + " token.");
throw new
MailcapParseException("Encountered a " +
MailcapTokenizer.
nameForToken(
actualToken) + " token (" +
actualTokenValue + ") while expecting a " +
MailcapTokenizer.
nameForToken(
expectedToken) + ", a " +
MailcapTokenizer.
nameForToken(
otherExpectedToken) + ", or a " +
MailcapTokenizer.
nameForToken(
anotherExpectedToken) + " token.");
}
/** for debugging
public static void main(String[] args) throws Exception {
Map masterHash = new HashMap();
for (int i = 0; i < args.length; ++i) {
System.out.println("Entry " + i + ": " + args[i]);
parseLine(args[i], masterHash);
}
Enumeration types = masterHash.keys();
while (types.hasMoreElements()) {
String key = (String)types.nextElement();
System.out.println("MIME Type: " + key);
Map commandHash = (Map)masterHash.get(key);
Enumeration commands = commandHash.keys();
while (commands.hasMoreElements()) {
String command = (String)commands.nextElement();
System.out.println(" Command: " + command);
Vector classes = (Vector)commandHash.get(command);
for (int i = 0; i < classes.size(); ++i) {
System.out.println(" Class: " +
(String)classes.elementAt(i));
}
}
System.out.println("");
}
}
*/
}