Build JavaMail activation inline

pull/175/head
M66B 6 years ago
parent 21fb16e678
commit d88b7036e1

@ -170,7 +170,7 @@ repositories {
jcenter()
maven { url "https://repo1.maven.org/maven2/" }
maven { url "https://jitpack.io" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
//maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
//maven { url "https://jakarta.oss.sonatype.org/content/repositories/snapshots/" }
}
@ -297,7 +297,7 @@ dependencies {
// https://projects.eclipse.org/projects/ee4j.javamail
// https://mvnrepository.com/artifact/com.sun.mail
//implementation "com.sun.mail:android-mail:$javamail_version"
implementation "com.sun.mail:android-activation:$javamail_version"
//implementation "com.sun.mail:android-activation:$javamail_version"
// https://jsoup.org/news/
implementation "org.jsoup:jsoup:$jsoup_version"

@ -0,0 +1,55 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.activation.registries;
import java.io.*;
import java.util.logging.*;
/**
* Logging related methods.
*/
public class LogSupport {
private static boolean debug = false;
private static Logger logger;
private static final Level level = Level.FINE;
static {
try {
debug = Boolean.getBoolean("javax.activation.debug");
} catch (Throwable t) {
// ignore any errors
}
logger = Logger.getLogger("javax.activation");
}
/**
* Constructor.
*/
private LogSupport() {
// private constructor, can't create instances
}
public static void log(String msg) {
if (debug)
System.out.println(msg);
logger.log(level, msg);
}
public static void log(String msg, Throwable t) {
if (debug)
System.out.println(msg + "; Exception: " + t);
logger.log(level, msg, t);
}
public static boolean isLoggable() {
return debug || logger.isLoggable(level);
}
}

@ -0,0 +1,548 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
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 <i>type</i>/* 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 <i>type</i>/* 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<br>
*
* 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("");
}
}
*/
}

@ -0,0 +1,25 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.activation.registries;
/**
* A class to encapsulate Mailcap parsing related exceptions
*/
public class MailcapParseException extends Exception {
public MailcapParseException() {
super();
}
public MailcapParseException(String inInfo) {
super(inInfo);
}
}

@ -0,0 +1,307 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.activation.registries;
/**
* A tokenizer for strings in the form of "foo/bar; prop1=val1; ... ".
* Useful for parsing MIME content types.
*/
public class MailcapTokenizer {
public static final int UNKNOWN_TOKEN = 0;
public static final int START_TOKEN = 1;
public static final int STRING_TOKEN = 2;
public static final int EOI_TOKEN = 5;
public static final int SLASH_TOKEN = '/';
public static final int SEMICOLON_TOKEN = ';';
public static final int EQUALS_TOKEN = '=';
/**
* Constructor
*
* @param inputString the string to tokenize
*/
public MailcapTokenizer(String inputString) {
data = inputString;
dataIndex = 0;
dataLength = inputString.length();
currentToken = START_TOKEN;
currentTokenValue = "";
isAutoquoting = false;
autoquoteChar = ';';
}
/**
* Set whether auto-quoting is on or off.
*
* Auto-quoting means that all characters after the first
* non-whitespace, non-control character up to the auto-quote
* terminator character or EOI (minus any whitespace immediatley
* preceeding it) is considered a token.
*
* This is required for handling command strings in a mailcap entry.
*/
public void setIsAutoquoting(boolean value) {
isAutoquoting = value;
}
/**
* Retrieve current token.
*
* @return The current token value
*/
public int getCurrentToken() {
return currentToken;
}
/*
* Get a String that describes the given token.
*/
public static String nameForToken(int token) {
String name = "really unknown";
switch(token) {
case UNKNOWN_TOKEN:
name = "unknown";
break;
case START_TOKEN:
name = "start";
break;
case STRING_TOKEN:
name = "string";
break;
case EOI_TOKEN:
name = "EOI";
break;
case SLASH_TOKEN:
name = "'/'";
break;
case SEMICOLON_TOKEN:
name = "';'";
break;
case EQUALS_TOKEN:
name = "'='";
break;
}
return name;
}
/*
* Retrieve current token value.
*
* @return A String containing the current token value
*/
public String getCurrentTokenValue() {
return currentTokenValue;
}
/*
* Process the next token.
*
* @return the next token
*/
public int nextToken() {
if (dataIndex < dataLength) {
// skip white space
while ((dataIndex < dataLength) &&
(isWhiteSpaceChar(data.charAt(dataIndex)))) {
++dataIndex;
}
if (dataIndex < dataLength) {
// examine the current character and see what kind of token we have
char c = data.charAt(dataIndex);
if (isAutoquoting) {
if (c == ';' || c == '=') {
currentToken = c;
currentTokenValue = new Character(c).toString();
++dataIndex;
} else {
processAutoquoteToken();
}
} else {
if (isStringTokenChar(c)) {
processStringToken();
} else if ((c == '/') || (c == ';') || (c == '=')) {
currentToken = c;
currentTokenValue = new Character(c).toString();
++dataIndex;
} else {
currentToken = UNKNOWN_TOKEN;
currentTokenValue = new Character(c).toString();
++dataIndex;
}
}
} else {
currentToken = EOI_TOKEN;
currentTokenValue = null;
}
} else {
currentToken = EOI_TOKEN;
currentTokenValue = null;
}
return currentToken;
}
private void processStringToken() {
// capture the initial index
int initialIndex = dataIndex;
// skip to 1st non string token character
while ((dataIndex < dataLength) &&
isStringTokenChar(data.charAt(dataIndex))) {
++dataIndex;
}
currentToken = STRING_TOKEN;
currentTokenValue = data.substring(initialIndex, dataIndex);
}
private void processAutoquoteToken() {
// capture the initial index
int initialIndex = dataIndex;
// now skip to the 1st non-escaped autoquote termination character
// XXX - doesn't actually consider escaping
boolean foundTerminator = false;
while ((dataIndex < dataLength) && !foundTerminator) {
char c = data.charAt(dataIndex);
if (c != autoquoteChar) {
++dataIndex;
} else {
foundTerminator = true;
}
}
currentToken = STRING_TOKEN;
currentTokenValue =
fixEscapeSequences(data.substring(initialIndex, dataIndex));
}
private static boolean isSpecialChar(char c) {
boolean lAnswer = false;
switch(c) {
case '(':
case ')':
case '<':
case '>':
case '@':
case ',':
case ';':
case ':':
case '\\':
case '"':
case '/':
case '[':
case ']':
case '?':
case '=':
lAnswer = true;
break;
}
return lAnswer;
}
private static boolean isControlChar(char c) {
return Character.isISOControl(c);
}
private static boolean isWhiteSpaceChar(char c) {
return Character.isWhitespace(c);
}
private static boolean isStringTokenChar(char c) {
return !isSpecialChar(c) && !isControlChar(c) && !isWhiteSpaceChar(c);
}
private static String fixEscapeSequences(String inputString) {
int inputLength = inputString.length();
StringBuffer buffer = new StringBuffer();
buffer.ensureCapacity(inputLength);
for (int i = 0; i < inputLength; ++i) {
char currentChar = inputString.charAt(i);
if (currentChar != '\\') {
buffer.append(currentChar);
} else {
if (i < inputLength - 1) {
char nextChar = inputString.charAt(i + 1);
buffer.append(nextChar);
// force a skip over the next character too
++i;
} else {
buffer.append(currentChar);
}
}
}
return buffer.toString();
}
private String data;
private int dataIndex;
private int dataLength;
private int currentToken;
private String currentTokenValue;
private boolean isAutoquoting;
private char autoquoteChar;
/*
public static void main(String[] args) {
for (int i = 0; i < args.length; ++i) {
MailcapTokenizer tokenizer = new MailcapTokenizer(args[i]);
System.out.println("Original: |" + args[i] + "|");
int currentToken = tokenizer.nextToken();
while (currentToken != EOI_TOKEN) {
switch(currentToken) {
case UNKNOWN_TOKEN:
System.out.println(" Unknown Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
case START_TOKEN:
System.out.println(" Start Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
case STRING_TOKEN:
System.out.println(" String Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
case EOI_TOKEN:
System.out.println(" EOI Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
case SLASH_TOKEN:
System.out.println(" Slash Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
case SEMICOLON_TOKEN:
System.out.println(" Semicolon Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
case EQUALS_TOKEN:
System.out.println(" Equals Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
default:
System.out.println(" Really Unknown Token: |" + tokenizer.getCurrentTokenValue() + "|");
break;
}
currentToken = tokenizer.nextToken();
}
System.out.println("");
}
}
*/
}

@ -0,0 +1,35 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.activation.registries;
import java.lang.*;
public class MimeTypeEntry {
private String type;
private String extension;
public MimeTypeEntry(String mime_type, String file_ext) {
type = mime_type;
extension = file_ext;
}
public String getMIMEType() {
return type;
}
public String getFileExtension() {
return extension;
}
public String toString() {
return "MIMETypeEntry: " + type + ", " + extension;
}
}

@ -0,0 +1,302 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package com.sun.activation.registries;
import java.io.*;
import java.util.*;
public class MimeTypeFile {
private String fname = null;
private Hashtable type_hash = new Hashtable();
/**
* The construtor that takes a filename as an argument.
*
* @param new_fname The file name of the mime types file.
*/
public MimeTypeFile(String new_fname) throws IOException {
File mime_file = null;
FileReader fr = null;
fname = new_fname; // remember the file name
mime_file = new File(fname); // get a file object
fr = new FileReader(mime_file);
try {
parse(new BufferedReader(fr));
} finally {
try {
fr.close(); // close it
} catch (IOException e) {
// ignore it
}
}
}
public MimeTypeFile(InputStream is) throws IOException {
parse(new BufferedReader(new InputStreamReader(is, "iso-8859-1")));
}
/**
* Creates an empty DB.
*/
public MimeTypeFile() {
}
/**
* get the MimeTypeEntry based on the file extension
*/
public MimeTypeEntry getMimeTypeEntry(String file_ext) {
return (MimeTypeEntry)type_hash.get((Object)file_ext);
}
/**
* Get the MIME type string corresponding to the file extension.
*/
public String getMIMETypeString(String file_ext) {
MimeTypeEntry entry = this.getMimeTypeEntry(file_ext);
if (entry != null)
return entry.getMIMEType();
else
return null;
}
/**
* Appends string of entries to the types registry, must be valid
* .mime.types format.
* A mime.types entry is one of two forms:
*
* type/subtype ext1 ext2 ...
* or
* type=type/subtype desc="description of type" exts=ext1,ext2,...
*
* Example:
* # this is a test
* audio/basic au
* text/plain txt text
* type=application/postscript exts=ps,eps
*/
public void appendToRegistry(String mime_types) {
try {
parse(new BufferedReader(new StringReader(mime_types)));
} catch (IOException ex) {
// can't happen
}
}
/**
* Parse a stream of mime.types entries.
*/
private void parse(BufferedReader buf_reader) throws IOException {
String line = null, prev = null;
while ((line = buf_reader.readLine()) != null) {
if (prev == null)
prev = line;
else
prev += line;
int end = prev.length();
if (prev.length() > 0 && prev.charAt(end - 1) == '\\') {
prev = prev.substring(0, end - 1);
continue;
}
this.parseEntry(prev);
prev = null;
}
if (prev != null)
this.parseEntry(prev);
}
/**
* Parse single mime.types entry.
*/
private void parseEntry(String line) {
String mime_type = null;
String file_ext = null;
line = line.trim();
if (line.length() == 0) // empty line...
return; // BAIL!
// check to see if this is a comment line?
if (line.charAt(0) == '#')
return; // then we are done!
// is it a new format line or old format?
if (line.indexOf('=') > 0) {
// new format
LineTokenizer lt = new LineTokenizer(line);
while (lt.hasMoreTokens()) {
String name = lt.nextToken();
String value = null;
if (lt.hasMoreTokens() && lt.nextToken().equals("=") &&
lt.hasMoreTokens())
value = lt.nextToken();
if (value == null) {
if (LogSupport.isLoggable())
LogSupport.log("Bad .mime.types entry: " + line);
return;
}
if (name.equals("type"))
mime_type = value;
else if (name.equals("exts")) {
StringTokenizer st = new StringTokenizer(value, ",");
while (st.hasMoreTokens()) {
file_ext = st.nextToken();
MimeTypeEntry entry =
new MimeTypeEntry(mime_type, file_ext);
type_hash.put(file_ext, entry);
if (LogSupport.isLoggable())
LogSupport.log("Added: " + entry.toString());
}
}
}
} else {
// old format
// count the tokens
StringTokenizer strtok = new StringTokenizer(line);
int num_tok = strtok.countTokens();
if (num_tok == 0) // empty line
return;
mime_type = strtok.nextToken(); // get the MIME type
while (strtok.hasMoreTokens()) {
MimeTypeEntry entry = null;
file_ext = strtok.nextToken();
entry = new MimeTypeEntry(mime_type, file_ext);
type_hash.put(file_ext, entry);
if (LogSupport.isLoggable())
LogSupport.log("Added: " + entry.toString());
}
}
}
// for debugging
/*
public static void main(String[] argv) throws Exception {
MimeTypeFile mf = new MimeTypeFile(argv[0]);
System.out.println("ext " + argv[1] + " type " +
mf.getMIMETypeString(argv[1]));
System.exit(0);
}
*/
}
class LineTokenizer {
private int currentPosition;
private int maxPosition;
private String str;
private Vector stack = new Vector();
private static final String singles = "="; // single character tokens
/**
* Constructs a tokenizer for the specified string.
* <p>
*
* @param str a string to be parsed.
*/
public LineTokenizer(String str) {
currentPosition = 0;
this.str = str;
maxPosition = str.length();
}
/**
* Skips white space.
*/
private void skipWhiteSpace() {
while ((currentPosition < maxPosition) &&
Character.isWhitespace(str.charAt(currentPosition))) {
currentPosition++;
}
}
/**
* Tests if there are more tokens available from this tokenizer's string.
*
* @return <code>true</code> if there are more tokens available from this
* tokenizer's string; <code>false</code> otherwise.
*/
public boolean hasMoreTokens() {
if (stack.size() > 0)
return true;
skipWhiteSpace();
return (currentPosition < maxPosition);
}
/**
* Returns the next token from this tokenizer.
*
* @return the next token from this tokenizer.
* @exception NoSuchElementException if there are no more tokens in this
* tokenizer's string.
*/
public String nextToken() {
int size = stack.size();
if (size > 0) {
String t = (String)stack.elementAt(size - 1);
stack.removeElementAt(size - 1);
return t;
}
skipWhiteSpace();
if (currentPosition >= maxPosition) {
throw new NoSuchElementException();
}
int start = currentPosition;
char c = str.charAt(start);
if (c == '"') {
currentPosition++;
boolean filter = false;
while (currentPosition < maxPosition) {
c = str.charAt(currentPosition++);
if (c == '\\') {
currentPosition++;
filter = true;
} else if (c == '"') {
String s;
if (filter) {
StringBuffer sb = new StringBuffer();
for (int i = start + 1; i < currentPosition - 1; i++) {
c = str.charAt(i);
if (c != '\\')
sb.append(c);
}
s = sb.toString();
} else
s = str.substring(start + 1, currentPosition - 1);
return s;
}
}
} else if (singles.indexOf(c) >= 0) {
currentPosition++;
} else {
while ((currentPosition < maxPosition) &&
singles.indexOf(str.charAt(currentPosition)) < 0 &&
!Character.isWhitespace(str.charAt(currentPosition))) {
currentPosition++;
}
}
return str.substring(start, currentPosition);
}
public void pushToken(String token) {
stack.addElement(token);
}
}

@ -0,0 +1,234 @@
/*
* Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
//import java.awt.datatransfer.DataFlavor;
import java.io.IOException;
import javax.activation.MimeType;
/**
* The ActivationDataFlavor class is a special subclass of
* <code>java.awt.datatransfer.DataFlavor</code>. It allows the JAF to
* set all three values stored by the DataFlavor class via a new
* constructor. It also contains improved MIME parsing in the <code>equals
* </code> method. Except for the improved parsing, its semantics are
* identical to that of the JDK's DataFlavor class.
*/
public class ActivationDataFlavor /*extends DataFlavor*/ {
/*
* Raison d'etre:
*
* The DataFlavor class included in JDK 1.1 has several limitations
* including piss poor MIME type parsing, and the limitation of
* only supporting serialized objects and InputStreams as
* representation objects. This class 'fixes' that.
*/
// I think for now I'll keep copies of all the variables and
// then later I may choose try to better coexist with the base
// class *sigh*
private String mimeType = null;
private MimeType mimeObject = null;
private String humanPresentableName = null;
private Class representationClass = null;
/**
* Construct a DataFlavor that represents an arbitrary
* Java object. This constructor is an extension of the
* JDK's DataFlavor in that it allows the explicit setting
* of all three DataFlavor attributes.
* <p>
* The returned DataFlavor will have the following characteristics:
* <p>
* representationClass = representationClass<br>
* mimeType = mimeType<br>
* humanName = humanName
* <p>
*
* @param representationClass the class used in this DataFlavor
* @param mimeType the MIME type of the data represented by this class
* @param humanPresentableName the human presentable name of the flavor
*/
public ActivationDataFlavor(Class representationClass,
String mimeType, String humanPresentableName) {
//super(mimeType, humanPresentableName); // need to call super
// init private variables:
this.mimeType = mimeType;
this.humanPresentableName = humanPresentableName;
this.representationClass = representationClass;
}
/**
* Construct a DataFlavor that represents a MimeType.
* <p>
* The returned DataFlavor will have the following characteristics:
* <p>
* If the mimeType is "application/x-java-serialized-object;
* class=", the result is the same as calling new
* DataFlavor(Class.forName()) as above.
* <p>
* otherwise:
* <p>
* representationClass = InputStream<p>
* mimeType = mimeType<p>
*
* @param representationClass the class used in this DataFlavor
* @param humanPresentableName the human presentable name of the flavor
*/
public ActivationDataFlavor(Class representationClass,
String humanPresentableName) {
//super(representationClass, humanPresentableName);
//this.mimeType = super.getMimeType();
this.mimeType = "application/x-java-serialized-object";
this.representationClass = representationClass;
this.humanPresentableName = humanPresentableName;
}
/**
* Construct a DataFlavor that represents a MimeType.
* <p>
* The returned DataFlavor will have the following characteristics:
* <p>
* If the mimeType is "application/x-java-serialized-object; class=",
* the result is the same as calling new DataFlavor(Class.forName()) as
* above, otherwise:
* <p>
* representationClass = InputStream<p>
* mimeType = mimeType
*
* @param mimeType the MIME type of the data represented by this class
* @param humanPresentableName the human presentable name of the flavor
*/
public ActivationDataFlavor(String mimeType, String humanPresentableName) {
//super(mimeType, humanPresentableName);
this.mimeType = mimeType;
try {
this.representationClass = Class.forName("java.io.InputStream");
} catch (ClassNotFoundException ex) {
// XXX - should never happen, ignore it
}
this.humanPresentableName = humanPresentableName;
}
/**
* Return the MIME type for this DataFlavor.
*
* @return the MIME type
*/
public String getMimeType() {
return mimeType;
}
/**
* Return the representation class.
*
* @return the representation class
*/
public Class getRepresentationClass() {
return representationClass;
}
/**
* Return the Human Presentable name.
*
* @return the human presentable name
*/
public String getHumanPresentableName() {
return humanPresentableName;
}
/**
* Set the human presentable name.
*
* @param humanPresentableName the name to set
*/
public void setHumanPresentableName(String humanPresentableName) {
this.humanPresentableName = humanPresentableName;
}
/**
* Compares the DataFlavor passed in with this DataFlavor; calls
* the <code>isMimeTypeEqual</code> method.
*
* @param dataFlavor the DataFlavor to compare with
* @return true if the MIME type and representation class
* are the same
*/
public boolean equals(ActivationDataFlavor dataFlavor) {
return (isMimeTypeEqual(dataFlavor.mimeType) &&
dataFlavor.getRepresentationClass() == representationClass);
}
/**
* Is the string representation of the MIME type passed in equivalent
* to the MIME type of this DataFlavor. <p>
*
* ActivationDataFlavor delegates the comparison of MIME types to
* the MimeType class included as part of Jakarta Activation.
* This provides a more robust comparison than is normally
* available in the DataFlavor class.
*
* @param mimeType the MIME type
* @return true if the same MIME type
*/
public boolean isMimeTypeEqual(String mimeType) {
MimeType mt = null;
try {
if (mimeObject == null)
mimeObject = new MimeType(this.mimeType);
mt = new MimeType(mimeType);
} catch (MimeTypeParseException e) {
// something didn't parse, do a crude comparison
return this.mimeType.equalsIgnoreCase(mimeType);
}
return mimeObject.match(mt);
}
/**
* Called on DataFlavor for every MIME Type parameter to allow DataFlavor
* subclasses to handle special parameters like the text/plain charset
* parameters, whose values are case insensitive. (MIME type parameter
* values are supposed to be case sensitive).
* <p>
* This method is called for each parameter name/value pair and should
* return the normalized representation of the parameterValue.
* This method is never invoked by this implementation.
*
* @param parameterName the parameter name
* @param parameterValue the parameter value
* @return the normalized parameter value
* @deprecated
*/
protected String normalizeMimeTypeParameter(String parameterName,
String parameterValue) {
return parameterValue;
}
/**
* Called for each MIME type string to give DataFlavor subtypes the
* opportunity to change how the normalization of MIME types is
* accomplished.
* One possible use would be to add default parameter/value pairs in cases
* where none are present in the MIME type string passed in.
* This method is never invoked by this implementation.
*
* @param mimeType the MIME type
* @return the normalized MIME type
* @deprecated
*/
protected String normalizeMimeType(String mimeType) {
return mimeType;
}
}

@ -0,0 +1,215 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
/**
* The CommandInfo class is used by CommandMap implementations to
* describe the results of command requests. It provides the requestor
* with both the verb requested, as well as an instance of the
* bean. There is also a method that will return the name of the
* class that implements the command but <i>it is not guaranteed to
* return a valid value</i>. The reason for this is to allow CommandMap
* implmentations that subclass CommandInfo to provide special
* behavior. For example a CommandMap could dynamically generate
* JavaBeans. In this case, it might not be possible to create an
* object with all the correct state information solely from the class
* name.
*/
public class CommandInfo {
private String verb;
private String className;
/**
* The Constructor for CommandInfo.
* @param verb The command verb this CommandInfo decribes.
* @param className The command's fully qualified class name.
*/
public CommandInfo(String verb, String className) {
this.verb = verb;
this.className = className;
}
/**
* Return the command verb.
*
* @return the command verb.
*/
public String getCommandName() {
return verb;
}
/**
* Return the command's class name. <i>This method MAY return null in
* cases where a CommandMap subclassed CommandInfo for its
* own purposes.</i> In other words, it might not be possible to
* create the correct state in the command by merely knowing
* its class name. <b>DO NOT DEPEND ON THIS METHOD RETURNING
* A VALID VALUE!</b>
*
* @return The class name of the command, or <i>null</i>
*/
public String getCommandClass() {
return className;
}
/**
* Return the instantiated JavaBean component.
* <p>
* If the current runtime environment supports
* {@link java.beans.Beans#instantiate Beans.instantiate},
* use it to instantiate the JavaBeans component. Otherwise, use
* {@link java.lang.Class#forName Class.forName}.
* <p>
* The component class needs to be public.
* On Java SE 9 and newer, if the component class is in a named module,
* it needs to be in an exported package.
* <p>
* If the bean implements the <code>javax.activation.CommandObject</code>
* interface, call its <code>setCommandContext</code> method.
* <p>
* If the DataHandler parameter is null, then the bean is
* instantiated with no data. NOTE: this may be useful
* if for some reason the DataHandler that is passed in
* throws IOExceptions when this method attempts to
* access its InputStream. It will allow the caller to
* retrieve a reference to the bean if it can be
* instantiated.
* <p>
* If the bean does NOT implement the CommandObject interface,
* this method will check if it implements the
* java.io.Externalizable interface. If it does, the bean's
* readExternal method will be called if an InputStream
* can be acquired from the DataHandler.<p>
*
* @param dh The DataHandler that describes the data to be
* passed to the command.
* @param loader The ClassLoader to be used to instantiate the bean.
* @return The bean
* @exception IOException for failures reading data
* @exception ClassNotFoundException if command object class can't
* be found
* @see java.beans.Beans#instantiate
* @see javax.activation.CommandObject
*/
public Object getCommandObject(DataHandler dh, ClassLoader loader)
throws IOException, ClassNotFoundException {
Object new_bean = null;
// try to instantiate the bean
new_bean = Beans.instantiate(loader, className);
// if we got one and it is a CommandObject
if (new_bean != null) {
if (new_bean instanceof CommandObject) {
((CommandObject)new_bean).setCommandContext(verb, dh);
} else if (new_bean instanceof Externalizable) {
if (dh != null) {
InputStream is = dh.getInputStream();
if (is != null) {
((Externalizable)new_bean).readExternal(
new ObjectInputStream(is));
}
}
}
}
return new_bean;
}
/**
* Helper class to invoke Beans.instantiate reflectively or the equivalent
* with core reflection when module java.desktop is not readable.
*/
private static final class Beans {
static final Method instantiateMethod;
static {
Method m;
try {
Class<?> c = Class.forName("java.beans.Beans");
m = c.getDeclaredMethod("instantiate", ClassLoader.class, String.class);
} catch (ClassNotFoundException e) {
m = null;
} catch (NoSuchMethodException e) {
m = null;
}
instantiateMethod = m;
}
/**
* Equivalent to invoking java.beans.Beans.instantiate(loader, cn)
*/
static Object instantiate(ClassLoader loader, String cn)
throws IOException, ClassNotFoundException {
Exception exception;
if (instantiateMethod != null) {
// invoke Beans.instantiate
try {
return instantiateMethod.invoke(null, loader, cn);
} catch (InvocationTargetException e) {
exception = e;
} catch (IllegalAccessException e) {
exception = e;
}
} else {
SecurityManager security = System.getSecurityManager();
if (security != null) {
// if it's ok with the SecurityManager, it's ok with me.
String cname = cn.replace('/', '.');
if (cname.startsWith("[")) {
int b = cname.lastIndexOf('[') + 2;
if (b > 1 && b < cname.length()) {
cname = cname.substring(b);
}
}
int i = cname.lastIndexOf('.');
if (i != -1) {
security.checkPackageAccess(cname.substring(0, i));
}
}
// Beans.instantiate specified to use SCL when loader is null
if (loader == null) {
loader = (ClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
ClassLoader cl = null;
try {
cl = ClassLoader.getSystemClassLoader();
} catch (SecurityException ex) { }
return cl;
}
});
}
Class<?> beanClass = Class.forName(cn, true, loader);
try {
return beanClass.newInstance();
} catch (Exception ex) {
throw new ClassNotFoundException(beanClass + ": " + ex, ex);
}
}
return null;
}
}
}

