diff --git a/app/build.gradle b/app/build.gradle index 2df5f8580d..ecab02eb47 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/com/sun/activation/registries/LogSupport.java b/app/src/main/java/com/sun/activation/registries/LogSupport.java new file mode 100644 index 0000000000..3228f3c790 --- /dev/null +++ b/app/src/main/java/com/sun/activation/registries/LogSupport.java @@ -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); + } +} diff --git a/app/src/main/java/com/sun/activation/registries/MailcapFile.java b/app/src/main/java/com/sun/activation/registries/MailcapFile.java new file mode 100644 index 0000000000..afcb5d6ef2 --- /dev/null +++ b/app/src/main/java/com/sun/activation/registries/MailcapFile.java @@ -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. + * + *
+ * Semantics: First check for the literal mime type, + * if that fails looks for wildcard type/* and return that. Return the + * list of all that hit. + */ + public Map getMailcapList(String mime_type) { + Map search_result = null; + Map wildcard_result = null; + + // first try the literal + search_result = (Map)type_hash.get(mime_type); + + // ok, now try the wildcard + int separator = mime_type.indexOf('/'); + String subtype = mime_type.substring(separator + 1); + if (!subtype.equals("*")) { + String type = mime_type.substring(0, separator + 1) + "*"; + wildcard_result = (Map)type_hash.get(type); + + if (wildcard_result != null) { // damn, we have to merge!!! + if (search_result != null) + search_result = + mergeResults(search_result, wildcard_result); + else + search_result = wildcard_result; + } + } + return search_result; + } + + /** + * Get the Map of fallback MailcapEntries based on the MIME type. + * + *
+ * Semantics: First check for the literal mime type,
+ * if that fails looks for wildcard type/* and return that. Return the
+ * list of all that hit.
+ */
+ public Map getMailcapFallbackList(String mime_type) {
+ Map search_result = null;
+ Map wildcard_result = null;
+
+ // first try the literal
+ search_result = (Map)fallback_hash.get(mime_type);
+
+ // ok, now try the wildcard
+ int separator = mime_type.indexOf('/');
+ String subtype = mime_type.substring(separator + 1);
+ if (!subtype.equals("*")) {
+ String type = mime_type.substring(0, separator + 1) + "*";
+ wildcard_result = (Map)fallback_hash.get(type);
+
+ if (wildcard_result != null) { // damn, we have to merge!!!
+ if (search_result != null)
+ search_result =
+ mergeResults(search_result, wildcard_result);
+ else
+ search_result = wildcard_result;
+ }
+ }
+ return search_result;
+ }
+
+ /**
+ * Return all the MIME types known to this mailcap file.
+ */
+ public String[] getMimeTypes() {
+ Set types = new HashSet(type_hash.keySet());
+ types.addAll(fallback_hash.keySet());
+ types.addAll(native_commands.keySet());
+ String[] mts = new String[types.size()];
+ mts = (String[])types.toArray(mts);
+ return mts;
+ }
+
+ /**
+ * Return all the native comands for the given MIME type.
+ */
+ public String[] getNativeCommands(String mime_type) {
+ String[] cmds = null;
+ List v =
+ (List)native_commands.get(mime_type.toLowerCase(Locale.ENGLISH));
+ if (v != null) {
+ cmds = new String[v.size()];
+ cmds = (String[])v.toArray(cmds);
+ }
+ return cmds;
+ }
+
+ /**
+ * Merge the first hash into the second.
+ * This merge will only effect the hashtable that is
+ * returned, we don't want to touch the one passed in since
+ * its integrity must be maintained.
+ */
+ private Map mergeResults(Map first, Map second) {
+ Iterator verb_enum = second.keySet().iterator();
+ Map clonedHash = new HashMap(first);
+
+ // iterate through the verbs in the second map
+ while (verb_enum.hasNext()) {
+ String verb = (String)verb_enum.next();
+ List cmdVector = (List)clonedHash.get(verb);
+ if (cmdVector == null) {
+ clonedHash.put(verb, second.get(verb));
+ } else {
+ // merge the two
+ List oldV = (List)second.get(verb);
+ cmdVector = new ArrayList(cmdVector);
+ cmdVector.addAll(oldV);
+ clonedHash.put(verb, cmdVector);
+ }
+ }
+ return clonedHash;
+ }
+
+ /**
+ * appendToMailcap: Append to this Mailcap DB, use the mailcap
+ * format:
+ * Comment == "# comment string
+ * Entry == "mimetype; javabeanclass
+ *
+ * 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("");
+ }
+ }
+ */
+}
diff --git a/app/src/main/java/com/sun/activation/registries/MailcapParseException.java b/app/src/main/java/com/sun/activation/registries/MailcapParseException.java
new file mode 100644
index 0000000000..754c405e5d
--- /dev/null
+++ b/app/src/main/java/com/sun/activation/registries/MailcapParseException.java
@@ -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);
+ }
+}
diff --git a/app/src/main/java/com/sun/activation/registries/MailcapTokenizer.java b/app/src/main/java/com/sun/activation/registries/MailcapTokenizer.java
new file mode 100644
index 0000000000..6437930e54
--- /dev/null
+++ b/app/src/main/java/com/sun/activation/registries/MailcapTokenizer.java
@@ -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("");
+ }
+ }
+ */
+}
diff --git a/app/src/main/java/com/sun/activation/registries/MimeTypeEntry.java b/app/src/main/java/com/sun/activation/registries/MimeTypeEntry.java
new file mode 100644
index 0000000000..3582f779dd
--- /dev/null
+++ b/app/src/main/java/com/sun/activation/registries/MimeTypeEntry.java
@@ -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;
+ }
+}
diff --git a/app/src/main/java/com/sun/activation/registries/MimeTypeFile.java b/app/src/main/java/com/sun/activation/registries/MimeTypeFile.java
new file mode 100644
index 0000000000..469279125c
--- /dev/null
+++ b/app/src/main/java/com/sun/activation/registries/MimeTypeFile.java
@@ -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.
+ *
+ *
+ * @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 true
if there are more tokens available from this
+ * tokenizer's string; false
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);
+ }
+}
diff --git a/app/src/main/java/javax/activation/ActivationDataFlavor.java b/app/src/main/java/javax/activation/ActivationDataFlavor.java
new file mode 100644
index 0000000000..9e62dae742
--- /dev/null
+++ b/app/src/main/java/javax/activation/ActivationDataFlavor.java
@@ -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
+ * java.awt.datatransfer.DataFlavor
. 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 equals
+ *
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.
+ *
+ * The returned DataFlavor will have the following characteristics: + *
+ * representationClass = representationClass
+ * mimeType = mimeType
+ * humanName = humanName
+ *
+ * + * @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. + *
+ * The returned DataFlavor will have the following characteristics: + *
+ * If the mimeType is "application/x-java-serialized-object; + * class=", the result is the same as calling new + * DataFlavor(Class.forName()) as above. + *
+ * otherwise: + *
+ * representationClass = InputStream
+ * mimeType = mimeType
+ * + * @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. + *
+ * The returned DataFlavor will have the following characteristics: + *
+ * If the mimeType is "application/x-java-serialized-object; class=", + * the result is the same as calling new DataFlavor(Class.forName()) as + * above, otherwise: + *
+ * representationClass = InputStream
+ * 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 isMimeTypeEqual
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.
+ * + * 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). + *
+ * 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; + } +} diff --git a/app/src/main/java/javax/activation/CommandInfo.java b/app/src/main/java/javax/activation/CommandInfo.java new file mode 100644 index 0000000000..792c582b19 --- /dev/null +++ b/app/src/main/java/javax/activation/CommandInfo.java @@ -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 it is not guaranteed to + * return a valid value. 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. This method MAY return null in + * cases where a CommandMap subclassed CommandInfo for its + * own purposes. In other words, it might not be possible to + * create the correct state in the command by merely knowing + * its class name. DO NOT DEPEND ON THIS METHOD RETURNING + * A VALID VALUE! + * + * @return The class name of the command, or null + */ + public String getCommandClass() { + return className; + } + + /** + * Return the instantiated JavaBean component. + *
+ * 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}. + *
+ * 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. + *
+ * If the bean implements the javax.activation.CommandObject
+ * interface, call its setCommandContext
method.
+ *
+ * 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. + *
+ * 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.
+ *
+ * @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;
+ }
+ }
+}
diff --git a/app/src/main/java/javax/activation/CommandMap.java b/app/src/main/java/javax/activation/CommandMap.java
new file mode 100644
index 0000000000..69f4910dec
--- /dev/null
+++ b/app/src/main/java/javax/activation/CommandMap.java
@@ -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
+ *
+ * The
+ *
+ * The
+ *
+ * The
+ *
+ * The
+ *
+ * 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
+ *
+ * DataHandler and the Transferable Interface
+ * 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.
+ *
+ * DataHandler and CommandMaps
+ * The DataHandler keeps track of the current CommandMap that it uses to
+ * service requests for commands (
+ *
+ * DataHandler and URLs
+ * 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
+ * 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 not
+ * 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
+ *
+ * For DataHandlers instantiated with a DataSource, the DataHandler
+ * calls the
+ * 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
+ *
+ * @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
+ *
+ * If the DataHandler was created with a DataSource, writeTo
+ * retrieves the InputStream and copies the bytes from the
+ * InputStream to the OutputStream passed in.
+ *
+ * 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
+ *
+ *
+ * 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.
+ *
+ * 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
+ *
+ *
+ * If a DataContentHandler can not 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
+ *
+ * This method iterates through the DataFlavors returned from
+ *
+ *
+ * For DataHandler's created with DataSources or URLs:
+ *
+ * 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
+ *
+ * For DataHandler's created with Objects:
+ *
+ * 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
+ *
+ * If the DataHandler was instantiated with an object, return
+ * the object.
+ *
+ * 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
+ *
+ * This method calls the CommandInfo's
+ *
+ * If a DataContentHandlerFactory is set, use it.
+ * Otherwise look for an object to serve DCH in the
+ * following order:
+ *
+ * 1) if a factory is set, use it
+ * 2) if a CommandMap is set, use it
+ * 3) use the default CommandMap
+ *
+ * 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.
+ *
+ * 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
+ *
+ * FileDataSource Typing Semantics
+ *
+ * The FileDataSource class delegates data typing of files
+ * to an object subclassed from the FileTypeMap class.
+ * The
+ * Mailcap file search order:
+ * 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:
+ *
+ * (The current implementation looks for the
+ * Mailcap file format:
+ *
+ * Mailcap files must conform to the mailcap
+ * file specification (RFC 1524, A User Agent Configuration Mechanism
+ * For Multimedia Mail Format Information).
+ * The file format consists of entries corresponding to
+ * particular MIME types. In general, the specification
+ * specifies applications 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.
+ *
+ * When a mailcap file is
+ * parsed, the MailcapCommandMap recognizes certain parameter signatures,
+ * specifically those parameter names that begin with
+ *
+ * When the command name is
+ *
+ * MailcapCommandMap aware mailcap files have the
+ * following general form:
+ *
+ *
+ * @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 mailcap file.
+ *
+ * @param fileName The name of the mailcap 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 InputStream
+ * containing a mailcap file.
+ *
+ * @param is InputStream of the mailcap 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
+ * Mailcap file search order.
+ *
+ * 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
+ *
+ * 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
+ * RFC 1524
+ * 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);
+ }
+ */
+}
diff --git a/app/src/main/java/javax/activation/MimeType.java b/app/src/main/java/javax/activation/MimeType.java
new file mode 100644
index 0000000000..49fe6e556b
--- /dev/null
+++ b/app/src/main/java/javax/activation/MimeType.java
@@ -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();
+ }
+ }
+ */
+}
diff --git a/app/src/main/java/javax/activation/MimeTypeParameterList.java b/app/src/main/java/javax/activation/MimeTypeParameterList.java
new file mode 100644
index 0000000000..629806398e
--- /dev/null
+++ b/app/src/main/java/javax/activation/MimeTypeParameterList.java
@@ -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();
+ }
+}
diff --git a/app/src/main/java/javax/activation/MimeTypeParseException.java b/app/src/main/java/javax/activation/MimeTypeParseException.java
new file mode 100644
index 0000000000..fd000e2c76
--- /dev/null
+++ b/app/src/main/java/javax/activation/MimeTypeParseException.java
@@ -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);
+ }
+}
diff --git a/app/src/main/java/javax/activation/MimetypesFileTypeMap.java b/app/src/main/java/javax/activation/MimetypesFileTypeMap.java
new file mode 100644
index 0000000000..e2dffb788f
--- /dev/null
+++ b/app/src/main/java/javax/activation/MimetypesFileTypeMap.java
@@ -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
+ *
+ * MIME types file search order:
+ * 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:
+ *
+ * (The current implementation looks for the
+ * MIME types file format:
+ *
+ *
+Jakarta Activation is used by Jakarta Mail to manage MIME data.
+
+ *
+ *
+ * @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 MailcapCommandMap
and
+ * set that to the default, returning its value.
+ *
+ * null
.
+ *
+ * @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. DataSource
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 getPreferredCommands
+ * 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. DataSource
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 getAllCommands
+ * 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. DataSource
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 getCommand
+ * 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. DataSource
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 createDataContentHandler
+ * 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;
+ }
+}
diff --git a/app/src/main/java/javax/activation/CommandObject.java b/app/src/main/java/javax/activation/CommandObject.java
new file mode 100644
index 0000000000..e44913bb20
--- /dev/null
+++ b/app/src/main/java/javax/activation/CommandObject.java
@@ -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. NOTE: it is acceptable for the caller
+ * to pass null as the value for DataHandler
.
+ *
+ * @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;
+}
diff --git a/app/src/main/java/javax/activation/DataContentHandler.java b/app/src/main/java/javax/activation/DataContentHandler.java
new file mode 100644
index 0000000000..ae786531a4
--- /dev/null
+++ b/app/src/main/java/javax/activation/DataContentHandler.java
@@ -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 DataContentHandlers
+ * the framework can be extended to convert streams in to objects, and
+ * to write objects to streams. getTransferDataFlavors
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;
+}
diff --git a/app/src/main/java/javax/activation/DataContentHandlerFactory.java b/app/src/main/java/javax/activation/DataContentHandlerFactory.java
new file mode 100644
index 0000000000..0cd0f5c23f
--- /dev/null
+++ b/app/src/main/java/javax/activation/DataContentHandlerFactory.java
@@ -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 DataContentHandlers
. 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
+ * java.net.URL
.
+ */
+
+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 DataContentHandler
, or null
+ * if none are found.
+ */
+ public DataContentHandler createDataContentHandler(String mimeType);
+}
diff --git a/app/src/main/java/javax/activation/DataHandler.java b/app/src/main/java/javax/activation/DataHandler.java
new file mode 100644
index 0000000000..40a842d956
--- /dev/null
+++ b/app/src/main/java/javax/activation/DataHandler.java
@@ -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. getCommand
,
+ * getAllCommands
, getPreferredCommands
).
+ * Each instance of a DataHandler may have a CommandMap associated with
+ * it using the setCommandMap
method. If a CommandMap was
+ * not set, DataHandler calls the getDefaultCommandMap
+ * method in CommandMap and uses the value it returns. See
+ * CommandMap for more information. DataHandler
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 DataHandler
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 DataHandler
instance referencing a URL.
+ * The DataHandler internally creates a URLDataSource
+ * 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.
+ * DataSource.getName
method, otherwise it
+ * returns null.
+ *
+ * @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 full
+ * 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. DataSource.getInputStream
method and
+ * returns the result to the caller.
+ * writeTo
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.OutputStream
.writeTo
method on the DataContentHandler
.
+ *
+ * @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 getOutputStream
method is called.
+ * Otherwise, null
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. getTransferDataFlavors
method. java.io.InputStream
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.getTransferDataFlavors
, 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.getTransferData
+ * method. If the DataHandler fails to locate a DataContentHandler
+ * and the flavor specifies this object's MIME type and the
+ * java.io.InputStream
class, this object's InputStream
+ * is returned.
+ * Otherwise it throws an UnsupportedFlavorException. null
causes the CommandMap to revert
+ * to the CommandMap returned by the
+ * CommandMap.getDefaultCommandMap
method.
+ * Changing the CommandMap, or setting it to null
,
+ * 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 preferred commands for this type of data.
+ * This method calls the getPreferredCommands
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 getAllCommands
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 cmdName. 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 getCommand
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. DataContentHandler
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.
+ * getCommandObject
+ * method with the ClassLoader
used to load
+ * the javax.activation.DataHandler
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: InputStream
representing this object.
+ * @return the InputStream
+ */
+ public InputStream getInputStream() throws IOException {
+ return dataHandler.getInputStream();
+ }
+
+ /**
+ * Returns the OutputStream
for this object.
+ * @return the OutputStream
+ */
+ 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 private 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 DataContentHandler
.
+ * @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 private 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 DataContentHandler
.
+ * @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);
+ }
+}
diff --git a/app/src/main/java/javax/activation/DataSource.java b/app/src/main/java/javax/activation/DataSource.java
new file mode 100644
index 0000000000..320d8ae4bf
--- /dev/null
+++ b/app/src/main/java/javax/activation/DataSource.java
@@ -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 InputStreams
and
+ * OutputStreams
where appropriate.
+ */
+
+public interface DataSource {
+
+ /**
+ * This method returns an InputStream
representing
+ * the data and throws the appropriate exception if it can
+ * not do so. Note that a new InputStream
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 OutputStream
where the
+ * data can be written and throws the appropriate exception if it can
+ * not do so. Note that a new OutputStream
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 name 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();
+}
diff --git a/app/src/main/java/javax/activation/FileDataSource.java b/app/src/main/java/javax/activation/FileDataSource.java
new file mode 100644
index 0000000000..3048918199
--- /dev/null
+++ b/app/src/main/java/javax/activation/FileDataSource.java
@@ -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. setFileTypeMap
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. Note:
+ * The file will not actually be opened until a method is
+ * called that requires the file to be opened.
+ *
+ * @param file the file
+ */
+ public FileDataSource(File file) {
+ _file = file; // save the file Object...
+ }
+
+ /**
+ * Creates a FileDataSource from
+ * the specified path name. Note:
+ * The file will not actually be opened until a method is
+ * called that requires the file to be opened.
+ *
+ * @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 getDefaultFileTypeMap
method on
+ * FileTypeMap to acquire a default FileTypeMap. Note: By
+ * default, the FileTypeMap used will be a MimetypesFileTypeMap.
+ *
+ * @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 name 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;
+ }
+}
diff --git a/app/src/main/java/javax/activation/FileTypeMap.java b/app/src/main/java/javax/activation/FileTypeMap.java
new file mode 100644
index 0000000000..683f9988f5
--- /dev/null
+++ b/app/src/main/java/javax/activation/FileTypeMap.java
@@ -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 MapMimetypesFileTypeMap
.
+ *
+ * @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;
+ }
+}
diff --git a/app/src/main/java/javax/activation/MailcapCommandMap.java b/app/src/main/java/javax/activation/MailcapCommandMap.java
new file mode 100644
index 0000000000..f5e0672d78
--- /dev/null
+++ b/app/src/main/java/javax/activation/MailcapCommandMap.java
@@ -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
+ * (RFC 1524).
+ * The MailcapCommandMap can be configured both programmatically
+ * and via configuration files.
+ *
+ *
+ * .mailcap
in the user's home directory.
+ * mailcap
in the Java runtime.
+ * META-INF/mailcap
.
+ * META-INF/mailcap.default
+ * (usually found only in the activation.jar
file).
+ * mailcap
file
+ * in the Java runtime in the directory java.home/conf
+ * if it exists, and otherwise in the directory
+ * java.home/lib
, where java.home is the value
+ * of the "java.home" System property. Note that the "conf" directory was
+ * introduced in JDK 9.)
+ * x-java-
.
+ * The MailcapCommandMap uses this signature to find
+ * command entries for inclusion into its registries.
+ * Parameter names with the form x-java-<name>
+ * are read by the MailcapCommandMap as identifying a command
+ * with the name name. When the name is
+ * content-handler
the MailcapCommandMap recognizes the class
+ * signified by this parameter as a DataContentHandler.
+ * 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: x-java-view=com.foo.ViewBean
.fallback-entry
, the value of
+ * the command may be true
or false
. An
+ * entry for a MIME type that includes a parameter of
+ * x-java-fallback-entry=true
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 text/*; ;
+ * x-java-fallback-entry=true; x-java-view=com.sun.TextViewer
+ * 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.
+ * # Comments begin with a '#' and continue to the end of the line.
+ *
+ * <mime type>; ; <parameter list>
+ * # Where a parameter list consists of one or more parameters,
+ * # where parameters look like: x-java-view=com.sun.TextViewer
+ * # and a parameter list looks like:
+ * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
+ *
+ * # Note that mailcap entries that do not contain 'x-java' parameters
+ * # and comply to RFC 1524 are simply ignored:
+ * image/gif; /usr/dt/bin/sdtimage %s
+ *
+ * cmdName
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..mime.types
format.
+ *
+ * .mime.types
in the user's home directory.
+ * mime.types
in the Java runtime.
+ * META-INF/mime.types
.
+ * META-INF/mimetypes.default
+ * (usually found only in the activation.jar
file).
+ * mime.types
file
+ * in the Java runtime in the directory java.home/conf
+ * if it exists, and otherwise in the directory
+ * java.home/lib
, where java.home is the value
+ * of the "java.home" System property. Note that the "conf" directory was
+ * introduced in JDK 9.)
+ *
+ * # comments begin with a '#'
+ *
+ * @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
+ *
+ * # the format is <mime type> <space separated file extensions>
+ * # for example:
+ * text/plain txt text TXT
+ * # this would map file.txt, file.text, and file.TXT to
+ * # the mime type "text/plain"
+ * getContentType(f.getName())
.
+ *
+ * @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
+ * MIME types file search order.
+ * 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);
+ }
+ */
+}
diff --git a/app/src/main/java/javax/activation/SecuritySupport.java b/app/src/main/java/javax/activation/SecuritySupport.java
new file mode 100644
index 0000000000..c37174bb67
--- /dev/null
+++ b/app/src/main/java/javax/activation/SecuritySupport.java
@@ -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();
+ }
+ }
+}
diff --git a/app/src/main/java/javax/activation/URLDataSource.java b/app/src/main/java/javax/activation/URLDataSource.java
new file mode 100644
index 0000000000..c65805f839
--- /dev/null
+++ b/app/src/main/java/javax/activation/URLDataSource.java
@@ -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 URL
+ * 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. NOTE: The
+ * DataHandler object creates a URLDataSource internally,
+ * when it is constructed with a URL.
+ *
+ * @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 URLConnection.getContentType
method
+ * after retrieving a URLConnection object.
+ * Note: this method attempts to call the openConnection
+ * 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.
+ *
+ * @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 getFile
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
+ * openStream
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;
+ }
+}
diff --git a/app/src/main/java/javax/activation/UnsupportedDataTypeException.java b/app/src/main/java/javax/activation/UnsupportedDataTypeException.java
new file mode 100644
index 0000000000..b24a7882be
--- /dev/null
+++ b/app/src/main/java/javax/activation/UnsupportedDataTypeException.java
@@ -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);
+ }
+}
diff --git a/app/src/main/java/javax/activation/package.html b/app/src/main/java/javax/activation/package.html
new file mode 100644
index 0000000000..53a9b7341a
--- /dev/null
+++ b/app/src/main/java/javax/activation/package.html
@@ -0,0 +1,26 @@
+
+
+