Copybara import of the project: - a08bf530f67a36b3b746a940140e47ddc9ded512 Initial Contribution by Vinay Vishal <vinay.vishal@oracle.com> - d8fd8aa774ada0b41c9ecfeb8427ab2aac060de1 Remove unnecessary file from repository (#6) by Vinay Vishal <vinayvishal@users.noreply.github.com> - f91b943ea39644e3b69ea8b46084849cbd13918d Changes groupId to jakarta (master) (#8) by Dmitry Kornilov <maiden168@gmail.com> - 5bef63640cfaca5f1fe853dfc5f553bde9db20c7 Fix javadoc errors so "mvn deploy" will work. by Bill Shannon <bill.shannon@oracle.com> - 7b4a3aeb2d8e4c929a59de0bac4acaad9db5b2e5 Switch to new parent pom; include LICENSE.md and NOTICE.m... by Bill Shannon <bill.shannon@oracle.com> - 38c6aed6bc0092e45f991b08719973c723646f6a Remove incorrect trademark and crypo sections, update lic... by Bill Shannon <bill.shannon@oracle.com> - e9d4a8c283d6e4d67bd2179251e7b50fe41eb637 Add .gitignore file. by Bill Shannon <bill.shannon@oracle.com> - 471681a2fd534749decbb39977202980f20f173b Switch to jakarta coordinates. by Bill Shannon <bill.shannon@oracle.com> - 6b6c467a2687ae6f47ec37a37af07cc4b0279541 JAF 1.2.1 Final Release. by Bill Shannon <bill.shannon@oracle.com> - 919fd80179befb05ebc067dcd859d43851a4445d Update gpg version by Vinay Vishal <vinay.vishal@oracle.com> - e5623f7365f4d10b38a9d931e7f7fbd190924439 Add nexus-staging-maven-plugin configuration. by Bill Shannon <bill.shannon@oracle.com> - 82f0d49e14d8aed9d252a8e9277c26561de50e5c Update version to 1.2.2-SNAPSHOT. by Bill Shannon <bill.shannon@oracle.com> - e958beac1987078335b6256660be276f0d56d527 Update to 1.0.5 parent pom and remove unneeded configurat... by Bill Shannon <bill.shannon@oracle.com> - 4bb76a8c3d349c506d5fbc80ab515c7ad958bf23 Add pointer to JAF web site. by Bill Shannon <bill.shannon@oracle.com> - 70e71e6aab37cb0a9621fe19b7ce03190eff4f03 Exclude com.sun.* packages from API jar file OSGi manifes... by Bill Shannon <bill.shannon@oracle.com> - ee77e3830b5c234fc0cfb15666e1897cf46de664 Add spec/ submodule with boilerplate asciidoc (#25) by David Blevins <david.blevins@gmail.com> - 747f285f63d0c59b06613b30a4f498acd783abdd Fix parent pom reference; fix version; fix copyright line. by Bill Shannon <bill.shannon@oracle.com> - f1ee8512d9c4aa36404b0fafb69ff151855b754e Update min maven version (#27) by Piotrek Żygieło <pzygielo@users.noreply.github.com> - 6c636d2509aa155e518a5ac5524763069ea14989 Update project name to Jakarta Activation - fix #20. by Bill Shannon <bill.shannon@oracle.com> - b50200a8b9e3fd4182728398db40644fff1ee6a4 Change name to Jakarta Activation. by Bill Shannon <bill.shannon@oracle.com> - b915c7c9be3db8a6a1d28586b0e3b9193ca0c16e Update spec for final release. by Bill Shannon <bill.shannon@oracle.com> - 69497814b252bfa9b5b88020713489b32525774b Build (only) with JDK 9+; build a real module. by Bill Shannon <bill.shannon@oracle.com> - 8fe222817d846334b55d6d74ca44396d453bd694 Add spec license file. by Bill Shannon <bill.shannon@oracle.com> - 177daf703c7cdb61cb63aeaf9744821e8d6dd192 Fix build of API jar file and demos. by Bill Shannon <bill.shannon@oracle.com> - 02c28b24426c90820067a69420143825bf4474b4 Need to link to SE 9 docs or javadoc fails running in Ecl... by Bill Shannon <bill.shannon@oracle.com> - 204c56e75e6a59c27db3ee37a4b7d41504ed0497 Force non-module javadocs. by Bill Shannon <bill.shannon@oracle.com> - fe43eca037845c051ffd9894e66d285adf8b9067 Build all but module-info.java with -source 1.8. by Bill Shannon <bill.shannon@oracle.com> - 67e9b0525099a81818cf89302e1ba6f3c68ebba0 Fix javadoc build to include doc-files directory. by Bill Shannon <bill.shannon@oracle.com> - 595cc35e9b3e836193a780806e95dfbeef031b86 1.2.2 Final Release by Bill Shannon <bill.shannon@oracle.com> - 7f39afbf1049849a957c3bf4e68ab9c8627b9a70 Remove links to Java SE javadocs. by Bill Shannon <bill.shannon@oracle.com> - eea7f7c022c582af1d84deb21fb23cbde19fb074 Disable links to Java APIs. by Bill Shannon <bill.shannon@oracle.com> - 287696ee85a93b0d9062b5d7411a5bdc813c3d73 Make all APIs available to API jar file javadoc. by Bill Shannon <bill.shannon@oracle.com> - 0345740ecc36698e11a22bea7f86a3ec4c85e0a2 Define oss-release profile. by Bill Shannon <bill.shannon@oracle.com> - 6b9e077c085be3646d56a2fe538c784e21b177e8 Add module-info to API jar file. by Bill Shannon <bill.shannon@oracle.com> - a1c5ecb351574c973b5e8f17855a2ca201c9f201 Need all the source files for javadoc, but exclude them i... by Bill Shannon <bill.shannon@oracle.com> - 6ca0946a067ba1c2424d9800768957fa486a1ca0 Add issue templates. by Bill Shannon <bill.shannon@oracle.com> - 942500e691f95b2ae7935387ff9b58290d6d4aeb Update to new parent pom. by Bill Shannon <bill.shannon@oracle.com> - 9a459923860d8dfba4ef7d4ef4250bd1c663ff87 Initial version of the full specification document text. by Bill Shannon <bill.shannon@oracle.com> - e199f61f414fa3455734a407a6e56469b3ad97b4 Update for Jakarta EE; fix formatting. by Bill Shannon <bill.shannon@oracle.com> GitOrigin-RevId: e199f61f414fa3455734a407a6e56469b3ad97b4 Change-Id: I472f7cc0f89f543c48d9b1aff8ce293a8cf0d7d7
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..dd84ea7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here.
diff --git a/.github/ISSUE_TEMPLATE/compatibility-certification-request.md b/.github/ISSUE_TEMPLATE/compatibility-certification-request.md new file mode 100644 index 0000000..a6648c8 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/compatibility-certification-request.md
@@ -0,0 +1,27 @@ +--- +name: Compatibility certification request +about: Request certification of a compatible implementation of Jakarta Mail. +title: Compatibility certification request for [product/project name] +labels: certification +assignees: '' + +--- + +- [ ] Organization Name ("Organization") and, if applicable, URL\ + // Add here +- [ ] Product Name, Version and download URL (if applicable)\ + // Add here +- [ ] Specification Name, Version and download URL\ + // Add here +- [ ] TCK Version, digital SHA-256 fingerprint and download URL\ + // Add here +- [ ] Public URL of TCK Results Summary\ + // Add here +- [ ] Any Additional Specification Certification Requirements\ + // Add here +- [ ] Java runtime used to run the implementation\ + // Add here +- [ ] Summary of the information for the certification environment, operating system, cloud, ...\ + // Add here +- [ ] By checking this box I acknowledge that the Organization I represent accepts the terms of the [EFTL](https://www.eclipse.org/legal/tck.php). +- [ ] By checking this box I attest that all TCK requirements have been met, including any compatibility rules.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..bbcbbe7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here.
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7f8f2f1 --- /dev/null +++ b/.gitignore
@@ -0,0 +1,3 @@ +target/ +*/target/ +.m2/
diff --git a/.hgtags b/.hgtags new file mode 100644 index 0000000..2a548dd --- /dev/null +++ b/.hgtags
@@ -0,0 +1 @@ +dfaec4fe670da9f3554e3639e6907aeccb2bc1bc JAF-1_2_0
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..46195fd --- /dev/null +++ b/CONTRIBUTING.md
@@ -0,0 +1,46 @@ +# Contributing to Jakarta Activation + +Thanks for your interest in this project. + +## Project description + +Jakarta Activation lets you take advantage of standard services to: +determine the type of an arbitrary piece of data; encapsulate access to +discover the operations available on it; and instantiate the +appropriate bean to perform the operation(s). + +* https://projects.eclipse.org/projects/ee4j.jaf + +## Developer resources + +Information regarding source code management, builds, coding standards, and +more. + +* https://projects.eclipse.org/projects/ee4j.jaf/developer + +The project maintains the following source code repositories + +* https://github.com/eclipse-ee4j/jaf + +## Eclipse Contributor Agreement + +Before your contribution can be accepted by the project team contributors must +electronically sign the Eclipse Contributor Agreement (ECA). + +* http://www.eclipse.org/legal/ECA.php + +Commits that are provided by non-committers must have a Signed-off-by field in +the footer indicating that the author is aware of the terms by which the +contribution has been provided to the project. The non-committer must +additionally have an Eclipse Foundation account and must have a signed Eclipse +Contributor Agreement (ECA) on file. + +For more information, please see the Eclipse Committer Handbook: +https://www.eclipse.org/projects/handbook/#resources-commit + +## Contact + +Contact the project developers via the project's "dev" list. + +* https://accounts.eclipse.org/mailing-list/jaf-dev +
diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..e0358f9 --- /dev/null +++ b/LICENSE.md
@@ -0,0 +1,29 @@ + + Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/NOTICE.md b/NOTICE.md new file mode 100644 index 0000000..010e450 --- /dev/null +++ b/NOTICE.md
@@ -0,0 +1,33 @@ +# Notices for Jakarta Activation + +This content is produced and maintained by Jakarta Activation project. + +* Project home: https://projects.eclipse.org/projects/ee4j.jaf + +## Copyright + +All content is the property of the respective authors or their employers. For +more information regarding authorship of content, please consult the listed +source code repository logs. + +## Declared Project Licenses + +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 + +## Source Code + +The project maintains the following source code repositories: + +* https://github.com/eclipse-ee4j/jaf + +## Third-party Content + +This project leverages the following third party content. + +JUnit (4.12) + +* License: Eclipse Public License
diff --git a/README.md b/README.md new file mode 100644 index 0000000..89be0a4 --- /dev/null +++ b/README.md
@@ -0,0 +1,9 @@ +# Jakarta Activation + +Jakarta Activation lets you take advantage of standard services to: +determine the type of an arbitrary piece of data; encapsulate access to +it; discover the operations available on it; and instantiate the +appropriate bean to perform the operation(s). + +See the +[Jakarta Activation web site](https://eclipse-ee4j.github.io/jaf/).
diff --git a/activation/pom.xml b/activation/pom.xml new file mode 100644 index 0000000..75b79db --- /dev/null +++ b/activation/pom.xml
@@ -0,0 +1,172 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- + + 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 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>com.sun.activation</groupId> + <artifactId>all</artifactId> + <version>1.2.2</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>com.sun.activation</groupId> + <artifactId>jakarta.activation</artifactId> + <packaging>jar</packaging> + <name>Jakarta Activation</name> + + <properties> + <activation.extensionName> + jakarta.activation + </activation.extensionName> + <activation.moduleName> + jakarta.activation + </activation.moduleName> + <activation.specificationTitle> + Jakarta Activation Specification + </activation.specificationTitle> + <activation.implementationTitle> + javax.activation + </activation.implementationTitle> + <activation.packages.export> + javax.activation.*; version=${activation.spec.version}, + com.sun.activation.*; version=${activation.osgiversion} + </activation.packages.export> + <findbugs.skip> + false + </findbugs.skip> + <findbugs.exclude> + ${project.basedir}/exclude.xml + </findbugs.exclude> + </properties> + + <build> + <resources> + <resource> + <directory>src/main/resources</directory> + <filtering>true</filtering> + </resource> + </resources> + <plugins> + <!-- + Configure compiler plugin to print lint warnings. + --> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <configuration> + <!-- + ignore some of the errors that are + too hard to fix for now + --> + <!-- + <compilerArguments> + <Xlint:all/> + <Xlint:-rawtypes/> + <Xlint:-unchecked/> + <Xlint:-finally/> + </compilerArguments> + <showWarnings>true</showWarnings> + --> + </configuration> + </execution> + </executions> + </plugin> + + <!-- + Configure test plugin to find *TestSuite classes. + --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <includes> + <include>**/*Test.java</include> + <include>**/*TestSuite.java</include> + </includes> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-javadoc-plugin</artifactId> + <inherited>false</inherited> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>javadoc</goal> + </goals> + <configuration> + <author>false</author> + <description> + Jakarta Activation API documentation + </description> + <doctitle> + Jakarta Activation API documentation + </doctitle> + <windowtitle> + Jakarta Activation API documentation + </windowtitle> + <splitindex>true</splitindex> + <use>true</use> + <notimestamp>true</notimestamp> + <serialwarn>true</serialwarn> + <quiet>true</quiet> + <bottom> +<![CDATA[Copyright © 2019 Eclipse Foundation. + Use is subject to + <a href="{@docRoot}/doc-files/speclicense.html" target="_top">license terms</a>. +]]> + </bottom> + <groups> + <group> + <title>Jakarta Activation API Packages</title> + <packages>javax.*</packages> + </group> + </groups> + <!-- + <subpackages>javax.activation</subpackages> + --> + <!-- + Force javadoc to produce non-module docs + since the module definition isn't part of + the API specification. + --> + <release>8</release> + <sourceFileExcludes> + <sourceFileExclude>module-info.java</sourceFileExclude> + <sourceFileExclude>com/**</sourceFileExclude> + </sourceFileExcludes> + <!-- force the doc-files directory to be copied --> + <docfilessubdirs>true</docfilessubdirs> + <detectJavaApiLink>false</detectJavaApiLink> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.12</version> + <scope>test</scope> + <optional>true</optional> + </dependency> + </dependencies> +</project>
diff --git a/activation/src/main/java/com/sun/activation/registries/LogSupport.java b/activation/src/main/java/com/sun/activation/registries/LogSupport.java new file mode 100644 index 0000000..3228f3c --- /dev/null +++ b/activation/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/activation/src/main/java/com/sun/activation/registries/MailcapFile.java b/activation/src/main/java/com/sun/activation/registries/MailcapFile.java new file mode 100644 index 0000000..09deb1b --- /dev/null +++ b/activation/src/main/java/com/sun/activation/registries/MailcapFile.java
@@ -0,0 +1,567 @@ +/* + * 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. + * @throws IOException for I/O errors + */ + 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 + * @throws IOException for I/O errors + */ + public MailcapFile(InputStream is) throws IOException { + if (LogSupport.isLoggable()) + LogSupport.log("new MailcapFile: InputStream"); + parse(new BufferedReader(new InputStreamReader(is, "iso-8859-1"))); + } + + /** + * Mailcap file default constructor. + */ + public MailcapFile() { + if (LogSupport.isLoggable()) + LogSupport.log("new MailcapFile: default"); + } + + /** + * Get the Map of MailcapEntries based on the MIME type. + * + * <p> + * <strong>Semantics:</strong> First check for the literal mime type, + * if that fails looks for wildcard <type>/\* and return that. + * Return the list of all that hit. + * + * @param mime_type the MIME type + * @return the map of MailcapEntries + */ + public Map getMailcapList(String mime_type) { + Map search_result = null; + Map wildcard_result = null; + + // first try the literal + search_result = (Map)type_hash.get(mime_type); + + // ok, now try the wildcard + int separator = mime_type.indexOf('/'); + String subtype = mime_type.substring(separator + 1); + if (!subtype.equals("*")) { + String type = mime_type.substring(0, separator + 1) + "*"; + wildcard_result = (Map)type_hash.get(type); + + if (wildcard_result != null) { // damn, we have to merge!!! + if (search_result != null) + search_result = + mergeResults(search_result, wildcard_result); + else + search_result = wildcard_result; + } + } + return search_result; + } + + /** + * Get the Map of fallback MailcapEntries based on the MIME type. + * + * <p> + * <strong>Semantics:</strong> First check for the literal mime type, + * if that fails looks for wildcard <type>/\* and return that. + * Return the list of all that hit. + * + * @param mime_type the MIME type + * @return the map of fallback MailcapEntries + */ + 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. + * + * @return a String array of the MIME types + */ + 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. + * + * @param mime_type the MIME type + * @return a String array of the commands + */ + public String[] getNativeCommands(String mime_type) { + String[] cmds = null; + List v = + (List)native_commands.get(mime_type.toLowerCase(Locale.ENGLISH)); + if (v != null) { + cmds = new String[v.size()]; + cmds = (String[])v.toArray(cmds); + } + return cmds; + } + + /** + * Merge the first hash into the second. + * This merge will only effect the hashtable that is + * returned, we don't want to touch the one passed in since + * its integrity must be maintained. + */ + private Map mergeResults(Map first, Map second) { + Iterator verb_enum = second.keySet().iterator(); + Map clonedHash = new HashMap(first); + + // iterate through the verbs in the second map + while (verb_enum.hasNext()) { + String verb = (String)verb_enum.next(); + List cmdVector = (List)clonedHash.get(verb); + if (cmdVector == null) { + clonedHash.put(verb, second.get(verb)); + } else { + // merge the two + List oldV = (List)second.get(verb); + cmdVector = new ArrayList(cmdVector); + cmdVector.addAll(oldV); + clonedHash.put(verb, cmdVector); + } + } + return clonedHash; + } + + /** + * appendToMailcap: Append to this Mailcap DB, use the mailcap + * format: + * Comment == "# <i>comment string</i>" + * Entry == "mimetype; javabeanclass" + * + * Example: + * # this is a comment + * image/gif jaf.viewers.ImageViewer + * + * @param mail_cap the mailcap string + */ + 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. + * + * @param mailcapEntry the mailcap entry + * @throws MailcapParseException for parse errors + * @throws IOException for I/O errors + */ + 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/activation/src/main/java/com/sun/activation/registries/MailcapParseException.java b/activation/src/main/java/com/sun/activation/registries/MailcapParseException.java new file mode 100644 index 0000000..754c405 --- /dev/null +++ b/activation/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/activation/src/main/java/com/sun/activation/registries/MailcapTokenizer.java b/activation/src/main/java/com/sun/activation/registries/MailcapTokenizer.java new file mode 100644 index 0000000..23728a5 --- /dev/null +++ b/activation/src/main/java/com/sun/activation/registries/MailcapTokenizer.java
@@ -0,0 +1,309 @@ +/* + * 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. + * + * @param value on or off + */ + 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. + * + * @returns A String containing the current token value + */ + public String getCurrentTokenValue() { + return currentTokenValue; + } + + /* + * Process the next token. + * + * @returns 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/activation/src/main/java/com/sun/activation/registries/MimeTypeEntry.java b/activation/src/main/java/com/sun/activation/registries/MimeTypeEntry.java new file mode 100644 index 0000000..3582f77 --- /dev/null +++ b/activation/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/activation/src/main/java/com/sun/activation/registries/MimeTypeFile.java b/activation/src/main/java/com/sun/activation/registries/MimeTypeFile.java new file mode 100644 index 0000000..b2c89f0 --- /dev/null +++ b/activation/src/main/java/com/sun/activation/registries/MimeTypeFile.java
@@ -0,0 +1,311 @@ +/* + * 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. + * @throws IOException for I/O errors + */ + 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 + * + * @param file_ext the file extension + * @return the MimeTypeEntry + */ + public MimeTypeEntry getMimeTypeEntry(String file_ext) { + return (MimeTypeEntry)type_hash.get((Object)file_ext); + } + + /** + * Get the MIME type string corresponding to the file extension. + * + * @param file_ext the file extension + * @return the MIME type string + */ + 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 + * + * @param mime_types the mime.types string + */ + public void appendToRegistry(String mime_types) { + try { + parse(new BufferedReader(new StringReader(mime_types))); + } catch (IOException ex) { + // can't happen + } + } + + /** + * Parse a stream of mime.types entries. + */ + private void parse(BufferedReader buf_reader) throws IOException { + String line = null, prev = null; + + while ((line = buf_reader.readLine()) != null) { + if (prev == null) + prev = line; + else + prev += line; + int end = prev.length(); + if (prev.length() > 0 && prev.charAt(end - 1) == '\\') { + prev = prev.substring(0, end - 1); + continue; + } + this.parseEntry(prev); + prev = null; + } + if (prev != null) + this.parseEntry(prev); + } + + /** + * Parse single mime.types entry. + */ + private void parseEntry(String line) { + String mime_type = null; + String file_ext = null; + line = line.trim(); + + if (line.length() == 0) // empty line... + return; // BAIL! + + // check to see if this is a comment line? + if (line.charAt(0) == '#') + return; // then we are done! + + // is it a new format line or old format? + if (line.indexOf('=') > 0) { + // new format + LineTokenizer lt = new LineTokenizer(line); + while (lt.hasMoreTokens()) { + String name = lt.nextToken(); + String value = null; + if (lt.hasMoreTokens() && lt.nextToken().equals("=") && + lt.hasMoreTokens()) + value = lt.nextToken(); + if (value == null) { + if (LogSupport.isLoggable()) + LogSupport.log("Bad .mime.types entry: " + line); + return; + } + if (name.equals("type")) + mime_type = value; + else if (name.equals("exts")) { + StringTokenizer st = new StringTokenizer(value, ","); + while (st.hasMoreTokens()) { + file_ext = st.nextToken(); + MimeTypeEntry entry = + new MimeTypeEntry(mime_type, file_ext); + type_hash.put(file_ext, entry); + if (LogSupport.isLoggable()) + LogSupport.log("Added: " + entry.toString()); + } + } + } + } else { + // old format + // count the tokens + StringTokenizer strtok = new StringTokenizer(line); + int num_tok = strtok.countTokens(); + + if (num_tok == 0) // empty line + return; + + mime_type = strtok.nextToken(); // get the MIME type + + while (strtok.hasMoreTokens()) { + MimeTypeEntry entry = null; + + file_ext = strtok.nextToken(); + entry = new MimeTypeEntry(mime_type, file_ext); + type_hash.put(file_ext, entry); + if (LogSupport.isLoggable()) + LogSupport.log("Added: " + entry.toString()); + } + } + } + + // for debugging + /* + public static void main(String[] argv) throws Exception { + MimeTypeFile mf = new MimeTypeFile(argv[0]); + System.out.println("ext " + argv[1] + " type " + + mf.getMIMETypeString(argv[1])); + System.exit(0); + } + */ +} + +class LineTokenizer { + private int currentPosition; + private int maxPosition; + private String str; + private Vector stack = new Vector(); + private static final String singles = "="; // single character tokens + + /** + * Constructs a tokenizer for the specified string. + * <p> + * + * @param str a string to be parsed. + */ + public LineTokenizer(String str) { + currentPosition = 0; + this.str = str; + maxPosition = str.length(); + } + + /** + * Skips white space. + */ + private void skipWhiteSpace() { + while ((currentPosition < maxPosition) && + Character.isWhitespace(str.charAt(currentPosition))) { + currentPosition++; + } + } + + /** + * Tests if there are more tokens available from this tokenizer's string. + * + * @return <code>true</code> if there are more tokens available from this + * tokenizer's string; <code>false</code> otherwise. + */ + public boolean hasMoreTokens() { + if (stack.size() > 0) + return true; + skipWhiteSpace(); + return (currentPosition < maxPosition); + } + + /** + * Returns the next token from this tokenizer. + * + * @return the next token from this tokenizer. + * @exception NoSuchElementException if there are no more tokens in this + * tokenizer's string. + */ + public String nextToken() { + int size = stack.size(); + if (size > 0) { + String t = (String)stack.elementAt(size - 1); + stack.removeElementAt(size - 1); + return t; + } + skipWhiteSpace(); + + if (currentPosition >= maxPosition) { + throw new NoSuchElementException(); + } + + int start = currentPosition; + char c = str.charAt(start); + if (c == '"') { + currentPosition++; + boolean filter = false; + while (currentPosition < maxPosition) { + c = str.charAt(currentPosition++); + if (c == '\\') { + currentPosition++; + filter = true; + } else if (c == '"') { + String s; + + if (filter) { + StringBuffer sb = new StringBuffer(); + for (int i = start + 1; i < currentPosition - 1; i++) { + c = str.charAt(i); + if (c != '\\') + sb.append(c); + } + s = sb.toString(); + } else + s = str.substring(start + 1, currentPosition - 1); + return s; + } + } + } else if (singles.indexOf(c) >= 0) { + currentPosition++; + } else { + while ((currentPosition < maxPosition) && + singles.indexOf(str.charAt(currentPosition)) < 0 && + !Character.isWhitespace(str.charAt(currentPosition))) { + currentPosition++; + } + } + return str.substring(start, currentPosition); + } + + public void pushToken(String token) { + stack.addElement(token); + } +}
diff --git a/activation/src/main/java/com/sun/activation/viewers/ImageViewer.java b/activation/src/main/java/com/sun/activation/viewers/ImageViewer.java new file mode 100644 index 0000000..b7b2294 --- /dev/null +++ b/activation/src/main/java/com/sun/activation/viewers/ImageViewer.java
@@ -0,0 +1,108 @@ +/* + * 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.viewers; + +import java.awt.*; +import java.io.*; +import java.beans.*; +import javax.activation.*; + +public class ImageViewer extends Panel implements CommandObject { + // UI Vars... + private ImageViewerCanvas canvas = null; + + // File Vars + // private InputStream data_ins = null; + private Image image = null; + private DataHandler _dh = null; + + private boolean DEBUG = false; + /** + * Constructor + */ + public ImageViewer(){ + + // create the ImageViewerCanvas + canvas = new ImageViewerCanvas(); + add(canvas); + } + /** + * Set the DataHandler for this CommandObject + * @param dh the DataHandler + */ + public void setCommandContext(String verb, DataHandler dh) throws IOException{ + _dh = dh; + this.setInputStream( _dh.getInputStream() ); + } + //-------------------------------------------------------------------- + + /** + * Set the data stream, component to assume it is ready to + * be read. + */ + private void setInputStream(InputStream ins) throws IOException { + MediaTracker mt = new MediaTracker(this); + int bytes_read = 0; + byte data[] = new byte[1024]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + while((bytes_read = ins.read(data)) >0) + baos.write(data, 0, bytes_read); + ins.close(); + + // convert the buffer into an image + image = getToolkit().createImage(baos.toByteArray()); + + mt.addImage(image, 0); + + try { + mt.waitForID(0); + mt.waitForAll(); + if(mt.statusID(0, true ) != MediaTracker.COMPLETE){ + System.out.println("Error occured in image loading = " + + mt.getErrorsID(0)); + + } + + } + catch(InterruptedException e) { + throw new IOException("Error reading image data"); + } + + canvas.setImage(image); + if(DEBUG) + System.out.println("calling invalidate"); + + } + //-------------------------------------------------------------------- + public void addNotify(){ + super.addNotify(); // call the real one first... + this.invalidate(); + this.validate(); + this.doLayout(); + } + //-------------------------------------------------------------------- + public Dimension getPreferredSize(){ + return canvas.getPreferredSize(); + } + +} + + + + + + + + + + +
diff --git a/activation/src/main/java/com/sun/activation/viewers/ImageViewerCanvas.java b/activation/src/main/java/com/sun/activation/viewers/ImageViewerCanvas.java new file mode 100644 index 0000000..d85e9e2 --- /dev/null +++ b/activation/src/main/java/com/sun/activation/viewers/ImageViewerCanvas.java
@@ -0,0 +1,66 @@ +/* + * 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.viewers; + +import java.awt.*; + +public class ImageViewerCanvas extends Canvas +{ + private Image canvas_image = null; + + /** + * The constructor + */ + public ImageViewerCanvas() + { + + } + + /** + * set the image + * @param new_image the image + */ + public void setImage(Image new_image) + { + canvas_image = new_image; + this.invalidate(); + this.repaint(); + } + + /** + * getPreferredSize + */ + public Dimension getPreferredSize() + { + Dimension d = null; + + if(canvas_image == null) + { + d = new Dimension(200, 200); + } + else + d = new Dimension(canvas_image.getWidth(this), + canvas_image.getHeight(this)); + + return d; + } + /** + * paint method + */ + public void paint(Graphics g) + { + + if(canvas_image != null) + g.drawImage(canvas_image, 0, 0, this); + + } + +}
diff --git a/activation/src/main/java/com/sun/activation/viewers/TextEditor.java b/activation/src/main/java/com/sun/activation/viewers/TextEditor.java new file mode 100644 index 0000000..693461e --- /dev/null +++ b/activation/src/main/java/com/sun/activation/viewers/TextEditor.java
@@ -0,0 +1,176 @@ +/* + * 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.viewers; + +import java.awt.*; +import java.awt.event.*; +import java.io.*; +import java.beans.*; +import javax.activation.*; + +public class TextEditor extends Panel implements CommandObject, + ActionListener { + // UI Vars... + private TextArea text_area = null; + private GridBagLayout panel_gb = null; + private Panel button_panel = null; + private Button save_button = null; + // File Vars + private File text_file = null; + private String text_buffer = null; + private InputStream data_ins = null; + private FileInputStream fis = null; + + private DataHandler _dh = null; + private boolean DEBUG = false; + /** + * Constructor + */ + public TextEditor() { + panel_gb = new GridBagLayout(); + setLayout(panel_gb); + + button_panel = new Panel(); + // button_panel.setBackground(Color.white); + button_panel.setLayout( new FlowLayout() ); + save_button = new Button("SAVE"); + button_panel.add(save_button); + addGridComponent(this, + button_panel, + panel_gb, + 0,0, + 1,1, + 1,0); + + // create the text area + text_area = new TextArea("This is text",24, 80, + TextArea.SCROLLBARS_VERTICAL_ONLY ); + // text_area.setBackground(Color.lightGray); + text_area.setEditable( true ); + + addGridComponent(this, + text_area, + panel_gb, + 0,1, + 1,2, + 1,1); + + // add listeners + save_button.addActionListener( this ); + + } + + //////////////////////////////////////////////////////////////////////// + /** + * adds a component to our gridbag layout + */ + private void addGridComponent(Container cont, + Component comp, + GridBagLayout mygb, + int gridx, + int gridy, + int gridw, + int gridh, + int weightx, + int weighty) { + GridBagConstraints c = new GridBagConstraints(); + c.gridx = gridx; + c.gridy = gridy; + c.gridwidth = gridw; + c.gridheight = gridh; + c.fill = GridBagConstraints.BOTH; + c.weighty = weighty; + c.weightx = weightx; + c.anchor = GridBagConstraints.CENTER; + mygb.setConstraints(comp, c); + cont.add(comp); + } + + //-------------------------------------------------------------------- + public void setCommandContext(String verb, DataHandler dh) throws IOException { + _dh = dh; + this.setInputStream( _dh.getInputStream() ); + + } + //-------------------------------------------------------------------- + + /** + * set the data stream, component to assume it is ready to + * be read. + * + * @param ins the data stream + * @throws IOException for I/O errors + */ + public void setInputStream(InputStream ins) throws IOException { + + byte data[] = new byte[1024]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + int bytes_read = 0; + // check that we can actually read + + while((bytes_read = ins.read(data)) >0) + baos.write(data, 0, bytes_read); + ins.close(); + + + // convert the buffer into a string + // popuplate the buffer + text_buffer = baos.toString(); + + // place in the text area + text_area.setText(text_buffer); + } + /////////////////////////////////////////////////////////////////////// + private void performSaveOperation(){ + OutputStream fos = null; + try { + fos = _dh.getOutputStream(); + } catch (Exception e) {} + + String buffer = text_area.getText(); + + // make sure we got one + if(fos == null) { + System.out.println("Invalid outputstream in TextEditor!"); + System.out.println("not saving!"); + return; + } + + try { + fos.write( buffer.getBytes() ); + fos.flush(); // flush it! + fos.close(); // close it! + } catch(IOException e) + { + System.out.println("TextEditor Save Operation failed with: " + e); + } + + } + //-------------------------------------------------------------------- + public void addNotify() { + super.addNotify(); + invalidate(); + } + //-------------------------------------------------------------------- + public Dimension getPreferredSize() { + return text_area.getMinimumSize(24, 80); + } + ///////////////////////////////////////////////////////////////////// + // for ActionListener + public void actionPerformed(ActionEvent evt){ + if(evt.getSource() == save_button) { // save button pressed! + + // Save ourselves + this.performSaveOperation(); + } + } + +}
diff --git a/activation/src/main/java/com/sun/activation/viewers/TextViewer.java b/activation/src/main/java/com/sun/activation/viewers/TextViewer.java new file mode 100644 index 0000000..3093ff4 --- /dev/null +++ b/activation/src/main/java/com/sun/activation/viewers/TextViewer.java
@@ -0,0 +1,91 @@ +/* + * 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.viewers; + +import java.awt.*; +import java.io.*; +import java.beans.*; +import javax.activation.*; + +public class TextViewer extends Panel implements CommandObject { + // UI Vars... + private TextArea text_area = null; + + // File Vars + private File text_file = null; + private String text_buffer = null; + + private DataHandler _dh = null; + private boolean DEBUG = false; + /** + * Constructor + */ + public TextViewer() { + setLayout( new GridLayout(1,1)); + // create the text area + text_area = new TextArea("", 24, 80, + TextArea.SCROLLBARS_VERTICAL_ONLY ); + text_area.setEditable( false ); + + add(text_area); + } + + //-------------------------------------------------------------------- + public void setCommandContext(String verb, DataHandler dh) throws IOException { + _dh = dh; + this.setInputStream( _dh.getInputStream() ); + } + //-------------------------------------------------------------------- + + /** + * set the data stream, component to assume it is ready to + * be read. + * + * @param ins the data stream + * @throws IOException for I/O errors + */ + public void setInputStream(InputStream ins) throws IOException { + + int bytes_read = 0; + // check that we can actually read + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + byte data[] = new byte[1024]; + + while((bytes_read = ins.read(data)) >0) + baos.write(data, 0, bytes_read); + + ins.close(); + + // convert the buffer into a string + // popuplate the buffer + text_buffer = baos.toString(); + + // place in the text area + text_area.setText(text_buffer); + + } + //-------------------------------------------------------------------- + public void addNotify() { + super.addNotify(); + invalidate(); + } + //-------------------------------------------------------------------- + public Dimension getPreferredSize() { + return text_area.getMinimumSize(24, 80); + } + +} + + + + + +
diff --git a/activation/src/main/java/javax/activation/ActivationDataFlavor.java b/activation/src/main/java/javax/activation/ActivationDataFlavor.java new file mode 100644 index 0000000..87f12a8 --- /dev/null +++ b/activation/src/main/java/javax/activation/ActivationDataFlavor.java
@@ -0,0 +1,233 @@ +/* + * Copyright (c) 1997, 2019 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package javax.activation; + +import java.awt.datatransfer.DataFlavor; +import java.io.IOException; +import javax.activation.MimeType; + +/** + * The ActivationDataFlavor class is a special subclass of + * <code>java.awt.datatransfer.DataFlavor</code>. It allows the JAF to + * set all three values stored by the DataFlavor class via a new + * constructor. It also contains improved MIME parsing in the <code>equals + * </code> method. Except for the improved parsing, its semantics are + * identical to that of the JDK's DataFlavor class. + */ + +public class ActivationDataFlavor extends DataFlavor { + + /* + * Raison d'etre: + * + * The DataFlavor class included in JDK 1.1 has several limitations + * including piss poor MIME type parsing, and the limitation of + * only supporting serialized objects and InputStreams as + * representation objects. This class 'fixes' that. + */ + + // I think for now I'll keep copies of all the variables and + // then later I may choose try to better coexist with the base + // class *sigh* + private String mimeType = null; + private MimeType mimeObject = null; + private String humanPresentableName = null; + private Class representationClass = null; + + /** + * Construct a DataFlavor that represents an arbitrary + * Java object. This constructor is an extension of the + * JDK's DataFlavor in that it allows the explicit setting + * of all three DataFlavor attributes. + * <p> + * The returned DataFlavor will have the following characteristics: + * <p> + * representationClass = representationClass<br> + * mimeType = mimeType<br> + * humanName = humanName + * <p> + * + * @param representationClass the class used in this DataFlavor + * @param mimeType the MIME type of the data represented by this class + * @param humanPresentableName the human presentable name of the flavor + */ + public ActivationDataFlavor(Class representationClass, + String mimeType, String humanPresentableName) { + super(mimeType, humanPresentableName); // need to call super + + // init private variables: + this.mimeType = mimeType; + this.humanPresentableName = humanPresentableName; + this.representationClass = representationClass; + } + + /** + * Construct a DataFlavor that represents a MimeType. + * <p> + * The returned DataFlavor will have the following characteristics: + * <p> + * If the mimeType is "application/x-java-serialized-object; + * class=", the result is the same as calling new + * DataFlavor(Class.forName()) as above. + * <p> + * otherwise: + * <p> + * representationClass = InputStream<p> + * mimeType = mimeType<p> + * + * @param representationClass the class used in this DataFlavor + * @param humanPresentableName the human presentable name of the flavor + */ + public ActivationDataFlavor(Class representationClass, + String humanPresentableName) { + super(representationClass, humanPresentableName); + this.mimeType = super.getMimeType(); + this.representationClass = representationClass; + this.humanPresentableName = humanPresentableName; + } + + /** + * Construct a DataFlavor that represents a MimeType. + * <p> + * The returned DataFlavor will have the following characteristics: + * <p> + * If the mimeType is "application/x-java-serialized-object; class=", + * the result is the same as calling new DataFlavor(Class.forName()) as + * above, otherwise: + * <p> + * representationClass = InputStream<p> + * mimeType = mimeType + * + * @param mimeType the MIME type of the data represented by this class + * @param humanPresentableName the human presentable name of the flavor + */ + public ActivationDataFlavor(String mimeType, String humanPresentableName) { + super(mimeType, humanPresentableName); + this.mimeType = mimeType; + try { + this.representationClass = Class.forName("java.io.InputStream"); + } catch (ClassNotFoundException ex) { + // XXX - should never happen, ignore it + } + this.humanPresentableName = humanPresentableName; + } + + /** + * Return the MIME type for this DataFlavor. + * + * @return the MIME type + */ + public String getMimeType() { + return mimeType; + } + + /** + * Return the representation class. + * + * @return the representation class + */ + public Class getRepresentationClass() { + return representationClass; + } + + /** + * Return the Human Presentable name. + * + * @return the human presentable name + */ + public String getHumanPresentableName() { + return humanPresentableName; + } + + /** + * Set the human presentable name. + * + * @param humanPresentableName the name to set + */ + public void setHumanPresentableName(String humanPresentableName) { + this.humanPresentableName = humanPresentableName; + } + + /** + * Compares the DataFlavor passed in with this DataFlavor; calls + * the <code>isMimeTypeEqual</code> method. + * + * @param dataFlavor the DataFlavor to compare with + * @return true if the MIME type and representation class + * are the same + */ + public boolean equals(DataFlavor dataFlavor) { + return (isMimeTypeEqual(dataFlavor) && + dataFlavor.getRepresentationClass() == representationClass); + } + + /** + * Is the string representation of the MIME type passed in equivalent + * to the MIME type of this DataFlavor. <p> + * + * ActivationDataFlavor delegates the comparison of MIME types to + * the MimeType class included as part of Jakarta Activation. + * This provides a more robust comparison than is normally + * available in the DataFlavor class. + * + * @param mimeType the MIME type + * @return true if the same MIME type + */ + public boolean isMimeTypeEqual(String mimeType) { + MimeType mt = null; + try { + if (mimeObject == null) + mimeObject = new MimeType(this.mimeType); + mt = new MimeType(mimeType); + } catch (MimeTypeParseException e) { + // something didn't parse, do a crude comparison + return this.mimeType.equalsIgnoreCase(mimeType); + } + + return mimeObject.match(mt); + } + + /** + * Called on DataFlavor for every MIME Type parameter to allow DataFlavor + * subclasses to handle special parameters like the text/plain charset + * parameters, whose values are case insensitive. (MIME type parameter + * values are supposed to be case sensitive). + * <p> + * This method is called for each parameter name/value pair and should + * return the normalized representation of the parameterValue. + * This method is never invoked by this implementation. + * + * @param parameterName the parameter name + * @param parameterValue the parameter value + * @return the normalized parameter value + * @deprecated + */ + protected String normalizeMimeTypeParameter(String parameterName, + String parameterValue) { + return parameterValue; + } + + /** + * Called for each MIME type string to give DataFlavor subtypes the + * opportunity to change how the normalization of MIME types is + * accomplished. + * One possible use would be to add default parameter/value pairs in cases + * where none are present in the MIME type string passed in. + * This method is never invoked by this implementation. + * + * @param mimeType the MIME type + * @return the normalized MIME type + * @deprecated + */ + protected String normalizeMimeType(String mimeType) { + return mimeType; + } +}
diff --git a/activation/src/main/java/javax/activation/CommandInfo.java b/activation/src/main/java/javax/activation/CommandInfo.java new file mode 100644 index 0000000..792c582 --- /dev/null +++ b/activation/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 <i>it is not guaranteed to + * return a valid value</i>. The reason for this is to allow CommandMap + * implmentations that subclass CommandInfo to provide special + * behavior. For example a CommandMap could dynamically generate + * JavaBeans. In this case, it might not be possible to create an + * object with all the correct state information solely from the class + * name. + */ + +public class CommandInfo { + private String verb; + private String className; + + /** + * The Constructor for CommandInfo. + * @param verb The command verb this CommandInfo decribes. + * @param className The command's fully qualified class name. + */ + public CommandInfo(String verb, String className) { + this.verb = verb; + this.className = className; + } + + /** + * Return the command verb. + * + * @return the command verb. + */ + public String getCommandName() { + return verb; + } + + /** + * Return the command's class name. <i>This method MAY return null in + * cases where a CommandMap subclassed CommandInfo for its + * own purposes.</i> In other words, it might not be possible to + * create the correct state in the command by merely knowing + * its class name. <b>DO NOT DEPEND ON THIS METHOD RETURNING + * A VALID VALUE!</b> + * + * @return The class name of the command, or <i>null</i> + */ + public String getCommandClass() { + return className; + } + + /** + * Return the instantiated JavaBean component. + * <p> + * If the current runtime environment supports + * {@link java.beans.Beans#instantiate Beans.instantiate}, + * use it to instantiate the JavaBeans component. Otherwise, use + * {@link java.lang.Class#forName Class.forName}. + * <p> + * The component class needs to be public. + * On Java SE 9 and newer, if the component class is in a named module, + * it needs to be in an exported package. + * <p> + * If the bean implements the <code>javax.activation.CommandObject</code> + * interface, call its <code>setCommandContext</code> method. + * <p> + * If the DataHandler parameter is null, then the bean is + * instantiated with no data. NOTE: this may be useful + * if for some reason the DataHandler that is passed in + * throws IOExceptions when this method attempts to + * access its InputStream. It will allow the caller to + * retrieve a reference to the bean if it can be + * instantiated. + * <p> + * If the bean does NOT implement the CommandObject interface, + * this method will check if it implements the + * java.io.Externalizable interface. If it does, the bean's + * readExternal method will be called if an InputStream + * can be acquired from the DataHandler.<p> + * + * @param dh The DataHandler that describes the data to be + * passed to the command. + * @param loader The ClassLoader to be used to instantiate the bean. + * @return The bean + * @exception IOException for failures reading data + * @exception ClassNotFoundException if command object class can't + * be found + * @see java.beans.Beans#instantiate + * @see javax.activation.CommandObject + */ + public Object getCommandObject(DataHandler dh, ClassLoader loader) + throws IOException, ClassNotFoundException { + Object new_bean = null; + + // try to instantiate the bean + new_bean = Beans.instantiate(loader, className); + + // if we got one and it is a CommandObject + if (new_bean != null) { + if (new_bean instanceof CommandObject) { + ((CommandObject)new_bean).setCommandContext(verb, dh); + } else if (new_bean instanceof Externalizable) { + if (dh != null) { + InputStream is = dh.getInputStream(); + if (is != null) { + ((Externalizable)new_bean).readExternal( + new ObjectInputStream(is)); + } + } + } + } + + return new_bean; + } + + /** + * Helper class to invoke Beans.instantiate reflectively or the equivalent + * with core reflection when module java.desktop is not readable. + */ + private static final class Beans { + static final Method instantiateMethod; + + static { + Method m; + try { + Class<?> c = Class.forName("java.beans.Beans"); + m = c.getDeclaredMethod("instantiate", ClassLoader.class, String.class); + } catch (ClassNotFoundException e) { + m = null; + } catch (NoSuchMethodException e) { + m = null; + } + instantiateMethod = m; + } + + /** + * Equivalent to invoking java.beans.Beans.instantiate(loader, cn) + */ + static Object instantiate(ClassLoader loader, String cn) + throws IOException, ClassNotFoundException { + + Exception exception; + + if (instantiateMethod != null) { + + // invoke Beans.instantiate + try { + return instantiateMethod.invoke(null, loader, cn); + } catch (InvocationTargetException e) { + exception = e; + } catch (IllegalAccessException e) { + exception = e; + } + + } else { + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + // if it's ok with the SecurityManager, it's ok with me. + String cname = cn.replace('/', '.'); + if (cname.startsWith("[")) { + int b = cname.lastIndexOf('[') + 2; + if (b > 1 && b < cname.length()) { + cname = cname.substring(b); + } + } + int i = cname.lastIndexOf('.'); + if (i != -1) { + security.checkPackageAccess(cname.substring(0, i)); + } + } + + // Beans.instantiate specified to use SCL when loader is null + if (loader == null) { + loader = (ClassLoader) + AccessController.doPrivileged(new PrivilegedAction() { + public Object run() { + ClassLoader cl = null; + try { + cl = ClassLoader.getSystemClassLoader(); + } catch (SecurityException ex) { } + return cl; + } + }); + } + Class<?> beanClass = Class.forName(cn, true, loader); + try { + return beanClass.newInstance(); + } catch (Exception ex) { + throw new ClassNotFoundException(beanClass + ": " + ex, ex); + } + + } + return null; + } + } +}
diff --git a/activation/src/main/java/javax/activation/CommandMap.java b/activation/src/main/java/javax/activation/CommandMap.java new file mode 100644 index 0000000..69f4910 --- /dev/null +++ b/activation/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<ClassLoader,CommandMap> map = + new WeakHashMap<ClassLoader,CommandMap>(); + + + /** + * Get the default CommandMap. + * + * <ul> + * <li> In cases where a CommandMap instance has been previously set + * to some value (via <i>setDefaultCommandMap</i>) + * return the CommandMap. + * <li> + * In cases where no CommandMap has been set, the CommandMap + * creates an instance of <code>MailcapCommandMap</code> and + * set that to the default, returning its value. + * + * </ul> + * + * @return the CommandMap + */ + public static synchronized CommandMap getDefaultCommandMap() { + if (defaultCommandMap != null) + return defaultCommandMap; + + // fetch per-thread-context-class-loader default + ClassLoader tccl = SecuritySupport.getContextClassLoader(); + CommandMap def = map.get(tccl); + if (def == null) { + def = new MailcapCommandMap(); + map.put(tccl, def); + } + return def; + } + + /** + * Set the default CommandMap. Reset the CommandMap to the default by + * calling this method with <code>null</code>. + * + * @param commandMap The new default CommandMap. + * @exception SecurityException if the caller doesn't have permission + * to change the default + */ + public static synchronized void setDefaultCommandMap(CommandMap commandMap) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + // if it's ok with the SecurityManager, it's ok with me... + security.checkSetFactory(); + } catch (SecurityException ex) { + // otherwise, we also allow it if this code and the + // factory come from the same (non-system) class loader (e.g., + // the JAF classes were loaded with the applet classes). + ClassLoader cl = CommandMap.class.getClassLoader(); + if (cl == null || cl.getParent() == null || + cl != commandMap.getClass().getClassLoader()) { + throw ex; + } + } + } + // remove any per-thread-context-class-loader CommandMap + map.remove(SecuritySupport.getContextClassLoader()); + defaultCommandMap = commandMap; + } + + /** + * Get the preferred command list from a MIME Type. The actual semantics + * are determined by the implementation of the CommandMap. + * + * @param mimeType the MIME type + * @return the CommandInfo classes that represent the command Beans. + */ + abstract public CommandInfo[] getPreferredCommands(String mimeType); + + /** + * Get the preferred command list from a MIME Type. The actual semantics + * are determined by the implementation of the CommandMap. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the list of commands that are returned. The implementation + * in this class simply calls the <code>getPreferredCommands</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param ds a DataSource for the data + * @return the CommandInfo classes that represent the command Beans. + * @since JAF 1.1 + */ + public CommandInfo[] getPreferredCommands(String mimeType, DataSource ds) { + return getPreferredCommands(mimeType); + } + + /** + * Get all the available commands for this type. This method + * should return all the possible commands for this MIME type. + * + * @param mimeType the MIME type + * @return the CommandInfo objects representing all the commands. + */ + abstract public CommandInfo[] getAllCommands(String mimeType); + + /** + * Get all the available commands for this type. This method + * should return all the possible commands for this MIME type. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the list of commands that are returned. The implementation + * in this class simply calls the <code>getAllCommands</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param ds a DataSource for the data + * @return the CommandInfo objects representing all the commands. + * @since JAF 1.1 + */ + public CommandInfo[] getAllCommands(String mimeType, DataSource ds) { + return getAllCommands(mimeType); + } + + /** + * Get the default command corresponding to the MIME type. + * + * @param mimeType the MIME type + * @param cmdName the command name + * @return the CommandInfo corresponding to the command. + */ + abstract public CommandInfo getCommand(String mimeType, String cmdName); + + /** + * Get the default command corresponding to the MIME type. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the command that is chosen. The implementation + * in this class simply calls the <code>getCommand</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param cmdName the command name + * @param ds a DataSource for the data + * @return the CommandInfo corresponding to the command. + * @since JAF 1.1 + */ + public CommandInfo getCommand(String mimeType, String cmdName, + DataSource ds) { + return getCommand(mimeType, cmdName); + } + + /** + * Locate a DataContentHandler that corresponds to the MIME type. + * The mechanism and semantics for determining this are determined + * by the implementation of the particular CommandMap. + * + * @param mimeType the MIME type + * @return the DataContentHandler for the MIME type + */ + abstract public DataContentHandler createDataContentHandler(String + mimeType); + + /** + * Locate a DataContentHandler that corresponds to the MIME type. + * The mechanism and semantics for determining this are determined + * by the implementation of the particular CommandMap. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the choice of DataContentHandler. The implementation + * in this class simply calls the <code>createDataContentHandler</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param ds a DataSource for the data + * @return the DataContentHandler for the MIME type + * @since JAF 1.1 + */ + public DataContentHandler createDataContentHandler(String mimeType, + DataSource ds) { + return createDataContentHandler(mimeType); + } + + /** + * Get all the MIME types known to this command map. + * If the command map doesn't support this operation, + * null is returned. + * + * @return array of MIME types as strings, or null if not supported + * @since JAF 1.1 + */ + public String[] getMimeTypes() { + return null; + } +}
diff --git a/activation/src/main/java/javax/activation/CommandObject.java b/activation/src/main/java/javax/activation/CommandObject.java new file mode 100644 index 0000000..e44913b --- /dev/null +++ b/activation/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. <b>NOTE:</b> it is acceptable for the caller + * to pass <i>null</i> as the value for <code>DataHandler</code>. + * + * @param verb The Command Verb this object refers to. + * @param dh The DataHandler. + * @exception IOException for failures accessing data + */ + public void setCommandContext(String verb, DataHandler dh) + throws IOException; +}
diff --git a/activation/src/main/java/javax/activation/DataContentHandler.java b/activation/src/main/java/javax/activation/DataContentHandler.java new file mode 100644 index 0000000..42e3148 --- /dev/null +++ b/activation/src/main/java/javax/activation/DataContentHandler.java
@@ -0,0 +1,84 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package javax.activation; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import javax.activation.DataSource; + +/** + * The DataContentHandler interface is implemented by objects that can + * be used to extend the capabilities of the DataHandler's implementation + * of the Transferable interface. Through <code>DataContentHandlers</code> + * the framework can be extended to convert streams in to objects, and + * to write objects to streams. <p> + * + * Applications don't generally call the methods in DataContentHandlers + * directly. Instead, an application calls the equivalent methods in + * DataHandler. The DataHandler will attempt to find an appropriate + * DataContentHandler that corresponds to its MIME type using the + * current DataContentHandlerFactory. The DataHandler then calls + * through to the methods in the DataContentHandler. + */ + +public interface DataContentHandler { + /** + * Returns an array of DataFlavor objects indicating the flavors the + * data can be provided in. The array should be ordered according to + * preference for providing the data (from most richly descriptive to + * least descriptive). + * + * @return The DataFlavors. + */ + public DataFlavor[] 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 UnsupportedFlavorException if the handler doesn't + * support the requested flavor + * @exception IOException if the data can't be accessed + */ + public Object getTransferData(DataFlavor df, DataSource ds) + throws UnsupportedFlavorException, IOException; + + /** + * Return an object representing the data in its most preferred form. + * Generally this will be the form described by the first DataFlavor + * returned by the <code>getTransferDataFlavors</code> method. + * + * @param ds The DataSource representing the data to be converted. + * @return The constructed Object. + * @exception IOException if the data can't be accessed + */ + public Object getContent(DataSource ds) throws IOException; + + /** + * Convert the object to a byte stream of the specified MIME type + * and write it to the output stream. + * + * @param obj The object to be converted. + * @param mimeType The requested MIME type of the resulting byte stream. + * @param os The output stream into which to write the converted + * byte stream. + * @exception IOException errors writing to the stream + */ + public void writeTo(Object obj, String mimeType, OutputStream os) + throws IOException; +}
diff --git a/activation/src/main/java/javax/activation/DataContentHandlerFactory.java b/activation/src/main/java/javax/activation/DataContentHandlerFactory.java new file mode 100644 index 0000000..0cd0f5c --- /dev/null +++ b/activation/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 <code>DataContentHandlers</code>. An + * implementation of this interface should map a MIME type into an + * instance of DataContentHandler. The design pattern for classes implementing + * this interface is the same as for the ContentHandler mechanism used in + * <code>java.net.URL</code>. + */ + +public interface DataContentHandlerFactory { + + /** + * Creates a new DataContentHandler object for the MIME type. + * + * @param mimeType the MIME type to create the DataContentHandler for. + * @return The new <code>DataContentHandler</code>, or <i>null</i> + * if none are found. + */ + public DataContentHandler createDataContentHandler(String mimeType); +}
diff --git a/activation/src/main/java/javax/activation/DataHandler.java b/activation/src/main/java/javax/activation/DataHandler.java new file mode 100644 index 0000000..897fd95 --- /dev/null +++ b/activation/src/main/java/javax/activation/DataHandler.java
@@ -0,0 +1,882 @@ +/* + * Copyright (c) 1997, 2018 Oracle and/or its affiliates. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Distribution License v. 1.0, which is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +package javax.activation; + +import java.io.InputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.PipedInputStream; +import java.io.PipedOutputStream; +import java.io.OutputStreamWriter; +import java.net.URL; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.UnsupportedFlavorException; + +/** + * The DataHandler class provides a consistent interface to data + * available in many different sources and formats. + * It manages simple stream to string conversions and related operations + * using DataContentHandlers. + * It provides access to commands that can operate on the data. + * The commands are found using a CommandMap. <p> + * + * <b>DataHandler and the Transferable Interface</b><p> + * DataHandler implements the Transferable interface so that data can + * be used in AWT data transfer operations, such as cut and paste and + * drag and drop. The implementation of the Transferable interface + * relies on the availability of an installed DataContentHandler + * object corresponding to the MIME type of the data represented in + * the specific instance of the DataHandler.<p> + * + * <b>DataHandler and CommandMaps</b><p> + * The DataHandler keeps track of the current CommandMap that it uses to + * service requests for commands (<code>getCommand</code>, + * <code>getAllCommands</code>, <code>getPreferredCommands</code>). + * Each instance of a DataHandler may have a CommandMap associated with + * it using the <code>setCommandMap</code> method. If a CommandMap was + * not set, DataHandler calls the <code>getDefaultCommandMap</code> + * method in CommandMap and uses the value it returns. See + * <i>CommandMap</i> for more information. <p> + * + * <b>DataHandler and URLs</b><p> + * The current DataHandler implementation creates a private + * instance of URLDataSource when it is constructed with a URL. + * + * @see javax.activation.CommandMap + * @see javax.activation.DataContentHandler + * @see javax.activation.DataSource + * @see javax.activation.URLDataSource + */ + +public class DataHandler implements Transferable { + + // Use the datasource to indicate whether we were started via the + // DataSource constructor or the object constructor. + private DataSource dataSource = null; + private DataSource objDataSource = null; + + // The Object and mimetype from the constructor (if passed in). + // object remains null if it was instantiated with a + // DataSource. + private Object object = null; + private String objectMimeType = null; + + // Keep track of the CommandMap + private CommandMap currentCommandMap = null; + + // our transfer flavors + private static final DataFlavor emptyFlavors[] = new DataFlavor[0]; + private DataFlavor transferFlavors[] = emptyFlavors; + + // our DataContentHandler + private DataContentHandler dataContentHandler = null; + private DataContentHandler factoryDCH = null; + + // our DataContentHandlerFactory + private static DataContentHandlerFactory factory = null; + private DataContentHandlerFactory oldFactory = null; + // the short representation of the ContentType (sans params) + private String shortType = null; + + /** + * Create a <code>DataHandler</code> instance referencing the + * specified DataSource. The data exists in a byte stream form. + * The DataSource will provide an InputStream to access the data. + * + * @param ds the DataSource + */ + public DataHandler(DataSource ds) { + // save a reference to the incoming DS + dataSource = ds; + oldFactory = factory; // keep track of the factory + } + + /** + * Create a <code>DataHandler</code> instance representing an object + * of this MIME type. This constructor is + * used when the application already has an in-memory representation + * of the data in the form of a Java Object. + * + * @param obj the Java Object + * @param mimeType the MIME type of the object + */ + public DataHandler(Object obj, String mimeType) { + object = obj; + objectMimeType = mimeType; + oldFactory = factory; // keep track of the factory + } + + /** + * Create a <code>DataHandler</code> instance referencing a URL. + * The DataHandler internally creates a <code>URLDataSource</code> + * instance to represent the URL. + * + * @param url a URL object + */ + public DataHandler(URL url) { + dataSource = new URLDataSource(url); + oldFactory = factory; // keep track of the factory + } + + /** + * Return the CommandMap for this instance of DataHandler. + */ + private synchronized CommandMap getCommandMap() { + if (currentCommandMap != null) + return currentCommandMap; + else + return CommandMap.getDefaultCommandMap(); + } + + /** + * Return the DataSource associated with this instance + * of DataHandler. + * <p> + * For DataHandlers that have been instantiated with a DataSource, + * this method returns the DataSource that was used to create the + * DataHandler object. In other cases the DataHandler + * constructs a DataSource from the data used to construct + * the DataHandler. DataSources created for DataHandlers <b>not</b> + * instantiated with a DataSource are cached for performance + * reasons. + * + * @return a valid DataSource object for this DataHandler + */ + public DataSource getDataSource() { + if (dataSource == null) { + // create one on the fly + if (objDataSource == null) + objDataSource = new DataHandlerDataSource(this); + return objDataSource; + } + return dataSource; + } + + /** + * Return the name of the data object. If this DataHandler + * was created with a DataSource, this method calls through + * to the <code>DataSource.getName</code> method, otherwise it + * returns <i>null</i>. + * + * @return the name of the object + */ + public String getName() { + if (dataSource != null) + return dataSource.getName(); + else + return null; + } + + /** + * Return the MIME type of this object as retrieved from + * the source object. Note that this is the <i>full</i> + * type with parameters. + * + * @return the MIME type + */ + public String getContentType() { + if (dataSource != null) // data source case + return dataSource.getContentType(); + else + return objectMimeType; // obj/type case + } + + /** + * Get the InputStream for this object. <p> + * + * For DataHandlers instantiated with a DataSource, the DataHandler + * calls the <code>DataSource.getInputStream</code> method and + * returns the result to the caller. + * <p> + * For DataHandlers instantiated with an Object, the DataHandler + * first attempts to find a DataContentHandler for the Object. If + * the DataHandler can not find a DataContentHandler for this MIME + * type, it throws an UnsupportedDataTypeException. If it is + * successful, it creates a pipe and a thread. The thread uses the + * DataContentHandler's <code>writeTo</code> method to write the + * stream data into one end of the pipe. The other end of the pipe + * is returned to the caller. Because a thread is created to copy + * the data, IOExceptions that may occur during the copy can not be + * propagated back to the caller. The result is an empty stream.<p> + * + * @return the InputStream representing this data + * @exception IOException if an I/O error occurs + * + * @see javax.activation.DataContentHandler#writeTo + * @see javax.activation.UnsupportedDataTypeException + */ + public InputStream getInputStream() throws IOException { + InputStream ins = null; + + if (dataSource != null) { + ins = dataSource.getInputStream(); + } else { + DataContentHandler dch = getDataContentHandler(); + // we won't even try if we can't get a dch + if (dch == null) + throw new UnsupportedDataTypeException( + "no DCH for MIME type " + getBaseType()); + + if (dch instanceof ObjectDataContentHandler) { + if (((ObjectDataContentHandler)dch).getDCH() == null) + throw new UnsupportedDataTypeException( + "no object DCH for MIME type " + getBaseType()); + } + // there is none but the default^^^^^^^^^^^^^^^^ + final DataContentHandler fdch = dch; + + // from bill s. + // ce n'est pas une pipe! + // + // NOTE: This block of code needs to throw exceptions, but + // can't because it is in another thread!!! ARG! + // + final PipedOutputStream pos = new PipedOutputStream(); + PipedInputStream pin = new PipedInputStream(pos); + new Thread( + new Runnable() { + public void run() { + try { + fdch.writeTo(object, objectMimeType, pos); + } catch (IOException e) { + + } finally { + try { + pos.close(); + } catch (IOException ie) { } + } + } + }, + "DataHandler.getInputStream").start(); + ins = pin; + } + + return ins; + } + + /** + * Write the data to an <code>OutputStream</code>.<p> + * + * If the DataHandler was created with a DataSource, writeTo + * retrieves the InputStream and copies the bytes from the + * InputStream to the OutputStream passed in. + * <p> + * If the DataHandler was created with an object, writeTo + * retrieves the DataContentHandler for the object's type. + * If the DataContentHandler was found, it calls the + * <code>writeTo</code> method on the <code>DataContentHandler</code>. + * + * @param os the OutputStream to write to + * @exception IOException if an I/O error occurs + */ + public void writeTo(OutputStream os) throws IOException { + // for the DataSource case + if (dataSource != null) { + InputStream is = null; + byte data[] = new byte[8*1024]; + int bytes_read; + + is = dataSource.getInputStream(); + + try { + while ((bytes_read = is.read(data)) > 0) { + os.write(data, 0, bytes_read); + } + } finally { + is.close(); + is = null; + } + } else { // for the Object case + DataContentHandler dch = getDataContentHandler(); + dch.writeTo(object, objectMimeType, os); + } + } + + /** + * Get an OutputStream for this DataHandler to allow overwriting + * the underlying data. + * If the DataHandler was created with a DataSource, the + * DataSource's <code>getOutputStream</code> method is called. + * Otherwise, <code>null</code> is returned. + * + * @return the OutputStream + * @exception IOException for failures creating the OutputStream + * + * @see javax.activation.DataSource#getOutputStream + * @see javax.activation.URLDataSource + */ + public OutputStream getOutputStream() throws IOException { + if (dataSource != null) + return dataSource.getOutputStream(); + else + return null; + } + + /** + * Return the DataFlavors in which this data is available. <p> + * + * Returns an array of DataFlavor objects indicating the flavors + * the data can be provided in. The array is usually ordered + * according to preference for providing the data, from most + * richly descriptive to least richly descriptive.<p> + * + * The DataHandler attempts to find a DataContentHandler that + * corresponds to the MIME type of the data. If one is located, + * the DataHandler calls the DataContentHandler's + * <code>getTransferDataFlavors</code> method. <p> + * + * If a DataContentHandler can <i>not</i> be located, and if the + * DataHandler was created with a DataSource (or URL), one + * DataFlavor is returned that represents this object's MIME type + * and the <code>java.io.InputStream</code> class. If the + * DataHandler was created with an object and a MIME type, + * getTransferDataFlavors returns one DataFlavor that represents + * this object's MIME type and the object's class. + * + * @return an array of data flavors in which this data can be transferred + * @see javax.activation.DataContentHandler#getTransferDataFlavors + */ + public synchronized DataFlavor[] getTransferDataFlavors() { + if (factory != oldFactory) // if the factory has changed, clear cache + transferFlavors = emptyFlavors; + + // if it's not set, set it... + if (transferFlavors == emptyFlavors) + transferFlavors = getDataContentHandler().getTransferDataFlavors(); + if (transferFlavors == emptyFlavors) + return transferFlavors; // no need to clone an empty array + else + return transferFlavors.clone(); + } + + /** + * Returns whether the specified data flavor is supported + * for this object.<p> + * + * This method iterates through the DataFlavors returned from + * <code>getTransferDataFlavors</code>, comparing each with + * the specified flavor. + * + * @param flavor the requested flavor for the data + * @return true if the data flavor is supported + * @see javax.activation.DataHandler#getTransferDataFlavors + */ + public boolean isDataFlavorSupported(DataFlavor flavor) { + DataFlavor[] lFlavors = getTransferDataFlavors(); + + for (int i = 0; i < lFlavors.length; i++) { + if (lFlavors[i].equals(flavor)) + return true; + } + return false; + } + + /** + * Returns an object that represents the data to be + * transferred. The class of the object returned is defined by the + * representation class of the data flavor.<p> + * + * <b>For DataHandler's created with DataSources or URLs:</b><p> + * + * The DataHandler attempts to locate a DataContentHandler + * for this MIME type. If one is found, the passed in DataFlavor + * and the type of the data are passed to its <code>getTransferData</code> + * method. If the DataHandler fails to locate a DataContentHandler + * and the flavor specifies this object's MIME type and the + * <code>java.io.InputStream</code> class, this object's InputStream + * is returned. + * Otherwise it throws an UnsupportedFlavorException. <p> + * + * <b>For DataHandler's created with Objects:</b><p> + * + * The DataHandler attempts to locate a DataContentHandler + * for this MIME type. If one is found, the passed in DataFlavor + * and the type of the data are passed to its getTransferData + * method. If the DataHandler fails to locate a DataContentHandler + * and the flavor specifies this object's MIME type and its class, + * this DataHandler's referenced object is returned. + * Otherwise it throws an UnsupportedFlavorException. + * + * @param flavor the requested flavor for the data + * @return the object + * @exception UnsupportedFlavorException if the data could not be + * converted to the requested flavor + * @exception IOException if an I/O error occurs + * @see javax.activation.ActivationDataFlavor + */ + public Object getTransferData(DataFlavor flavor) + throws UnsupportedFlavorException, IOException { + return getDataContentHandler().getTransferData(flavor, dataSource); + } + + /** + * Set the CommandMap for use by this DataHandler. + * Setting it to <code>null</code> causes the CommandMap to revert + * to the CommandMap returned by the + * <code>CommandMap.getDefaultCommandMap</code> method. + * Changing the CommandMap, or setting it to <code>null</code>, + * clears out any data cached from the previous CommandMap. + * + * @param commandMap the CommandMap to use in this DataHandler + * + * @see javax.activation.CommandMap#setDefaultCommandMap + */ + public synchronized void setCommandMap(CommandMap commandMap) { + if (commandMap != currentCommandMap || commandMap == null) { + // clear cached values... + transferFlavors = emptyFlavors; + dataContentHandler = null; + + currentCommandMap = commandMap; + } + } + + /** + * Return the <i>preferred</i> commands for this type of data. + * This method calls the <code>getPreferredCommands</code> method + * in the CommandMap associated with this instance of DataHandler. + * This method returns an array that represents a subset of + * available commands. In cases where multiple commands for the + * MIME type represented by this DataHandler are present, the + * installed CommandMap chooses the appropriate commands. + * + * @return the CommandInfo objects representing the preferred commands + * + * @see javax.activation.CommandMap#getPreferredCommands + */ + public CommandInfo[] getPreferredCommands() { + if (dataSource != null) + return getCommandMap().getPreferredCommands(getBaseType(), + dataSource); + else + return getCommandMap().getPreferredCommands(getBaseType()); + } + + /** + * Return all the commands for this type of data. + * This method returns an array containing all commands + * for the type of data represented by this DataHandler. The + * MIME type for the underlying data represented by this DataHandler + * is used to call through to the <code>getAllCommands</code> method + * of the CommandMap associated with this DataHandler. + * + * @return the CommandInfo objects representing all the commands + * + * @see javax.activation.CommandMap#getAllCommands + */ + public CommandInfo[] getAllCommands() { + if (dataSource != null) + return getCommandMap().getAllCommands(getBaseType(), dataSource); + else + return getCommandMap().getAllCommands(getBaseType()); + } + + /** + * Get the command <i>cmdName</i>. Use the search semantics as + * defined by the CommandMap installed in this DataHandler. The + * MIME type for the underlying data represented by this DataHandler + * is used to call through to the <code>getCommand</code> method + * of the CommandMap associated with this DataHandler. + * + * @param cmdName the command name + * @return the CommandInfo corresponding to the command + * + * @see javax.activation.CommandMap#getCommand + */ + public CommandInfo getCommand(String cmdName) { + if (dataSource != null) + return getCommandMap().getCommand(getBaseType(), cmdName, + dataSource); + else + return getCommandMap().getCommand(getBaseType(), cmdName); + } + + /** + * Return the data in its preferred Object form. <p> + * + * If the DataHandler was instantiated with an object, return + * the object. <p> + * + * If the DataHandler was instantiated with a DataSource, + * this method uses a DataContentHandler to return the content + * object for the data represented by this DataHandler. If no + * <code>DataContentHandler</code> can be found for the + * the type of this data, the DataHandler returns an + * InputStream for the data. + * + * @return the content. + * @exception IOException if an IOException occurs during + * this operation. + */ + public Object getContent() throws IOException { + if (object != null) + return object; + else + return getDataContentHandler().getContent(getDataSource()); + } + + /** + * A convenience method that takes a CommandInfo object + * and instantiates the corresponding command, usually + * a JavaBean component. + * <p> + * This method calls the CommandInfo's <code>getCommandObject</code> + * method with the <code>ClassLoader</code> used to load + * the <code>javax.activation.DataHandler</code> class itself. + * + * @param cmdinfo the CommandInfo corresponding to a command + * @return the instantiated command object + */ + public Object getBean(CommandInfo cmdinfo) { + Object bean = null; + + try { + // make the bean + ClassLoader cld = null; + // First try the "application's" class loader. + cld = SecuritySupport.getContextClassLoader(); + if (cld == null) + cld = this.getClass().getClassLoader(); + bean = cmdinfo.getCommandObject(this, cld); + } catch (IOException e) { + } catch (ClassNotFoundException e) { } + + return bean; + } + + /** + * Get the DataContentHandler for this DataHandler: <p> + * + * If a DataContentHandlerFactory is set, use it. + * Otherwise look for an object to serve DCH in the + * following order: <p> + * + * 1) if a factory is set, use it <p> + * 2) if a CommandMap is set, use it <p> + * 3) use the default CommandMap <p> + * + * In any case, wrap the real DataContentHandler with one of our own + * to handle any missing cases, fill in defaults, and to ensure that + * we always have a non-null DataContentHandler. + * + * @return the requested DataContentHandler + */ + private synchronized DataContentHandler getDataContentHandler() { + + // make sure the factory didn't change + if (factory != oldFactory) { + oldFactory = factory; + factoryDCH = null; + dataContentHandler = null; + transferFlavors = emptyFlavors; + } + + if (dataContentHandler != null) + return dataContentHandler; + + String simpleMT = getBaseType(); + + if (factoryDCH == null && factory != null) + factoryDCH = factory.createDataContentHandler(simpleMT); + + if (factoryDCH != null) + dataContentHandler = factoryDCH; + + if (dataContentHandler == null) { + if (dataSource != null) + dataContentHandler = getCommandMap(). + createDataContentHandler(simpleMT, dataSource); + else + dataContentHandler = getCommandMap(). + createDataContentHandler(simpleMT); + } + + // getDataContentHandler always uses these 'wrapper' handlers + // to make sure it returns SOMETHING meaningful... + if (dataSource != null) + dataContentHandler = new DataSourceDataContentHandler( + dataContentHandler, + dataSource); + else + dataContentHandler = new ObjectDataContentHandler( + dataContentHandler, + object, + objectMimeType); + return dataContentHandler; + } + + /** + * Use the MimeType class to extract the MIME type/subtype, + * ignoring the parameters. The type is cached. + */ + private synchronized String getBaseType() { + if (shortType == null) { + String ct = getContentType(); + try { + MimeType mt = new MimeType(ct); + shortType = mt.getBaseType(); + } catch (MimeTypeParseException e) { + shortType = ct; + } + } + return shortType; + } + + /** + * Sets the DataContentHandlerFactory. The DataContentHandlerFactory + * is called first to find DataContentHandlers. + * The DataContentHandlerFactory can only be set once. + * <p> + * If the DataContentHandlerFactory has already been set, + * this method throws an Error. + * + * @param newFactory the DataContentHandlerFactory + * @exception Error if the factory has already been defined. + * + * @see javax.activation.DataContentHandlerFactory + */ + public static synchronized void setDataContentHandlerFactory( + DataContentHandlerFactory newFactory) { + if (factory != null) + throw new Error("DataContentHandlerFactory already defined"); + + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + // if it's ok with the SecurityManager, it's ok with me... + security.checkSetFactory(); + } catch (SecurityException ex) { + // otherwise, we also allow it if this code and the + // factory come from the same class loader (e.g., + // the JAF classes were loaded with the applet classes). + if (DataHandler.class.getClassLoader() != + newFactory.getClass().getClassLoader()) + throw ex; + } + } + factory = newFactory; + } +} + +/** + * The DataHanderDataSource class implements the + * DataSource interface when the DataHandler is constructed + * with an Object and a mimeType string. + */ +class DataHandlerDataSource implements DataSource { + DataHandler dataHandler = null; + + /** + * The constructor. + */ + public DataHandlerDataSource(DataHandler dh) { + this.dataHandler = dh; + } + + /** + * Returns an <code>InputStream</code> representing this object. + * @return the <code>InputStream</code> + */ + public InputStream getInputStream() throws IOException { + return dataHandler.getInputStream(); + } + + /** + * Returns the <code>OutputStream</code> for this object. + * @return the <code>OutputStream</code> + */ + public OutputStream getOutputStream() throws IOException { + return dataHandler.getOutputStream(); + } + + /** + * Returns the MIME type of the data represented by this object. + * @return the MIME type + */ + public String getContentType() { + return dataHandler.getContentType(); + } + + /** + * Returns the name of this object. + * @return the name of this object + */ + public String getName() { + return dataHandler.getName(); // what else would it be? + } +} + +/* + * DataSourceDataContentHandler + * + * This is a <i>private</i> DataContentHandler that wraps the real + * DataContentHandler in the case where the DataHandler was instantiated + * with a DataSource. + */ +class DataSourceDataContentHandler implements DataContentHandler { + private DataSource ds = null; + private DataFlavor transferFlavors[] = null; + private DataContentHandler dch = null; + + /** + * The constructor. + */ + public DataSourceDataContentHandler(DataContentHandler dch, DataSource ds) { + this.ds = ds; + this.dch = dch; + } + + /** + * Return the DataFlavors for this <code>DataContentHandler</code>. + * @return the DataFlavors + */ + public DataFlavor[] getTransferDataFlavors() { + + if (transferFlavors == null) { + if (dch != null) { // is there a dch? + transferFlavors = dch.getTransferDataFlavors(); + } else { + transferFlavors = new DataFlavor[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(DataFlavor 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); + } + + public Object getContent(DataSource ds) throws IOException { + + if (dch != null) + return dch.getContent(ds); + else + return ds.getInputStream(); + } + + /** + * Write the object to the output stream. + */ + public void writeTo(Object obj, String mimeType, OutputStream os) + throws IOException { + if (dch != null) + dch.writeTo(obj, mimeType, os); + else + throw new UnsupportedDataTypeException( + "no DCH for content type " + ds.getContentType()); + } +} + +/* + * ObjectDataContentHandler + * + * This is a <i>private</i> DataContentHandler that wraps the real + * DataContentHandler in the case where the DataHandler was instantiated + * with an object. + */ +class ObjectDataContentHandler implements DataContentHandler { + private DataFlavor transferFlavors[] = null; + private Object obj; + private String mimeType; + private DataContentHandler dch = null; + + /** + * The constructor. + */ + public ObjectDataContentHandler(DataContentHandler dch, + Object obj, String mimeType) { + this.obj = obj; + this.mimeType = mimeType; + this.dch = dch; + } + + /** + * Return the DataContentHandler for this object. + * Used only by the DataHandler class. + */ + public DataContentHandler getDCH() { + return dch; + } + + /** + * Return the DataFlavors for this <code>DataContentHandler</code>. + * @return the DataFlavors + */ + public synchronized DataFlavor[] getTransferDataFlavors() { + if (transferFlavors == null) { + if (dch != null) { + transferFlavors = dch.getTransferDataFlavors(); + } else { + transferFlavors = new DataFlavor[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(DataFlavor 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); + + } + + 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/activation/src/main/java/javax/activation/DataSource.java b/activation/src/main/java/javax/activation/DataSource.java new file mode 100644 index 0000000..320d8ae --- /dev/null +++ b/activation/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 <code>InputStreams</code> and + * <code>OutputStreams</code> where appropriate. + */ + +public interface DataSource { + + /** + * This method returns an <code>InputStream</code> representing + * the data and throws the appropriate exception if it can + * not do so. Note that a new <code>InputStream</code> object must be + * returned each time this method is called, and the stream must be + * positioned at the beginning of the data. + * + * @return an InputStream + * @exception IOException for failures creating the InputStream + */ + public InputStream getInputStream() throws IOException; + + /** + * This method returns an <code>OutputStream</code> where the + * data can be written and throws the appropriate exception if it can + * not do so. Note that a new <code>OutputStream</code> object must + * be returned each time this method is called, and the stream must + * be positioned at the location the data is to be written. + * + * @return an OutputStream + * @exception IOException for failures creating the OutputStream + */ + public OutputStream getOutputStream() throws IOException; + + /** + * This method returns the MIME type of the data in the form of a + * string. It should always return a valid type. It is suggested + * that getContentType return "application/octet-stream" if the + * DataSource implementation can not determine the data type. + * + * @return the MIME Type + */ + public String getContentType(); + + /** + * Return the <i>name</i> of this object where the name of the object + * is dependant on the nature of the underlying objects. DataSources + * encapsulating files may choose to return the filename of the object. + * (Typically this would be the last component of the filename, not an + * entire pathname.) + * + * @return the name of the object. + */ + public String getName(); +}
diff --git a/activation/src/main/java/javax/activation/FileDataSource.java b/activation/src/main/java/javax/activation/FileDataSource.java new file mode 100644 index 0000000..3048918 --- /dev/null +++ b/activation/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. <p> + * + * <b>FileDataSource Typing Semantics</b><p> + * + * The FileDataSource class delegates data typing of files + * to an object subclassed from the FileTypeMap class. + * The <code>setFileTypeMap</code> method can be used to explicitly + * set the FileTypeMap for an instance of FileDataSource. If no + * FileTypeMap is set, the FileDataSource will call the FileTypeMap's + * getDefaultFileTypeMap method to get the System's default FileTypeMap. + * + * @see javax.activation.DataSource + * @see javax.activation.FileTypeMap + * @see javax.activation.MimetypesFileTypeMap + */ +public class FileDataSource implements DataSource { + + // keep track of original 'ref' passed in, non-null + // one indicated which was passed in: + private File _file = null; + private FileTypeMap typeMap = null; + + /** + * Creates a FileDataSource from a File object. <i>Note: + * The file will not actually be opened until a method is + * called that requires the file to be opened.</i> + * + * @param file the file + */ + public FileDataSource(File file) { + _file = file; // save the file Object... + } + + /** + * Creates a FileDataSource from + * the specified path name. <i>Note: + * The file will not actually be opened until a method is + * called that requires the file to be opened.</i> + * + * @param name the system-dependent file name. + */ + public FileDataSource(String name) { + this(new File(name)); // use the file constructor + } + + /** + * This method will return an InputStream representing the + * the data and will throw an IOException if it can + * not do so. This method will return a new + * instance of InputStream with each invocation. + * + * @return an InputStream + */ + public InputStream getInputStream() throws IOException { + return new FileInputStream(_file); + } + + /** + * This method will return an OutputStream representing the + * the data and will throw an IOException if it can + * not do so. This method will return a new instance of + * OutputStream with each invocation. + * + * @return an OutputStream + */ + public OutputStream getOutputStream() throws IOException { + return new FileOutputStream(_file); + } + + /** + * This method returns the MIME type of the data in the form of a + * string. This method uses the currently installed FileTypeMap. If + * there is no FileTypeMap explictly set, the FileDataSource will + * call the <code>getDefaultFileTypeMap</code> method on + * FileTypeMap to acquire a default FileTypeMap. <i>Note: By + * default, the FileTypeMap used will be a MimetypesFileTypeMap.</i> + * + * @return the MIME Type + * @see javax.activation.FileTypeMap#getDefaultFileTypeMap + */ + public String getContentType() { + // check to see if the type map is null? + if (typeMap == null) + return FileTypeMap.getDefaultFileTypeMap().getContentType(_file); + else + return typeMap.getContentType(_file); + } + + /** + * Return the <i>name</i> of this object. The FileDataSource + * will return the file name of the object. + * + * @return the name of the object. + * @see javax.activation.DataSource + */ + public String getName() { + return _file.getName(); + } + + /** + * Return the File object that corresponds to this FileDataSource. + * @return the File object for the file represented by this object. + */ + public File getFile() { + return _file; + } + + /** + * Set the FileTypeMap to use with this FileDataSource + * + * @param map The FileTypeMap for this object. + */ + public void setFileTypeMap(FileTypeMap map) { + typeMap = map; + } +}
diff --git a/activation/src/main/java/javax/activation/FileTypeMap.java b/activation/src/main/java/javax/activation/FileTypeMap.java new file mode 100644 index 0000000..683f998 --- /dev/null +++ b/activation/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 Map<ClassLoader,FileTypeMap> map = + new WeakHashMap<ClassLoader,FileTypeMap>(); + + /** + * The default constructor. + */ + public FileTypeMap() { + super(); + } + + /** + * Return the type of the file object. This method should + * always return a valid MIME type. + * + * @param file A file to be typed. + * @return The content type. + */ + abstract public String getContentType(File file); + + /** + * Return the type of the file passed in. This method should + * always return a valid MIME type. + * + * @param filename the pathname of the file. + * @return The content type. + */ + abstract public String getContentType(String filename); + + /** + * Sets the default FileTypeMap for the system. This instance + * will be returned to callers of getDefaultFileTypeMap. + * + * @param fileTypeMap The FileTypeMap. + * @exception SecurityException if the caller doesn't have permission + * to change the default + */ + public static synchronized void setDefaultFileTypeMap(FileTypeMap fileTypeMap) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + try { + // if it's ok with the SecurityManager, it's ok with me... + security.checkSetFactory(); + } catch (SecurityException ex) { + // otherwise, we also allow it if this code and the + // factory come from the same (non-system) class loader (e.g., + // the JAF classes were loaded with the applet classes). + ClassLoader cl = FileTypeMap.class.getClassLoader(); + if (cl == null || cl.getParent() == null || + cl != fileTypeMap.getClass().getClassLoader()) + throw ex; + } + } + // remove any per-thread-context-class-loader FileTypeMap + map.remove(SecuritySupport.getContextClassLoader()); + defaultMap = fileTypeMap; + } + + /** + * Return the default FileTypeMap for the system. + * If setDefaultFileTypeMap was called, return + * that instance, otherwise return an instance of + * <code>MimetypesFileTypeMap</code>. + * + * @return The default FileTypeMap + * @see javax.activation.FileTypeMap#setDefaultFileTypeMap + */ + public static synchronized FileTypeMap getDefaultFileTypeMap() { + if (defaultMap != null) + return defaultMap; + + // fetch per-thread-context-class-loader default + ClassLoader tccl = SecuritySupport.getContextClassLoader(); + FileTypeMap def = map.get(tccl); + if (def == null) { + def = new MimetypesFileTypeMap(); + map.put(tccl, def); + } + return def; + } +}
diff --git a/activation/src/main/java/javax/activation/MailcapCommandMap.java b/activation/src/main/java/javax/activation/MailcapCommandMap.java new file mode 100644 index 0000000..f5e0672 --- /dev/null +++ b/activation/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 + * (<A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>). + * The MailcapCommandMap can be configured both programmatically + * and via configuration files. + * <p> + * <b>Mailcap file search order:</b><p> + * The MailcapCommandMap looks in various places in the user's + * system for mailcap file entries. When requests are made + * to search for commands in the MailcapCommandMap, it searches + * mailcap files in the following order: + * <ol> + * <li> Programatically added entries to the MailcapCommandMap instance. + * <li> The file <code>.mailcap</code> in the user's home directory. + * <li> The file <code>mailcap</code> in the Java runtime. + * <li> The file or resources named <code>META-INF/mailcap</code>. + * <li> The file or resource named <code>META-INF/mailcap.default</code> + * (usually found only in the <code>activation.jar</code> file). + * </ol> + * <p> + * (The current implementation looks for the <code>mailcap</code> file + * in the Java runtime in the directory <code><i>java.home</i>/conf</code> + * if it exists, and otherwise in the directory + * <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value + * of the "java.home" System property. Note that the "conf" directory was + * introduced in JDK 9.) + * <p> + * <b>Mailcap file format:</b><p> + * + * Mailcap files must conform to the mailcap + * file specification (RFC 1524, <i>A User Agent Configuration Mechanism + * For Multimedia Mail Format Information</i>). + * The file format consists of entries corresponding to + * particular MIME types. In general, the specification + * specifies <i>applications</i> for clients to use when they + * themselves cannot operate on the specified MIME type. The + * MailcapCommandMap extends this specification by using a parameter mechanism + * in mailcap files that allows JavaBeans(tm) components to be specified as + * corresponding to particular commands for a MIME type.<p> + * + * When a mailcap file is + * parsed, the MailcapCommandMap recognizes certain parameter signatures, + * specifically those parameter names that begin with <code>x-java-</code>. + * The MailcapCommandMap uses this signature to find + * command entries for inclusion into its registries. + * Parameter names with the form <code>x-java-<name></code> + * are read by the MailcapCommandMap as identifying a command + * with the name <i>name</i>. When the <i>name</i> is <code> + * content-handler</code> the MailcapCommandMap recognizes the class + * signified by this parameter as a <i>DataContentHandler</i>. + * All other commands are handled generically regardless of command + * name. The command implementation is specified by a fully qualified + * class name of a JavaBean(tm) component. For example; a command for viewing + * some data can be specified as: <code>x-java-view=com.foo.ViewBean</code>.<p> + * + * When the command name is <code>fallback-entry</code>, the value of + * the command may be <code>true</code> or <code>false</code>. An + * entry for a MIME type that includes a parameter of + * <code>x-java-fallback-entry=true</code> defines fallback commands + * for that MIME type that will only be used if no non-fallback entry + * can be found. For example, an entry of the form <code>text/*; ; + * x-java-fallback-entry=true; x-java-view=com.sun.TextViewer</code> + * specifies a view command to be used for any text MIME type. This + * view command would only be used if a non-fallback view command for + * the MIME type could not be found.<p> + * + * MailcapCommandMap aware mailcap files have the + * following general form:<p> + * <code> + * # Comments begin with a '#' and continue to the end of the line.<br> + * <mime type>; ; <parameter list><br> + * # Where a parameter list consists of one or more parameters,<br> + * # where parameters look like: x-java-view=com.sun.TextViewer<br> + * # and a parameter list looks like: <br> + * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit + * <br> + * # Note that mailcap entries that do not contain 'x-java' parameters<br> + * # and comply to RFC 1524 are simply ignored:<br> + * image/gif; /usr/dt/bin/sdtimage %s<br> + * + * </code> + * <p> + * + * @author Bart Calder + * @author Bill Shannon + */ + +public class MailcapCommandMap extends CommandMap { + /* + * We manage a collection of databases, searched in order. + */ + private MailcapFile[] DB; + private static final int PROG = 0; // programmatically added entries + + private static final String confDir; + + static { + String dir = null; + try { + dir = (String)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + String home = System.getProperty("java.home"); + String newdir = home + File.separator + "conf"; + File conf = new File(newdir); + if (conf.exists()) + return newdir + File.separator; + else + return home + File.separator + "lib" + File.separator; + } + }); + } catch (Exception ex) { + // ignore any exceptions + } + confDir = dir; + } + + /** + * The default Constructor. + */ + public MailcapCommandMap() { + super(); + List dbv = new ArrayList(5); // usually 5 or less databases + MailcapFile mf = null; + dbv.add(null); // place holder for PROG entry + + LogSupport.log("MailcapCommandMap: load HOME"); + try { + String user_home = System.getProperty("user.home"); + + if (user_home != null) { + String path = user_home + File.separator + ".mailcap"; + mf = loadFile(path); + if (mf != null) + dbv.add(mf); + } + } catch (SecurityException ex) {} + + LogSupport.log("MailcapCommandMap: load SYS"); + try { + // check system's home + if (confDir != null) { + mf = loadFile(confDir + "mailcap"); + if (mf != null) + dbv.add(mf); + } + } catch (SecurityException ex) {} + + LogSupport.log("MailcapCommandMap: load JAR"); + // load from the app's jar file + loadAllResources(dbv, "META-INF/mailcap"); + + LogSupport.log("MailcapCommandMap: load DEF"); + mf = loadResource("/META-INF/mailcap.default"); + + if (mf != null) + dbv.add(mf); + + DB = new MailcapFile[dbv.size()]; + DB = (MailcapFile[])dbv.toArray(DB); + } + + /** + * Load from the named resource. + */ + private MailcapFile loadResource(String name) { + InputStream clis = null; + try { + clis = SecuritySupport.getResourceAsStream(this.getClass(), name); + if (clis != null) { + MailcapFile mf = new MailcapFile(clis); + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: successfully loaded " + + "mailcap file: " + name); + return mf; + } else { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: not loading " + + "mailcap file: " + name); + } + } catch (IOException e) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: can't load " + name, e); + } catch (SecurityException sex) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: can't load " + name, sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException ex) { } // ignore it + } + return null; + } + + /** + * Load all of the named resource. + */ + private void loadAllResources(List v, String name) { + boolean anyLoaded = false; + try { + URL[] urls; + ClassLoader cld = null; + // First try the "application's" class loader. + cld = SecuritySupport.getContextClassLoader(); + if (cld == null) + cld = this.getClass().getClassLoader(); + if (cld != null) + urls = SecuritySupport.getResources(cld, name); + else + urls = SecuritySupport.getSystemResources(name); + if (urls != null) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: getResources"); + for (int i = 0; i < urls.length; i++) { + URL url = urls[i]; + InputStream clis = null; + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: URL " + url); + try { + clis = SecuritySupport.openStream(url); + if (clis != null) { + v.add(new MailcapFile(clis)); + anyLoaded = true; + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: " + + "successfully loaded " + + "mailcap file from URL: " + + url); + } else { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: " + + "not loading mailcap " + + "file from URL: " + url); + } + } catch (IOException ioex) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: can't load " + + url, ioex); + } catch (SecurityException sex) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: can't load " + + url, sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException cex) { } + } + } + } + } catch (Exception ex) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: can't load " + name, ex); + } + + // if failed to load anything, fall back to old technique, just in case + if (!anyLoaded) { + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: !anyLoaded"); + MailcapFile mf = loadResource("/" + name); + if (mf != null) + v.add(mf); + } + } + + /** + * Load from the named file. + */ + private MailcapFile loadFile(String name) { + MailcapFile mtf = null; + + try { + mtf = new MailcapFile(name); + } catch (IOException e) { + // e.printStackTrace(); + } + return mtf; + } + + /** + * Constructor that allows the caller to specify the path + * of a <i>mailcap</i> file. + * + * @param fileName The name of the <i>mailcap</i> file to open + * @exception IOException if the file can't be accessed + */ + public MailcapCommandMap(String fileName) throws IOException { + this(); + + if (LogSupport.isLoggable()) + LogSupport.log("MailcapCommandMap: load PROG from " + fileName); + if (DB[PROG] == null) { + DB[PROG] = new MailcapFile(fileName); + } + } + + + /** + * Constructor that allows the caller to specify an <i>InputStream</i> + * containing a mailcap file. + * + * @param is InputStream of the <i>mailcap</i> file to open + */ + public MailcapCommandMap(InputStream is) { + this(); + + LogSupport.log("MailcapCommandMap: load PROG"); + if (DB[PROG] == null) { + try { + DB[PROG] = new MailcapFile(is); + } catch (IOException ex) { + // XXX - should throw it + } + } + } + + /** + * Get the preferred command list for a MIME Type. The MailcapCommandMap + * searches the mailcap files as described above under + * <i>Mailcap file search order</i>.<p> + * + * The result of the search is a proper subset of available + * commands in all mailcap files known to this instance of + * MailcapCommandMap. The first entry for a particular command + * is considered the preferred command. + * + * @param mimeType the MIME type + * @return the CommandInfo objects representing the preferred commands. + */ + public synchronized CommandInfo[] getPreferredCommands(String mimeType) { + List cmdList = new ArrayList(); + if (mimeType != null) + mimeType = mimeType.toLowerCase(Locale.ENGLISH); + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + Map cmdMap = DB[i].getMailcapList(mimeType); + if (cmdMap != null) + appendPrefCmdsToList(cmdMap, cmdList); + } + + // now add the fallback commands + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + Map cmdMap = DB[i].getMailcapFallbackList(mimeType); + if (cmdMap != null) + appendPrefCmdsToList(cmdMap, cmdList); + } + + CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()]; + cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos); + + return cmdInfos; + } + + /** + * Put the commands that are in the hash table, into the list. + */ + private void appendPrefCmdsToList(Map cmdHash, List cmdList) { + Iterator verb_enum = cmdHash.keySet().iterator(); + + while (verb_enum.hasNext()) { + String verb = (String)verb_enum.next(); + if (!checkForVerb(cmdList, verb)) { + List cmdList2 = (List)cmdHash.get(verb); // get the list + String className = (String)cmdList2.get(0); + cmdList.add(new CommandInfo(verb, className)); + } + } + } + + /** + * Check the cmdList to see if this command exists, return + * true if the verb is there. + */ + private boolean checkForVerb(List cmdList, String verb) { + Iterator ee = cmdList.iterator(); + while (ee.hasNext()) { + String enum_verb = + (String)((CommandInfo)ee.next()).getCommandName(); + if (enum_verb.equals(verb)) + return true; + } + return false; + } + + /** + * Get all the available commands in all mailcap files known to + * this instance of MailcapCommandMap for this MIME type. + * + * @param mimeType the MIME type + * @return the CommandInfo objects representing all the commands. + */ + public synchronized CommandInfo[] getAllCommands(String mimeType) { + List cmdList = new ArrayList(); + if (mimeType != null) + mimeType = mimeType.toLowerCase(Locale.ENGLISH); + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + Map cmdMap = DB[i].getMailcapList(mimeType); + if (cmdMap != null) + appendCmdsToList(cmdMap, cmdList); + } + + // now add the fallback commands + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + Map cmdMap = DB[i].getMailcapFallbackList(mimeType); + if (cmdMap != null) + appendCmdsToList(cmdMap, cmdList); + } + + CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()]; + cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos); + + return cmdInfos; + } + + /** + * Put the commands that are in the hash table, into the list. + */ + private void appendCmdsToList(Map typeHash, List cmdList) { + Iterator verb_enum = typeHash.keySet().iterator(); + + while (verb_enum.hasNext()) { + String verb = (String)verb_enum.next(); + List cmdList2 = (List)typeHash.get(verb); + Iterator cmd_enum = ((List)cmdList2).iterator(); + + while (cmd_enum.hasNext()) { + String cmd = (String)cmd_enum.next(); + cmdList.add(new CommandInfo(verb, cmd)); + // cmdList.add(0, new CommandInfo(verb, cmd)); + } + } + } + + /** + * Get the command corresponding to <code>cmdName</code> for the MIME type. + * + * @param mimeType the MIME type + * @param cmdName the command name + * @return the CommandInfo object corresponding to the command. + */ + public synchronized CommandInfo getCommand(String mimeType, + String cmdName) { + if (mimeType != null) + mimeType = mimeType.toLowerCase(Locale.ENGLISH); + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + Map cmdMap = DB[i].getMailcapList(mimeType); + if (cmdMap != null) { + // get the cmd list for the cmd + List v = (List)cmdMap.get(cmdName); + if (v != null) { + String cmdClassName = (String)v.get(0); + + if (cmdClassName != null) + return new CommandInfo(cmdName, cmdClassName); + } + } + } + + // now try the fallback list + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + Map cmdMap = DB[i].getMailcapFallbackList(mimeType); + if (cmdMap != null) { + // get the cmd list for the cmd + List v = (List)cmdMap.get(cmdName); + if (v != null) { + String cmdClassName = (String)v.get(0); + + if (cmdClassName != null) + return new CommandInfo(cmdName, cmdClassName); + } + } + } + return null; + } + + /** + * Add entries to the registry. Programmatically + * added entries are searched before other entries.<p> + * + * The string that is passed in should be in mailcap + * format. + * + * @param mail_cap a correctly formatted mailcap string + */ + public synchronized void addMailcap(String mail_cap) { + // check to see if one exists + LogSupport.log("MailcapCommandMap: add to PROG"); + if (DB[PROG] == null) + DB[PROG] = new MailcapFile(); + + DB[PROG].appendToMailcap(mail_cap); + } + + /** + * Return the DataContentHandler for the specified MIME type. + * + * @param mimeType the MIME type + * @return the DataContentHandler + */ + public synchronized DataContentHandler createDataContentHandler( + String mimeType) { + if (LogSupport.isLoggable()) + LogSupport.log( + "MailcapCommandMap: createDataContentHandler for " + mimeType); + if (mimeType != null) + mimeType = mimeType.toLowerCase(Locale.ENGLISH); + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + if (LogSupport.isLoggable()) + LogSupport.log(" search DB #" + i); + Map cmdMap = DB[i].getMailcapList(mimeType); + if (cmdMap != null) { + List v = (List)cmdMap.get("content-handler"); + if (v != null) { + String name = (String)v.get(0); + DataContentHandler dch = getDataContentHandler(name); + if (dch != null) + return dch; + } + } + } + + // now try the fallback entries + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + if (LogSupport.isLoggable()) + LogSupport.log(" search fallback DB #" + i); + Map cmdMap = DB[i].getMailcapFallbackList(mimeType); + if (cmdMap != null) { + List v = (List)cmdMap.get("content-handler"); + if (v != null) { + String name = (String)v.get(0); + DataContentHandler dch = getDataContentHandler(name); + if (dch != null) + return dch; + } + } + } + return null; + } + + private DataContentHandler getDataContentHandler(String name) { + if (LogSupport.isLoggable()) + LogSupport.log(" got content-handler"); + if (LogSupport.isLoggable()) + LogSupport.log(" class " + name); + try { + ClassLoader cld = null; + // First try the "application's" class loader. + cld = SecuritySupport.getContextClassLoader(); + if (cld == null) + cld = this.getClass().getClassLoader(); + Class cl = null; + try { + cl = cld.loadClass(name); + } catch (Exception ex) { + // if anything goes wrong, do it the old way + cl = Class.forName(name); + } + if (cl != null) // XXX - always true? + return (DataContentHandler)cl.newInstance(); + } catch (IllegalAccessException e) { + if (LogSupport.isLoggable()) + LogSupport.log("Can't load DCH " + name, e); + } catch (ClassNotFoundException e) { + if (LogSupport.isLoggable()) + LogSupport.log("Can't load DCH " + name, e); + } catch (InstantiationException e) { + if (LogSupport.isLoggable()) + LogSupport.log("Can't load DCH " + name, e); + } + return null; + } + + /** + * Get all the MIME types known to this command map. + * + * @return array of MIME types as strings + * @since JAF 1.1 + */ + public synchronized String[] getMimeTypes() { + List mtList = new ArrayList(); + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + String[] ts = DB[i].getMimeTypes(); + if (ts != null) { + for (int j = 0; j < ts.length; j++) { + // eliminate duplicates + if (!mtList.contains(ts[j])) + mtList.add(ts[j]); + } + } + } + + String[] mts = new String[mtList.size()]; + mts = (String[])mtList.toArray(mts); + + return mts; + } + + /** + * Get the native commands for the given MIME type. + * Returns an array of strings where each string is + * an entire mailcap file entry. The application + * will need to parse the entry to extract the actual + * command as well as any attributes it needs. See + * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A> + * for details of the mailcap entry syntax. Only mailcap + * entries that specify a view command for the specified + * MIME type are returned. + * + * @param mimeType the MIME type + * @return array of native command entries + * @since JAF 1.1 + */ + public synchronized String[] getNativeCommands(String mimeType) { + List cmdList = new ArrayList(); + if (mimeType != null) + mimeType = mimeType.toLowerCase(Locale.ENGLISH); + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + String[] cmds = DB[i].getNativeCommands(mimeType); + if (cmds != null) { + for (int j = 0; j < cmds.length; j++) { + // eliminate duplicates + if (!cmdList.contains(cmds[j])) + cmdList.add(cmds[j]); + } + } + } + + String[] cmds = new String[cmdList.size()]; + cmds = (String[])cmdList.toArray(cmds); + + return cmds; + } + + /** + * for debugging... + * + public static void main(String[] argv) throws Exception { + MailcapCommandMap map = new MailcapCommandMap(); + CommandInfo[] cmdInfo; + + cmdInfo = map.getPreferredCommands(argv[0]); + System.out.println("Preferred Commands:"); + for (int i = 0; i < cmdInfo.length; i++) + System.out.println("Command " + cmdInfo[i].getCommandName() + " [" + + cmdInfo[i].getCommandClass() + "]"); + cmdInfo = map.getAllCommands(argv[0]); + System.out.println(); + System.out.println("All Commands:"); + for (int i = 0; i < cmdInfo.length; i++) + System.out.println("Command " + cmdInfo[i].getCommandName() + " [" + + cmdInfo[i].getCommandClass() + "]"); + DataContentHandler dch = map.createDataContentHandler(argv[0]); + if (dch != null) + System.out.println("DataContentHandler " + + dch.getClass().toString()); + System.exit(0); + } + */ +}
diff --git a/activation/src/main/java/javax/activation/MimeType.java b/activation/src/main/java/javax/activation/MimeType.java new file mode 100644 index 0000000..49fe6e5 --- /dev/null +++ b/activation/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/activation/src/main/java/javax/activation/MimeTypeParameterList.java b/activation/src/main/java/javax/activation/MimeTypeParameterList.java new file mode 100644 index 0000000..6298063 --- /dev/null +++ b/activation/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/activation/src/main/java/javax/activation/MimeTypeParseException.java b/activation/src/main/java/javax/activation/MimeTypeParseException.java new file mode 100644 index 0000000..fd000e2 --- /dev/null +++ b/activation/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/activation/src/main/java/javax/activation/MimetypesFileTypeMap.java b/activation/src/main/java/javax/activation/MimetypesFileTypeMap.java new file mode 100644 index 0000000..e2dffb7 --- /dev/null +++ b/activation/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 <code>.mime.types</code> format. <p> + * + * <b>MIME types file search order:</b><p> + * The MimetypesFileTypeMap looks in various places in the user's + * system for MIME types file entries. When requests are made + * to search for MIME types in the MimetypesFileTypeMap, it searches + * MIME types files in the following order: + * <ol> + * <li> Programmatically added entries to the MimetypesFileTypeMap instance. + * <li> The file <code>.mime.types</code> in the user's home directory. + * <li> The file <code>mime.types</code> in the Java runtime. + * <li> The file or resources named <code>META-INF/mime.types</code>. + * <li> The file or resource named <code>META-INF/mimetypes.default</code> + * (usually found only in the <code>activation.jar</code> file). + * </ol> + * <p> + * (The current implementation looks for the <code>mime.types</code> file + * in the Java runtime in the directory <code><i>java.home</i>/conf</code> + * if it exists, and otherwise in the directory + * <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value + * of the "java.home" System property. Note that the "conf" directory was + * introduced in JDK 9.) + * <p> + * <b>MIME types file format:</b><p> + * + * <code> + * # comments begin with a '#'<br> + * # the format is <mime type> <space separated file extensions><br> + * # for example:<br> + * text/plain txt text TXT<br> + * # this would map file.txt, file.text, and file.TXT to<br> + * # the mime type "text/plain"<br> + * </code> + * + * @author Bart Calder + * @author Bill Shannon + */ +public class MimetypesFileTypeMap extends FileTypeMap { + /* + * We manage a collection of databases, searched in order. + */ + private MimeTypeFile[] DB; + private static final int PROG = 0; // programmatically added entries + + private static final String defaultType = "application/octet-stream"; + + private static final String confDir; + + static { + String dir = null; + try { + dir = (String)AccessController.doPrivileged( + new PrivilegedAction() { + public Object run() { + String home = System.getProperty("java.home"); + String newdir = home + File.separator + "conf"; + File conf = new File(newdir); + if (conf.exists()) + return newdir + File.separator; + else + return home + File.separator + "lib" + File.separator; + } + }); + } catch (Exception ex) { + // ignore any exceptions + } + confDir = dir; + } + + /** + * The default constructor. + */ + public MimetypesFileTypeMap() { + Vector dbv = new Vector(5); // usually 5 or less databases + MimeTypeFile mf = null; + dbv.addElement(null); // place holder for PROG entry + + LogSupport.log("MimetypesFileTypeMap: load HOME"); + try { + String user_home = System.getProperty("user.home"); + + if (user_home != null) { + String path = user_home + File.separator + ".mime.types"; + mf = loadFile(path); + if (mf != null) + dbv.addElement(mf); + } + } catch (SecurityException ex) {} + + LogSupport.log("MimetypesFileTypeMap: load SYS"); + try { + // check system's home + if (confDir != null) { + mf = loadFile(confDir + "mime.types"); + if (mf != null) + dbv.addElement(mf); + } + } catch (SecurityException ex) {} + + LogSupport.log("MimetypesFileTypeMap: load JAR"); + // load from the app's jar file + loadAllResources(dbv, "META-INF/mime.types"); + + LogSupport.log("MimetypesFileTypeMap: load DEF"); + mf = loadResource("/META-INF/mimetypes.default"); + + if (mf != null) + dbv.addElement(mf); + + DB = new MimeTypeFile[dbv.size()]; + dbv.copyInto(DB); + } + + /** + * Load from the named resource. + */ + private MimeTypeFile loadResource(String name) { + InputStream clis = null; + try { + clis = SecuritySupport.getResourceAsStream(this.getClass(), name); + if (clis != null) { + MimeTypeFile mf = new MimeTypeFile(clis); + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: successfully " + + "loaded mime types file: " + name); + return mf; + } else { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: not loading " + + "mime types file: " + name); + } + } catch (IOException e) { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: can't load " + name, e); + } catch (SecurityException sex) { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: can't load " + name, sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException ex) { } // ignore it + } + return null; + } + + /** + * Load all of the named resource. + */ + private void loadAllResources(Vector v, String name) { + boolean anyLoaded = false; + try { + URL[] urls; + ClassLoader cld = null; + // First try the "application's" class loader. + cld = SecuritySupport.getContextClassLoader(); + if (cld == null) + cld = this.getClass().getClassLoader(); + if (cld != null) + urls = SecuritySupport.getResources(cld, name); + else + urls = SecuritySupport.getSystemResources(name); + if (urls != null) { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: getResources"); + for (int i = 0; i < urls.length; i++) { + URL url = urls[i]; + InputStream clis = null; + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: URL " + url); + try { + clis = SecuritySupport.openStream(url); + if (clis != null) { + v.addElement(new MimeTypeFile(clis)); + anyLoaded = true; + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: " + + "successfully loaded " + + "mime types from URL: " + url); + } else { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: " + + "not loading " + + "mime types from URL: " + url); + } + } catch (IOException ioex) { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: can't load " + + url, ioex); + } catch (SecurityException sex) { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: can't load " + + url, sex); + } finally { + try { + if (clis != null) + clis.close(); + } catch (IOException cex) { } + } + } + } + } catch (Exception ex) { + if (LogSupport.isLoggable()) + LogSupport.log("MimetypesFileTypeMap: can't load " + name, ex); + } + + // if failed to load anything, fall back to old technique, just in case + if (!anyLoaded) { + LogSupport.log("MimetypesFileTypeMap: !anyLoaded"); + MimeTypeFile mf = loadResource("/" + name); + if (mf != null) + v.addElement(mf); + } + } + + /** + * Load the named file. + */ + private MimeTypeFile loadFile(String name) { + MimeTypeFile mtf = null; + + try { + mtf = new MimeTypeFile(name); + } catch (IOException e) { + // e.printStackTrace(); + } + return mtf; + } + + /** + * Construct a MimetypesFileTypeMap with programmatic entries + * added from the named file. + * + * @param mimeTypeFileName the file name + * @exception IOException for errors reading the file + */ + public MimetypesFileTypeMap(String mimeTypeFileName) throws IOException { + this(); + DB[PROG] = new MimeTypeFile(mimeTypeFileName); + } + + /** + * Construct a MimetypesFileTypeMap with programmatic entries + * added from the InputStream. + * + * @param is the input stream to read from + */ + public MimetypesFileTypeMap(InputStream is) { + this(); + try { + DB[PROG] = new MimeTypeFile(is); + } catch (IOException ex) { + // XXX - really should throw it + } + } + + /** + * Prepend the MIME type values to the registry. + * + * @param mime_types A .mime.types formatted string of entries. + */ + public synchronized void addMimeTypes(String mime_types) { + // check to see if we have created the registry + if (DB[PROG] == null) + DB[PROG] = new MimeTypeFile(); // make one + + DB[PROG].appendToRegistry(mime_types); + } + + /** + * Return the MIME type of the file object. + * The implementation in this class calls + * <code>getContentType(f.getName())</code>. + * + * @param f the file + * @return the file's MIME type + */ + public String getContentType(File f) { + return this.getContentType(f.getName()); + } + + /** + * Return the MIME type based on the specified file name. + * The MIME type entries are searched as described above under + * <i>MIME types file search order</i>. + * If no entry is found, the type "application/octet-stream" is returned. + * + * @param filename the file name + * @return the file's MIME type + */ + public synchronized String getContentType(String filename) { + int dot_pos = filename.lastIndexOf("."); // period index + + if (dot_pos < 0) + return defaultType; + + String file_ext = filename.substring(dot_pos + 1); + if (file_ext.length() == 0) + return defaultType; + + for (int i = 0; i < DB.length; i++) { + if (DB[i] == null) + continue; + String result = DB[i].getMIMETypeString(file_ext); + if (result != null) + return result; + } + return defaultType; + } + + /** + * for debugging... + * + public static void main(String[] argv) throws Exception { + MimetypesFileTypeMap map = new MimetypesFileTypeMap(); + System.out.println("File " + argv[0] + " has MIME type " + + map.getContentType(argv[0])); + System.exit(0); + } + */ +}
diff --git a/activation/src/main/java/javax/activation/SecuritySupport.java b/activation/src/main/java/javax/activation/SecuritySupport.java new file mode 100644 index 0000000..c37174b --- /dev/null +++ b/activation/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/activation/src/main/java/javax/activation/URLDataSource.java b/activation/src/main/java/javax/activation/URLDataSource.java new file mode 100644 index 0000000..c65805f --- /dev/null +++ b/activation/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 <code>URL</code> + * object in a DataSource interface. URLDataSource simplifies the handling + * of data described by URLs within Jakarta Activation + * because this class can be used to create new DataHandlers. <i>NOTE: The + * DataHandler object creates a URLDataSource internally, + * when it is constructed with a URL.</i> + * + * @see javax.activation.DataSource + * @see javax.activation.DataHandler + */ +public class URLDataSource implements DataSource { + private URL url = null; + private URLConnection url_conn = null; + + /** + * URLDataSource constructor. The URLDataSource class will + * not open a connection to the URL until a method requiring it + * to do so is called. + * + * @param url The URL to be encapsulated in this object. + */ + public URLDataSource(URL url) { + this.url = url; + } + + /** + * Returns the value of the URL content-type header field. + * It calls the URL's <code>URLConnection.getContentType</code> method + * after retrieving a URLConnection object. + * <i>Note: this method attempts to call the <code>openConnection</code> + * method on the URL. If this method fails, or if a content type is not + * returned from the URLConnection, getContentType returns + * "application/octet-stream" as the content type.</i> + * + * @return the content type. + */ + public String getContentType() { + String type = null; + + try { + if (url_conn == null) + url_conn = url.openConnection(); + } catch (IOException e) { } + + if (url_conn != null) + type = url_conn.getContentType(); + + if (type == null) + type = "application/octet-stream"; + + return type; + } + + /** + * Calls the <code>getFile</code> method on the URL used to + * instantiate the object. + * + * @return the result of calling the URL's getFile method. + */ + public String getName() { + return url.getFile(); + } + + /** + * The getInputStream method from the URL. Calls the + * <code>openStream</code> method on the URL. + * + * @return the InputStream. + */ + public InputStream getInputStream() throws IOException { + return url.openStream(); + } + + /** + * The getOutputStream method from the URL. First an attempt is + * made to get the URLConnection object for the URL. If that + * succeeds, the getOutputStream method on the URLConnection + * is returned. + * + * @return the OutputStream. + */ + public OutputStream getOutputStream() throws IOException { + // get the url connection if it is available + url_conn = url.openConnection(); + + if (url_conn != null) { + url_conn.setDoOutput(true); + return url_conn.getOutputStream(); + } else + return null; + } + + /** + * Return the URL used to create this DataSource. + * + * @return The URL. + */ + public URL getURL() { + return url; + } +}
diff --git a/activation/src/main/java/javax/activation/UnsupportedDataTypeException.java b/activation/src/main/java/javax/activation/UnsupportedDataTypeException.java new file mode 100644 index 0000000..b24a788 --- /dev/null +++ b/activation/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/activation/src/main/java/javax/activation/package.html b/activation/src/main/java/javax/activation/package.html new file mode 100644 index 0000000..6cc89ab --- /dev/null +++ b/activation/src/main/java/javax/activation/package.html
@@ -0,0 +1,22 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> +<HTML> +<HEAD> +<!-- + + 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 + +--> + +</HEAD> +<BODY BGCOLOR="white"> + +Jakarta Activation is used by Jakarta Mail to manage MIME data. + +</BODY> +</HTML>
diff --git a/activation/src/main/java/module-info.java b/activation/src/main/java/module-info.java new file mode 100644 index 0000000..b18cd91 --- /dev/null +++ b/activation/src/main/java/module-info.java
@@ -0,0 +1,15 @@ +/* + * 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 + */ + +module jakarta.activation { + exports javax.activation; + requires java.logging; + requires java.desktop; // for image/jpeg handler +}
diff --git a/activation/src/main/javadoc/doc-files/speclicense.html b/activation/src/main/javadoc/doc-files/speclicense.html new file mode 100644 index 0000000..ba29e5e --- /dev/null +++ b/activation/src/main/javadoc/doc-files/speclicense.html
@@ -0,0 +1,72 @@ +<html> +<head> +<title>Eclipse Foundation Specification License - v1.0</title> +</head> +<body> +<h1>Eclipse Foundation Specification License - v1.0</h1> +<p>By using and/or copying this document, or the Eclipse Foundation + document from which this statement is linked, you (the licensee) agree + that you have read, understood, and will comply with the following + terms and conditions:</p> + +<p>Permission to copy, and distribute the contents of this document, or + the Eclipse Foundation document from which this statement is linked, in + any medium for any purpose and without fee or royalty is hereby + granted, provided that you include the following on ALL copies of the + document, or portions thereof, that you use:</p> + +<ul> + <li> link or URL to the original Eclipse Foundation document.</li> + <li>All existing copyright notices, or if one does not exist, a notice + (hypertext is preferred, but a textual representation is permitted) + of the form: "Copyright © [$date-of-document] + “Eclipse Foundation, Inc. <<url to this license>> + " + </li> +</ul> + +<p>Inclusion of the full text of this NOTICE must be provided. We + request that authorship attribution be provided in any software, + documents, or other items or products that you create pursuant to the + implementation of the contents of this document, or any portion + thereof.</p> + +<p>No right to create modifications or derivatives of Eclipse Foundation + documents is granted pursuant to this license, except anyone may + prepare and distribute derivative works and portions of this document + in software that implements the specification, in supporting materials + accompanying such software, and in documentation of such software, + PROVIDED that all such works include the notice below. HOWEVER, the + publication of derivative works of this document for use as a technical + specification is expressly prohibited.</p> + +<p>The notice is:</p> + +<p>"Copyright © 2018 Eclipse Foundation. This software or + document includes material copied from or derived from [title and URI + of the Eclipse Foundation specification document]."</p> + +<h2>Disclaimers</h2> + +<p>THIS DOCUMENT IS PROVIDED "AS IS," AND THE COPYRIGHT + HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR + WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, + NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE + SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS + WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR + OTHER RIGHTS.</p> + +<p>THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE + FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT + OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE + CONTENTS THEREOF.</p> + +<p>The name and trademarks of the copyright holders or the Eclipse + Foundation may NOT be used in advertising or publicity pertaining to + this document or its contents without specific, written prior + permission. Title to copyright in this document will at all times + remain with copyright holders.</p> + +</body> +</html>
diff --git a/activation/src/main/resources/META-INF/LICENSE.md b/activation/src/main/resources/META-INF/LICENSE.md new file mode 100644 index 0000000..e0358f9 --- /dev/null +++ b/activation/src/main/resources/META-INF/LICENSE.md
@@ -0,0 +1,29 @@ + + Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/activation/src/main/resources/META-INF/mailcap.default b/activation/src/main/resources/META-INF/mailcap.default new file mode 100644 index 0000000..542de7d --- /dev/null +++ b/activation/src/main/resources/META-INF/mailcap.default
@@ -0,0 +1,7 @@ +# +# This is a very simple 'mailcap' file +# +image/gif;; x-java-view=com.sun.activation.viewers.ImageViewer +image/jpeg;; x-java-view=com.sun.activation.viewers.ImageViewer +text/*;; x-java-view=com.sun.activation.viewers.TextViewer +text/*;; x-java-edit=com.sun.activation.viewers.TextEditor
diff --git a/activation/src/main/resources/META-INF/mimetypes.default b/activation/src/main/resources/META-INF/mimetypes.default new file mode 100644 index 0000000..1b4056b --- /dev/null +++ b/activation/src/main/resources/META-INF/mimetypes.default
@@ -0,0 +1,25 @@ +# +# A simple, old format, mime.types file +# +text/html html htm HTML HTM +text/plain txt text TXT TEXT +image/gif gif GIF +image/ief ief +image/jpeg jpeg jpg jpe JPG +image/tiff tiff tif +image/png png PNG +image/x-xwindowdump xwd +application/postscript ai eps ps +application/rtf rtf +application/x-tex tex +application/x-texinfo texinfo texi +application/x-troff t tr roff +audio/basic au +audio/midi midi mid +audio/x-aifc aifc +audio/x-aiff aif aiff +audio/x-mpeg mpeg mpg +audio/x-wav wav +video/mpeg mpeg mpg mpe +video/quicktime qt mov +video/x-msvideo avi
diff --git a/activationapi/pom.xml b/activationapi/pom.xml new file mode 100644 index 0000000..2650521 --- /dev/null +++ b/activationapi/pom.xml
@@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- + + 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 + +--> +<!-- + This project builds the Activation API jar file, which contains only + the javax.activation.* API definitions and is *only* intended to be used + for programs to compile against. Note that it includes none of the + implementation-specific classes that the javax.activation.* classes rely on. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>com.sun.activation</groupId> + <artifactId>all</artifactId> + <version>1.2.2</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>jakarta.activation</groupId> + <artifactId>jakarta.activation-api</artifactId> + <packaging>jar</packaging> + <name>Jakarta Activation API jar</name> + <properties> + <activation.extensionName> + jakarta.activation + </activation.extensionName> + <activation.moduleName> + jakarta.activation + </activation.moduleName> + <activation.packages.export> + javax.activation.*; version=${activation.spec.version}, + !com.sun.* + </activation.packages.export> + <activation.packages.import> + !com.sun.* + </activation.packages.import> + <activation.bundle.symbolicName> + jakarta.activation-api + </activation.bundle.symbolicName> + </properties> + <build> + <plugins> + <plugin> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <!-- download the binaries --> + <id>get-binaries</id> + <phase>process-sources</phase> + <goals> + <goal>unpack</goal> + </goals> + </execution> + <execution> + <!-- download the sources --> + <id>get-sources</id> + <phase>process-sources</phase> + <goals> + <goal>unpack</goal> + </goals> + <configuration> + <artifactItems> + <artifactItem> + <groupId>com.sun.activation</groupId> + <artifactId>jakarta.activation</artifactId> + <version>${project.version}</version> + <classifier>sources</classifier> + <outputDirectory> + ${project.build.directory}/sources + </outputDirectory> + </artifactItem> + </artifactItems> + </configuration> + </execution> + </executions> + <configuration> + <artifactItems> + <artifactItem> + <groupId>com.sun.activation</groupId> + <artifactId>jakarta.activation</artifactId> + <version>${project.version}</version> + </artifactItem> + </artifactItems> + <outputDirectory> + ${project.build.outputDirectory} + </outputDirectory> + <!-- + Include all the implementation source files so that + javadoc run as part of "deploy" will find all the + required classes. + + Don't include the metadata files from the original + jar file. + --> + <excludes> + META-INF/** + </excludes> + </configuration> + </plugin> + + <!-- + Skip compiling since the dependency plugin pulled in + the sources and class files. + --> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <configuration> + <skipMain>true</skipMain> + </configuration> + </execution> + </executions> + </plugin> + + <!-- + Don't include the implementation classes in the jar files. + --> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <finalName>${project.artifactId}</finalName> + <excludes> + <exclude>com/**</exclude> + </excludes> + </configuration> + </plugin> + + <plugin> + <artifactId>maven-source-plugin</artifactId> + <configuration> + <excludes> + <exclude>com/**</exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </build> +</project>
diff --git a/copyright-exclude b/copyright-exclude new file mode 100644 index 0000000..1f0ceed --- /dev/null +++ b/copyright-exclude
@@ -0,0 +1,8 @@ +copyright-exclude +LICENSE +CONTRIBUTING.md +README +/META-INF/ +/speclicense.html +doc/release/ +doc/spec/
diff --git a/demo/pom.xml b/demo/pom.xml new file mode 100644 index 0000000..b800c51 --- /dev/null +++ b/demo/pom.xml
@@ -0,0 +1,68 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- + + 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 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>com.sun.activation</groupId> + <artifactId>all</artifactId> + <version>1.2.2</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>com.sun.activation</groupId> + <artifactId>demo</artifactId> + <packaging>jar</packaging> + <name>Jakarta Activation demos</name> + + <build> + <plugins> + <!-- + Need to disable the maven-bundle-plugin because the + new version doesn't like classes in the default package. + --> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <executions> + <execution> + <id>osgi-manifest</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + + <!-- + Because the maven-jar-plugin depends on the manifest file + created by the maven-bundle-plugin, we need to disable it too. + --> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>default-jar</id> + <phase>none</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>com.sun.activation</groupId> + <artifactId>jakarta.activation</artifactId> + </dependency> + </dependencies> +</project>
diff --git a/demo/src/main/java/CompViewer.java b/demo/src/main/java/CompViewer.java new file mode 100644 index 0000000..3b4a831 --- /dev/null +++ b/demo/src/main/java/CompViewer.java
@@ -0,0 +1,121 @@ +/* + * 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 + */ + +import java.awt.*; +import java.beans.*; +import java.lang.reflect.Method; +import java.io.*; +import java.awt.event.*; + +/** + * Class <code>CompViewer</code> creates a 'viewer' component + * that implements the CommandObject interface. + * + */ +public class CompViewer extends Frame implements WindowListener { + + /** + * Our constructor... + */ + public CompViewer(){ + super("Component"); + this.initCompViewer(null); + } + + public CompViewer(String name){ + super(name); + this.initCompViewer(name); + } + + public void initCompViewer(String name){ + if (name != null) + setTitle(name); + setSize(400,400); + setLayout(new BorderLayout()); + this.addWindowListener(this); + } + + //////////////////////////////////////////////////////////////////////// + // we got our bean as a component display it! + void setBean(Component new_bean) + { + Dimension start_dim = null; + add((Component)new_bean, "Center"); + start_dim = ((Component)new_bean).getPreferredSize(); + + if(start_dim.width != 0 && start_dim.height != 0) { + // this is what we do under normal conditions + start_dim.height += 40; + start_dim.width += 15; + this.setSize( start_dim ); + ((Component)new_bean).invalidate(); + ((Component)new_bean).validate(); + ((Component)new_bean).doLayout(); + show(); + } + else { + // we get here if for some reason our child's + // getPref size needs to have it's peer created + // first... + show(); + start_dim = ((Component)new_bean).getPreferredSize(); + start_dim.height += 40; + start_dim.width += 15; + this.setSize( start_dim ); + ((Component)new_bean).validate(); + } + this.setSize(this.getSize()); + validate(); + } + + + /** + * Make the bean based on it's class loader and name + */ + private Object makeBean(ClassLoader cls, String beanName) { + Object new_bean = null; + + try { + try { + new_bean = java.beans.Beans.instantiate(cls, beanName); + } + catch(IOException e) { + System.out.println("CompViewer:Beans.instantiate:IOException " + beanName + "."); + System.out.println(e); + System.exit(1); + } + } + catch (ClassNotFoundException e) { + System.out.println("CompViewer:Beans.instantiate:ClassNotFoundException " + beanName + "."); + System.out.println(e); + System.exit(1); + } + + if( !(new_bean instanceof Component) ) { + System.out.println("CompViewer: " + beanName + " not instance of awt.Component exiting"); + System.exit(1); + } + return new_bean; + } + + public void windowOpened(WindowEvent e) {} + public void windowClosing(WindowEvent e) { + this.setVisible(false); + } + public void windowClosed(WindowEvent e) {} + public void windowIconified(WindowEvent e) {} + public void windowDeiconified(WindowEvent e) {} + public void windowActivated(WindowEvent e) {} + public void windowDeactivated(WindowEvent e) {} + +} + + +
diff --git a/demo/src/main/java/DCHTest.java b/demo/src/main/java/DCHTest.java new file mode 100644 index 0000000..929aa0d --- /dev/null +++ b/demo/src/main/java/DCHTest.java
@@ -0,0 +1,85 @@ +/* + * 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 + */ + +import java.io.*; +import javax.activation.*; +import java.awt.datatransfer.*; + +public class DCHTest { + private FileDataSource fds = null; + private DataHandler dh = null; + private DataContentHandlerFactory dchf = null; + /** + * main function + */ + public static void main(String args[]) { + DCHTest test = new DCHTest(); + + if(args.length == 0) { + System.out.println("usage: DCHTest file.txt"); + System.exit(1); + } + + // first let's get a DataSource + + test.setFile(args[0]); + + test.doit(); + } + + private void doit() { + DataFlavor xfer_flavors[]; + Object content = null; + + // now let's create a DataHandler + dh = new DataHandler(fds); + System.out.println("DCHTest: DataHandler created"); + + // now lets set a DataContentHandlerFactory + dchf = new SimpleDCF("text/plain:PlainDCH\n"); + System.out.println("DCHTest: Simple dchf created"); + + // now let's set the dchf in the dh + dh.setDataContentHandlerFactory(dchf); + System.out.println("DCHTest: DataContentHandlerFactory set in DataHandler"); + + // get the dataflavors + xfer_flavors = dh.getTransferDataFlavors(); + System.out.println("DCHTest: dh.getTransferDF returned " + + xfer_flavors.length + " data flavors."); + + // get the content: + try { + content = dh.getContent(); + } catch (Exception e) { e.printStackTrace(); } + + if(content == null) + System.out.println("DCHTest: no content to be had!!!"); + else + System.out.println("DCHTest: got content of the following type: " + + content.getClass().getName()); + + } + + /** + * set the file + */ + public void setFile(String filename) { + fds = new FileDataSource(filename); + System.out.println("DCHTest: FileDataSource created"); + if(!fds.getContentType().equals("text/plain")) { + System.out.println("Type must be text/plain"); + System.exit(1); + } + } + +} + +
diff --git a/demo/src/main/java/DCHTest2.java b/demo/src/main/java/DCHTest2.java new file mode 100644 index 0000000..155ee06 --- /dev/null +++ b/demo/src/main/java/DCHTest2.java
@@ -0,0 +1,93 @@ +/* + * 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 + */ + +import java.io.*; +import javax.activation.*; +import java.awt.datatransfer.*; + +public class DCHTest2 { + private FileDataSource fds = null; + private MailcapCommandMap cmdMap = null; + private DataHandler dh = null; + private DataContentHandlerFactory dchf = null; + /** + * main function + */ + public static void main(String args[]) { + DCHTest2 test = new DCHTest2(); + + if(args.length < 2) { + System.out.println("usage: DCHTest2 file.txt mailcap"); + System.exit(1); + } + + // first let's get a DataSource + + test.setFile(args[0]); + + test.setMailcap(args[1]); + test.doit(); + } + + private void doit() { + DataFlavor xfer_flavors[]; + Object content = null; + + // now let's create a DataHandler + dh = new DataHandler(fds); + dh.setCommandMap(cmdMap); + System.out.println("DCHTest2: DataHandler created"); + + // get the dataflavors + xfer_flavors = dh.getTransferDataFlavors(); + System.out.println("DCHTest2: dh.getTransferDF returned " + + xfer_flavors.length + " data flavors."); + + // get the content: + try { + content = dh.getContent(); + } catch (Exception e) { e.printStackTrace(); } + if(content == null) + System.out.println("DCHTest2: no content to be had!!!"); + else + System.out.println("DCHTest2: got content of the following type: " + + content.getClass().getName()); + + } + + /** + * set the file + */ + public void setFile(String filename) { + fds = new FileDataSource(filename); + System.out.println("DCHTest2: FileDataSource created"); + if(!fds.getContentType().equals("text/plain")) { + System.out.println("Type must be text/plain"); + System.exit(1); + } + } + + /** + * set the mailcap file in the CMD Map + */ + public void setMailcap(String mailcap) { + + try { + cmdMap = new MailcapCommandMap(mailcap); + } catch(Exception e){ + e.printStackTrace(); + System.exit(1); + } + } + + +} + +
diff --git a/demo/src/main/java/DHURL.java b/demo/src/main/java/DHURL.java new file mode 100644 index 0000000..b79a05e --- /dev/null +++ b/demo/src/main/java/DHURL.java
@@ -0,0 +1,64 @@ +/* + * 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 + */ + +import java.net.*; +import java.io.*; +import javax.activation.*; + +public class DHURL { + URL url = null; + DataHandler dh = null; + + public static void main(String args[]){ + DHURL test = new DHURL(); + + if(args.length == 0) { + System.out.println("usage: DHURL url"); + System.exit(1); + } + + test.setURL(args[0]); + + test.doit(); + + } + + public void setURL(String url) { + + try { + this.url = new URL(url); + } catch(MalformedURLException e) { + e.printStackTrace(); + System.out.println("malformed URL!!!"); + System.exit(1); + } + + } + + public void doit() { + System.out.print("Creating DataHandler..."); + dh = new DataHandler(url); + System.out.println("...done."); + + System.out.println("The MimeType of the DH : " + + dh.getContentType()); + try { + InputStream is = dh.getInputStream(); + if(is != null) + System.out.println("got an inputstream"); + } catch(Exception e) { + e.printStackTrace(); + } + + + } + + +}
diff --git a/demo/src/main/java/FileView.java b/demo/src/main/java/FileView.java new file mode 100644 index 0000000..6aae7f7 --- /dev/null +++ b/demo/src/main/java/FileView.java
@@ -0,0 +1,47 @@ +/* + * 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 + */ + +import java.awt.*; +import java.beans.*; +import java.net.*; +import javax.activation.*; + +public class FileView { + private Frame frame; + + public static void main(String args[]) throws Exception { + FileView fv = new FileView(); + if (args.length == 0) { + System.out.println("usage: FileView file.txt"); + System.exit(1); + } + fv.view(args[0]); + } + + private void view(String filename) throws Exception { + FileDataSource fds = new FileDataSource(filename); + DataHandler dh = new DataHandler(fds); + // comment out previous two lines, and uncomment next + // line and pass in a URL on the command line. + // DataHandler dh = new DataHandler(new URL(filename)); + + CommandInfo bi = dh.getCommand("view"); + + if (bi == null) { + System.out.println("no viewer found, exiting"); + System.exit(1); + } + + frame = new Frame("Viewer"); + frame.add((Component)dh.getBean(bi)); + frame.setSize(new Dimension(400,300)); + frame.show(); + } +}
diff --git a/demo/src/main/java/JAFApp.java b/demo/src/main/java/JAFApp.java new file mode 100644 index 0000000..6531f49 --- /dev/null +++ b/demo/src/main/java/JAFApp.java
@@ -0,0 +1,391 @@ +/* + * 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 + */ + +import java.awt.*; +import java.awt.event.*; +import java.beans.*; +import java.io.*; +import javax.activation.*; + +public class JAFApp extends Frame implements WindowListener, ItemListener, ActionListener { + + // UI stuff... + private GridBagLayout gridbag = null; + private GridBagLayout panel_gb = null; + private List file_list = null; + private Panel results_panel = null; + private Label f_title = null; + private Label f_name_label = null; + private Label type_label = null; + private Label type_title = null; + private Button launch_button = null; + private Choice verb_choice = null; + // File stuff... + private File _file = null; + // private FileDataSource fds[] = null; + private DataHandler dh[] = null; + + private boolean debug = false; + // private + /////////////////////////////////////////////////////////////////////// + // ctor + public JAFApp(String title) + { + super(title); + initMe(); + } + + public JAFApp() + { + super(); + initMe(); + } + + /////////////////////////////////////////////////////////////////////// + // initME -- init the UI + private void initMe() + { + Font label_font = new Font("Default", Font.PLAIN, 12); + Font title_font = new Font("Default", Font.BOLD, 12); + + gridbag = new GridBagLayout(); + setLayout(gridbag); + + this.setBackground(Color.white); + + // results panel create + results_panel = new Panel(); + panel_gb = new GridBagLayout(); + results_panel.setLayout(panel_gb); + results_panel.setBackground(Color.lightGray); + addGridComponent(this, + results_panel, + gridbag, + 0,0, + 1,1, + 1,0 ); + + // file name label + f_title = new Label("File Name:"); + f_title.setBackground(Color.lightGray); + f_title.setAlignment(Label.RIGHT); + f_title.setFont(title_font); + addGridComponent(results_panel, + f_title, + panel_gb, + 0, 0, + 1, 1, + 5, 100); + + // file name label + f_name_label = new Label("<none selected>"); + f_name_label.setBackground(Color.lightGray); + f_name_label.setAlignment(Label.LEFT); + f_name_label.setFont(label_font); + addGridComponent(results_panel, + f_name_label, + panel_gb, + 1, 0, + 1, 1, + 15, 100); + + // file name label + f_title = new Label("MIME Type:"); + f_title.setBackground(Color.lightGray); + f_title.setAlignment(Label.RIGHT); + f_title.setFont(title_font); + addGridComponent(results_panel, + f_title, + panel_gb, + 2, 0, + 1, 1, + 5, 100); + + // type_label + type_label = new Label("<no type>"); + type_label.setBackground(Color.lightGray); + type_label.setAlignment(Label.LEFT); + type_label.setFont(label_font); + addGridComponent(results_panel, + type_label, + panel_gb, + 3,0, + 1,1, + 10, 100); + + // launch button + launch_button = new Button("Launch!"); + launch_button.setEnabled( false ); + launch_button.setBackground(Color.red); + launch_button.setFont(title_font); + addGridComponent(results_panel, + launch_button, + panel_gb, + 4,0, + 1,1, + 5, 100); + + // verb popup + verb_choice = new Choice(); + verb_choice.setEnabled( false ); + verb_choice.setFont(label_font); + verb_choice.add("<empty>"); + addGridComponent(results_panel, + verb_choice, + panel_gb, + 5,0, + 1,1, + 10, 100); + + // file list + file_list = new List(); + file_list.setBackground(Color.white); + file_list.setFont(label_font); + addGridComponent(this, + file_list, + gridbag, + 0,1, + 1, 2, + 1,1); + + this.invalidate(); + + // set up events + this.addWindowListener(this); + file_list.addItemListener(this); + launch_button.addActionListener(this); + + } + + //////////////////////////////////////////////////////////////////////// + /** + * adds a component to our gridbag layout + */ + private void addGridComponent(Container cont, + Component comp, + GridBagLayout mygb, + int gridx, + int gridy, + int gridw, + int gridh, + int weightx, + int weighty) + { + GridBagConstraints c = new GridBagConstraints(); + c.gridx = gridx; + c.gridy = gridy; + c.gridwidth = gridw; + c.gridheight = gridh; + c.fill = GridBagConstraints.BOTH; + c.weighty = weighty; + c.weightx = weightx; + c.anchor = GridBagConstraints.CENTER; + mygb.setConstraints(comp, c); + cont.add(comp); + } + + /////////////////////////////////////////////////////////////////////// + // setFile - sets the file/directory for this frame at startup usually + // + public void setFile( File file ) + { + _file = file; + int i; + MailcapCommandMap cmdmap = + (MailcapCommandMap)CommandMap.getDefaultCommandMap(); + cmdmap.addMailcap("application/x-int; jaf.viewers.IntViewer"); + + if(_file.isFile()) + { + dh = new DataHandler[1]; + dh[0] = new DataHandler(new FileDataSource( _file )); + + file_list.add( dh[0].getName() ); + } + else if( _file.isDirectory() ) + { + String files[] = _file.list(); + MimetypesFileTypeMap map = new MimetypesFileTypeMap(); + map.addMimeTypes("text/java java\n"); + FileTypeMap.setDefaultFileTypeMap(map); + dh = new DataHandler[ files.length ]; + // iterate through the list and make fds + for( i = 0; i < files.length; i++) + { + + + if(debug) + System.out.println(files[i]); + + dh[i] = new DataHandler( + new FileDataSource( _file.getAbsolutePath() + + _file.separator + + files[i] ) + ); + dh[i].setCommandMap(cmdmap); + +// try { +// ((FileDataSource)dh[i].getDataSource()).addMimeTypes("text/plain java\n"); +// } catch(IOException e){ System.out.println(e); } + file_list.add(dh[i].getName()); + } + System.out.println("number of files: " + files.length + + " read : " + i); + + } + + } + /////////////////***ACTION LISTENER***/////////////////////// + // is called when a monkey (user) presses the launch button + public void actionPerformed(ActionEvent evt) + { + Object source = evt.getSource(); + int index = file_list.getSelectedIndex(); + + // make sure it's the launch button and that + // a list item is selected... + if(source == launch_button && index > -1){ + CommandInfo cmds[] = null; + String mimeType = dh[index].getContentType(); + + if(debug) + System.out.println("Finding..."); + + // get the available commands for this type + cmds = dh[index].getPreferredCommands(); + + // if we actually got some back... + if(cmds.length > 0){ + CompViewer cont = null; + Object my_bean = null; + // take the first one + if(debug) + System.out.println("Launching..."); + + // get the first one + my_bean = dh[index].getBean( cmds[verb_choice.getSelectedIndex()] ); + + // if it isn't a CommandObject we still + // have to give it it's data... +// if(!(my_bean instanceof javax.activation.CommandObject)) +// { +// System.out.println("WHOOOAAA!"); +// if(my_bean instanceof +// java.io.Externalizable) + +// { +// try +// { +// ((Externalizable)my_bean).readExternal( +// new ObjectInputStream( +// dh[index].getInputStream() +// ) +// ); +// } catch(Exception e) +// { +// System.out.println("There was a problem reading the Externalized Data in the viewer bean : " + e); +// } +// } +// } + if(debug) + System.out.println("GOTTA BEAN!: " + + my_bean.getClass().getName() ); + // create a new bean container + cont = new CompViewer( "JAF Component" ); + cont.setBean( (Component) my_bean); + cont.show(); + } + } + } + /////////////////***ITEM LISTENER***///////////////////////// + public void itemStateChanged(ItemEvent evt) + { + Integer id = (Integer)evt.getItem(); + + if(evt.getStateChange() == ItemEvent.SELECTED) + { + // get the content type from the data handler... + String mime_type = dh[id.intValue()].getContentType(); + // set the state in the info dlog: + f_name_label.setText(dh[id.intValue()].getName()); + + // set the field in the UI and enable launch button + if(mime_type != null) + { + CommandInfo cmds[] = null; + // set the text label + type_label.setText(mime_type); + + // check to see if any commands are available + cmds = dh[id.intValue()].getPreferredCommands(); + if(cmds != null && cmds.length > 0) // we got a command! + { + launch_button.setEnabled( true ); + verb_choice.setEnabled( true ); + launch_button.setBackground( Color.green ); + + verb_choice.removeAll(); + for(int i = 0; i < cmds.length; i++) + { + // verb_choice.addItem(cmds[i].getBeanDescriptor().getName()); + verb_choice.addItem(cmds[i].getCommandName()); + } + } + else + { + launch_button.setEnabled( false ); + verb_choice.setEnabled( false ); + launch_button.setBackground( Color.red ); + } + + } + else + { + // set label to be unknown and launch button off! + type_label.setText("<unknown>"); + launch_button.setEnabled( false ); + launch_button.setBackground( Color.red ); + } + + } + } + /////////////////***WINDOW LISTENER***//////////////////////// + public void windowOpened(WindowEvent e){} + public void windowClosing(WindowEvent e) + { + System.exit(0); // quit + } + public void windowClosed(WindowEvent e){} + public void windowIconified(WindowEvent e){} + public void windowDeiconified(WindowEvent e){} + public void windowActivated(WindowEvent e){} + public void windowDeactivated(WindowEvent e){} + + /////////////////////////////////////////////////////////////////////// + // main + public static void main(String args[]) + { + JAFApp appFrame = new JAFApp("JAF Testing Application"); + appFrame.setSize(600, 400); + appFrame.show(); + + // set the directory + if(args.length == 0) + { + appFrame.setFile( new File(".")); + } + else + { + appFrame.setFile( new File(args[0]) ); + } + } + + +}
diff --git a/demo/src/main/java/MCDump.java b/demo/src/main/java/MCDump.java new file mode 100644 index 0000000..eab6d9a --- /dev/null +++ b/demo/src/main/java/MCDump.java
@@ -0,0 +1,85 @@ +/* + * 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 + */ + +import java.io.*; +import java.beans.*; +import com.sun.activation.registries.*; +import javax.activation.*; + +/** + * Dump out everything we know about a MailcapCommandMap. + */ +public class MCDump { + static MailcapCommandMap mcf = null; + + public static void main(String args[]) { + + try { + if (args.length == 0) + mcf = new MailcapCommandMap(); + else + mcf = new MailcapCommandMap(args[0]); + } catch (Exception e){ + e.printStackTrace(); + System.exit(1); + } + + String[] types = mcf.getMimeTypes(); + if (types == null) { + System.out.println("No known MIME types"); + System.exit(0); + } else { + System.out.println("Known MIME types:"); + for (int i = 0; i < types.length; i++) + System.out.println("\t" + types[i]); + } + + System.out.println(); + System.out.println("All commands for each MIME type:"); + for (int i = 0; i < types.length; i++) { + System.out.println(" " + types[i]); + CommandInfo[] cmdinfo = mcf.getAllCommands(types[i]); + if (cmdinfo == null) { + System.out.println("\tNONE"); + } else { + for (int k = 0; k < cmdinfo.length; k++) + System.out.println("\t" + cmdinfo[k].getCommandName() + + ": " + cmdinfo[k].getCommandClass()); + } + } + + System.out.println(); + System.out.println("Preferred commands for each MIME type:"); + for (int i = 0; i < types.length; i++) { + System.out.println(" " + types[i]); + CommandInfo[] cmdinfo = mcf.getPreferredCommands(types[i]); + if (cmdinfo == null) { + System.out.println("\tNONE"); + } else { + for (int k = 0; k < cmdinfo.length; k++) + System.out.println("\t" + cmdinfo[k].getCommandName() + + ": " + cmdinfo[k].getCommandClass()); + } + } + + System.out.println(); + System.out.println("Native commands for each MIME type:"); + for (int i = 0; i < types.length; i++) { + System.out.println(" " + types[i]); + String[] cmds = mcf.getNativeCommands(types[i]); + if (cmds.length == 0) { + System.out.println("\tNONE"); + } else { + for (int k = 0; k < cmds.length; k++) + System.out.println("\t" + cmds[k]); + } + } + } +}
diff --git a/demo/src/main/java/MCTest.java b/demo/src/main/java/MCTest.java new file mode 100644 index 0000000..b34ffac --- /dev/null +++ b/demo/src/main/java/MCTest.java
@@ -0,0 +1,56 @@ +/* + * 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 + */ + +import java.io.*; +import java.beans.*; +import com.sun.activation.registries.*; +import javax.activation.*; + +public class MCTest { + static MailcapCommandMap mcf = null; + + public static void main(String args[]) { + + try { + if (args.length == 0) + mcf = new MailcapCommandMap(); + else + mcf = new MailcapCommandMap(args[0]); + } catch (Exception e){ + e.printStackTrace(); + System.exit(1); + } + + CommandInfo cmdinfo[] = mcf.getAllCommands("text/plain"); + System.out.print("Are there any commands for text/plain?"); + + if (cmdinfo != null) { + System.out.println("number of cmds = " + cmdinfo.length); + System.out.println("now try an individual cmd"); + CommandInfo info = mcf.getCommand("text/plain", "view"); + if (info != null) { + System.out.println("Got command..."); + } else { + System.out.println("no cmds"); + } + + mcf.addMailcap("text/plain;; x-java-flobotz=com.sun.activation.flobotz\n"); + // System.out.println("...dome"); + if (cmdinfo != null) { + cmdinfo = mcf.getAllCommands("text/plain"); + System.out.println("now we have cmds = " + cmdinfo.length); + + } + + } else { + System.out.println("NO CMDS AT ALL!"); + } + } +}
diff --git a/demo/src/main/java/ODCHTest.java b/demo/src/main/java/ODCHTest.java new file mode 100644 index 0000000..a4c1183 --- /dev/null +++ b/demo/src/main/java/ODCHTest.java
@@ -0,0 +1,76 @@ +/* + * 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 + */ + +import java.io.*; +import javax.activation.*; +import java.awt.datatransfer.*; + +public class ODCHTest { + private FileDataSource fds = null; + private DataHandler dh = null; + private DataContentHandlerFactory dchf = null; + private String str; + /** + * main function + */ + public static void main(String args[]) { + ODCHTest test = new ODCHTest(); + + if(args.length != 0) { + System.out.println("usage: ODCHTest"); + System.exit(1); + } + + // first let's get a DataSource + + + test.doit(); + } + + private void doit() { + DataFlavor xfer_flavors[]; + Object content = null; + + str = new String("This is a test"); + + // now let's create a DataHandler + dh = new DataHandler(str, "text/plain"); + System.out.println("ODCHTest: DataHandler created with str & text/plain"); + + // now lets set a DataContentHandlerFactory + dchf = new SimpleDCF("text/plain:PlainDCH\n"); + System.out.println("ODCHTest: Simple dchf created"); + + // now let's set the dchf in the dh + dh.setDataContentHandlerFactory(dchf); + System.out.println("ODCHTest: DataContentHandlerFactory set in DataHandler"); + + // get the dataflavors + xfer_flavors = dh.getTransferDataFlavors(); + System.out.println("ODCHTest: dh.getTransferDF returned " + + xfer_flavors.length + " data flavors."); + + // get the content: + try { + content = dh.getContent(); + } catch (Exception e) { e.printStackTrace(); } + + if(content == null) + System.out.println("ODCHTest: no content to be had!!!"); + else { + System.out.println("ODCHTest: got content of the following type: " + + content.getClass().getName()); + if(content == str) + System.out.println("get content works"); + + } + } + +}
diff --git a/demo/src/main/java/PlainDCH.java b/demo/src/main/java/PlainDCH.java new file mode 100644 index 0000000..447937b --- /dev/null +++ b/demo/src/main/java/PlainDCH.java
@@ -0,0 +1,127 @@ +/* + * 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 + */ + +import java.io.*; +import java.awt.datatransfer.DataFlavor; +import javax.activation.*; + + +public class PlainDCH implements DataContentHandler { + /** + * return the DataFlavors for this <code>DataContentHandler</code> + * @return The DataFlavors. + */ + public DataFlavor[] getTransferDataFlavors() { // throws Exception; + DataFlavor flavors[] = new DataFlavor[2]; + + + try { + flavors[0] = new ActivationDataFlavor(Class.forName("java.lang.String"), + "text/plain", + "text string"); + } catch(Exception e) + { System.out.println(e); } + + flavors[1] = new DataFlavor("text/plain", "Plain Text"); + return flavors; + } + /** + * return the Transfer Data of type DataFlavor from InputStream + * @param df The DataFlavor. + * @param ins The InputStream corresponding to the data. + * @return The constructed Object. + */ + public Object getTransferData(DataFlavor df, DataSource ds) { + + // this is sort of hacky, but will work for the + // sake of testing... + if(df.getMimeType().equals("text/plain")) { + if(df.getRepresentationClass().getName().equals( + "java.lang.String")) { + // spit out String + StringBuffer buf = new StringBuffer(); + char data[] = new char[1024]; + // InputStream is = null; + InputStreamReader isr = null; + int bytes_read = 0; + int total_bytes = 0; + + try { + isr = new InputStreamReader(ds.getInputStream()); + +// while(is.read(data) > 0) +// buf.append(data); + + while(true){ + bytes_read = isr.read(data); + if(bytes_read > 0) + buf.append(data, total_bytes, bytes_read); + else + break; + total_bytes += bytes_read; + } + } catch(Exception e) {} + + return buf.toString(); + + } + else if(df.getRepresentationClass().getName().equals( + "java.io.InputStream")){ + // spit out InputStream + try { + return ds.getInputStream(); + } catch (Exception e) {} + } + + } + + return null; + } + + /** + * + */ + public Object getContent(DataSource ds) { // throws Exception; + StringBuffer buf = new StringBuffer(); + char data[] = new char[1024]; + // InputStream is = null; + InputStreamReader isr = null; + int bytes_read = 0; + int total_bytes = 0; + + try { + isr = new InputStreamReader(ds.getInputStream()); + + // while(is.read(data) > 0) + // buf.append(data); + + while(true){ + bytes_read = isr.read(data); + if(bytes_read > 0) + buf.append(data, total_bytes, bytes_read); + else + break; + total_bytes += bytes_read; + } + } catch(Exception e) {} + + return buf.toString(); + } + /** + * construct an object from a byte stream + * (similar semantically to previous method, we are deciding + * which one to support) + */ + public void writeTo(Object obj, String mimeTye, OutputStream os) + throws IOException { + // throws Exception; + } + +}
diff --git a/demo/src/main/java/PrefTest.java b/demo/src/main/java/PrefTest.java new file mode 100644 index 0000000..b165fda --- /dev/null +++ b/demo/src/main/java/PrefTest.java
@@ -0,0 +1,57 @@ +/* + * 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 + */ + +import java.io.*; +import java.beans.*; +import javax.activation.*; + +public class PrefTest { + + public static void main(String args[]) { + MailcapCommandMap mcf = null; + + if (args.length > 0) { + try { + mcf = new MailcapCommandMap(args[0]); + } catch (Exception e) { + e.printStackTrace(); + System.exit(1); + } + } else + mcf = new MailcapCommandMap(); + + CommandInfo cmdinfo[] = mcf.getAllCommands("text/plain"); + + if (cmdinfo != null) { + System.out.println("ALL Commands for text/plain:"); + for (int i = 0; i < cmdinfo.length; i++) { + System.out.println("Verb: " + cmdinfo[i].getCommandName() + + " Class: " + cmdinfo[i].getCommandClass()); + } + System.out.println("done"); + } else { + System.out.println("no commands"); + } + System.out.println(); + + cmdinfo = mcf.getPreferredCommands("text/plain"); + if (cmdinfo != null) { + System.out.println("PREFERRED Commands for text/plain:"); + for (int i = 0; i < cmdinfo.length; i++) { + System.out.println("Verb: " + cmdinfo[i].getCommandName() + + " Class: " + cmdinfo[i].getCommandClass()); + } + System.out.println("done"); + } else { + System.out.println("no commands"); + } + System.out.println(); + } +}
diff --git a/demo/src/main/java/SimpleDCF.java b/demo/src/main/java/SimpleDCF.java new file mode 100644 index 0000000..c7997dc --- /dev/null +++ b/demo/src/main/java/SimpleDCF.java
@@ -0,0 +1,94 @@ +/* + * 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 + */ + +import javax.activation.*; +import java.util.StringTokenizer; +import java.util.Hashtable; + +public class SimpleDCF implements DataContentHandlerFactory { + Hashtable entry_hash = new Hashtable(); + /** + * the constructor, takes a list of classes as an argument in the + * form: + * <mimetype>:<class name>\n + * + * For Example: + * + * application/x-wombat:com.womco.WombatDCH + * text/plain:com.textco.TextDCH + * + */ + public SimpleDCF(String entries) { + StringTokenizer tok = new StringTokenizer(entries); + + String entry; + System.out.println("SimpleDCH: new SimpleDCF being created"); + + // parse the string + while(tok.hasMoreTokens()) { + int colon; + + entry = tok.nextToken(); + System.out.println("full entry = " + entry); + + // parse out the fields + colon = entry.indexOf(':'); + VectorEntry ve = new VectorEntry(entry.substring(0,colon), + entry.substring(colon + 1, + entry.length())); + System.out.println("adding element = " + ve); + entry_hash.put(ve.getMimeType(),ve); + } + } + + /** + * implement the factor interface + */ + public DataContentHandler createDataContentHandler(String mimeType){ + DataContentHandler dch = null; + + System.out.print("SimpleDCF: trying to create a DCH"); + + VectorEntry ve = (VectorEntry)entry_hash.get(mimeType); + if(ve != null) { + System.out.print("...found token"); + try { + + dch = (DataContentHandler)Class.forName( + ve.getClassName()).newInstance(); + if(dch == null) + System.out.println("...FAILED!!!"); + else + System.out.println("...SUCCESS!!!"); + + } catch(Exception e) { + System.out.println(e); + } + } + return dch; + } +} + +class VectorEntry { + private String mimeType; + private String className; + + public VectorEntry(String mimeType, String className) { + this.mimeType = mimeType; + this.className = className; + } + + public String getMimeType() { return mimeType; } + public String getClassName() { return className; } + public String toString() { + return new String("type: " + mimeType + " class name: " + className); + } + +}
diff --git a/demo/src/main/java/TextInternalizer.java b/demo/src/main/java/TextInternalizer.java new file mode 100644 index 0000000..a7ed047 --- /dev/null +++ b/demo/src/main/java/TextInternalizer.java
@@ -0,0 +1,90 @@ +/* + * 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 + */ + +import java.awt.*; +import java.io.*; +import java.beans.*; +import javax.activation.*; + +public class TextInternalizer extends Panel implements Externalizable { + // UI Vars... + private TextArea text_area = null; + + // File Vars + private File text_file = null; + private String text_buffer = null; + + private DataHandler _dh = null; + private boolean DEBUG = false; + /** + * Constructor + */ + public TextInternalizer() { + System.out.println("TextInternalizer!!!!!"); + + setLayout( new GridLayout(1,1)); + // create the text area + text_area = new TextArea("", 24, 80, + TextArea.SCROLLBARS_VERTICAL_ONLY ); + text_area.setEditable( false ); + + add(text_area); + } + + public void writeExternal(ObjectOutput out) throws IOException{ + + } + + public void readExternal(ObjectInput in) throws IOException, + ClassNotFoundException { + + + this.setObjectInput(in); + } + + + + //-------------------------------------------------------------------- +// public void setCommandContext(String verb, DataHandler dh) throws IOException { +// _dh = dh; +// this.setInputStream( _dh.getInputStream() ); +// } + //-------------------------------------------------------------------- + + /** + * set the data stream, component to assume it is ready to + * be read. + */ + public void setObjectInput(ObjectInput ins) throws IOException { + try { + + text_buffer = (String)ins.readObject(); + } catch(Exception e){ e.printStackTrace(); } + // place in the text area + text_area.setText(text_buffer); + + } + //-------------------------------------------------------------------- + public void addNotify() { + super.addNotify(); + invalidate(); + } + //-------------------------------------------------------------------- + public Dimension getPreferredSize() { + return text_area.getMinimumSize(24, 80); + } + +} + + + + + +
diff --git a/doc/release/LICENSE.md b/doc/release/LICENSE.md new file mode 100644 index 0000000..e0358f9 --- /dev/null +++ b/doc/release/LICENSE.md
@@ -0,0 +1,29 @@ + + Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the Eclipse Foundation, Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/doc/release/README.txt b/doc/release/README.txt new file mode 100644 index 0000000..25eecb3 --- /dev/null +++ b/doc/release/README.txt
@@ -0,0 +1,21 @@ + **JAVABEANS(tm) ACTIVATION FRAMEWORK 1.2 RELEASE** + +Thank you for downloading the release of the JavaBeans(tm) Activation +Framework! We hope you find it useful. Included in this release are +the following files and directories: + +README.txt : This file! + +RELNOTES.txt : The release notes for this release including, + installation instructions, system requirements, + known bugs, and open issues. + +LICENSE.txt : The license covering this software. + +javax.activation.jar : This JAR file contains the classes that + make up JavaBeans(tm) Activation Framework. + +demo : This directory contains some simple *unsupported* + demos that make use of some of the JAF's features. + +doc : This directory contains the JAF specification.
diff --git a/doc/release/RELNOTES.txt b/doc/release/RELNOTES.txt new file mode 100644 index 0000000..a07f374 --- /dev/null +++ b/doc/release/RELNOTES.txt
@@ -0,0 +1,62 @@ + JavaBeans(tm) Activation Framework 1.2 Release Notes + +Welcome to the JavaBeans(tm) Activation Framework! + +System Requirements: +-------------------- + +The JavaBeans(tm) Activation Framework (JAF) was developed and tested +against JDK 1.5. JAF is written in Java (with no native code). It +will run on any JDK 1.5 (or newer) compatible virtual machine. + +Note that JAF is included in Java SE 6 and later releases. + +Installation: +------------- + +There is effectively *no* installation of the JAF. The classes that +make up the JAF are contained in the included Java(tm) Archive (JAR) file, +"javax.activation.jar". This file can be placed anywhere accessible to the +Java virtual machine running on your system. The only requirement is +that the javax.activation.jar be included in your system's class path so +Java can find the JAF classes. + +Related Web Sites: +------------------ + +There are a number of web sites you might find useful if you haven't +already run across them: + +https://github.com/eclipse-ee4j/javamail -- The JavaMail(tm) API provides a set + of abstract classes that model a mail system. + Its implementation is dependent on the JAF. + + +Bugs fixed in the 1.2 Release: +------------------------------ + +JDK-8047773 Remove JAF's API dependency on java.beans.Beans.instantiate +JDK-8049379 JDK 1.9 moves config files to <java.home>/conf + + +Bugs fixed in the 1.1.1 Release: +-------------------------------- + +6456395 DataHandler.writeTo should have built-in support for byte arrays + and Strings +6538484 JAF fails in Turkish locale + + +Bugs fixed in the 1.1 Release: +------------------------------ + +<no id> use context class loader to load data content handlers +<no id> guard against possible NPE when parsing mailcap file +<no id> really make sure input stream gets closed in DataHandler.writeTo +4134676 need way to find all known MIME types +4786035 API doc: some bugs in javax/activation/MimeType.cmnt +4848096 MailcapCommandMap should store *ALL* mailcap entries +5090200 CommandMap needs file name to choose proper command on Windows +6245613 fix ActivationDataFlavor normalize methods to match DataFlavor +6252930 MailcapCommandMap needs support for "fallback" entries in mailcap file +6357487 DataHandler.getBean should load class using application's class loader
diff --git a/doc/spec/JAF-1.1-changes.txt b/doc/spec/JAF-1.1-changes.txt new file mode 100644 index 0000000..531c7a3 --- /dev/null +++ b/doc/spec/JAF-1.1-changes.txt
@@ -0,0 +1,197 @@ + + JavaBeans Activation Framework 1.1 + ================================== + +Please send comments and feedback to activation-comments@sun.com. + +The JavaBeans Activation Framework (JAF) 1.0 has been in use for eight +years. In that time it has served its original purpose quite well, +with only a few enhancements being requested. + +JAF 1.1 proposes a few minor changes to the JAF APIs to address the +most commonly requested enhancements. Following is a description of +the new APIs that are being introduced in JAF 1.1. The numbers in +parentheses are bug numbers; you can find more information about the +bug reports at: + + http://bugs.sun.com + +The JAF 1.1 RI will require J2SE 1.4 or later. (The JAF 1.0.2 RI +requires only JDK 1.1.6.) JAF 1.1 will be included in J2EE 5.0 +and J2SE 6.0. + +The JAF 1.1 RI and TCK will be available under the same terms as the +JAF 1.0 RI and TCK. + + +=================================================================== + +1. Need way to find all known MIME types (4134676) +--------------------------------------------------- + +To support this we add a new method to CommandMap: + + /** + * 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() + +The default implementation of this method returns null for +compatibility with applications that have provided a CommandMap +implementation based on JAF 1.0. The JAF 1.1 implementation of +MailcapCommandMap will return all the MIME types known to the +command map. + + +=================================================================== + +2. CommandMap needs file name to choose proper command on Windows (5090200) +---------------------------------------------------------------------------- + +This RFE requests additional CommandMap methods that allow +the CommandMap implementation to determine the file name, to +better support systems such as Windows that use the file name +extension to choose between the commands available to support +a given MIME type. We add the following methods to CommandMap: + + /** + * Get the preferred command list from a MIME Type. The actual semantics + * are determined by the implementation of the CommandMap. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the list of commands that are returned. The implementation + * in this class simply calls the <code>getPreferredCommands</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param ds a DataSource for the data + * @return the CommandInfo classes that represent the command Beans. + * @since JAF 1.1 + */ + public CommandInfo[] getPreferredCommands(String mimeType, DataSource ds) + + /** + * Get all the available commands for this type. This method + * should return all the possible commands for this MIME type. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the list of commands that are returned. The implementation + * in this class simply calls the <code>getAllCommands</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param ds a DataSource for the data + * @return the CommandInfo objects representing all the commands. + * @since JAF 1.1 + */ + public CommandInfo[] getAllCommands(String mimeType, DataSource ds) + + /** + * Get the default command corresponding to the MIME type. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the command that is chosen. The implementation + * in this class simply calls the <code>getCommand</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param cmdName the command name + * @param ds a DataSource for the data + * @return the CommandInfo corresponding to the command. + * @since JAF 1.1 + */ + public CommandInfo getCommand(String mimeType, String cmdName, + DataSource ds) + + /** + * Locate a DataContentHandler that corresponds to the MIME type. + * The mechanism and semantics for determining this are determined + * by the implementation of the particular CommandMap. <p> + * + * The <code>DataSource</code> provides extra information, such as + * the file name, that a CommandMap implementation may use to further + * refine the choice of DataContentHandler. The implementation + * in this class simply calls the <code>createDataContentHandler</code> + * method that ignores this argument. + * + * @param mimeType the MIME type + * @param ds a DataSource for the data + * @return the DataContentHandler for the MIME type + * @since JAF 1.1 + */ + public DataContentHandler createDataContentHandler(String mimeType, + DataSource ds) + + +As described, the default implementations of these methods simply +call the existing CommandMap methods that don't require a +DataSource object. The DataHandler implementation is changed to +call these new methods if created with a DataSource. + +The JAF 1.1 Reference Implementation would not provide a CommandMap +implementation that uses these new methods. + + +=================================================================== + +3. MailcapCommandMap needs support for "fallback" entries (6252930) +-------------------------------------------------------------------- + +This RFE describes a problem with wildcard matching and providing +fallbacks for JAF commands, in particular DataContentHandlers used +by JavaMail. To support the scenario described in that bug report, +we add a special "command" entry that can be added to a mailcap +entry to indicate that the mailcap entry defines fallback commands +that should only be used if no regular entry is available. An +attribute of the form + + x-java-fallback-entry=true + +in the mailcap entry indicates a fallback entry. For example: + + text/*;; x-java-fallback-entry=true; x-java-view=com.sun.TextViewer + +The MailcapCommandMap implementation supports this attribute. +Methods that return an array of commands will include commands +from fallback entries after commands from regular entries. +Methods that use or return only a single command will search +fallback entries after regular entries. This two level hierarchy, +in combination with the existing search rules for mailcap files, +is sufficient to handle the common use cases. + + +=================================================================== + +4. MailcapCommandMap should store *ALL* mailcap entries (4848096) +------------------------------------------------------------------ + +This RFE requests the ability to retrieve the native command +line associated with a mailcap entry. To support this we add +a method to MailcapCommandMap: + + /** + * Get the native commands for the given MIME type. + * Returns an array of strings where each string is + * an entire mailcap file entry. The application + * will need to parse the entry to extract the actual + * command as well as any attributes it needs. See + * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A> + * for details of the mailcap entry syntax. Only mailcap + * entries that specify a view command for the specified + * MIME type are returned. + * + * @return array of native command entries + * @since JAF 1.1 + */ + public String[] getNativeCommands(String mimeType) + +An application can choose to use these native "view" commands +using (e.g.) the Runtime.exec method.
diff --git a/doc/spec/JAF-1.2-changes.txt b/doc/spec/JAF-1.2-changes.txt new file mode 100644 index 0000000..bfd349b --- /dev/null +++ b/doc/spec/JAF-1.2-changes.txt
@@ -0,0 +1,94 @@ + + JavaBeans Activation Framework 1.2 + ================================== + +JAF 1.2 proposes a few minor changes to the JAF specification to +align with other changes in JDK 9. Following is a description of +the changes that are being introduced in JAF 1.2. The numbers in +parentheses are bug numbers; you can find more information about the +bug reports at: + + http://bugs.openjdk.java.net + +The JAF 1.2 RI will require Java SE 7 or later. JAF 1.2 will be +included in Java SE 9. + +The JAF 1.2 RI and TCK will be available under the same terms as the +JAF 1.1 RI and TCK. + + +=================================================================== + +1. JDK 9 moves config files to <java.home>/conf (JDK-8049379) +---------------------------------------------------------------- + +The specification for MailcapCommandMap is changed as follows: + + * <b>Mailcap file search order:</b><p> + * The MailcapCommandMap looks in various places in the user's + * system for mailcap file entries. When requests are made + * to search for commands in the MailcapCommandMap, it searches + * mailcap files in the following order: + * <p> + * <ol> + * <li> Programatically added entries to the MailcapCommandMap instance. + * <li> The file <code>.mailcap</code> in the user's home directory. + * <li> The file <code>mailcap</code> in the Java runtime. + * <li> The file or resources named <code>META-INF/mailcap</code>. + * <li> The file or resource named <code>META-INF/mailcap.default</code> + * (usually found only in the <code>activation.jar</code> file). + * </ol> + * <p> + * (The current implementation looks for the <code>mailcap</code> file + * in the Java runtime in the directory <code><i>java.home</i>/conf</code> + * if it exists, and otherwise in the directory + * <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value + * of the "java.home" System property. Note that the "conf" directory was + * introduced in JDK 9.) + + +The specification for MimetypesFileTypeMap is changed as follows: + + * <b>MIME types file search order:</b><p> + * The MimetypesFileTypeMap looks in various places in the user's + * system for MIME types file entries. When requests are made + * to search for MIME types in the MimetypesFileTypeMap, it searches + * MIME types files in the following order: + * <ol> + * <li> Programmatically added entries to the MimetypesFileTypeMap instance. + * <li> The file <code>.mime.types</code> in the user's home directory. + * <li> The file <code>mime.types</code> in the Java runtime. + * <li> The file or resources named <code>META-INF/mime.types</code>. + * <li> The file or resource named <code>META-INF/mimetypes.default</code> + * (usually found only in the <code>activation.jar</code> file). + * </ol> + * <p> + * (The current implementation looks for the <code>mime.types</code> file + * in the Java runtime in the directory <code><i>java.home</i>/conf</code> + * if it exists, and otherwise in the directory + * <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value + * of the "java.home" System property. Note that the "conf" directory was + * introduced in JDK 9.) + + +=================================================================== + +2. Remove JAF's API dependency on java.beans.Beans.instantiate (JDK-804773) +---------------------------------------------------------------------------- + +To allow custom runtimes to be created without the java.desktop module, +the getCommandObject method of CommandInfo is changed to make the use +of java.beans.Beans.instantiate conditional: + + /** + * Return the instantiated JavaBean component. + * <p> + * If the current runtime environment supports + * {@link java.beans.Beans#instantiate Beans.instantiate}, + * use it to instantiate the JavaBeans component. Otherwise, use + * {@link java.lang.Class#forName Class.forName}. + * <p> + * The component class needs to be public. + * On Java SE 9 and newer, if the component class is in a named module, + * it needs to be in an exported package. + ...
diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..1053cae --- /dev/null +++ b/pom.xml
@@ -0,0 +1,552 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- + + Copyright (c) 1997, 2020 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 + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 + http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>org.eclipse.ee4j</groupId> + <artifactId>project</artifactId> + <version>1.0.6</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>com.sun.activation</groupId> + <artifactId>all</artifactId> + <packaging>pom</packaging> + <version>1.2.2</version> + <name>Jakarta Activation distribution</name> + <description>${project.name}</description> + <url>https://github.com/eclipse-ee4j/jaf</url> + + <scm> + <connection>scm:git:ssh://git@github.com/eclipse/jaf.git</connection> + <developerConnection>scm:git:ssh://git@github.com/eclipse/jaf.git</developerConnection> + <url>https://github.com/eclipse-ee4j/jaf</url> + <tag>HEAD</tag> + </scm> + + <issueManagement> + <system>github</system> + <url>https://github.com/eclipse-ee4j/jaf/issues/</url> + </issueManagement> + + <licenses> + <license> + <name>EDL 1.0</name> + <url>http://www.eclipse.org/org/documents/edl-v10.php</url> + <distribution>repo</distribution> + </license> + </licenses> + + <properties> + <activation.spec.version>1.2</activation.spec.version> + <!-- defaults that are overridden in activation module --> + <activation.extensionName> + ${project.groupId}.${project.artifactId} + </activation.extensionName> + <activation.specificationTitle> + ${project.groupId}.${project.artifactId} + </activation.specificationTitle> + <activation.implementationTitle> + ${project.groupId}.${project.artifactId} + </activation.implementationTitle> + <activation.bundle.symbolicName> + ${project.groupId}.${project.artifactId} + </activation.bundle.symbolicName> + <activation.bundle.symbolicName> + ${project.groupId}.${project.artifactId} + </activation.bundle.symbolicName> + <activation.packages.export> + javax.activation.*; version=${activation.spec.version} + </activation.packages.export> + <activation.packages.import> + * + </activation.packages.import> + <activation.packages.private> + com.sun.activation.* + </activation.packages.private> + <!-- for the osgiversion-maven-plugin --> + <hk2.plugin.version>2.0.0</hk2.plugin.version> + <project.build.sourceEncoding>iso-8859-1</project.build.sourceEncoding> + <findbugs.threshold> + High + </findbugs.threshold> + <findbugs.version> + 3.0.1 + </findbugs.version> + <findbugs.skip> + true + </findbugs.skip> + <findbugs.exclude/> + <copyright-plugin.version>2.2</copyright-plugin.version> + </properties> + + <developers> + <developer> + <id>shannon</id> + <name>Bill Shannon</name> + <email>bill.shannon@oracle.com</email> + <organization>Oracle</organization> + <roles> + <role>lead</role> + </roles> + </developer> + </developers> + + <!-- following to enable use of "mvn site:stage" --> + <distributionManagement> + <site> + <id>oracle.com</id> + <url>file:/tmp</url> <!-- not used --> + </site> + </distributionManagement> + + <modules> + <module>activation</module> + <module>activationapi</module> + </modules> + + <profiles> + <!-- + This profile contains modules that should only be built + but not installed or deployed. + --> + <profile> + <id>build-only</id> + <modules> + <module>demo</module> + </modules> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + </profile> + + <!-- + This profile is used for deploying a Jakarta Activation + final release. + + Activating this profile manually for deployment causes + the above profile to be deactivated, which works around + an apparent bug in maven that prevents me from manually + deactivating a profile. This profile purposely has none + of the modules I don't want to be deployed. + --> + <profile> + <id>oss-release</id> + </profile> + </profiles> + + <build> + <defaultGoal>install</defaultGoal> + <plugins> + <!-- + Make sure we're using the correct version of maven. + --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <executions> + <execution> + <id>enforce-version</id> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireMavenVersion> + <version>[3.0.3,)</version> + </requireMavenVersion> + <requireJavaVersion> + <version>[9,)</version> + </requireJavaVersion> + </rules> + </configuration> + </execution> + </executions> + </plugin> + + <!-- + This plugin is reponsible for packaging artifacts + as OSGi bundles. Please refer to + http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html + for more information about how to use this plugin. + --> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <configuration> + <instructions> + <Bundle-SymbolicName> + ${activation.bundle.symbolicName} + </Bundle-SymbolicName> + <Export-Package> + ${activation.packages.export} + </Export-Package> + <Import-Package> + ${activation.packages.import} + </Import-Package> + <Private-Package> + ${activation.packages.private} + </Private-Package> + <DynamicImport-Package> + * + </DynamicImport-Package> + </instructions> + </configuration> + <!-- + Since we don't change the packaging type to bundle, we + need to configure the plugin to execute the manifest goal + during the process-classes phase of the build life cycle. + --> + <executions> + <execution> + <id>osgi-manifest</id> + <phase>process-classes</phase> + <goals> + <goal>manifest</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- + Since we don't want a qualifier like b05 or SNAPSHOT to + appear in the OSGi package version attribute, we use + the following plugin to populate a project property + with an OSGi version that is equivalent to the maven + version without the qualifier. + --> + <plugin> + <groupId>org.glassfish.hk2</groupId> + <artifactId>osgiversion-maven-plugin</artifactId> + <version>${hk2.plugin.version}</version> + <configuration> + <dropVersionComponent>qualifier</dropVersionComponent> + <versionPropertyName>activation.osgiversion</versionPropertyName> + </configuration> + <executions> + <execution> + <id>compute-osgi-version</id> + <goals> + <goal>compute-osgi-version</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- + Use the JDK 9+ compiler but with -source 1.8 for all + but the module-info.java file. + --> + <plugin> + <artifactId>maven-compiler-plugin</artifactId> + <executions> + <execution> + <id>default-compile</id> + <configuration> + <source>1.8</source> + <target>1.8</target> + <excludes> + <exclude>module-info.java</exclude> + </excludes> + </configuration> + </execution> + <execution> + <id>module-info-compile</id> + <goals> + <goal>compile</goal> + </goals> + <configuration> + <release>9</release> + <includes> + <include>module-info.java</include> + </includes> + </configuration> + </execution> + <execution> + <id>default-testCompile</id> + <configuration> + <release>9</release> + </configuration> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <!-- need at least this version to make excludes work --> + <configuration> + <finalName>${project.artifactId}</finalName> + <archive> + <!-- + Configure the maven-jar-plugin to pick up + META-INF/MANIFEST.MF that's generated by + the maven-bundle-plugin. + --> + <manifestFile> + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + </manifestFile> + <manifestEntries> + <Extension-Name> + ${activation.extensionName} + </Extension-Name> + <Specification-Title> + ${activation.specificationTitle} + </Specification-Title> + <Specification-Version> + ${activation.spec.version} + </Specification-Version> + <Specification-Vendor> + ${project.organization.name} + </Specification-Vendor> + <Implementation-Title> + ${activation.implementationTitle} + </Implementation-Title> + <Implementation-Version> + ${project.version} + </Implementation-Version> + <Implementation-Vendor> + ${project.organization.name} + </Implementation-Vendor> + <Implementation-Vendor-Id> + com.sun + </Implementation-Vendor-Id> + </manifestEntries> + </archive> + <excludes> + <exclude>**/*.java</exclude> + </excludes> + </configuration> + </plugin> + + <!-- + Tell the source plugin about the sources that may have + been downloaded by the maven-dependency-plugin. + --> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-source</id> + <phase>generate-sources</phase> + <goals> + <goal>add-source</goal> + </goals> + <configuration> + <sources> + <source> <!-- for dependencies --> + ${project.build.directory}/sources + </source> + </sources> + </configuration> + </execution> + <execution> + <id>add-resource</id> + <phase>generate-resources</phase> + <goals> + <goal>add-resource</goal> + </goals> + <configuration> + <resources> + <resource> + <directory>${main.basedir}</directory> + <targetPath>META-INF</targetPath> + <includes> + <include>LICENSE.md</include> + <include>NOTICE.md</include> + </includes> + </resource> + </resources> + </configuration> + </execution> + </executions> + </plugin> + + <!-- + Directory plugin to find parent root directory absolute path. + Sets ${main.basedir}, used above with build-helper-maven-plugin + to include LICENSE.md and NOTICE.md in jar files. + --> + <plugin> + <groupId>org.commonjava.maven.plugins</groupId> + <artifactId>directory-maven-plugin</artifactId> + <executions> + <execution> + <id>directories</id> + <goals> + <goal>highest-basedir</goal> + </goals> + <phase>initialize</phase> + <configuration> + <property>main.basedir</property> + </configuration> + </execution> + </executions> + </plugin> + + <!-- + Configure the source plugin here so that it will know + about the sources that may have been downloaded by the + maven-dependency-plugin and configured by the + build-helper-maven-plugin. + --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-sources</id> + <goals> + <goal>jar-no-fork</goal> + </goals> + </execution> + </executions> + <configuration> + <includePom>true</includePom> + <!-- + Since we added the classes directory using the + build-helper-maven-plugin above, we need to exclude + the class files from the source jar file. + --> + <excludes> + <exclude>**/*.class</exclude> + </excludes> + </configuration> + </plugin> + </plugins> + + <pluginManagement> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-compiler-plugin</artifactId> + <version>3.7.0</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <version>2.4.3</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <version>2.4</version> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <version>1.7</version> + </plugin> + <plugin> + <groupId>org.commonjava.maven.plugins</groupId> + <artifactId>directory-maven-plugin</artifactId> + <version>0.3</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>2.4</version> + </plugin> + <plugin> + <!-- + By default, disable the FindBugs plugin for all modules. + It's enabled in the modules where we actually want to + run it. + --> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <skip>${findbugs.skip}</skip> + <threshold>${findbugs.threshold}</threshold> + <findbugsXmlWithMessages>true</findbugsXmlWithMessages> + <excludeFilterFile> + ${findbugs.exclude} + </excludeFilterFile> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <version>1.0</version> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>3.5.0</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-source-plugin</artifactId> + <version>2.1.2</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-javadoc-plugin</artifactId> + <version>3.1.1</version> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-project-info-reports-plugin</artifactId> + <version>2.7</version> + </plugin> + <plugin> + <groupId>org.glassfish.copyright</groupId> + <artifactId>glassfish-copyright-maven-plugin</artifactId> + <version>${copyright-plugin.version}</version> + <configuration> + <scm>git</scm> + <scmOnly>true</scmOnly> + <excludeFile> + copyright-exclude + </excludeFile> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + + <dependencyManagement> + <dependencies> + <dependency> + <groupId>com.sun.activation</groupId> + <artifactId>jakarta.activation</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </dependencyManagement> + + <reporting> + <plugins> + <!-- + Configure FindBugs to run with "mvn site" and + generate html output that can be viewed directly. + --> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <version>${findbugs.version}</version> + <configuration> + <skip>${findbugs.skip}</skip> + <threshold>${findbugs.threshold}</threshold> + <excludeFilterFile> + ${findbugs.exclude} + </excludeFilterFile> + </configuration> + </plugin> + </plugins> + </reporting> +</project>
diff --git a/spec/README.md b/spec/README.md new file mode 100644 index 0000000..0536e11 --- /dev/null +++ b/spec/README.md
@@ -0,0 +1,22 @@ +Jakarta Activation Specification +================================ + +This project generates the Jakarta Activation Specification. + +Building +-------- + +Prerequisites: + +* JDK8+ +* Maven 3.0.5+ + +Run the full build: + +`mvn install` + +Locate the html files: +- `target/generated-docs/activation-spec-<version>.html` + +Locate the PDF files: +- `target/generated-docs/activation-spec-<version>.pdf`
diff --git a/spec/assembly.xml b/spec/assembly.xml new file mode 100644 index 0000000..16d6a85 --- /dev/null +++ b/spec/assembly.xml
@@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="iso-8859-1"?> +<!-- + + Copyright (c) 2019 Contributors to the Eclipse Foundation. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> + +<assembly> + <id>spec</id> + <formats> + <format>zip</format> + </formats> + <baseDirectory>activation-spec</baseDirectory> + <fileSets> + <fileSet> + <directory>target/generated-docs</directory> + <outputDirectory></outputDirectory> + </fileSet> + </fileSets> +</assembly>
diff --git a/spec/pom.xml b/spec/pom.xml new file mode 100644 index 0000000..3a20a23 --- /dev/null +++ b/spec/pom.xml
@@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright (c) 2017, 2019 Oracle and/or its affiliates. All rights reserved. + + This program and the accompanying materials are made available under the + terms of the Eclipse Public License v. 2.0, which is available at + http://www.eclipse.org/legal/epl-2.0. + + This Source Code may also be made available under the following Secondary + Licenses when the conditions for such availability set forth in the + Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + version 2 with the GNU Classpath Exception, which is available at + https://www.gnu.org/software/classpath/license.html. + + SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.eclipse.ee4j</groupId> + <artifactId>project</artifactId> + <version>1.0.5</version> + <relativePath/> + </parent> + <artifactId>activation-spec</artifactId> + <version>1.2-RevA</version> + <packaging>pom</packaging> + <name>Jakarta Activation Specification</name> + + <properties> + <site.output.dir>${project.build.directory}/staging</site.output.dir> + <maven.site.skip>true</maven.site.skip> + <asciidoctor.maven.plugin.version>1.5.7.1</asciidoctor.maven.plugin.version> + <asciidoctorj.version>1.6.2</asciidoctorj.version> + <asciidoctorj.pdf.version>1.5.0-alpha.16</asciidoctorj.pdf.version> + <jruby.version>9.2.6.0</jruby.version> + <!-- status: DRAFT, BETA, etc., or blank for final --> + <status></status> + <maven.build.timestamp.format>MMMM dd, yyyy</maven.build.timestamp.format> + <revisiondate>${maven.build.timestamp}</revisiondate> + </properties> + + <build> + <defaultGoal>package</defaultGoal> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-enforcer-plugin</artifactId> + <version>1.4.1</version> + <executions> + <execution> + <id>enforce-versions</id> + <goals> + <goal>enforce</goal> + </goals> + <configuration> + <rules> + <requireJavaVersion> + <version>[1.8.0,1.9.0)</version> + <message>You need JDK8 or lower</message> + </requireJavaVersion> + </rules> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctor-maven-plugin</artifactId> + <version>${asciidoctor.maven.plugin.version}</version> + <dependencies> + <dependency> + <groupId>org.jruby</groupId> + <artifactId>jruby-complete</artifactId> + <version>${jruby.version}</version> + </dependency> + <dependency> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctorj</artifactId> + <version>${asciidoctorj.version}</version> + </dependency> + <dependency> + <groupId>org.asciidoctor</groupId> + <artifactId>asciidoctorj-pdf</artifactId> + <version>${asciidoctorj.pdf.version}</version> + </dependency> + </dependencies> + <executions> + <execution> + <id>asciidoc-to-html</id> + <phase>generate-resources</phase> + <goals> + <goal>process-asciidoc</goal> + </goals> + <configuration> + <backend>html5</backend> + <outputFile>${project.build.directory}/generated-docs/activation-spec-${project.version}.html</outputFile> + <attributes> + <doctype>book</doctype> + <status>${status}</status> + <data-uri /> + <icons>font</icons> + <toc>left</toc> + <icons>font</icons> + <sectanchors>true</sectanchors> + <idprefix /> + <idseparator>-</idseparator> + <docinfo1>true</docinfo1> + </attributes> + </configuration> + </execution> + <execution> + <id>asciidoc-to-pdf</id> + <phase>generate-resources</phase> + <goals> + <goal>process-asciidoc</goal> + </goals> + <configuration> + <backend>pdf</backend> + <outputFile>${project.build.directory}/generated-docs/activation-spec-${project.version}.pdf</outputFile> + <attributes> + <pdf-stylesdir>${project.basedir}/src/theme</pdf-stylesdir> + <pdf-style>jakartaee</pdf-style> + <doctype>book</doctype> + <status>${status}</status> + <data-uri /> + <icons>font</icons> + <pagenums /> + <toc /> + <icons>font</icons> + <sectanchors>true</sectanchors> + <idprefix /> + <idseparator>-</idseparator> + <docinfo1>true</docinfo1> + <embedAssets>true</embedAssets> + </attributes> + </configuration> + </execution> + </executions> + <configuration> + <sourceDocumentName>activation-spec.adoc</sourceDocumentName> + <sourceHighlighter>coderay</sourceHighlighter> + <attributes> + <revnumber>${project.version}</revnumber> + <revremark>${status}</revremark> + <revdate>${revisiondate}</revdate> + </attributes> + </configuration> + + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-release-plugin</artifactId> + <version>2.5.2</version> + <configuration> + <mavenExecutorId>forked-path</mavenExecutorId> + <useReleaseProfile>false</useReleaseProfile> + <arguments>${release.arguments}</arguments> + </configuration> + <dependencies> + <dependency> + <groupId>org.apache.maven.scm</groupId> + <artifactId>maven-scm-provider-gitexe</artifactId> + <version>1.9.4</version> + </dependency> + </dependencies> + </plugin> + + <!-- + This is the rule that builds the zip file for download. + --> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-assembly-plugin</artifactId> + <version>3.1.1</version> + <inherited>false</inherited> + <executions> + <execution> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + <configuration> + <appendAssemblyId>false</appendAssemblyId> + <descriptors> + <descriptor>assembly.xml</descriptor> + </descriptors> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project>
diff --git a/spec/src/main/asciidoc/activation-spec.adoc b/spec/src/main/asciidoc/activation-spec.adoc new file mode 100644 index 0000000..75b00ee --- /dev/null +++ b/spec/src/main/asciidoc/activation-spec.adoc
@@ -0,0 +1,28 @@ +// +// Copyright (c) 2017, 2019 Contributors to the Eclipse Foundation +// + += Jakarta Activation 1.2 +:authors: Jakarta Activation Team, https://projects.eclipse.org/projects/ee4j.jaf +:email: https://dev.eclipse.org/mailman/listinfo/jaf-dev +:version-label!: +:doctype: book +:license: Eclipse Foundation Specification License v1.0 +:source-highlighter: coderay +:toc: left +:toclevels: 4 +:sectnumlevels: 4 +:sectanchors: +ifdef::backend-pdf[] +:pagenums: +:numbered: +:title-logo-image: image:jakarta_ee_logo_schooner_color_stacked_default.png[pdfwidth=4.25in,align=right] +endif::[] + +// == License +:sectnums!: +include::license-efsl.adoc[] + +// == Spec +:sectnums: +include::activation.adoc[]
diff --git a/spec/src/main/asciidoc/activation.adoc b/spec/src/main/asciidoc/activation.adoc new file mode 100644 index 0000000..0bd070b --- /dev/null +++ b/spec/src/main/asciidoc/activation.adoc
@@ -0,0 +1,837 @@ +== Overview + + +JavaBeans™ is proving to be a popular technology. As +more people embrace JavaBeans™ and the Java™ platform, some of the +environment’s shortcomings are brought to light. JavaBeans™ was meant to +satisfy needs in builder and development environments but its +capabilities fall short of those needed to deploy stand alone components +as content editing and creating entities. + +Neither JavaBeans™ nor the Java™ platform define a +consistent strategy for typing data, a method for determining the +supported data types of a software component, a method for binding typed +data to a component, or an architecture and implementation that supports +these features. + +Presumably with these pieces in place, a developer can +write a JavaBeans™ based component that provides helper application like +functionality in a web browser, added functionality to an office suite, +or a content viewer in a Java™ application. + +== Goals + + +This document describes Jakarta Activation. +Jakarta Activation implements the following services: + +* It determines the type of arbitrary data. +* It encapsulates access to data. +* It discovers the operations available on a particular +type of data. +* It instantiates the software component that +corresponds to the desired operation on a particular piece of data. + +Jakarta Activation is packaged as a Standard Extension to the +Java™ platform. + +== Architectural Overview + + +The Java™ platform (including JavaBeans™) already +provides some support for a modest activation framework. Jakarta Activation +leverages as much of that existing technology as possible. Jakarta Activation +integrates these mechanisms. + +image:activation.png[image] + +This diagram shows the major elements comprising the +Jakarta Activation architecture. +Note that the framework shown here is not bound to a particular application. + +=== The DataHandler Class + +The DataHandler class (shown in the diagram above) +provides a consistent interface between Activation-aware clients and other +subsystems. + +=== The DataSource Interface + +The DataSource interface encapsulates an object that +contains data, and that can return both a stream providing data access, +and a string defining the MIME type describing the data. + +Classes can be implemented for common data sources +(web, file system, IMAP, ftp, etc.). The DataSource interface can also +be extended to allow per data source user customizations. Once the +DataSource is set in the DataHandler, the client can determine the +operations available on that data. + +Jakarta Activation includes two DataSource class implementations +for convenience: + +* FileDataSource accesses data held in a file. +* URLDataSource accesses data held at a URL. + +=== The CommandMap Interface + +The CommandMap provides a service that allows consumers +of its interfaces to determine the ‘commands’ available on a particular +MIME type as well as an interface to retrieve an object that can operate +on an object of a particular MIME type (effectively a component +registry). The Command Map can generate and maintain a list of available +capabilities on a particular data type by a mechanism defined by the +implementation of the particular instance of the CommandMap. + +The JavaBeans™ package provides the programming model +for the software components that implement the commands. Each JavaBeans™ +component can use externalization, or can implement the CommandObject +interface to allow the typed data to be passed to it. + +Jakarta Activation defines the CommandMap interface, which +provides a flexible and extensible framework for the CommandMap. The +CommandMap interface allows developers to develop their own solutions +for discovering which commands are available on the system. A possible +implementation can access the ‘types registry’ on the platform or use a +server-based solution. Jakarta Activation provides a simple default +solution based on RFC 1524 (.mailcap) like functionality. +See “Deliverables” below. + +=== The Command Object Interface + +Beans extend the CommandObject interface in order to +interact with Activation services. +Activation-aware JavaBeans™ components can directly +access their DataSource and DataHandler objects in order to retrieve the +data type and to act on the data. + +== Using The Framework + + +We intend to make this infrastructure widely available +for any Java™ Application that needs this functionality. The ‘canonical’ +consumer of this framework accesses it through the DataHandler (although +the major subsystems are designed to also operate independently). An +underlying DataSource object is associated with the DataHandler when the +DataHandler class is constructed. + +* The DataHandler retrieves the data typing information +from the DataSource or gets the data type directly from the constructor. +* Once this initialization step is complete, a list of +commands that can be performed on the data item can be accessed from the +DataHandler. + +When an application issues a request for this list, the +DataHandler uses the MIME data type specifier returned to request a list +of available commands from the CommandMap object. The CommandMap has +knowledge of available commands (implemented as Beans) and their +supported data types. The CommandMap returns a subset of the full list +of all commands based on the requested MIME type and the semantics of +the CommandMap implementation, to the DataHandler. + +When the application wishes to apply a command to some +data, it is accomplished through the appropriate DataHandler interface, +which uses the CommandMap to retrieve the appropriate Bean that is used +to operate on the data. The container (user of the framework) makes the +association between the data and the Bean. + +== Usage Scenarios + + +This scenario uses the example of a hypothetical file +viewer application in order to illustrate the normal flow of tasks +involved when implementing Jakarta Activation. The file viewer is similar to the +Windows Explorer utility. When launched, it presents the user with a +display of available files. It includes a function like Explorer’s +‘right mouse’ menu, where all operations that can be performed on a +selected data item are listed in a popup menu for that item. + +A typical user launches this application to view a +directory of files. When the user specifies a file by clicking on it, +the application displays a popup menu that lists the available +operations on that file. File system viewer utilities normally include +‘edit,’ ‘view,’ and ‘print’ commands as available operations. For +instance selecting ‘view’ causes the utility to open the selected file +in a viewer that can display data of the data type held in that file. + +=== Scenario Architecture + +Description of tasks performed by the application is +broken down into three discrete steps, for clarity: + +* Initialization: The application constructs a view of +the file system. +* Getting the Command List: The application presents +the command list for a selected data item. +* Performing the Command: The application performs a +command on the selected data object. + +=== Initialization + +One of the interfaces mentioned below is the +‘DataSource’ object. Recall that the DataSource object encapsulates the +underlying data object in a class that abstracts the underlying data +storage mechanism, and presents its consumers with a common data access +and typing interface. The file viewer application queries the file +system for its contents. + +The viewer instantiates a DataSource object for each +file in the directory. Then it instantiates a a DataHandler with the +DataSource as its constructor argument. The DataHandler object provides +the client application with access to the CommandMap, which provides a +service that enables access to commands that can operate on the data. +The application maintains a list of the DataHandler objects, queries +them for their names to generate its display. + +[source, java] +---- +// for each file in the directory: +File file = new File(file_name); +DataSource ds = new FileDataSource(file); +DataHandler dh = new DataHandler(ds); +---- + +=== Getting the Command List + +Once the application has been initialized and has +presented a list of files to the user, the user can select a file on the +list. When the user selects a file, the application displays a popup +menu that lists the available operations on that file. + +The application implements this functionality by +requesting the list of available commands from the DataHandler object +associated with a file. The DataHandler retrieves the MIME type of the +data from the DataSource object and queries the CommandMap for +operations that are available on that type. The application interprets +the list and presents it to the user on a popup menu. The user then +selects one of the operations from that list. + +[source, java] +---- +// get the command list for an object +CommandInfo cmdInfo[] = dh.getPreferredCommands(); + +PopupMenu popup = new PopupMenu(“Item Menu”); + +// populate the popup with available commands +for (i = 0; i < cmdInfo.length; i++) + popup.add(cmdInfo[i].getCommandName()); + +// add and show popup +add(popup); +popup.show(x_pos, y_pos); +---- + +=== Performing a Command + +After the user has selected a command from the popup +menu, the application uses the appropriate CommandInfo class to retrieve +the Bean that corresponds to the selected command, and associates the +data with that Bean using the appropriate mechanism (DataHandler, +Externalization etc.). Some CommandObjects (viewers for instance) are +subclassed from java.awt.Component and require that they are given a +parent container. Others (like a default print Command) might not +present a user interface. This allows them to be flexible enough to +function as stand alone viewer/editors, or perhaps as components in a +compound document system. The ‘application’ is responsible for providing +the proper environment (containment, life cycle, etc.) for the +CommandObject to execute in. We expect that the requirements will be +lightweight (not much beyond JavaBeans™ containers and AWT containment +for visible components). + +[source, java] +---- +// get the command object +Object cmdBean = cmdInfo[cmd_id].getCommandObject(dh, + this.getClassLoader()); + + ... // use serialization/externalization where appropriate + +my_awt_container.add((Component)cmdBean); +---- + +=== An Alternative Scenario + +The first scenario was the ‘canonical’ case. There are +also circumstances where the application has already created objects to +represent its data. In this case creating an in-memory instance of a +DataSource that converted an existing object into an InputStream is an +inefficient use of system resources and can result in a loss of data +fidelity. + +In these cases, the application can instantiate a +DataHandler, using the DataHandler(Object obj, String mimeType) +constructor. DataHandler implements the Transferable interface, so the +consuming Bean can request representations other than InputStreams. The +DataHandler also constructs a DataSource for consumers that request it. +The DataContentHandler mechanism is extended to also allow conversion +from Objects to InputStreams. + +The following code is an example of a database front +end using Jakarta Activation, which provides query results in terms of objects. + +[source, java] +---- + /** + * Get the viewer to view my query results: + */ + Component getQueryViewer(QueryObject qo) throws Exception { + String mime_type = qo.getType(); + Object q_result = qo.getResultObject(); + DataHandler my_dh = new DataHandler(q_result, mime_type); + + return (Component)my_dh.getCommand(“view”). + getCommandObject(my_dh, null)); + } +---- + +== Primary Framework Interfaces + + +This section describes interfaces required to implement the +Jakarta Activation architecture introduced in Section Three. + +=== The DataSource Interface + +The DataSource interface is used by the DataHandler +(and possibly other classes elsewhere) to access the underlying data. +The DataSource object encapsulates the underlying data object in a class +that abstracts the underlying data storage and typing mechanism, and +presents its consumers with a common data access interface. + +Jakarta Activation provides DataSource implementations that +support file systems and URLs. Application system vendors can use the +DataSource interface to implement their own specialized DataSource +classes to support IMAP servers, object databases, or other sources. + +There is a one-to-one correspondence between underlying +data items (files for instance) and DataSource objects. Also note that +the class that implements the DataSource interface is responsible for +typing the data. To manage a file system, a DataSource can use a simple +mechanism such as a file extension to type data, while a DataSource that +supports incoming web-based data can actually examine the data stream to +determine its type. + +=== The DataHandler Class + +The DataHandler class encapsulates a Data object, and +provides methods which act on that data. + +DataHandler encapsulates the type-to-command object +binding service of the CommandMap interface for applications. It +provides a handle to the operations and data available on a data +element. + +DataHandler also implements the Transferable interface. +This allows applications and applets to retrieve alternative +representations of the underlying data, in the form of objects. The +DataHandler encapsulates the interface to the component repository and +data source. + +Let’s examine these groups of features in more detail: + +==== Data Encapsulation + +A DataHandler object can only be instantiated with +data. The data can be in the form of an object implementing the +DataSource interface (the preferred way) or as an object with an +associated content type. + +Once instantiated, the DataHandler tries to provide its +data in a flexible way. The DataHandler implements the Transferable +interface which allows an object to provide alternative representations +of the data. The Transferable interface’s functionality can be extended +via objects implementing the DataContentHandler interface, and then made +available to the DataHandler either by a DataContentHandlerFactory +object, or via a CommandMap. + +==== Command Binding + +The DataHandler provides wrappers around commonly used +functions for command discovery. DataHandler has methods that call into +the current CommandMap associated with the DataHandler. By default the +DataHandler calls CommandMap’s getDefaultCommandMap method if no +CommandMap was explicitly set. As a convenience, DataHandler uses the +content type of its data when calls are made to the CommandMap. + +=== The DataContentHandler Interface + +The DataContentHandler interface is implemented by +classes that are used by the DataHandler to convert InputStreams into +objects and vice versa. In effect, the DataHandler object uses a +DataContentHandler object to implement the Transferable interface. +DataContentHandlers are discovered via the current CommandMap. A +DataContentHandler uses DataFlavors to represent the data types it can +access. + +The DataContentHandler also converts data from objects +into InputStreams. For instance, if an application needs to access a +.gif file, it passes the file to the image/gif DataContentHandler. The +image/gif DataContentHandler converts the image object into a +gif-formatted byte stream. + +Applications will typically need to provide +DataContentHandlers for all the MIME types they intend to support. (Note +that the Jakarta Mail implementation provides DataContentHandlers +for many of the MIME types used in mail messages.) + +=== The CommandMap Interface + +Once the DataHandler has a MIME type describing the +content, it can query the CommandMap for the operations, or commands +that are available for that data type. The application requests commands +available through the DataHandler and specifies a command on that list. +The DataHandler uses the CommandMap to retrieve the Bean associated with +that command. Some or all of the command map is stored in some ‘common’ +place, like a .mailcap (RFC 1524) file. Other more complex +implementations can be distributed, or can provide licensing or +authentication features. + +=== The CommandInfo Class + +The CommandInfo class is used to represent commands in +an underlying registry. From a CommandInfo object, an application can +instantiate the Bean or request the verb (command) it describes. + +=== The CommandObject Interface + +Beans designed specifically for use with Jakarta Activation +should implement the CommandObject interface. This +interface provides direct access to DataHandler methods and notifies an +Activation-aware Bean which verb was used to call it. Upon instantiation, the +Bean takes a string specifying a user-selected command verb, and the +DataHandler object managing the target data. The DataHandler takes a +DataSource object, which provides an input stream linked to that data, +and a string specifying the data type. + +=== The DataContentHandlerFactory + +Like the ContentHandler factory in the java.net +package, the DataContentHandlerFactory is an interface that allows +developers to write objects that map MIME types to DataContentHandlers. +The interface is extremely simple, in order to allow developers as much +design and implementation freedom as possible. + +== Writing Beans for the Framework + + +=== Overview + +This section describes the specification of +well-behaved Activation-aware Bean viewers. Note that this proposal assumes the +reader is comfortable with the JavaBeans™ Specification. +Developers intending to implement viewer Beans for Jakarta Activation +should be familiar with JavaBeans™ concepts and architecture. + +=== Viewer Goals + +. Make the implementation of viewers and editors as +simple as implementing Beans. That is, require low cost of entry to be a +good citizen. + +. Allow developers to have a certain amount of flexibility in their +implementations. + +=== General + +We are attempting to limit the amount of extra baggage +that needs to be implemented beyond ‘generic’ Beans. In many cases, +JavaBeans™ components that weren’t developed with knowledge of the +framework can be used. Jakarta Activation exploits the existing features of +JavaBeans™ and the JDK™, and defines as few additional interfaces and +policies as possible. + +We expect that viewers/editors will be bound to data +via a simple registry mechanism similar in function to a .mailcap file. +In addition, mailcap format files may be bundled with components, +allowing additional packages to be added at runtime. + +Our viewers/editors and related classes and files are +encapsulated into JAR files, as is the preferred method for JavaBeans™. +Jakarta Activation does not restrict the choice of classes used to implement +Activaiton-aware ‘viewer’ Beans, beyond those expected of well-behaved Beans. + +=== Interfaces + +A viewer Bean that communicates directly with a Jakarta Activation +DataHandler should implement the CommandObject interface. This interface +is small and easy to implement. However, Beans can still use standard +Serialization and Externalization methods available in the JDK. + +=== Storage + +Jakarta Activation expects applications and viewer Beans to +implement storage tasks via the DataSource object. However; it is +possible to use Externalization. An Activation-aware application can implement +the following storage mechanism: + +[source, java] +---- +ObjectOutputStream oos = new ObjectOutputStream( + data_handler.getOutputStream()); +my_externalizable_bean.writeExternal(oos); +---- + +=== Packaging + +The basic format for packaging of the Viewer/Editors is +the JAR file as described in the JavaBeans™ Specification. This format +allows the convenient packaging of collections of files that are related +to a particular Bean or applet. For more information concerning +integration points, see Section 8. + +=== Container Support + +Jakarta Activation is designed to be flexible enough to support +the needs of a variety of applications. Jakarta Activation expects these +applications to provide the appropriate containers and life cycle +support for these Beans. Beans written for the framework should be +compatible with the guidelines in the JavaBeans™ documentation and +should be tested against the BDK BeanBox (and the JDK Appletviewer if +they are subclassed from Applet). + +=== Lifecycle + +In general Jakarta Activation expects that its viewer bean life +cycle semantics are the same as those for all Beans. In the case of +Beans that implement the CommandObject interface we encourage +application developers to not parent Beans subclassed from +java.awt.Component to an AWT container until after they have called the +javax.activation.CommandObject.setCommandContext method. + +=== Command Verbs + +The MailcapCommandMap implementation provides a +mechanism that allows for an extensible set of command verbs. +Applications using Jakarta Activation can query the system for commands +available for a particular MIME type, and retrieve the Bean associated +with that MIME type. + +== Framework Integration Points + + +This section presents several examples that clarify how +JavaBeans™ developers can write Beans that are integrated with +Jakarta Activation. + +First, let’s review the pluggable components of the +Jakarta Activation framework: + +* A mechanism that accesses target data where it is +stored: DataSource +* A mechanism to convert data objects to and from an +external byte stream format: DataContentHandler +* A mechanism to locate visual components that operate +on data objects: CommandMap +* The visual components that operate on data objects: +Activation-aware Beans + +As a JavaBeans™ developer, you may build visual Beans. +You can also develop DataContentHandlers to supply data to those Beans. +You might also need to develop a new DataSource or CommandMap class to +access data and specify a data type. + +=== Bean + +Suppose you’re building a new Wombat Editor product, +with its corresponding Wombat file format. You’ve built the Wombat +Editor as one big Bean. Your WombatBean can do anything and everything +that you might want to do with a Wombat. It can edit, it can print, it +can view, it can save Wombats to files, and it can read Wombats in from +files. You’ve defined a language-independent Wombat file format. You +consider the Wombat data and file formats to be proprietary so you have +no need to offer programmatic interfaces to Wombats beyond what your +WombatBean supports. + +You’ve chosen the MIME type “application/x-wombat” to +describe your Wombat file format, and you’ve chosen the filename +extension “.wom” to be used by files containing Wombats. + +To integrate with the framework, you’ll need some +simple wrappers for your WombatBean for each command you want to +implement. For example, for a Print command wrapper you can write the +following code: + +[source, java] +---- +public class WombatPrintBean extends WombatBean { + public WombatPrintBean() { + super(); + initPrinting(); + } +} +---- + +You will need to create a mailcap file that lists the +MIME type “application/x-wombat” and user visible commands that are +supported by your WombatBean. Your WombatBean wrappers will be listed as +the objects supporting each of these commands. + +[source] +---- +application/x-wombat; ; x-java-view=com.foo.WombatViewBean; \ + x-java-edit=com.foo.WombatEditBean; \ + x-java-print=com.foo.WombatPrintBean +---- + +You’ll also need to create a mime.types file with an +entry: + +[source] +---- +type=application/x-wombat desc=”Wombat” exts=wom +---- + +All of these components are packaged in a JAR file: + +[source] +---- +META-INF/mailcap +META-INF/mime.types +com/foo/WombatBean.class +com/foo/WombatEditBean.class +com/foo/WombatViewBean.class +---- + +Because everything is built into one Bean, and because +no third party programmatic access to your Wombat objects is required, +there’s no need for a DataContentHandler. Your WombatBean can therefore +implement the Externalizable interface instead; and use its methods to +read and write your Wombat files. The DataHandler can call the +Externalizable methods when appropriate. + +=== Beans + +Your Wombat Editor product has really taken off, and +you’re now adding significant new functionality and flexibility to your +Wombat Editor. It’s no longer feasible to put everything into one giant +Bean. Instead, you’ve broken the product into a number of Beans and +other components: + +* A WombatViewer Bean that can be used to quickly view +a Wombat in read-only mode. +* A WombatEditor Bean that is heavier than the +WombatViewer, but also allows editing. +* A WombatPrinter Bean that simply prints a Wombat. +* A component that reads and writes Wombat files. +* A Wombat class that encapsulates the Wombat data and +is used by your other Beans and components. + +In addition, customers have demanded to be able to +programmatically manipulate Wombats, independently from the visual +viewer or editor Beans. You’ll need to create a DataContentHandler that +can convert a byte stream to and from a Wombat object. When reading, the +WombatDataContentHandler reads a byte stream and returns a new Wombat +object. When writing, the WombatDataContentHandler takes a Wombat object +and produces a corresponding byte stream. You’ll need to publish the API +to the Wombat class. + +The WombatDataContentHandler is delivered as a class +and is designated as a DataContentHandler that can operate on Wombats in +the mailcap file included in your JAR file. + +Your mailcap file changes to list the appropriate +Wombat Beans, which implement user commands: + +[source] +---- +application/x-wombat; ; x-java-View=com.foo.WombatViewBean; \ + x-java-edit=com.foo.WombatEditBean; \ + x-java-print=com.foo.WombatPrintBean; \ + x-java-content-handler=com.foo.WombatDataContentHandler +---- + +Your Wombat Beans can continue to implement the +Externalizable interface, and thus read and write Wombat byte streams. +They are more likely to simply operate on Wombat objects directly. To +find the Wombat object they’re being invoked to operate on, they +implement the CommandObject interface. The setCommandContext method +refers them to the corresponding DataHandler, from which they can invoke +the getContent method, which will return a Wombat object (produced by +the WombatDataContentHandler). + +All components are packaged in a JAR file. + +=== Viewer Only + +The Wombat product has been wildly successful. The +ViewAll Company has decided that it can produce a Wombat viewer that’s +much faster than the WombatViewer Bean. Since they don’t want to depend +on the presence of any Wombat components, their viewer must parse the +Wombat file format, which they reverse engineered. + +The ViewAll WombatViewerBean implements the +Externalizable interface to read the Wombat data format. + +ViewAll delivers an appropriate mailcap file: + +[source] +---- +application/x-wombat; ; x-java-view=com.viewall.WombatViewer +---- + +and mime.types file: + +[source] +---- +type=application/x-wombat desc=”Wombat” exts=wom +---- + +All components are packaged in a JAR file. + +=== ContentHandler Bean Only + +Now that everyone is using Wombats, you’ve decided that +it would be nice if you could notify people by email when new Wombats +are created. You have designed a new WombatNotification class and a +corresponding data format to be sent by email using the MIME type +“application/x-wombat-notification”. Your server detects the presence of +new Wombats, constructs a WombatNotification object, and constructs and +sends an email message with the Wombat notification data as an +attachment. Your customers run a program that scans their email INBOX +for messages with Wombat notification attachments and use the +WombatNotification class to notify their users of the new Wombats. + +In addition to the server application and user +application described, you’ll need a DataContentHandler to plug into the +DataHandler infrastructure and construct the WombatNotification objects. +The WombatNotification DataContentHandler is delivered as a class named +WombatNotificationDataContentHandler and is delivered in a JAR file with +the following mailcap file: + +[source] +---- +application/x-wombat-notification; \ + WombatNotificationDataContentHandler +---- + +The server application creates DataHandlers for its +WombatNotification objects. The email system uses the DataHandler to +fetch a byte stream corresponding to the WombatNotification object. (The +DataHandler uses the DataContentHandler to do this.) + +The client application retrieves a DataHandler for the +email attachment and uses the getContent method to get the corresponding +WombatNotification object, which will then notify the user. + + + +== Framework Deliverables + + +=== Packaging Details + +Jakarta Activation is implemented as a Standard Extension to the +Java™ Platform. +The following are some more details about the package: + +* The package name is javax.activation. +* The Jakarta Activation implementation does not include +DataContentHandlers for any MIME data types; applications must include +the DataContentHandlers they need. Note that the Jakarta Mail +implementation includes DataContentHandlers for some basic data types +used in mail messages. + +=== Framework Core Classes + +**interface DataSource:** The DataSource interface provides +Jakarta Activation with an abstraction of some 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. + +**class DataHandler:** 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. + +**interface DataContentHandler:** 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. + +**interface DataContentHandlerFactory:** 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. + +**class CommandMap:** The CommandMap class provides an +interface to the registry of viewer, editor, print, etc. 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. + +**interface CommandObject:** Beans that are 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. Beans 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. + +**class CommandInfo:** 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 +implementations that subclass CommandInfo to provide special behavior. +For example a CommandMap could dynamically generate Beans. In this case, +it might not be possible to create an object with all the correct state +information solely from the class name. + +=== Framework Auxiliary Classes + +**class FileDataSource:** The FileDataSource class +implements a simple DataSource object that encapsulates a file. It +provides data typing services via a FileTypeMap object. + +**class FileTypeMap:** 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. + +**class MimetypesFileTypeMap:** This class extends +FileTypeMap and provides data typing of files via their file extension. +It uses the .mime.types format. + +**class URLDataSource:** 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. + +**class MailcapCommandMap:** 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. + +**class ActivationDataFlavor:** The ActivationDataFlavor is +a special subclass of java.awt.datatransfer.DataFlavor. It allows +Jakarta Activation to set all three values stored by the DataFlavor +class via a new constructor as well as improved MIME parsing in the +equals method. +Except for the improved parsing, its semantics are identical to that of +the JDK’s DataFlavor class. + +**class UnsupportedDataTypeException:** Signals that +requested operation does not support the requested data type. + +**class MimeType:** A Multipurpose Internet Extension +(MIME) type, as defined in RFC 2045 and 2046. + +**class com.sun.activation.viewers.*:** A few simple +example viewer Beans (text and image). + +== Document Change History + +Oct 21, 2019: First complete Jakarta EE version.
diff --git a/spec/src/main/asciidoc/images/activation.png b/spec/src/main/asciidoc/images/activation.png new file mode 100644 index 0000000..d2b4a73 --- /dev/null +++ b/spec/src/main/asciidoc/images/activation.png Binary files differ
diff --git a/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png new file mode 100644 index 0000000..97b46ce --- /dev/null +++ b/spec/src/main/asciidoc/images/jakarta_ee_logo_schooner_color_stacked_default.png Binary files differ
diff --git a/spec/src/main/asciidoc/license-efsl.adoc b/spec/src/main/asciidoc/license-efsl.adoc new file mode 100644 index 0000000..a544e8d --- /dev/null +++ b/spec/src/main/asciidoc/license-efsl.adoc
@@ -0,0 +1,79 @@ +[subs="normal"] +.... +Specification: {doctitle} + +Version: {revnumber} + +ifeval::["{revremark}" != ""] +Status: {revremark} +endif::[] +ifeval::["{revremark}" == ""] +Status: Final Release +endif::[] + +Release: {revdate} +.... + +Copyright (c) 2019 Eclipse Foundation. + +=== Eclipse Foundation Specification License + +By using and/or copying this document, or the Eclipse Foundation +document from which this statement is linked, you (the licensee) agree +that you have read, understood, and will comply with the following +terms and conditions: + +Permission to copy, and distribute the contents of this document, or +the Eclipse Foundation document from which this statement is linked, in +any medium for any purpose and without fee or royalty is hereby +granted, provided that you include the following on ALL copies of the +document, or portions thereof, that you use: + +* link or URL to the original Eclipse Foundation document. +* All existing copyright notices, or if one does not exist, a notice + (hypertext is preferred, but a textual representation is permitted) + of the form: "Copyright (c) [$date-of-document] + Eclipse Foundation, Inc. <<url to this license>>" + +Inclusion of the full text of this NOTICE must be provided. We +request that authorship attribution be provided in any software, +documents, or other items or products that you create pursuant to the +implementation of the contents of this document, or any portion +thereof. + +No right to create modifications or derivatives of Eclipse Foundation +documents is granted pursuant to this license, except anyone may +prepare and distribute derivative works and portions of this document +in software that implements the specification, in supporting materials +accompanying such software, and in documentation of such software, +PROVIDED that all such works include the notice below. HOWEVER, the +publication of derivative works of this document for use as a technical +specification is expressly prohibited. + +The notice is: + +"Copyright (c) 2018 Eclipse Foundation. This software or +document includes material copied from or derived from [title and URI +of the Eclipse Foundation specification document]." + +==== Disclaimers + +THIS DOCUMENT IS PROVIDED "AS IS," AND THE COPYRIGHT +HOLDERS AND THE ECLIPSE FOUNDATION MAKE NO REPRESENTATIONS OR +WARRANTIES, EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, +NON-INFRINGEMENT, OR TITLE; THAT THE CONTENTS OF THE DOCUMENT ARE +SUITABLE FOR ANY PURPOSE; NOR THAT THE IMPLEMENTATION OF SUCH CONTENTS +WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR +OTHER RIGHTS. + +THE COPYRIGHT HOLDERS AND THE ECLIPSE FOUNDATION WILL NOT BE LIABLE +FOR ANY DIRECT, INDIRECT, SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT +OF ANY USE OF THE DOCUMENT OR THE PERFORMANCE OR IMPLEMENTATION OF THE +CONTENTS THEREOF. + +The name and trademarks of the copyright holders or the Eclipse +Foundation may NOT be used in advertising or publicity pertaining to +this document or its contents without specific, written prior +permission. Title to copyright in this document will at all times +remain with copyright holders.
diff --git a/spec/src/theme/jakartaee-theme.yml b/spec/src/theme/jakartaee-theme.yml new file mode 100644 index 0000000..6092a2f --- /dev/null +++ b/spec/src/theme/jakartaee-theme.yml
@@ -0,0 +1,299 @@ +# +# Following is the asciidoctor-pdf default theme [1], with small +# customizations, mostly for header and footer, marked "EE". +# +# [1] https://github.com/asciidoctor/asciidoctor-pdf/blob/master/data/themes/default-theme.yml +# +font: + catalog: + # Noto Serif supports Latin, Latin-1 Supplement, Latin Extended-A, Greek, Cyrillic, Vietnamese & an assortment of symbols + Noto Serif: + normal: notoserif-regular-subset.ttf + bold: notoserif-bold-subset.ttf + italic: notoserif-italic-subset.ttf + bold_italic: notoserif-bold_italic-subset.ttf + # M+ 1mn supports ASCII and the circled numbers used for conums + M+ 1mn: + normal: mplus1mn-regular-ascii-conums.ttf + bold: mplus1mn-bold-ascii.ttf + italic: mplus1mn-italic-ascii.ttf + bold_italic: mplus1mn-bold_italic-ascii.ttf + # M+ 1p supports Latin, Latin-1 Supplement, Latin Extended, Greek, Cyrillic, Vietnamese, Japanese & an assortment of symbols + # It also provides arrows for ->, <-, => and <= replacements in case these glyphs are missing from font + M+ 1p Fallback: + normal: mplus1p-regular-fallback.ttf + bold: mplus1p-regular-fallback.ttf + italic: mplus1p-regular-fallback.ttf + bold_italic: mplus1p-regular-fallback.ttf + fallbacks: + - M+ 1p Fallback +page: + background_color: ffffff + layout: portrait + margin: [0.5in, 0.67in, 0.67in, 0.67in] + # margin_inner and margin_outer keys are used for recto/verso print margins when media=prepress + margin_inner: 0.75in + margin_outer: 0.59in + #size: A4 # EE + size: Letter # EE +base: + align: justify + # color as hex string (leading # is optional) + font_color: 333333 + # color as RGB array + #font_color: [51, 51, 51] + # color as CMYK array (approximated) + #font_color: [0, 0, 0, 0.92] + #font_color: [0, 0, 0, 92%] + font_family: Noto Serif + # choose one of these font_size/line_height_length combinations + #font_size: 14 + #line_height_length: 20 + #font_size: 11.25 + #line_height_length: 18 + #font_size: 11.2 + #line_height_length: 16 + font_size: 10.5 + #line_height_length: 15 + # correct line height for Noto Serif metrics + line_height_length: 12 + #font_size: 11.25 + #line_height_length: 18 + line_height: $base_line_height_length / $base_font_size + font_size_large: round($base_font_size * 1.25) + font_size_small: round($base_font_size * 0.85) + font_size_min: $base_font_size * 0.75 + font_style: normal + border_color: eeeeee + border_radius: 4 + border_width: 0.5 +# FIXME vertical_rhythm is weird; we should think in terms of ems +#vertical_rhythm: $base_line_height_length * 2 / 3 +# correct line height for Noto Serif metrics (comes with built-in line height) +vertical_rhythm: $base_line_height_length +horizontal_rhythm: $base_line_height_length +# QUESTION should vertical_spacing be block_spacing instead? +vertical_spacing: $vertical_rhythm +link: + font_color: 428bca +# literal is currently used for inline monospaced in prose and table cells +literal: + font_color: b12146 + font_family: M+ 1mn +menu_caret_content: " <font size=\"1.15em\"><color rgb=\"b12146\">\u203a</color></font> " +heading: + align: left + #font_color: 181818 + font_color: $base_font_color + font_family: $base_font_family + font_style: bold + # h1 is used for part titles (book doctype) or the doctitle (article doctype) + #h1_font_size: floor($base_font_size * 2.6) # EE + h1_font_size: floor($base_font_size * 2.5) # EE, squeeze title onto one line + # h2 is used for chapter titles (book doctype only) + h2_font_size: floor($base_font_size * 2.15) + h3_font_size: round($base_font_size * 1.7) + h4_font_size: $base_font_size_large + h5_font_size: $base_font_size + h6_font_size: $base_font_size_small + #line_height: 1.4 + # correct line height for Noto Serif metrics (comes with built-in line height) + line_height: 1 + margin_top: $vertical_rhythm * 0.4 + margin_bottom: $vertical_rhythm * 0.9 +title_page: + align: right + logo: + top: 10% + title: + top: 55% + font_size: $heading_h1_font_size + font_color: 999999 + line_height: 0.9 + subtitle: + font_size: $heading_h3_font_size + font_style: bold_italic + line_height: 1 + authors: + margin_top: $base_font_size * 1.25 + font_size: $base_font_size_large + font_color: 181818 + revision: + margin_top: $base_font_size * 1.25 +block: + margin_top: 0 + margin_bottom: $vertical_rhythm +caption: + align: left + font_size: $base_font_size * 0.95 + font_style: italic + # FIXME perhaps set line_height instead of / in addition to margins? + margin_inside: $vertical_rhythm / 3 + #margin_inside: $vertical_rhythm / 4 + margin_outside: 0 +lead: + font_size: $base_font_size_large + line_height: 1.4 +abstract: + font_color: 5c6266 + font_size: $lead_font_size + line_height: $lead_line_height + font_style: italic + first_line_font_style: bold + title: + align: center + font_color: $heading_font_color + font_family: $heading_font_family + font_size: $heading_h4_font_size + font_style: $heading_font_style +admonition: + column_rule_color: $base_border_color + column_rule_width: $base_border_width + padding: [0, $horizontal_rhythm, 0, $horizontal_rhythm] + #icon: + # tip: + # name: fa-lightbulb-o + # stroke_color: 111111 + # size: 24 + label: + text_transform: uppercase + font_style: bold +blockquote: + font_color: $base_font_color + font_size: $base_font_size_large + border_color: $base_border_color + border_width: 5 + # FIXME disable negative padding bottom once margin collapsing is implemented + padding: [0, $horizontal_rhythm, $block_margin_bottom * -0.75, $horizontal_rhythm + $blockquote_border_width / 2] + cite_font_size: $base_font_size_small + cite_font_color: 999999 +# code is used for source blocks (perhaps change to source or listing?) +code: + font_color: $base_font_color + font_family: $literal_font_family + font_size: ceil($base_font_size) + padding: $code_font_size + line_height: 1.25 + # line_gap is an experimental property to control how a background color is applied to an inline block element + line_gap: 3.8 + background_color: f5f5f5 + border_color: cccccc + border_radius: $base_border_radius + border_width: 0.75 +conum: + font_family: M+ 1mn + font_color: $literal_font_color + font_size: $base_font_size + line_height: 4 / 3 +example: + border_color: $base_border_color + border_radius: $base_border_radius + border_width: 0.75 + background_color: ffffff + # FIXME reenable padding bottom once margin collapsing is implemented + padding: [$vertical_rhythm, $horizontal_rhythm, 0, $horizontal_rhythm] +image: + align: left +prose: + margin_top: $block_margin_top + margin_bottom: $block_margin_bottom +sidebar: + background_color: eeeeee + border_color: e1e1e1 + border_radius: $base_border_radius + border_width: $base_border_width + # FIXME reenable padding bottom once margin collapsing is implemented + padding: [$vertical_rhythm, $vertical_rhythm * 1.25, 0, $vertical_rhythm * 1.25] + title: + align: center + font_color: $heading_font_color + font_family: $heading_font_family + font_size: $heading_h4_font_size + font_style: $heading_font_style +thematic_break: + border_color: $base_border_color + border_style: solid + border_width: $base_border_width + margin_top: $vertical_rhythm * 0.5 + margin_bottom: $vertical_rhythm * 1.5 +description_list: + term_font_style: bold + term_spacing: $vertical_rhythm / 4 + description_indent: $horizontal_rhythm * 1.25 +outline_list: + indent: $horizontal_rhythm * 1.5 + #marker_font_color: 404040 + # NOTE outline_list_item_spacing applies to list items that do not have complex content + item_spacing: $vertical_rhythm / 2 +table: + background_color: $page_background_color + #head_background_color: <hex value> + #head_font_color: $base_font_color + head_font_style: bold + #body_background_color: <hex value> + body_stripe_background_color: f9f9f9 + foot_background_color: f0f0f0 + border_color: dddddd + border_width: $base_border_width + cell_padding: 3 +toc: + indent: $horizontal_rhythm + line_height: 1.4 + dot_leader: + #content: ". " + font_color: a9a9a9 + #levels: 2 3 +# NOTE in addition to footer, header is also supported +footer: + font_size: $base_font_size_small + # NOTE if background_color is set, background and border will span width of page + #border_color: dddddd # EE + #border_width: 0.25 # EE + height: $base_line_height_length * 2.5 + line_height: 1 + padding: [$base_line_height_length / 2, 1, 0, 1] + vertical_align: top + #image_vertical_align: <alignment> or <number> + # additional attributes for content: + # * {page-count} + # * {page-number} + # * {document-title} + # * {document-subtitle} + # * {chapter-title} + # * {section-title} + # * {section-or-chapter-title} + recto: + #columns: "<50% =0% >50%" + right: + #content: '{page-number}' # EE + #content: '{section-or-chapter-title} | {page-number}' + #content: '{document-title} | {page-number}' + content: '{document-title}{nbsp}{nbsp}{nbsp} *{page-number}*' # EE + #center: + # content: '{page-number}' + left: # EE + content: '{status}' # EE + verso: + #columns: $footer_recto_columns + left: + #content: $footer_recto_right_content # EE + #content: '{page-number} | {chapter-title}' + content: '*{page-number}* {nbsp}{nbsp}{nbsp}{document-title}' # EE + #center: + # content: '{page-number}' + right: # EE + content: '{status}' # EE +header: # EE + font_size: $base_font_size_small # EE + border_color: dddddd # EE + border_width: 0.25 # EE + height: $base_line_height_length * 2.5 # EE + line_height: 1 # EE + padding: [$base_line_height_length / 2, 1, 0, 1] # EE + vertical_align: top # EE + recto: # EE + right: # EE + content: '{section-or-chapter-title}' # EE + verso: # EE + left: # EE + content: '{section-or-chapter-title}' # EE