@ -0,0 +1,219 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.util.Map;
import java.util.WeakHashMap;
/**
* The CommandMap class provides an interface to a registry of
* command objects available in the system.
* Developers are expected to either use the CommandMap
* implementation included with this package (MailcapCommandMap) or
* develop their own. Note that some of the methods in this class are
* abstract.
*/
public abstract class CommandMap {
private static CommandMap defaultCommandMap = null;
private static Map<ClassLoader,CommandMap> map =
new WeakHashMap<ClassLoader,CommandMap>();
/**
* Get the default CommandMap.
*
* <ul>
* <li> In cases where a CommandMap instance has been previously set
* to some value (via <i>setDefaultCommandMap</i>)
* return the CommandMap.
* <li>
* In cases where no CommandMap has been set, the CommandMap
* creates an instance of <code>MailcapCommandMap</code> and
* set that to the default, returning its value.
*
* </ul>
*
* @return the CommandMap
*/
public static synchronized CommandMap getDefaultCommandMap() {
if (defaultCommandMap != null)
return defaultCommandMap;
// fetch per-thread-context-class-loader default
ClassLoader tccl = SecuritySupport.getContextClassLoader();
CommandMap def = map.get(tccl);
if (def == null) {
def = new MailcapCommandMap();
map.put(tccl, def);
}
return def;
}
/**
* Set the default CommandMap. Reset the CommandMap to the default by
* calling this method with <code>null</code>.
*
* @param commandMap The new default CommandMap.
* @exception SecurityException if the caller doesn't have permission
* to change the default
*/
public static synchronized void setDefaultCommandMap(CommandMap commandMap) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
try {
// if it's ok with the SecurityManager, it's ok with me...
security.checkSetFactory();
} catch (SecurityException ex) {
// otherwise, we also allow it if this code and the
// factory come from the same (non-system) class loader (e.g.,
// the JAF classes were loaded with the applet classes).
ClassLoader cl = CommandMap.class.getClassLoader();
if (cl == null || cl.getParent() == null ||
cl != commandMap.getClass().getClassLoader()) {
throw ex;
}
}
}
// remove any per-thread-context-class-loader CommandMap
map.remove(SecuritySupport.getContextClassLoader());
defaultCommandMap = commandMap;
}
/**
* Get the preferred command list from a MIME Type. The actual semantics
* are determined by the implementation of the CommandMap.
*
* @param mimeType the MIME type
* @return the CommandInfo classes that represent the command Beans.
*/
abstract public CommandInfo[] getPreferredCommands(String mimeType);
/**
* Get the preferred command list from a MIME Type. The actual semantics
* are determined by the implementation of the CommandMap. <p>
*
* The <code>DataSource</code> provides extra information, such as
* the file name, that a CommandMap implementation may use to further
* refine the list of commands that are returned. The implementation
* in this class simply calls the <code>getPreferredCommands</code>
* method that ignores this argument.
*
* @param mimeType the MIME type
* @param ds a DataSource for the data
* @return the CommandInfo classes that represent the command Beans.
* @since JAF 1.1
*/
public CommandInfo[] getPreferredCommands(String mimeType, DataSource ds) {
return getPreferredCommands(mimeType);
}
/**
* Get all the available commands for this type. This method
* should return all the possible commands for this MIME type.
*
* @param mimeType the MIME type
* @return the CommandInfo objects representing all the commands.
*/
abstract public CommandInfo[] getAllCommands(String mimeType);
/**
* Get all the available commands for this type. This method
* should return all the possible commands for this MIME type. <p>
*
* The <code>DataSource</code> provides extra information, such as
* the file name, that a CommandMap implementation may use to further
* refine the list of commands that are returned. The implementation
* in this class simply calls the <code>getAllCommands</code>
* method that ignores this argument.
*
* @param mimeType the MIME type
* @param ds a DataSource for the data
* @return the CommandInfo objects representing all the commands.
* @since JAF 1.1
*/
public CommandInfo[] getAllCommands(String mimeType, DataSource ds) {
return getAllCommands(mimeType);
}
/**
* Get the default command corresponding to the MIME type.
*
* @param mimeType the MIME type
* @param cmdName the command name
* @return the CommandInfo corresponding to the command.
*/
abstract public CommandInfo getCommand(String mimeType, String cmdName);
/**
* Get the default command corresponding to the MIME type. <p>
*
* The <code>DataSource</code> provides extra information, such as
* the file name, that a CommandMap implementation may use to further
* refine the command that is chosen. The implementation
* in this class simply calls the <code>getCommand</code>
* method that ignores this argument.
*
* @param mimeType the MIME type
* @param cmdName the command name
* @param ds a DataSource for the data
* @return the CommandInfo corresponding to the command.
* @since JAF 1.1
*/
public CommandInfo getCommand(String mimeType, String cmdName,
DataSource ds) {
return getCommand(mimeType, cmdName);
}
/**
* Locate a DataContentHandler that corresponds to the MIME type.
* The mechanism and semantics for determining this are determined
* by the implementation of the particular CommandMap.
*
* @param mimeType the MIME type
* @return the DataContentHandler for the MIME type
*/
abstract public DataContentHandler createDataContentHandler(String
mimeType);
/**
* Locate a DataContentHandler that corresponds to the MIME type.
* The mechanism and semantics for determining this are determined
* by the implementation of the particular CommandMap. <p>
*
* The <code>DataSource</code> provides extra information, such as
* the file name, that a CommandMap implementation may use to further
* refine the choice of DataContentHandler. The implementation
* in this class simply calls the <code>createDataContentHandler</code>
* method that ignores this argument.
*
* @param mimeType the MIME type
* @param ds a DataSource for the data
* @return the DataContentHandler for the MIME type
* @since JAF 1.1
*/
public DataContentHandler createDataContentHandler(String mimeType,
DataSource ds) {
return createDataContentHandler(mimeType);
}
/**
* Get all the MIME types known to this command map.
* If the command map doesn't support this operation,
* null is returned.
*
* @return array of MIME types as strings, or null if not supported
* @since JAF 1.1
*/
public String[] getMimeTypes() {
return null;
}
}

@ -0,0 +1,38 @@
/*
* Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.IOException;
/**
* JavaBeans components that are Jakarta Activation aware implement
* this interface to find out which command verb they're being asked
* to perform, and to obtain the DataHandler representing the
* data they should operate on. JavaBeans that don't implement
* this interface may be used as well. Such commands may obtain
* the data using the Externalizable interface, or using an
* application-specific method.
*/
public interface CommandObject {
/**
* Initialize the Command with the verb it is requested to handle
* and the DataHandler that describes the data it will
* operate on. <b>NOTE:</b> it is acceptable for the caller
* to pass <i>null</i> as the value for <code>DataHandler</code>.
*
* @param verb The Command Verb this object refers to.
* @param dh The DataHandler.
* @exception IOException for failures accessing data
*/
public void setCommandContext(String verb, DataHandler dh)
throws IOException;
}

@ -0,0 +1,82 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
//import java.awt.datatransfer.DataFlavor;
//import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import javax.activation.DataSource;
/**
* The DataContentHandler interface is implemented by objects that can
* be used to extend the capabilities of the DataHandler's implementation
* of the Transferable interface. Through <code>DataContentHandlers</code>
* the framework can be extended to convert streams in to objects, and
* to write objects to streams. <p>
*
* Applications don't generally call the methods in DataContentHandlers
* directly. Instead, an application calls the equivalent methods in
* DataHandler. The DataHandler will attempt to find an appropriate
* DataContentHandler that corresponds to its MIME type using the
* current DataContentHandlerFactory. The DataHandler then calls
* through to the methods in the DataContentHandler.
*/
public interface DataContentHandler {
/**
* Returns an array of DataFlavor objects indicating the flavors the
* data can be provided in. The array should be ordered according to
* preference for providing the data (from most richly descriptive to
* least descriptive).
*
* @return The DataFlavors.
*/
public ActivationDataFlavor[] getTransferDataFlavors();
/**
* Returns an object which represents the data to be transferred.
* The class of the object returned is defined by the representation class
* of the flavor.
*
* @param df The DataFlavor representing the requested type.
* @param ds The DataSource representing the data to be converted.
* @return The constructed Object.
* @exception IOException if the data can't be accessed
*/
public Object getTransferData(ActivationDataFlavor df, DataSource ds)
throws /*UnsupportedFlavorException,*/ IOException;
/**
* Return an object representing the data in its most preferred form.
* Generally this will be the form described by the first DataFlavor
* returned by the <code>getTransferDataFlavors</code> method.
*
* @param ds The DataSource representing the data to be converted.
* @return The constructed Object.
* @exception IOException if the data can't be accessed
*/
public Object getContent(DataSource ds) throws IOException;
/**
* Convert the object to a byte stream of the specified MIME type
* and write it to the output stream.
*
* @param obj The object to be converted.
* @param mimeType The requested MIME type of the resulting byte stream.
* @param os The output stream into which to write the converted
* byte stream.
* @exception IOException errors writing to the stream
*/
public void writeTo(Object obj, String mimeType, OutputStream os)
throws IOException;
}

@ -0,0 +1,31 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
/**
* This interface defines a factory for <code>DataContentHandlers</code>. An
* implementation of this interface should map a MIME type into an
* instance of DataContentHandler. The design pattern for classes implementing
* this interface is the same as for the ContentHandler mechanism used in
* <code>java.net.URL</code>.
*/
public interface DataContentHandlerFactory {
/**
* Creates a new DataContentHandler object for the MIME type.
*
* @param mimeType the MIME type to create the DataContentHandler for.
* @return The new <code>DataContentHandler</code>, or <i>null</i>
* if none are found.
*/
public DataContentHandler createDataContentHandler(String mimeType);
}

@ -0,0 +1,881 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
//import java.awt.datatransfer.Transferable;
//import java.awt.datatransfer.DataFlavor;
//import java.awt.datatransfer.UnsupportedFlavorException;
/**
* The DataHandler class provides a consistent interface to data
* available in many different sources and formats.
* It manages simple stream to string conversions and related operations
* using DataContentHandlers.
* It provides access to commands that can operate on the data.
* The commands are found using a CommandMap. <p>
*
* <b>DataHandler and the Transferable Interface</b><p>
* DataHandler implements the Transferable interface so that data can
* be used in AWT data transfer operations, such as cut and paste and
* drag and drop. The implementation of the Transferable interface
* relies on the availability of an installed DataContentHandler
* object corresponding to the MIME type of the data represented in
* the specific instance of the DataHandler.<p>
*
* <b>DataHandler and CommandMaps</b><p>
* The DataHandler keeps track of the current CommandMap that it uses to
* service requests for commands (<code>getCommand</code>,
* <code>getAllCommands</code>, <code>getPreferredCommands</code>).
* Each instance of a DataHandler may have a CommandMap associated with
* it using the <code>setCommandMap</code> method. If a CommandMap was
* not set, DataHandler calls the <code>getDefaultCommandMap</code>
* method in CommandMap and uses the value it returns. See
* <i>CommandMap</i> for more information. <p>
*
* <b>DataHandler and URLs</b><p>
* The current DataHandler implementation creates a private
* instance of URLDataSource when it is constructed with a URL.
*
* @see javax.activation.CommandMap
* @see javax.activation.DataContentHandler
* @see javax.activation.DataSource
* @see javax.activation.URLDataSource
*/
public class DataHandler /*implements Transferable*/ {
// Use the datasource to indicate whether we were started via the
// DataSource constructor or the object constructor.
private DataSource dataSource = null;
private DataSource objDataSource = null;
// The Object and mimetype from the constructor (if passed in).
// object remains null if it was instantiated with a
// DataSource.
private Object object = null;
private String objectMimeType = null;
// Keep track of the CommandMap
private CommandMap currentCommandMap = null;
// our transfer flavors
private static final ActivationDataFlavor emptyFlavors[] = new ActivationDataFlavor[0];
private ActivationDataFlavor transferFlavors[] = emptyFlavors;
// our DataContentHandler
private DataContentHandler dataContentHandler = null;
private DataContentHandler factoryDCH = null;
// our DataContentHandlerFactory
private static DataContentHandlerFactory factory = null;
private DataContentHandlerFactory oldFactory = null;
// the short representation of the ContentType (sans params)
private String shortType = null;
/**
* Create a <code>DataHandler</code> instance referencing the
* specified DataSource. The data exists in a byte stream form.
* The DataSource will provide an InputStream to access the data.
*
* @param ds the DataSource
*/
public DataHandler(DataSource ds) {
// save a reference to the incoming DS
dataSource = ds;
oldFactory = factory; // keep track of the factory
}
/**
* Create a <code>DataHandler</code> instance representing an object
* of this MIME type. This constructor is
* used when the application already has an in-memory representation
* of the data in the form of a Java Object.
*
* @param obj the Java Object
* @param mimeType the MIME type of the object
*/
public DataHandler(Object obj, String mimeType) {
object = obj;
objectMimeType = mimeType;
oldFactory = factory; // keep track of the factory
}
/**
* Create a <code>DataHandler</code> instance referencing a URL.
* The DataHandler internally creates a <code>URLDataSource</code>
* instance to represent the URL.
*
* @param url a URL object
*/
public DataHandler(URL url) {
dataSource = new URLDataSource(url);
oldFactory = factory; // keep track of the factory
}
/**
* Return the CommandMap for this instance of DataHandler.
*/
private synchronized CommandMap getCommandMap() {
if (currentCommandMap != null)
return currentCommandMap;
else
return CommandMap.getDefaultCommandMap();
}
/**
* Return the DataSource associated with this instance
* of DataHandler.
* <p>
* For DataHandlers that have been instantiated with a DataSource,
* this method returns the DataSource that was used to create the
* DataHandler object. In other cases the DataHandler
* constructs a DataSource from the data used to construct
* the DataHandler. DataSources created for DataHandlers <b>not</b>
* instantiated with a DataSource are cached for performance
* reasons.
*
* @return a valid DataSource object for this DataHandler
*/
public DataSource getDataSource() {
if (dataSource == null) {
// create one on the fly
if (objDataSource == null)
objDataSource = new DataHandlerDataSource(this);
return objDataSource;
}
return dataSource;
}
/**
* Return the name of the data object. If this DataHandler
* was created with a DataSource, this method calls through
* to the <code>DataSource.getName</code> method, otherwise it
* returns <i>null</i>.
*
* @return the name of the object
*/
public String getName() {
if (dataSource != null)
return dataSource.getName();
else
return null;
}
/**
* Return the MIME type of this object as retrieved from
* the source object. Note that this is the <i>full</i>
* type with parameters.
*
* @return the MIME type
*/
public String getContentType() {
if (dataSource != null) // data source case
return dataSource.getContentType();
else
return objectMimeType; // obj/type case
}
/**
* Get the InputStream for this object. <p>
*
* For DataHandlers instantiated with a DataSource, the DataHandler
* calls the <code>DataSource.getInputStream</code> method and
* returns the result to the caller.
* <p>
* For DataHandlers instantiated with an Object, the DataHandler
* first attempts to find a DataContentHandler for the Object. If
* the DataHandler can not find a DataContentHandler for this MIME
* type, it throws an UnsupportedDataTypeException. If it is
* successful, it creates a pipe and a thread. The thread uses the
* DataContentHandler's <code>writeTo</code> method to write the
* stream data into one end of the pipe. The other end of the pipe
* is returned to the caller. Because a thread is created to copy
* the data, IOExceptions that may occur during the copy can not be
* propagated back to the caller. The result is an empty stream.<p>
*
* @return the InputStream representing this data
* @exception IOException if an I/O error occurs
*
* @see javax.activation.DataContentHandler#writeTo
* @see javax.activation.UnsupportedDataTypeException
*/
public InputStream getInputStream() throws IOException {
InputStream ins = null;
if (dataSource != null) {
ins = dataSource.getInputStream();
} else {
DataContentHandler dch = getDataContentHandler();
// we won't even try if we can't get a dch
if (dch == null)
throw new UnsupportedDataTypeException(
"no DCH for MIME type " + getBaseType());
if (dch instanceof ObjectDataContentHandler) {
if (((ObjectDataContentHandler)dch).getDCH() == null)
throw new UnsupportedDataTypeException(
"no object DCH for MIME type " + getBaseType());
}
// there is none but the default^^^^^^^^^^^^^^^^
final DataContentHandler fdch = dch;
// from bill s.
// ce n'est pas une pipe!
//
// NOTE: This block of code needs to throw exceptions, but
// can't because it is in another thread!!! ARG!
//
final PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pin = new PipedInputStream(pos);
new Thread(
new Runnable() {
public void run() {
try {
fdch.writeTo(object, objectMimeType, pos);
} catch (IOException e) {
} finally {
try {
pos.close();
} catch (IOException ie) { }
}
}
},
"DataHandler.getInputStream").start();
ins = pin;
}
return ins;
}
/**
* Write the data to an <code>OutputStream</code>.<p>
*
* If the DataHandler was created with a DataSource, writeTo
* retrieves the InputStream and copies the bytes from the
* InputStream to the OutputStream passed in.
* <p>
* If the DataHandler was created with an object, writeTo
* retrieves the DataContentHandler for the object's type.
* If the DataContentHandler was found, it calls the
* <code>writeTo</code> method on the <code>DataContentHandler</code>.
*
* @param os the OutputStream to write to
* @exception IOException if an I/O error occurs
*/
public void writeTo(OutputStream os) throws IOException {
// for the DataSource case
if (dataSource != null) {
InputStream is = null;
byte data[] = new byte[8*1024];
int bytes_read;
is = dataSource.getInputStream();
try {
while ((bytes_read = is.read(data)) > 0) {
os.write(data, 0, bytes_read);
}
} finally {
is.close();
is = null;
}
} else { // for the Object case
DataContentHandler dch = getDataContentHandler();
dch.writeTo(object, objectMimeType, os);
}
}
/**
* Get an OutputStream for this DataHandler to allow overwriting
* the underlying data.
* If the DataHandler was created with a DataSource, the
* DataSource's <code>getOutputStream</code> method is called.
* Otherwise, <code>null</code> is returned.
*
* @return the OutputStream
*
* @see javax.activation.DataSource#getOutputStream
* @see javax.activation.URLDataSource
*/
public OutputStream getOutputStream() throws IOException {
if (dataSource != null)
return dataSource.getOutputStream();
else
return null;
}
/**
* Return the DataFlavors in which this data is available. <p>
*
* Returns an array of DataFlavor objects indicating the flavors
* the data can be provided in. The array is usually ordered
* according to preference for providing the data, from most
* richly descriptive to least richly descriptive.<p>
*
* The DataHandler attempts to find a DataContentHandler that
* corresponds to the MIME type of the data. If one is located,
* the DataHandler calls the DataContentHandler's
* <code>getTransferDataFlavors</code> method. <p>
*
* If a DataContentHandler can <i>not</i> be located, and if the
* DataHandler was created with a DataSource (or URL), one
* DataFlavor is returned that represents this object's MIME type
* and the <code>java.io.InputStream</code> class. If the
* DataHandler was created with an object and a MIME type,
* getTransferDataFlavors returns one DataFlavor that represents
* this object's MIME type and the object's class.
*
* @return an array of data flavors in which this data can be transferred
* @see javax.activation.DataContentHandler#getTransferDataFlavors
*/
public synchronized ActivationDataFlavor[] getTransferDataFlavors() {
if (factory != oldFactory) // if the factory has changed, clear cache
transferFlavors = emptyFlavors;
// if it's not set, set it...
if (transferFlavors == emptyFlavors)
transferFlavors = getDataContentHandler().getTransferDataFlavors();
if (transferFlavors == emptyFlavors)
return transferFlavors; // no need to clone an empty array
else
return transferFlavors.clone();
}
/**
* Returns whether the specified data flavor is supported
* for this object.<p>
*
* This method iterates through the DataFlavors returned from
* <code>getTransferDataFlavors</code>, comparing each with
* the specified flavor.
*
* @param flavor the requested flavor for the data
* @return true if the data flavor is supported
* @see javax.activation.DataHandler#getTransferDataFlavors
*/
public boolean isDataFlavorSupported(ActivationDataFlavor flavor) {
ActivationDataFlavor[] lFlavors = getTransferDataFlavors();
for (int i = 0; i < lFlavors.length; i++) {
if (lFlavors[i].equals(flavor))
return true;
}
return false;
}
/**
* Returns an object that represents the data to be
* transferred. The class of the object returned is defined by the
* representation class of the data flavor.<p>
*
* <b>For DataHandler's created with DataSources or URLs:</b><p>
*
* The DataHandler attempts to locate a DataContentHandler
* for this MIME type. If one is found, the passed in DataFlavor
* and the type of the data are passed to its <code>getTransferData</code>
* method. If the DataHandler fails to locate a DataContentHandler
* and the flavor specifies this object's MIME type and the
* <code>java.io.InputStream</code> class, this object's InputStream
* is returned.
* Otherwise it throws an UnsupportedFlavorException. <p>
*
* <b>For DataHandler's created with Objects:</b><p>
*
* The DataHandler attempts to locate a DataContentHandler
* for this MIME type. If one is found, the passed in DataFlavor
* and the type of the data are passed to its getTransferData
* method. If the DataHandler fails to locate a DataContentHandler
* and the flavor specifies this object's MIME type and its class,
* this DataHandler's referenced object is returned.
* Otherwise it throws an UnsupportedFlavorException.
*
* @param flavor the requested flavor for the data
* @return the object
* @exception IOException if an I/O error occurs
* @see javax.activation.ActivationDataFlavor
*/
public Object getTransferData(ActivationDataFlavor flavor)
throws /*UnsupportedFlavorException,*/ IOException {
return getDataContentHandler().getTransferData(flavor, dataSource);
}
/**
* Set the CommandMap for use by this DataHandler.
* Setting it to <code>null</code> causes the CommandMap to revert
* to the CommandMap returned by the
* <code>CommandMap.getDefaultCommandMap</code> method.
* Changing the CommandMap, or setting it to <code>null</code>,
* clears out any data cached from the previous CommandMap.
*
* @param commandMap the CommandMap to use in this DataHandler
*
* @see javax.activation.CommandMap#setDefaultCommandMap
*/
public synchronized void setCommandMap(CommandMap commandMap) {
if (commandMap != currentCommandMap || commandMap == null) {
// clear cached values...
transferFlavors = emptyFlavors;
dataContentHandler = null;
currentCommandMap = commandMap;
}
}
/**
* Return the <i>preferred</i> commands for this type of data.
* This method calls the <code>getPreferredCommands</code> method
* in the CommandMap associated with this instance of DataHandler.
* This method returns an array that represents a subset of
* available commands. In cases where multiple commands for the
* MIME type represented by this DataHandler are present, the
* installed CommandMap chooses the appropriate commands.
*
* @return the CommandInfo objects representing the preferred commands
*
* @see javax.activation.CommandMap#getPreferredCommands
*/
public CommandInfo[] getPreferredCommands() {
if (dataSource != null)
return getCommandMap().getPreferredCommands(getBaseType(),
dataSource);
else
return getCommandMap().getPreferredCommands(getBaseType());
}
/**
* Return all the commands for this type of data.
* This method returns an array containing all commands
* for the type of data represented by this DataHandler. The
* MIME type for the underlying data represented by this DataHandler
* is used to call through to the <code>getAllCommands</code> method
* of the CommandMap associated with this DataHandler.
*
* @return the CommandInfo objects representing all the commands
*
* @see javax.activation.CommandMap#getAllCommands
*/
public CommandInfo[] getAllCommands() {
if (dataSource != null)
return getCommandMap().getAllCommands(getBaseType(), dataSource);
else
return getCommandMap().getAllCommands(getBaseType());
}
/**
* Get the command <i>cmdName</i>. Use the search semantics as
* defined by the CommandMap installed in this DataHandler. The
* MIME type for the underlying data represented by this DataHandler
* is used to call through to the <code>getCommand</code> method
* of the CommandMap associated with this DataHandler.
*
* @param cmdName the command name
* @return the CommandInfo corresponding to the command
*
* @see javax.activation.CommandMap#getCommand
*/
public CommandInfo getCommand(String cmdName) {
if (dataSource != null)
return getCommandMap().getCommand(getBaseType(), cmdName,
dataSource);
else
return getCommandMap().getCommand(getBaseType(), cmdName);
}
/**
* Return the data in its preferred Object form. <p>
*
* If the DataHandler was instantiated with an object, return
* the object. <p>
*
* If the DataHandler was instantiated with a DataSource,
* this method uses a DataContentHandler to return the content
* object for the data represented by this DataHandler. If no
* <code>DataContentHandler</code> can be found for the
* the type of this data, the DataHandler returns an
* InputStream for the data.
*
* @return the content.
* @exception IOException if an IOException occurs during
* this operation.
*/
public Object getContent() throws IOException {
if (object != null)
return object;
else
return getDataContentHandler().getContent(getDataSource());
}
/**
* A convenience method that takes a CommandInfo object
* and instantiates the corresponding command, usually
* a JavaBean component.
* <p>
* This method calls the CommandInfo's <code>getCommandObject</code>
* method with the <code>ClassLoader</code> used to load
* the <code>javax.activation.DataHandler</code> class itself.
*
* @param cmdinfo the CommandInfo corresponding to a command
* @return the instantiated command object
*/
public Object getBean(CommandInfo cmdinfo) {
Object bean = null;
try {
// make the bean
ClassLoader cld = null;
// First try the "application's" class loader.
cld = SecuritySupport.getContextClassLoader();
if (cld == null)
cld = this.getClass().getClassLoader();
bean = cmdinfo.getCommandObject(this, cld);
} catch (IOException e) {
} catch (ClassNotFoundException e) { }
return bean;
}
/**
* Get the DataContentHandler for this DataHandler: <p>
*
* If a DataContentHandlerFactory is set, use it.
* Otherwise look for an object to serve DCH in the
* following order: <p>
*
* 1) if a factory is set, use it <p>
* 2) if a CommandMap is set, use it <p>
* 3) use the default CommandMap <p>
*
* In any case, wrap the real DataContentHandler with one of our own
* to handle any missing cases, fill in defaults, and to ensure that
* we always have a non-null DataContentHandler.
*
* @return the requested DataContentHandler
*/
private synchronized DataContentHandler getDataContentHandler() {
// make sure the factory didn't change
if (factory != oldFactory) {
oldFactory = factory;
factoryDCH = null;
dataContentHandler = null;
transferFlavors = emptyFlavors;
}
if (dataContentHandler != null)
return dataContentHandler;
String simpleMT = getBaseType();
if (factoryDCH == null && factory != null)
factoryDCH = factory.createDataContentHandler(simpleMT);
if (factoryDCH != null)
dataContentHandler = factoryDCH;
if (dataContentHandler == null) {
if (dataSource != null)
dataContentHandler = getCommandMap().
createDataContentHandler(simpleMT, dataSource);
else
dataContentHandler = getCommandMap().
createDataContentHandler(simpleMT);
}
// getDataContentHandler always uses these 'wrapper' handlers
// to make sure it returns SOMETHING meaningful...
if (dataSource != null)
dataContentHandler = new DataSourceDataContentHandler(
dataContentHandler,
dataSource);
else
dataContentHandler = new ObjectDataContentHandler(
dataContentHandler,
object,
objectMimeType);
return dataContentHandler;
}
/**
* Use the MimeType class to extract the MIME type/subtype,
* ignoring the parameters. The type is cached.
*/
private synchronized String getBaseType() {
if (shortType == null) {
String ct = getContentType();
try {
MimeType mt = new MimeType(ct);
shortType = mt.getBaseType();
} catch (MimeTypeParseException e) {
shortType = ct;
}
}
return shortType;
}
/**
* Sets the DataContentHandlerFactory. The DataContentHandlerFactory
* is called first to find DataContentHandlers.
* The DataContentHandlerFactory can only be set once.
* <p>
* If the DataContentHandlerFactory has already been set,
* this method throws an Error.
*
* @param newFactory the DataContentHandlerFactory
* @exception Error if the factory has already been defined.
*
* @see javax.activation.DataContentHandlerFactory
*/
public static synchronized void setDataContentHandlerFactory(
DataContentHandlerFactory newFactory) {
if (factory != null)
throw new Error("DataContentHandlerFactory already defined");
SecurityManager security = System.getSecurityManager();
if (security != null) {
try {
// if it's ok with the SecurityManager, it's ok with me...
security.checkSetFactory();
} catch (SecurityException ex) {
// otherwise, we also allow it if this code and the
// factory come from the same class loader (e.g.,
// the JAF classes were loaded with the applet classes).
if (DataHandler.class.getClassLoader() !=
newFactory.getClass().getClassLoader())
throw ex;
}
}
factory = newFactory;
}
}
/**
* The DataHanderDataSource class implements the
* DataSource interface when the DataHandler is constructed
* with an Object and a mimeType string.
*/
class DataHandlerDataSource implements DataSource {
DataHandler dataHandler = null;
/**
* The constructor.
*/
public DataHandlerDataSource(DataHandler dh) {
this.dataHandler = dh;
}
/**
* Returns an <code>InputStream</code> representing this object.
* @return the <code>InputStream</code>
*/
public InputStream getInputStream() throws IOException {
return dataHandler.getInputStream();
}
/**
* Returns the <code>OutputStream</code> for this object.
* @return the <code>OutputStream</code>
*/
public OutputStream getOutputStream() throws IOException {
return dataHandler.getOutputStream();
}
/**
* Returns the MIME type of the data represented by this object.
* @return the MIME type
*/
public String getContentType() {
return dataHandler.getContentType();
}
/**
* Returns the name of this object.
* @return the name of this object
*/
public String getName() {
return dataHandler.getName(); // what else would it be?
}
}
/*
* DataSourceDataContentHandler
*
* This is a <i>private</i> DataContentHandler that wraps the real
* DataContentHandler in the case where the DataHandler was instantiated
* with a DataSource.
*/
class DataSourceDataContentHandler implements DataContentHandler {
private DataSource ds = null;
private ActivationDataFlavor transferFlavors[] = null;
private DataContentHandler dch = null;
/**
* The constructor.
*/
public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) {
this.ds = ds;
this.dch = dch;
}
/**
* Return the DataFlavors for this <code>DataContentHandler</code>.
* @return the DataFlavors
*/
public ActivationDataFlavor[] getTransferDataFlavors() {
if (transferFlavors == null) {
if (dch != null) { // is there a dch?
transferFlavors = dch.getTransferDataFlavors();
} else {
transferFlavors = new ActivationDataFlavor[1];
transferFlavors[0] =
new ActivationDataFlavor(ds.getContentType(),
ds.getContentType());
}
}
return transferFlavors;
}
/**
* Return the Transfer Data of type DataFlavor from InputStream.
* @param df the DataFlavor
* @param ds the DataSource
* @return the constructed Object
*/
public Object getTransferData(ActivationDataFlavor df, DataSource ds) throws
/*UnsupportedFlavorException,*/ IOException {
if (dch != null)
return dch.getTransferData(df, ds);
else if (df.equals(getTransferDataFlavors()[0])) // only have one now
return ds.getInputStream();
else
//throw new UnsupportedFlavorException(df);
throw new IOException("Unsupported DataFlavor: " + df);
}
public Object getContent(DataSource ds) throws IOException {
if (dch != null)
return dch.getContent(ds);
else
return ds.getInputStream();
}
/**
* Write the object to the output stream.
*/
public void writeTo(Object obj, String mimeType, OutputStream os)
throws IOException {
if (dch != null)
dch.writeTo(obj, mimeType, os);
else
throw new UnsupportedDataTypeException(
"no DCH for content type " + ds.getContentType());
}
}
/*
* ObjectDataContentHandler
*
* This is a <i>private</i> DataContentHandler that wraps the real
* DataContentHandler in the case where the DataHandler was instantiated
* with an object.
*/
class ObjectDataContentHandler implements DataContentHandler {
private ActivationDataFlavor transferFlavors[] = null;
private Object obj;
private String mimeType;
private DataContentHandler dch = null;
/**
* The constructor.
*/
public ObjectDataContentHandler(DataContentHandler dch,
Object obj, String mimeType) {
this.obj = obj;
this.mimeType = mimeType;
this.dch = dch;
}
/**
* Return the DataContentHandler for this object.
* Used only by the DataHandler class.
*/
public DataContentHandler getDCH() {
return dch;
}
/**
* Return the DataFlavors for this <code>DataContentHandler</code>.
* @return the DataFlavors
*/
public synchronized ActivationDataFlavor[] getTransferDataFlavors() {
if (transferFlavors == null) {
if (dch != null) {
transferFlavors = dch.getTransferDataFlavors();
} else {
transferFlavors = new ActivationDataFlavor[1];
transferFlavors[0] = new ActivationDataFlavor(obj.getClass(),
mimeType, mimeType);
}
}
return transferFlavors;
}
/**
* Return the Transfer Data of type DataFlavor from InputStream.
* @param df the DataFlavor
* @param ds the DataSource
* @return the constructed Object
*/
public Object getTransferData(ActivationDataFlavor df, DataSource ds)
throws /*UnsupportedFlavorException,*/ IOException {
if (dch != null)
return dch.getTransferData(df, ds);
else if (df.equals(getTransferDataFlavors()[0])) // only have one now
return obj;
else
//throw new UnsupportedFlavorException(df);
throw new IOException("Unsupported DataFlavor: " + df);
}
public Object getContent(DataSource ds) {
return obj;
}
/**
* Write the object to the output stream.
*/
public void writeTo(Object obj, String mimeType, OutputStream os)
throws IOException {
if (dch != null)
dch.writeTo(obj, mimeType, os);
else if (obj instanceof byte[])
os.write((byte[])obj);
else if (obj instanceof String) {
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write((String)obj);
osw.flush();
} else
throw new UnsupportedDataTypeException(
"no object DCH for MIME type " + this.mimeType);
}
}

@ -0,0 +1,71 @@
/*
* Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
/**
* The DataSource interface provides Jakarta Activation
* with an abstraction of an arbitrary collection of data. It
* provides a type for that data as well as access
* to it in the form of <code>InputStreams</code> and
* <code>OutputStreams</code> where appropriate.
*/
public interface DataSource {
/**
* This method returns an <code>InputStream</code> representing
* the data and throws the appropriate exception if it can
* not do so. Note that a new <code>InputStream</code> object must be
* returned each time this method is called, and the stream must be
* positioned at the beginning of the data.
*
* @return an InputStream
* @exception IOException for failures creating the InputStream
*/
public InputStream getInputStream() throws IOException;
/**
* This method returns an <code>OutputStream</code> where the
* data can be written and throws the appropriate exception if it can
* not do so. Note that a new <code>OutputStream</code> object must
* be returned each time this method is called, and the stream must
* be positioned at the location the data is to be written.
*
* @return an OutputStream
* @exception IOException for failures creating the OutputStream
*/
public OutputStream getOutputStream() throws IOException;
/**
* This method returns the MIME type of the data in the form of a
* string. It should always return a valid type. It is suggested
* that getContentType return "application/octet-stream" if the
* DataSource implementation can not determine the data type.
*
* @return the MIME Type
*/
public String getContentType();
/**
* Return the <i>name</i> of this object where the name of the object
* is dependant on the nature of the underlying objects. DataSources
* encapsulating files may choose to return the filename of the object.
* (Typically this would be the last component of the filename, not an
* entire pathname.)
*
* @return the name of the object.
*/
public String getName();
}

@ -0,0 +1,141 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import com.sun.activation.registries.MimeTypeFile;
/**
* The FileDataSource class implements a simple DataSource object
* that encapsulates a file. It provides data typing services via
* a FileTypeMap object. <p>
*
* <b>FileDataSource Typing Semantics</b><p>
*
* The FileDataSource class delegates data typing of files
* to an object subclassed from the FileTypeMap class.
* The <code>setFileTypeMap</code> method can be used to explicitly
* set the FileTypeMap for an instance of FileDataSource. If no
* FileTypeMap is set, the FileDataSource will call the FileTypeMap's
* getDefaultFileTypeMap method to get the System's default FileTypeMap.
*
* @see javax.activation.DataSource
* @see javax.activation.FileTypeMap
* @see javax.activation.MimetypesFileTypeMap
*/
public class FileDataSource implements DataSource {
// keep track of original 'ref' passed in, non-null
// one indicated which was passed in:
private File _file = null;
private FileTypeMap typeMap = null;
/**
* Creates a FileDataSource from a File object. <i>Note:
* The file will not actually be opened until a method is
* called that requires the file to be opened.</i>
*
* @param file the file
*/
public FileDataSource(File file) {
_file = file; // save the file Object...
}
/**
* Creates a FileDataSource from
* the specified path name. <i>Note:
* The file will not actually be opened until a method is
* called that requires the file to be opened.</i>
*
* @param name the system-dependent file name.
*/
public FileDataSource(String name) {
this(new File(name)); // use the file constructor
}
/**
* This method will return an InputStream representing the
* the data and will throw an IOException if it can
* not do so. This method will return a new
* instance of InputStream with each invocation.
*
* @return an InputStream
*/
public InputStream getInputStream() throws IOException {
return new FileInputStream(_file);
}
/**
* This method will return an OutputStream representing the
* the data and will throw an IOException if it can
* not do so. This method will return a new instance of
* OutputStream with each invocation.
*
* @return an OutputStream
*/
public OutputStream getOutputStream() throws IOException {
return new FileOutputStream(_file);
}
/**
* This method returns the MIME type of the data in the form of a
* string. This method uses the currently installed FileTypeMap. If
* there is no FileTypeMap explictly set, the FileDataSource will
* call the <code>getDefaultFileTypeMap</code> method on
* FileTypeMap to acquire a default FileTypeMap. <i>Note: By
* default, the FileTypeMap used will be a MimetypesFileTypeMap.</i>
*
* @return the MIME Type
* @see javax.activation.FileTypeMap#getDefaultFileTypeMap
*/
public String getContentType() {
// check to see if the type map is null?
if (typeMap == null)
return FileTypeMap.getDefaultFileTypeMap().getContentType(_file);
else
return typeMap.getContentType(_file);
}
/**
* Return the <i>name</i> of this object. The FileDataSource
* will return the file name of the object.
*
* @return the name of the object.
* @see javax.activation.DataSource
*/
public String getName() {
return _file.getName();
}
/**
* Return the File object that corresponds to this FileDataSource.
* @return the File object for the file represented by this object.
*/
public File getFile() {
return _file;
}
/**
* Set the FileTypeMap to use with this FileDataSource
*
* @param map The FileTypeMap for this object.
*/
public void setFileTypeMap(FileTypeMap map) {
typeMap = map;
}
}

@ -0,0 +1,115 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.File;
import java.util.Map;
import java.util.WeakHashMap;
/**
* The FileTypeMap is an abstract class that provides a data typing
* interface for files. Implementations of this class will
* implement the getContentType methods which will derive a content
* type from a file name or a File object. FileTypeMaps could use any
* scheme to determine the data type, from examining the file extension
* of a file (like the MimetypesFileTypeMap) to opening the file and
* trying to derive its type from the contents of the file. The
* FileDataSource class uses the default FileTypeMap (a MimetypesFileTypeMap
* unless changed) to determine the content type of files.
*
* @see javax.activation.FileTypeMap
* @see javax.activation.FileDataSource
* @see javax.activation.MimetypesFileTypeMap
*/
public abstract class FileTypeMap {
private static FileTypeMap defaultMap = null;
private static Map<ClassLoader,FileTypeMap> map =
new WeakHashMap<ClassLoader,FileTypeMap>();
/**
* The default constructor.
*/
public FileTypeMap() {
super();
}
/**
* Return the type of the file object. This method should
* always return a valid MIME type.
*
* @param file A file to be typed.
* @return The content type.
*/
abstract public String getContentType(File file);
/**
* Return the type of the file passed in. This method should
* always return a valid MIME type.
*
* @param filename the pathname of the file.
* @return The content type.
*/
abstract public String getContentType(String filename);
/**
* Sets the default FileTypeMap for the system. This instance
* will be returned to callers of getDefaultFileTypeMap.
*
* @param fileTypeMap The FileTypeMap.
* @exception SecurityException if the caller doesn't have permission
* to change the default
*/
public static synchronized void setDefaultFileTypeMap(FileTypeMap fileTypeMap) {
SecurityManager security = System.getSecurityManager();
if (security != null) {
try {
// if it's ok with the SecurityManager, it's ok with me...
security.checkSetFactory();
} catch (SecurityException ex) {
// otherwise, we also allow it if this code and the
// factory come from the same (non-system) class loader (e.g.,
// the JAF classes were loaded with the applet classes).
ClassLoader cl = FileTypeMap.class.getClassLoader();
if (cl == null || cl.getParent() == null ||
cl != fileTypeMap.getClass().getClassLoader())
throw ex;
}
}
// remove any per-thread-context-class-loader FileTypeMap
map.remove(SecuritySupport.getContextClassLoader());
defaultMap = fileTypeMap;
}
/**
* Return the default FileTypeMap for the system.
* If setDefaultFileTypeMap was called, return
* that instance, otherwise return an instance of
* <code>MimetypesFileTypeMap</code>.
*
* @return The default FileTypeMap
* @see javax.activation.FileTypeMap#setDefaultFileTypeMap
*/
public static synchronized FileTypeMap getDefaultFileTypeMap() {
if (defaultMap != null)
return defaultMap;
// fetch per-thread-context-class-loader default
ClassLoader tccl = SecuritySupport.getContextClassLoader();
FileTypeMap def = map.get(tccl);
if (def == null) {
def = new MimetypesFileTypeMap();
map.put(tccl, def);
}
return def;
}
}

@ -0,0 +1,707 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.util.*;
import java.io.*;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import com.sun.activation.registries.MailcapFile;
import com.sun.activation.registries.LogSupport;
/**
* MailcapCommandMap extends the CommandMap
* abstract class. It implements a CommandMap whose configuration
* is based on mailcap files
* (<A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>).
* The MailcapCommandMap can be configured both programmatically
* and via configuration files.
* <p>
* <b>Mailcap file search order:</b><p>
* The MailcapCommandMap looks in various places in the user's
* system for mailcap file entries. When requests are made
* to search for commands in the MailcapCommandMap, it searches
* mailcap files in the following order:
* <ol>
* <li> Programatically added entries to the MailcapCommandMap instance.
* <li> The file <code>.mailcap</code> in the user's home directory.
* <li> The file <code>mailcap</code> in the Java runtime.
* <li> The file or resources named <code>META-INF/mailcap</code>.
* <li> The file or resource named <code>META-INF/mailcap.default</code>
* (usually found only in the <code>activation.jar</code> file).
* </ol>
* <p>
* (The current implementation looks for the <code>mailcap</code> file
* in the Java runtime in the directory <code><i>java.home</i>/conf</code>
* if it exists, and otherwise in the directory
* <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value
* of the "java.home" System property. Note that the "conf" directory was
* introduced in JDK 9.)
* <p>
* <b>Mailcap file format:</b><p>
*
* Mailcap files must conform to the mailcap
* file specification (RFC 1524, <i>A User Agent Configuration Mechanism
* For Multimedia Mail Format Information</i>).
* The file format consists of entries corresponding to
* particular MIME types. In general, the specification
* specifies <i>applications</i> for clients to use when they
* themselves cannot operate on the specified MIME type. The
* MailcapCommandMap extends this specification by using a parameter mechanism
* in mailcap files that allows JavaBeans(tm) components to be specified as
* corresponding to particular commands for a MIME type.<p>
*
* When a mailcap file is
* parsed, the MailcapCommandMap recognizes certain parameter signatures,
* specifically those parameter names that begin with <code>x-java-</code>.
* The MailcapCommandMap uses this signature to find
* command entries for inclusion into its registries.
* Parameter names with the form <code>x-java-&lt;name&gt;</code>
* are read by the MailcapCommandMap as identifying a command
* with the name <i>name</i>. When the <i>name</i> is <code>
* content-handler</code> the MailcapCommandMap recognizes the class
* signified by this parameter as a <i>DataContentHandler</i>.
* All other commands are handled generically regardless of command
* name. The command implementation is specified by a fully qualified
* class name of a JavaBean(tm) component. For example; a command for viewing
* some data can be specified as: <code>x-java-view=com.foo.ViewBean</code>.<p>
*
* When the command name is <code>fallback-entry</code>, the value of
* the command may be <code>true</code> or <code>false</code>. An
* entry for a MIME type that includes a parameter of
* <code>x-java-fallback-entry=true</code> defines fallback commands
* for that MIME type that will only be used if no non-fallback entry
* can be found. For example, an entry of the form <code>text/*; ;
* x-java-fallback-entry=true; x-java-view=com.sun.TextViewer</code>
* specifies a view command to be used for any text MIME type. This
* view command would only be used if a non-fallback view command for
* the MIME type could not be found.<p>
*
* MailcapCommandMap aware mailcap files have the
* following general form:<p>
* <code>
* # Comments begin with a '#' and continue to the end of the line.<br>
* &lt;mime type&gt;; ; &lt;parameter list&gt;<br>
* # Where a parameter list consists of one or more parameters,<br>
* # where parameters look like: x-java-view=com.sun.TextViewer<br>
* # and a parameter list looks like: <br>
* text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
* <br>
* # Note that mailcap entries that do not contain 'x-java' parameters<br>
* # and comply to RFC 1524 are simply ignored:<br>
* image/gif; /usr/dt/bin/sdtimage %s<br>
*
* </code>
* <p>
*
* @author Bart Calder
* @author Bill Shannon
*/
public class MailcapCommandMap extends CommandMap {
/*
* We manage a collection of databases, searched in order.
*/
private MailcapFile[] DB;
private static final int PROG = 0; // programmatically added entries
private static final String confDir;
static {
String dir = null;
try {
dir = (String)AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
String home = System.getProperty("java.home");
String newdir = home + File.separator + "conf";
File conf = new File(newdir);
if (conf.exists())
return newdir + File.separator;
else
return home + File.separator + "lib" + File.separator;
}
});
} catch (Exception ex) {
// ignore any exceptions
}
confDir = dir;
}
/**
* The default Constructor.
*/
public MailcapCommandMap() {
super();
List dbv = new ArrayList(5); // usually 5 or less databases
MailcapFile mf = null;
dbv.add(null); // place holder for PROG entry
LogSupport.log("MailcapCommandMap: load HOME");
try {
String user_home = System.getProperty("user.home");
if (user_home != null) {
String path = user_home + File.separator + ".mailcap";
mf = loadFile(path);
if (mf != null)
dbv.add(mf);
}
} catch (SecurityException ex) {}
LogSupport.log("MailcapCommandMap: load SYS");
try {
// check system's home
if (confDir != null) {
mf = loadFile(confDir + "mailcap");
if (mf != null)
dbv.add(mf);
}
} catch (SecurityException ex) {}
LogSupport.log("MailcapCommandMap: load JAR");
// load from the app's jar file
loadAllResources(dbv, "META-INF/mailcap");
LogSupport.log("MailcapCommandMap: load DEF");
mf = loadResource("/META-INF/mailcap.default");
if (mf != null)
dbv.add(mf);
DB = new MailcapFile[dbv.size()];
DB = (MailcapFile[])dbv.toArray(DB);
}
/**
* Load from the named resource.
*/
private MailcapFile loadResource(String name) {
InputStream clis = null;
try {
clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
if (clis != null) {
MailcapFile mf = new MailcapFile(clis);
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: successfully loaded " +
"mailcap file: " + name);
return mf;
} else {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: not loading " +
"mailcap file: " + name);
}
} catch (IOException e) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " + name, e);
} catch (SecurityException sex) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " + name, sex);
} finally {
try {
if (clis != null)
clis.close();
} catch (IOException ex) { } // ignore it
}
return null;
}
/**
* Load all of the named resource.
*/
private void loadAllResources(List v, String name) {
boolean anyLoaded = false;
try {
URL[] urls;
ClassLoader cld = null;
// First try the "application's" class loader.
cld = SecuritySupport.getContextClassLoader();
if (cld == null)
cld = this.getClass().getClassLoader();
if (cld != null)
urls = SecuritySupport.getResources(cld, name);
else
urls = SecuritySupport.getSystemResources(name);
if (urls != null) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: getResources");
for (int i = 0; i < urls.length; i++) {
URL url = urls[i];
InputStream clis = null;
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: URL " + url);
try {
clis = SecuritySupport.openStream(url);
if (clis != null) {
v.add(new MailcapFile(clis));
anyLoaded = true;
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: " +
"successfully loaded " +
"mailcap file from URL: " +
url);
} else {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: " +
"not loading mailcap " +
"file from URL: " + url);
}
} catch (IOException ioex) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " +
url, ioex);
} catch (SecurityException sex) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " +
url, sex);
} finally {
try {
if (clis != null)
clis.close();
} catch (IOException cex) { }
}
}
}
} catch (Exception ex) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: can't load " + name, ex);
}
// if failed to load anything, fall back to old technique, just in case
if (!anyLoaded) {
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: !anyLoaded");
MailcapFile mf = loadResource("/" + name);
if (mf != null)
v.add(mf);
}
}
/**
* Load from the named file.
*/
private MailcapFile loadFile(String name) {
MailcapFile mtf = null;
try {
mtf = new MailcapFile(name);
} catch (IOException e) {
// e.printStackTrace();
}
return mtf;
}
/**
* Constructor that allows the caller to specify the path
* of a <i>mailcap</i> file.
*
* @param fileName The name of the <i>mailcap</i> file to open
* @exception IOException if the file can't be accessed
*/
public MailcapCommandMap(String fileName) throws IOException {
this();
if (LogSupport.isLoggable())
LogSupport.log("MailcapCommandMap: load PROG from " + fileName);
if (DB[PROG] == null) {
DB[PROG] = new MailcapFile(fileName);
}
}
/**
* Constructor that allows the caller to specify an <i>InputStream</i>
* containing a mailcap file.
*
* @param is InputStream of the <i>mailcap</i> file to open
*/
public MailcapCommandMap(InputStream is) {
this();
LogSupport.log("MailcapCommandMap: load PROG");
if (DB[PROG] == null) {
try {
DB[PROG] = new MailcapFile(is);
} catch (IOException ex) {
// XXX - should throw it
}
}
}
/**
* Get the preferred command list for a MIME Type. The MailcapCommandMap
* searches the mailcap files as described above under
* <i>Mailcap file search order</i>.<p>
*
* The result of the search is a proper subset of available
* commands in all mailcap files known to this instance of
* MailcapCommandMap. The first entry for a particular command
* is considered the preferred command.
*
* @param mimeType the MIME type
* @return the CommandInfo objects representing the preferred commands.
*/
public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
List cmdList = new ArrayList();
if (mimeType != null)
mimeType = mimeType.toLowerCase(Locale.ENGLISH);
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
Map cmdMap = DB[i].getMailcapList(mimeType);
if (cmdMap != null)
appendPrefCmdsToList(cmdMap, cmdList);
}
// now add the fallback commands
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
if (cmdMap != null)
appendPrefCmdsToList(cmdMap, cmdList);
}
CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
return cmdInfos;
}
/**
* Put the commands that are in the hash table, into the list.
*/
private void appendPrefCmdsToList(Map cmdHash, List cmdList) {
Iterator verb_enum = cmdHash.keySet().iterator();
while (verb_enum.hasNext()) {
String verb = (String)verb_enum.next();
if (!checkForVerb(cmdList, verb)) {
List cmdList2 = (List)cmdHash.get(verb); // get the list
String className = (String)cmdList2.get(0);
cmdList.add(new CommandInfo(verb, className));
}
}
}
/**
* Check the cmdList to see if this command exists, return
* true if the verb is there.
*/
private boolean checkForVerb(List cmdList, String verb) {
Iterator ee = cmdList.iterator();
while (ee.hasNext()) {
String enum_verb =
(String)((CommandInfo)ee.next()).getCommandName();
if (enum_verb.equals(verb))
return true;
}
return false;
}
/**
* Get all the available commands in all mailcap files known to
* this instance of MailcapCommandMap for this MIME type.
*
* @param mimeType the MIME type
* @return the CommandInfo objects representing all the commands.
*/
public synchronized CommandInfo[] getAllCommands(String mimeType) {
List cmdList = new ArrayList();
if (mimeType != null)
mimeType = mimeType.toLowerCase(Locale.ENGLISH);
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
Map cmdMap = DB[i].getMailcapList(mimeType);
if (cmdMap != null)
appendCmdsToList(cmdMap, cmdList);
}
// now add the fallback commands
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
if (cmdMap != null)
appendCmdsToList(cmdMap, cmdList);
}
CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
return cmdInfos;
}
/**
* Put the commands that are in the hash table, into the list.
*/
private void appendCmdsToList(Map typeHash, List cmdList) {
Iterator verb_enum = typeHash.keySet().iterator();
while (verb_enum.hasNext()) {
String verb = (String)verb_enum.next();
List cmdList2 = (List)typeHash.get(verb);
Iterator cmd_enum = ((List)cmdList2).iterator();
while (cmd_enum.hasNext()) {
String cmd = (String)cmd_enum.next();
cmdList.add(new CommandInfo(verb, cmd));
// cmdList.add(0, new CommandInfo(verb, cmd));
}
}
}
/**
* Get the command corresponding to <code>cmdName</code> for the MIME type.
*
* @param mimeType the MIME type
* @param cmdName the command name
* @return the CommandInfo object corresponding to the command.
*/
public synchronized CommandInfo getCommand(String mimeType,
String cmdName) {
if (mimeType != null)
mimeType = mimeType.toLowerCase(Locale.ENGLISH);
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
Map cmdMap = DB[i].getMailcapList(mimeType);
if (cmdMap != null) {
// get the cmd list for the cmd
List v = (List)cmdMap.get(cmdName);
if (v != null) {
String cmdClassName = (String)v.get(0);
if (cmdClassName != null)
return new CommandInfo(cmdName, cmdClassName);
}
}
}
// now try the fallback list
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
if (cmdMap != null) {
// get the cmd list for the cmd
List v = (List)cmdMap.get(cmdName);
if (v != null) {
String cmdClassName = (String)v.get(0);
if (cmdClassName != null)
return new CommandInfo(cmdName, cmdClassName);
}
}
}
return null;
}
/**
* Add entries to the registry. Programmatically
* added entries are searched before other entries.<p>
*
* The string that is passed in should be in mailcap
* format.
*
* @param mail_cap a correctly formatted mailcap string
*/
public synchronized void addMailcap(String mail_cap) {
// check to see if one exists
LogSupport.log("MailcapCommandMap: add to PROG");
if (DB[PROG] == null)
DB[PROG] = new MailcapFile();
DB[PROG].appendToMailcap(mail_cap);
}
/**
* Return the DataContentHandler for the specified MIME type.
*
* @param mimeType the MIME type
* @return the DataContentHandler
*/
public synchronized DataContentHandler createDataContentHandler(
String mimeType) {
if (LogSupport.isLoggable())
LogSupport.log(
"MailcapCommandMap: createDataContentHandler for " + mimeType);
if (mimeType != null)
mimeType = mimeType.toLowerCase(Locale.ENGLISH);
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
if (LogSupport.isLoggable())
LogSupport.log(" search DB #" + i);
Map cmdMap = DB[i].getMailcapList(mimeType);
if (cmdMap != null) {
List v = (List)cmdMap.get("content-handler");
if (v != null) {
String name = (String)v.get(0);
DataContentHandler dch = getDataContentHandler(name);
if (dch != null)
return dch;
}
}
}
// now try the fallback entries
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
if (LogSupport.isLoggable())
LogSupport.log(" search fallback DB #" + i);
Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
if (cmdMap != null) {
List v = (List)cmdMap.get("content-handler");
if (v != null) {
String name = (String)v.get(0);
DataContentHandler dch = getDataContentHandler(name);
if (dch != null)
return dch;
}
}
}
return null;
}
private DataContentHandler getDataContentHandler(String name) {
if (LogSupport.isLoggable())
LogSupport.log(" got content-handler");
if (LogSupport.isLoggable())
LogSupport.log(" class " + name);
try {
ClassLoader cld = null;
// First try the "application's" class loader.
cld = SecuritySupport.getContextClassLoader();
if (cld == null)
cld = this.getClass().getClassLoader();
Class cl = null;
try {
cl = cld.loadClass(name);
} catch (Exception ex) {
// if anything goes wrong, do it the old way
cl = Class.forName(name);
}
if (cl != null) // XXX - always true?
return (DataContentHandler)cl.newInstance();
} catch (IllegalAccessException e) {
if (LogSupport.isLoggable())
LogSupport.log("Can't load DCH " + name, e);
} catch (ClassNotFoundException e) {
if (LogSupport.isLoggable())
LogSupport.log("Can't load DCH " + name, e);
} catch (InstantiationException e) {
if (LogSupport.isLoggable())
LogSupport.log("Can't load DCH " + name, e);
}
return null;
}
/**
* Get all the MIME types known to this command map.
*
* @return array of MIME types as strings
* @since JAF 1.1
*/
public synchronized String[] getMimeTypes() {
List mtList = new ArrayList();
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
String[] ts = DB[i].getMimeTypes();
if (ts != null) {
for (int j = 0; j < ts.length; j++) {
// eliminate duplicates
if (!mtList.contains(ts[j]))
mtList.add(ts[j]);
}
}
}
String[] mts = new String[mtList.size()];
mts = (String[])mtList.toArray(mts);
return mts;
}
/**
* Get the native commands for the given MIME type.
* Returns an array of strings where each string is
* an entire mailcap file entry. The application
* will need to parse the entry to extract the actual
* command as well as any attributes it needs. See
* <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>
* for details of the mailcap entry syntax. Only mailcap
* entries that specify a view command for the specified
* MIME type are returned.
*
* @param mimeType the MIME type
* @return array of native command entries
* @since JAF 1.1
*/
public synchronized String[] getNativeCommands(String mimeType) {
List cmdList = new ArrayList();
if (mimeType != null)
mimeType = mimeType.toLowerCase(Locale.ENGLISH);
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
String[] cmds = DB[i].getNativeCommands(mimeType);
if (cmds != null) {
for (int j = 0; j < cmds.length; j++) {
// eliminate duplicates
if (!cmdList.contains(cmds[j]))
cmdList.add(cmds[j]);
}
}
}
String[] cmds = new String[cmdList.size()];
cmds = (String[])cmdList.toArray(cmds);
return cmds;
}
/**
* for debugging...
*
public static void main(String[] argv) throws Exception {
MailcapCommandMap map = new MailcapCommandMap();
CommandInfo[] cmdInfo;
cmdInfo = map.getPreferredCommands(argv[0]);
System.out.println("Preferred Commands:");
for (int i = 0; i < cmdInfo.length; i++)
System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
cmdInfo[i].getCommandClass() + "]");
cmdInfo = map.getAllCommands(argv[0]);
System.out.println();
System.out.println("All Commands:");
for (int i = 0; i < cmdInfo.length; i++)
System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
cmdInfo[i].getCommandClass() + "]");
DataContentHandler dch = map.createDataContentHandler(argv[0]);
if (dch != null)
System.out.println("DataContentHandler " +
dch.getClass().toString());
System.exit(0);
}
*/
}

@ -0,0 +1,329 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.*;
import java.util.Locale;
/**
* A Multipurpose Internet Mail Extension (MIME) type, as defined
* in RFC 2045 and 2046.
*/
public class MimeType implements Externalizable {
private String primaryType;
private String subType;
private MimeTypeParameterList parameters;
/**
* A string that holds all the special chars.
*/
private static final String TSPECIALS = "()<>@,;:/[]?=\\\"";
/**
* Default constructor.
*/
public MimeType() {
primaryType = "application";
subType = "*";
parameters = new MimeTypeParameterList();
}
/**
* Constructor that builds a MimeType from a String.
*
* @param rawdata the MIME type string
* @exception MimeTypeParseException if the MIME type can't be parsed
*/
public MimeType(String rawdata) throws MimeTypeParseException {
parse(rawdata);
}
/**
* Constructor that builds a MimeType with the given primary and sub type
* but has an empty parameter list.
*
* @param primary the primary MIME type
* @param sub the MIME sub-type
* @exception MimeTypeParseException if the primary type or subtype
* is not a valid token
*/
public MimeType(String primary, String sub) throws MimeTypeParseException {
// check to see if primary is valid
if (isValidToken(primary)) {
primaryType = primary.toLowerCase(Locale.ENGLISH);
} else {
throw new MimeTypeParseException("Primary type is invalid.");
}
// check to see if sub is valid
if (isValidToken(sub)) {
subType = sub.toLowerCase(Locale.ENGLISH);
} else {
throw new MimeTypeParseException("Sub type is invalid.");
}
parameters = new MimeTypeParameterList();
}
/**
* A routine for parsing the MIME type out of a String.
*/
private void parse(String rawdata) throws MimeTypeParseException {
int slashIndex = rawdata.indexOf('/');
int semIndex = rawdata.indexOf(';');
if ((slashIndex < 0) && (semIndex < 0)) {
// neither character is present, so treat it
// as an error
throw new MimeTypeParseException("Unable to find a sub type.");
} else if ((slashIndex < 0) && (semIndex >= 0)) {
// we have a ';' (and therefore a parameter list),
// but no '/' indicating a sub type is present
throw new MimeTypeParseException("Unable to find a sub type.");
} else if ((slashIndex >= 0) && (semIndex < 0)) {
// we have a primary and sub type but no parameter list
primaryType = rawdata.substring(0, slashIndex).trim().
toLowerCase(Locale.ENGLISH);
subType = rawdata.substring(slashIndex + 1).trim().
toLowerCase(Locale.ENGLISH);
parameters = new MimeTypeParameterList();
} else if (slashIndex < semIndex) {
// we have all three items in the proper sequence
primaryType = rawdata.substring(0, slashIndex).trim().
toLowerCase(Locale.ENGLISH);
subType = rawdata.substring(slashIndex + 1, semIndex).trim().
toLowerCase(Locale.ENGLISH);
parameters = new MimeTypeParameterList(rawdata.substring(semIndex));
} else {
// we have a ';' lexically before a '/' which means we
// have a primary type and a parameter list but no sub type
throw new MimeTypeParseException("Unable to find a sub type.");
}
// now validate the primary and sub types
// check to see if primary is valid
if (!isValidToken(primaryType))
throw new MimeTypeParseException("Primary type is invalid.");
// check to see if sub is valid
if (!isValidToken(subType))
throw new MimeTypeParseException("Sub type is invalid.");
}
/**
* Retrieve the primary type of this object.
*
* @return the primary MIME type
*/
public String getPrimaryType() {
return primaryType;
}
/**
* Set the primary type for this object to the given String.
*
* @param primary the primary MIME type
* @exception MimeTypeParseException if the primary type
* is not a valid token
*/
public void setPrimaryType(String primary) throws MimeTypeParseException {
// check to see if primary is valid
if (!isValidToken(primaryType))
throw new MimeTypeParseException("Primary type is invalid.");
primaryType = primary.toLowerCase(Locale.ENGLISH);
}
/**
* Retrieve the subtype of this object.
*
* @return the MIME subtype
*/
public String getSubType() {
return subType;
}
/**
* Set the subtype for this object to the given String.
*
* @param sub the MIME subtype
* @exception MimeTypeParseException if the subtype
* is not a valid token
*/
public void setSubType(String sub) throws MimeTypeParseException {
// check to see if sub is valid
if (!isValidToken(subType))
throw new MimeTypeParseException("Sub type is invalid.");
subType = sub.toLowerCase(Locale.ENGLISH);
}
/**
* Retrieve this object's parameter list.
*
* @return a MimeTypeParameterList object representing the parameters
*/
public MimeTypeParameterList getParameters() {
return parameters;
}
/**
* Retrieve the value associated with the given name, or null if there
* is no current association.
*
* @param name the parameter name
* @return the paramter's value
*/
public String getParameter(String name) {
return parameters.get(name);
}
/**
* Set the value to be associated with the given name, replacing
* any previous association.
*
* @param name the parameter name
* @param value the paramter's value
*/
public void setParameter(String name, String value) {
parameters.set(name, value);
}
/**
* Remove any value associated with the given name.
*
* @param name the parameter name
*/
public void removeParameter(String name) {
parameters.remove(name);
}
/**
* Return the String representation of this object.
*/
public String toString() {
return getBaseType() + parameters.toString();
}
/**
* Return a String representation of this object
* without the parameter list.
*
* @return the MIME type and sub-type
*/
public String getBaseType() {
return primaryType + "/" + subType;
}
/**
* Determine if the primary and sub type of this object is
* the same as what is in the given type.
*
* @param type the MimeType object to compare with
* @return true if they match
*/
public boolean match(MimeType type) {
return primaryType.equals(type.getPrimaryType())
&& (subType.equals("*")
|| type.getSubType().equals("*")
|| (subType.equals(type.getSubType())));
}
/**
* Determine if the primary and sub type of this object is
* the same as the content type described in rawdata.
*
* @param rawdata the MIME type string to compare with
* @return true if they match
* @exception MimeTypeParseException if the MIME type can't be parsed
*/
public boolean match(String rawdata) throws MimeTypeParseException {
return match(new MimeType(rawdata));
}
/**
* The object implements the writeExternal method to save its contents
* by calling the methods of DataOutput for its primitive values or
* calling the writeObject method of ObjectOutput for objects, strings
* and arrays.
*
* @param out the ObjectOutput object to write to
* @exception IOException Includes any I/O exceptions that may occur
*/
public void writeExternal(ObjectOutput out) throws IOException {
out.writeUTF(toString());
out.flush();
}
/**
* The object implements the readExternal method to restore its
* contents by calling the methods of DataInput for primitive
* types and readObject for objects, strings and arrays. The
* readExternal method must read the values in the same sequence
* and with the same types as were written by writeExternal.
*
* @param in the ObjectInput object to read from
* @exception ClassNotFoundException If the class for an object being
* restored cannot be found.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
try {
parse(in.readUTF());
} catch (MimeTypeParseException e) {
throw new IOException(e.toString());
}
}
// below here be scary parsing related things
/**
* Determine whether or not a given character belongs to a legal token.
*/
private static boolean isTokenChar(char c) {
return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0);
}
/**
* Determine whether or not a given string is a legal token.
*/
private boolean isValidToken(String s) {
int len = s.length();
if (len > 0) {
for (int i = 0; i < len; ++i) {
char c = s.charAt(i);
if (!isTokenChar(c)) {
return false;
}
}
return true;
} else {
return false;
}
}
/**
* A simple parser test,
* for debugging...
*
public static void main(String[] args)
throws MimeTypeParseException, IOException {
for (int i = 0; i < args.length; ++i) {
System.out.println("Original: " + args[i]);
MimeType type = new MimeType(args[i]);
System.out.println("Short: " + type.getBaseType());
System.out.println("Parsed: " + type.toString());
System.out.println();
}
}
*/
}

@ -0,0 +1,324 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Locale;
/**
* A parameter list of a MimeType
* as defined in RFC 2045 and 2046. The Primary type of the
* object must already be stripped off.
*
* @see javax.activation.MimeType
*/
public class MimeTypeParameterList {
private Hashtable parameters;
/**
* A string that holds all the special chars.
*/
private static final String TSPECIALS = "()<>@,;:/[]?=\\\"";
/**
* Default constructor.
*/
public MimeTypeParameterList() {
parameters = new Hashtable();
}
/**
* Constructs a new MimeTypeParameterList with the passed in data.
*
* @param parameterList an RFC 2045, 2046 compliant parameter list.
* @exception MimeTypeParseException if the MIME type can't be parsed
*/
public MimeTypeParameterList(String parameterList)
throws MimeTypeParseException {
parameters = new Hashtable();
// now parse rawdata
parse(parameterList);
}
/**
* A routine for parsing the parameter list out of a String.
*
* @param parameterList an RFC 2045, 2046 compliant parameter list.
* @exception MimeTypeParseException if the MIME type can't be parsed
*/
protected void parse(String parameterList) throws MimeTypeParseException {
if (parameterList == null)
return;
int length = parameterList.length();
if (length <= 0)
return;
int i;
char c;
for (i = skipWhiteSpace(parameterList, 0);
i < length && (c = parameterList.charAt(i)) == ';';
i = skipWhiteSpace(parameterList, i)) {
int lastIndex;
String name;
String value;
// eat the ';'
i++;
// now parse the parameter name
// skip whitespace
i = skipWhiteSpace(parameterList, i);
// tolerate trailing semicolon, even though it violates the spec
if (i >= length)
return;
// find the end of the token char run
lastIndex = i;
while ((i < length) && isTokenChar(parameterList.charAt(i)))
i++;
name = parameterList.substring(lastIndex, i).
toLowerCase(Locale.ENGLISH);
// now parse the '=' that separates the name from the value
i = skipWhiteSpace(parameterList, i);
if (i >= length || parameterList.charAt(i) != '=')
throw new MimeTypeParseException(
"Couldn't find the '=' that separates a " +
"parameter name from its value.");
// eat it and parse the parameter value
i++;
i = skipWhiteSpace(parameterList, i);
if (i >= length)
throw new MimeTypeParseException(
"Couldn't find a value for parameter named " + name);
// now find out whether or not we have a quoted value
c = parameterList.charAt(i);
if (c == '"') {
// yup it's quoted so eat it and capture the quoted string
i++;
if (i >= length)
throw new MimeTypeParseException(
"Encountered unterminated quoted parameter value.");
lastIndex = i;
// find the next unescaped quote
while (i < length) {
c = parameterList.charAt(i);
if (c == '"')
break;
if (c == '\\') {
// found an escape sequence
// so skip this and the
// next character
i++;
}
i++;
}
if (c != '"')
throw new MimeTypeParseException(
"Encountered unterminated quoted parameter value.");
value = unquote(parameterList.substring(lastIndex, i));
// eat the quote
i++;
} else if (isTokenChar(c)) {
// nope it's an ordinary token so it
// ends with a non-token char
lastIndex = i;
while (i < length && isTokenChar(parameterList.charAt(i)))
i++;
value = parameterList.substring(lastIndex, i);
} else {
// it ain't a value
throw new MimeTypeParseException(
"Unexpected character encountered at index " + i);
}
// now put the data into the hashtable
parameters.put(name, value);
}
if (i < length) {
throw new MimeTypeParseException(
"More characters encountered in input than expected.");
}
}
/**
* Return the number of name-value pairs in this list.
*
* @return the number of parameters
*/
public int size() {
return parameters.size();
}
/**
* Determine whether or not this list is empty.
*
* @return true if there are no parameters
*/
public boolean isEmpty() {
return parameters.isEmpty();
}
/**
* Retrieve the value associated with the given name, or null if there
* is no current association.
*
* @param name the parameter name
* @return the parameter's value
*/
public String get(String name) {
return (String)parameters.get(name.trim().toLowerCase(Locale.ENGLISH));
}
/**
* Set the value to be associated with the given name, replacing
* any previous association.
*
* @param name the parameter name
* @param value the parameter's value
*/
public void set(String name, String value) {
parameters.put(name.trim().toLowerCase(Locale.ENGLISH), value);
}
/**
* Remove any value associated with the given name.
*
* @param name the parameter name
*/
public void remove(String name) {
parameters.remove(name.trim().toLowerCase(Locale.ENGLISH));
}
/**
* Retrieve an enumeration of all the names in this list.
*
* @return an enumeration of all parameter names
*/
public Enumeration getNames() {
return parameters.keys();
}
/**
* Return a string representation of this object.
*/
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.ensureCapacity(parameters.size() * 16);
// heuristic: 8 characters per field
Enumeration keys = parameters.keys();
while (keys.hasMoreElements()) {
String key = (String)keys.nextElement();
buffer.append("; ");
buffer.append(key);
buffer.append('=');
buffer.append(quote((String)parameters.get(key)));
}
return buffer.toString();
}
// below here be scary parsing related things
/**
* Determine whether or not a given character belongs to a legal token.
*/
private static boolean isTokenChar(char c) {
return ((c > 040) && (c < 0177)) && (TSPECIALS.indexOf(c) < 0);
}
/**
* return the index of the first non white space character in
* rawdata at or after index i.
*/
private static int skipWhiteSpace(String rawdata, int i) {
int length = rawdata.length();
while ((i < length) && Character.isWhitespace(rawdata.charAt(i)))
i++;
return i;
}
/**
* A routine that knows how and when to quote and escape the given value.
*/
private static String quote(String value) {
boolean needsQuotes = false;
// check to see if we actually have to quote this thing
int length = value.length();
for (int i = 0; (i < length) && !needsQuotes; i++) {
needsQuotes = !isTokenChar(value.charAt(i));
}
if (needsQuotes) {
StringBuffer buffer = new StringBuffer();
buffer.ensureCapacity((int)(length * 1.5));
// add the initial quote
buffer.append('"');
// add the properly escaped text
for (int i = 0; i < length; ++i) {
char c = value.charAt(i);
if ((c == '\\') || (c == '"'))
buffer.append('\\');
buffer.append(c);
}
// add the closing quote
buffer.append('"');
return buffer.toString();
} else {
return value;
}
}
/**
* A routine that knows how to strip the quotes and
* escape sequences from the given value.
*/
private static String unquote(String value) {
int valueLength = value.length();
StringBuffer buffer = new StringBuffer();
buffer.ensureCapacity(valueLength);
boolean escaped = false;
for (int i = 0; i < valueLength; ++i) {
char currentChar = value.charAt(i);
if (!escaped && (currentChar != '\\')) {
buffer.append(currentChar);
} else if (escaped) {
buffer.append(currentChar);
escaped = false;
} else {
escaped = true;
}
}
return buffer.toString();
}
}

@ -0,0 +1,33 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
/**
* A class to encapsulate MimeType parsing related exceptions.
*/
public class MimeTypeParseException extends Exception {
/**
* Constructs a MimeTypeParseException with no specified detail message.
*/
public MimeTypeParseException() {
super();
}
/**
* Constructs a MimeTypeParseException with the specified detail message.
*
* @param s the detail message.
*/
public MimeTypeParseException(String s) {
super(s);
}
}

@ -0,0 +1,343 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.*;
import java.net.*;
import java.util.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import com.sun.activation.registries.MimeTypeFile;
import com.sun.activation.registries.LogSupport;
/**
* This class extends FileTypeMap and provides data typing of files
* via their file extension. It uses the <code>.mime.types</code> format. <p>
*
* <b>MIME types file search order:</b><p>
* The MimetypesFileTypeMap looks in various places in the user's
* system for MIME types file entries. When requests are made
* to search for MIME types in the MimetypesFileTypeMap, it searches
* MIME types files in the following order:
* <ol>
* <li> Programmatically added entries to the MimetypesFileTypeMap instance.
* <li> The file <code>.mime.types</code> in the user's home directory.
* <li> The file <code>mime.types</code> in the Java runtime.
* <li> The file or resources named <code>META-INF/mime.types</code>.
* <li> The file or resource named <code>META-INF/mimetypes.default</code>
* (usually found only in the <code>activation.jar</code> file).
* </ol>
* <p>
* (The current implementation looks for the <code>mime.types</code> file
* in the Java runtime in the directory <code><i>java.home</i>/conf</code>
* if it exists, and otherwise in the directory
* <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value
* of the "java.home" System property. Note that the "conf" directory was
* introduced in JDK 9.)
* <p>
* <b>MIME types file format:</b><p>
*
* <code>
* # comments begin with a '#'<br>
* # the format is &lt;mime type&gt; &lt;space separated file extensions&gt;<br>
* # for example:<br>
* text/plain txt text TXT<br>
* # this would map file.txt, file.text, and file.TXT to<br>
* # the mime type "text/plain"<br>
* </code>
*
* @author Bart Calder
* @author Bill Shannon
*/
public class MimetypesFileTypeMap extends FileTypeMap {
/*
* We manage a collection of databases, searched in order.
*/
private MimeTypeFile[] DB;
private static final int PROG = 0; // programmatically added entries
private static final String defaultType = "application/octet-stream";
private static final String confDir;
static {
String dir = null;
try {
dir = (String)AccessController.doPrivileged(
new PrivilegedAction() {
public Object run() {
String home = System.getProperty("java.home");
String newdir = home + File.separator + "conf";
File conf = new File(newdir);
if (conf.exists())
return newdir + File.separator;
else
return home + File.separator + "lib" + File.separator;
}
});
} catch (Exception ex) {
// ignore any exceptions
}
confDir = dir;
}
/**
* The default constructor.
*/
public MimetypesFileTypeMap() {
Vector dbv = new Vector(5); // usually 5 or less databases
MimeTypeFile mf = null;
dbv.addElement(null); // place holder for PROG entry
LogSupport.log("MimetypesFileTypeMap: load HOME");
try {
String user_home = System.getProperty("user.home");
if (user_home != null) {
String path = user_home + File.separator + ".mime.types";
mf = loadFile(path);
if (mf != null)
dbv.addElement(mf);
}
} catch (SecurityException ex) {}
LogSupport.log("MimetypesFileTypeMap: load SYS");
try {
// check system's home
if (confDir != null) {
mf = loadFile(confDir + "mime.types");
if (mf != null)
dbv.addElement(mf);
}
} catch (SecurityException ex) {}
LogSupport.log("MimetypesFileTypeMap: load JAR");
// load from the app's jar file
loadAllResources(dbv, "META-INF/mime.types");
LogSupport.log("MimetypesFileTypeMap: load DEF");
mf = loadResource("/META-INF/mimetypes.default");
if (mf != null)
dbv.addElement(mf);
DB = new MimeTypeFile[dbv.size()];
dbv.copyInto(DB);
}
/**
* Load from the named resource.
*/
private MimeTypeFile loadResource(String name) {
InputStream clis = null;
try {
clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
if (clis != null) {
MimeTypeFile mf = new MimeTypeFile(clis);
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: successfully " +
"loaded mime types file: " + name);
return mf;
} else {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: not loading " +
"mime types file: " + name);
}
} catch (IOException e) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " + name, e);
} catch (SecurityException sex) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " + name, sex);
} finally {
try {
if (clis != null)
clis.close();
} catch (IOException ex) { } // ignore it
}
return null;
}
/**
* Load all of the named resource.
*/
private void loadAllResources(Vector v, String name) {
boolean anyLoaded = false;
try {
URL[] urls;
ClassLoader cld = null;
// First try the "application's" class loader.
cld = SecuritySupport.getContextClassLoader();
if (cld == null)
cld = this.getClass().getClassLoader();
if (cld != null)
urls = SecuritySupport.getResources(cld, name);
else
urls = SecuritySupport.getSystemResources(name);
if (urls != null) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: getResources");
for (int i = 0; i < urls.length; i++) {
URL url = urls[i];
InputStream clis = null;
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: URL " + url);
try {
clis = SecuritySupport.openStream(url);
if (clis != null) {
v.addElement(new MimeTypeFile(clis));
anyLoaded = true;
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: " +
"successfully loaded " +
"mime types from URL: " + url);
} else {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: " +
"not loading " +
"mime types from URL: " + url);
}
} catch (IOException ioex) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " +
url, ioex);
} catch (SecurityException sex) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " +
url, sex);
} finally {
try {
if (clis != null)
clis.close();
} catch (IOException cex) { }
}
}
}
} catch (Exception ex) {
if (LogSupport.isLoggable())
LogSupport.log("MimetypesFileTypeMap: can't load " + name, ex);
}
// if failed to load anything, fall back to old technique, just in case
if (!anyLoaded) {
LogSupport.log("MimetypesFileTypeMap: !anyLoaded");
MimeTypeFile mf = loadResource("/" + name);
if (mf != null)
v.addElement(mf);
}
}
/**
* Load the named file.
*/
private MimeTypeFile loadFile(String name) {
MimeTypeFile mtf = null;
try {
mtf = new MimeTypeFile(name);
} catch (IOException e) {
// e.printStackTrace();
}
return mtf;
}
/**
* Construct a MimetypesFileTypeMap with programmatic entries
* added from the named file.
*
* @param mimeTypeFileName the file name
* @exception IOException for errors reading the file
*/
public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException {
this();
DB[PROG] = new MimeTypeFile(mimeTypeFileName);
}
/**
* Construct a MimetypesFileTypeMap with programmatic entries
* added from the InputStream.
*
* @param is the input stream to read from
*/
public MimetypesFileTypeMap(InputStream is) {
this();
try {
DB[PROG] = new MimeTypeFile(is);
} catch (IOException ex) {
// XXX - really should throw it
}
}
/**
* Prepend the MIME type values to the registry.
*
* @param mime_types A .mime.types formatted string of entries.
*/
public synchronized void addMimeTypes(String mime_types) {
// check to see if we have created the registry
if (DB[PROG] == null)
DB[PROG] = new MimeTypeFile(); // make one
DB[PROG].appendToRegistry(mime_types);
}
/**
* Return the MIME type of the file object.
* The implementation in this class calls
* <code>getContentType(f.getName())</code>.
*
* @param f the file
* @return the file's MIME type
*/
public String getContentType(File f) {
return this.getContentType(f.getName());
}
/**
* Return the MIME type based on the specified file name.
* The MIME type entries are searched as described above under
* <i>MIME types file search order</i>.
* If no entry is found, the type "application/octet-stream" is returned.
*
* @param filename the file name
* @return the file's MIME type
*/
public synchronized String getContentType(String filename) {
int dot_pos = filename.lastIndexOf("."); // period index
if (dot_pos < 0)
return defaultType;
String file_ext = filename.substring(dot_pos + 1);
if (file_ext.length() == 0)
return defaultType;
for (int i = 0; i < DB.length; i++) {
if (DB[i] == null)
continue;
String result = DB[i].getMIMETypeString(file_ext);
if (result != null)
return result;
}
return defaultType;
}
/**
* for debugging...
*
public static void main(String[] argv) throws Exception {
MimetypesFileTypeMap map = new MimetypesFileTypeMap();
System.out.println("File " + argv[0] + " has MIME type " +
map.getContentType(argv[0]));
System.exit(0);
}
*/
}

@ -0,0 +1,114 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.security.*;
import java.net.*;
import java.io.*;
import java.util.*;
/**
* Security related methods that only work on J2SE 1.2 and newer.
*/
class SecuritySupport {
private SecuritySupport() {
// private constructor, can't create an instance
}
public static ClassLoader getContextClassLoader() {
return (ClassLoader)
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
} catch (SecurityException ex) { }
return cl;
}
});
}
public static InputStream getResourceAsStream(final Class c,
final String name) throws IOException {
try {
return (InputStream)
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws IOException {
return c.getResourceAsStream(name);
}
});
} catch (PrivilegedActionException e) {
throw (IOException)e.getException();
}
}
public static URL[] getResources(final ClassLoader cl, final String name) {
return (URL[])
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL[] ret = null;
try {
List v = new ArrayList();
Enumeration e = cl.getResources(name);
while (e != null && e.hasMoreElements()) {
URL url = (URL)e.nextElement();
if (url != null)
v.add(url);
}
if (v.size() > 0) {
ret = new URL[v.size()];
ret = (URL[])v.toArray(ret);
}
} catch (IOException ioex) {
} catch (SecurityException ex) { }
return ret;
}
});
}
public static URL[] getSystemResources(final String name) {
return (URL[])
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
URL[] ret = null;
try {
List v = new ArrayList();
Enumeration e = ClassLoader.getSystemResources(name);
while (e != null && e.hasMoreElements()) {
URL url = (URL)e.nextElement();
if (url != null)
v.add(url);
}
if (v.size() > 0) {
ret = new URL[v.size()];
ret = (URL[])v.toArray(ret);
}
} catch (IOException ioex) {
} catch (SecurityException ex) { }
return ret;
}
});
}
public static InputStream openStream(final URL url) throws IOException {
try {
return (InputStream)
AccessController.doPrivileged(new PrivilegedExceptionAction() {
public Object run() throws IOException {
return url.openStream();
}
});
} catch (PrivilegedActionException e) {
throw (IOException)e.getException();
}
}
}

@ -0,0 +1,120 @@
/*
* Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.net.URL;
import java.net.URLConnection;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
/**
* The URLDataSource class provides an object that wraps a <code>URL</code>
* object in a DataSource interface. URLDataSource simplifies the handling
* of data described by URLs within Jakarta Activation
* because this class can be used to create new DataHandlers. <i>NOTE: The
* DataHandler object creates a URLDataSource internally,
* when it is constructed with a URL.</i>
*
* @see javax.activation.DataSource
* @see javax.activation.DataHandler
*/
public class URLDataSource implements DataSource {
private URL url = null;
private URLConnection url_conn = null;
/**
* URLDataSource constructor. The URLDataSource class will
* not open a connection to the URL until a method requiring it
* to do so is called.
*
* @param url The URL to be encapsulated in this object.
*/
public URLDataSource(URL url) {
this.url = url;
}
/**
* Returns the value of the URL content-type header field.
* It calls the URL's <code>URLConnection.getContentType</code> method
* after retrieving a URLConnection object.
* <i>Note: this method attempts to call the <code>openConnection</code>
* method on the URL. If this method fails, or if a content type is not
* returned from the URLConnection, getContentType returns
* "application/octet-stream" as the content type.</i>
*
* @return the content type.
*/
public String getContentType() {
String type = null;
try {
if (url_conn == null)
url_conn = url.openConnection();
} catch (IOException e) { }
if (url_conn != null)
type = url_conn.getContentType();
if (type == null)
type = "application/octet-stream";
return type;
}
/**
* Calls the <code>getFile</code> method on the URL used to
* instantiate the object.
*
* @return the result of calling the URL's getFile method.
*/
public String getName() {
return url.getFile();
}
/**
* The getInputStream method from the URL. Calls the
* <code>openStream</code> method on the URL.
*
* @return the InputStream.
*/
public InputStream getInputStream() throws IOException {
return url.openStream();
}
/**
* The getOutputStream method from the URL. First an attempt is
* made to get the URLConnection object for the URL. If that
* succeeds, the getOutputStream method on the URLConnection
* is returned.
*
* @return the OutputStream.
*/
public OutputStream getOutputStream() throws IOException {
// get the url connection if it is available
url_conn = url.openConnection();
if (url_conn != null) {
url_conn.setDoOutput(true);
return url_conn.getOutputStream();
} else
return null;
}
/**
* Return the URL used to create this DataSource.
*
* @return The URL.
*/
public URL getURL() {
return url;
}
}

@ -0,0 +1,40 @@
/*
* Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Distribution License v. 1.0, which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
package javax.activation;
import java.io.IOException;
/**
* Signals that the requested operation does not support the
* requested data type.
*
* @see javax.activation.DataHandler
*/
public class UnsupportedDataTypeException extends IOException {
/**
* Constructs an UnsupportedDataTypeException with no detail
* message.
*/
public UnsupportedDataTypeException() {
super();
}
/**
* Constructs an UnsupportedDataTypeException with the specified
* message.
*
* @param s The detail message.
*/
public UnsupportedDataTypeException(String s) {
super(s);
}
}

@ -0,0 +1,26 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<!--
Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved.
This program and the accompanying materials are made available under the
terms of the Eclipse Distribution License v. 1.0, which is available at
http://www.eclipse.org/org/documents/edl-v10.php.
SPDX-License-Identifier: BSD-3-Clause
-->
<TITLE>javax.activation package</TITLE>
</HEAD>
<BODY BGCOLOR="white">
<P>
Jakarta Activation is used by Jakarta Mail to manage MIME data.
</P>
</BODY>
</HTML>

@ -0,0 +1,9 @@
#
# This is a very simple 'mailcap' file
#
# No default viewers are included so these entries are commented out.
#
#image/gif;; x-java-view=com.sun.activation.viewers.ImageViewer
#image/jpeg;; x-java-view=com.sun.activation.viewers.ImageViewer
#text/*;; x-java-view=com.sun.activation.viewers.TextViewer
#text/*;; x-java-edit=com.sun.activation.viewers.TextEditor

@ -0,0 +1,25 @@
#
# A simple, old format, mime.types file
#
text/html html htm HTML HTM
text/plain txt text TXT TEXT
image/gif gif GIF
image/ief ief
image/jpeg jpeg jpg jpe JPG
image/tiff tiff tif
image/png png PNG
image/x-xwindowdump xwd
application/postscript ai eps ps
application/rtf rtf
application/x-tex tex
application/x-texinfo texinfo texi
application/x-troff t tr roff
audio/basic au
audio/midi midi mid
audio/x-aifc aifc
audio/x-aiff aif aiff
audio/x-mpeg mpeg mpg
audio/x-wav wav
video/mpeg mpeg mpg mpe
video/quicktime qt mov
video/x-msvideo avi
Loading…
Cancel
Save