javax.activation.MailcapCommandMap.java Source code

Java tutorial

Introduction

Here is the source code for javax.activation.MailcapCommandMap.java

Source

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://oss.oracle.com/licenses/CDDL+GPL-1.1
 * or LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */

package javax.activation;

import java.util.*;
import java.io.*;
import java.net.*;
import java.security.AccessController;
import java.security.PrivilegedAction;
import com.sun.activation.registries.MailcapFile;
import com.sun.activation.registries.LogSupport;

/**
 * MailcapCommandMap extends the CommandMap
 * abstract class. It implements a CommandMap whose configuration
 * is based on mailcap files
 * (<A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>).
 * The MailcapCommandMap can be configured both programmatically
 * and via configuration files.
 * <p>
 * <b>Mailcap file search order:</b><p>
 * The MailcapCommandMap looks in various places in the user's
 * system for mailcap file entries. When requests are made
 * to search for commands in the MailcapCommandMap, it searches  
 * mailcap files in the following order:
 * <ol>
 * <li> Programatically added entries to the MailcapCommandMap instance.
 * <li> The file <code>.mailcap</code> in the user's home directory.
 * <li> The file <code>mailcap</code> in the Java runtime.
 * <li> The file or resources named <code>META-INF/mailcap</code>.
 * <li> The file or resource named <code>META-INF/mailcap.default</code>
 * (usually found only in the <code>activation.jar</code> file).
 * </ol>
 * <p>
 * (The current implementation looks for the <code>mailcap</code> file
 * in the Java runtime in the directory <code><i>java.home</i>/conf</code>
 * if it exists, and otherwise in the directory
 * <code><i>java.home</i>/lib</code>, where <i>java.home</i> is the value
 * of the "java.home" System property.  Note that the "conf" directory was
 * introduced in JDK 9.)
 * <p>
 * <b>Mailcap file format:</b><p>
 *
 * Mailcap files must conform to the mailcap
 * file specification (RFC 1524, <i>A User Agent Configuration Mechanism
 * For Multimedia Mail Format Information</i>). 
 * The file format consists of entries corresponding to
 * particular MIME types. In general, the specification 
 * specifies <i>applications</i> for clients to use when they
 * themselves cannot operate on the specified MIME type. The 
 * MailcapCommandMap extends this specification by using a parameter mechanism
 * in mailcap files that allows JavaBeans(tm) components to be specified as
 * corresponding to particular commands for a MIME type.<p>
 *
 * When a mailcap file is
 * parsed, the MailcapCommandMap recognizes certain parameter signatures,
 * specifically those parameter names that begin with <code>x-java-</code>.
 * The MailcapCommandMap uses this signature to find
 * command entries for inclusion into its registries.
 * Parameter names with the form <code>x-java-&lt;name&gt;</code>
 * are read by the MailcapCommandMap as identifying a command
 * with the name <i>name</i>. When the <i>name</i> is <code>
 * content-handler</code> the MailcapCommandMap recognizes the class
 * signified by this parameter as a <i>DataContentHandler</i>.
 * All other commands are handled generically regardless of command 
 * name. The command implementation is specified by a fully qualified
 * class name of a JavaBean(tm) component. For example; a command for viewing
 * some data can be specified as: <code>x-java-view=com.foo.ViewBean</code>.<p>
 *
 * When the command name is <code>fallback-entry</code>, the value of
 * the command may be <code>true</code> or <code>false</code>.  An
 * entry for a MIME type that includes a parameter of
 * <code>x-java-fallback-entry=true</code> defines fallback commands
 * for that MIME type that will only be used if no non-fallback entry
 * can be found.  For example, an entry of the form <code>text/*; ;
 * x-java-fallback-entry=true; x-java-view=com.sun.TextViewer</code>
 * specifies a view command to be used for any text MIME type.  This
 * view command would only be used if a non-fallback view command for
 * the MIME type could not be found.<p>
 * 
 * MailcapCommandMap aware mailcap files have the 
 * following general form:<p>
 * <code>
 * # Comments begin with a '#' and continue to the end of the line.<br>
 * &lt;mime type&gt;; ; &lt;parameter list&gt;<br>
 * # Where a parameter list consists of one or more parameters,<br>
 * # where parameters look like: x-java-view=com.sun.TextViewer<br>
 * # and a parameter list looks like: <br>
 * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
 * <br>
 * # Note that mailcap entries that do not contain 'x-java' parameters<br>
 * # and comply to RFC 1524 are simply ignored:<br>
 * image/gif; /usr/dt/bin/sdtimage %s<br>
 *
 * </code>
 * <p>
 *
 * @author Bart Calder
 * @author Bill Shannon
 */

public class MailcapCommandMap extends CommandMap {
    /*
     * We manage a collection of databases, searched in order.
     */
    private MailcapFile[] DB;
    private static final int PROG = 0; // programmatically added entries

    private static final String confDir;

    static {
        String dir = null;
        try {
            dir = (String) AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    String home = System.getProperty("java.home");
                    String newdir = home + File.separator + "conf";
                    File conf = new File(newdir);
                    if (conf.exists())
                        return newdir + File.separator;
                    else
                        return home + File.separator + "lib" + File.separator;
                }
            });
        } catch (Exception ex) {
            // ignore any exceptions
        }
        confDir = dir;
    }

    /**
     * The default Constructor.
     */
    public MailcapCommandMap() {
        super();
        List dbv = new ArrayList(5); // usually 5 or less databases
        MailcapFile mf = null;
        dbv.add(null); // place holder for PROG entry

        LogSupport.log("MailcapCommandMap: load HOME");
        try {
            String user_home = System.getProperty("user.home");

            if (user_home != null) {
                String path = user_home + File.separator + ".mailcap";
                mf = loadFile(path);
                if (mf != null)
                    dbv.add(mf);
            }
        } catch (SecurityException ex) {
        }

        LogSupport.log("MailcapCommandMap: load SYS");
        try {
            // check system's home
            if (confDir != null) {
                mf = loadFile(confDir + "mailcap");
                if (mf != null)
                    dbv.add(mf);
            }
        } catch (SecurityException ex) {
        }

        LogSupport.log("MailcapCommandMap: load JAR");
        // load from the app's jar file
        loadAllResources(dbv, "META-INF/mailcap");

        LogSupport.log("MailcapCommandMap: load DEF");
        mf = loadResource("/META-INF/mailcap.default");

        if (mf != null)
            dbv.add(mf);

        DB = new MailcapFile[dbv.size()];
        DB = (MailcapFile[]) dbv.toArray(DB);
    }

    /**
     * Load from the named resource.
     */
    private MailcapFile loadResource(String name) {
        InputStream clis = null;
        try {
            clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
            if (clis != null) {
                MailcapFile mf = new MailcapFile(clis);
                if (LogSupport.isLoggable())
                    LogSupport.log("MailcapCommandMap: successfully loaded " + "mailcap file: " + name);
                return mf;
            } else {
                if (LogSupport.isLoggable())
                    LogSupport.log("MailcapCommandMap: not loading " + "mailcap file: " + name);
            }
        } catch (IOException e) {
            if (LogSupport.isLoggable())
                LogSupport.log("MailcapCommandMap: can't load " + name, e);
        } catch (SecurityException sex) {
            if (LogSupport.isLoggable())
                LogSupport.log("MailcapCommandMap: can't load " + name, sex);
        } finally {
            try {
                if (clis != null)
                    clis.close();
            } catch (IOException ex) {
            } // ignore it
        }
        return null;
    }

    /**
     * Load all of the named resource.
     */
    private void loadAllResources(List v, String name) {
        boolean anyLoaded = false;
        try {
            URL[] urls;
            ClassLoader cld = null;
            // First try the "application's" class loader.
            cld = SecuritySupport.getContextClassLoader();
            if (cld == null)
                cld = this.getClass().getClassLoader();
            if (cld != null)
                urls = SecuritySupport.getResources(cld, name);
            else
                urls = SecuritySupport.getSystemResources(name);
            if (urls != null) {
                if (LogSupport.isLoggable())
                    LogSupport.log("MailcapCommandMap: getResources");
                for (int i = 0; i < urls.length; i++) {
                    URL url = urls[i];
                    InputStream clis = null;
                    if (LogSupport.isLoggable())
                        LogSupport.log("MailcapCommandMap: URL " + url);
                    try {
                        clis = SecuritySupport.openStream(url);
                        if (clis != null) {
                            v.add(new MailcapFile(clis));
                            anyLoaded = true;
                            if (LogSupport.isLoggable())
                                LogSupport.log("MailcapCommandMap: " + "successfully loaded "
                                        + "mailcap file from URL: " + url);
                        } else {
                            if (LogSupport.isLoggable())
                                LogSupport.log(
                                        "MailcapCommandMap: " + "not loading mailcap " + "file from URL: " + url);
                        }
                    } catch (IOException ioex) {
                        if (LogSupport.isLoggable())
                            LogSupport.log("MailcapCommandMap: can't load " + url, ioex);
                    } catch (SecurityException sex) {
                        if (LogSupport.isLoggable())
                            LogSupport.log("MailcapCommandMap: can't load " + url, sex);
                    } finally {
                        try {
                            if (clis != null)
                                clis.close();
                        } catch (IOException cex) {
                        }
                    }
                }
            }
        } catch (Exception ex) {
            if (LogSupport.isLoggable())
                LogSupport.log("MailcapCommandMap: can't load " + name, ex);
        }

        // if failed to load anything, fall back to old technique, just in case
        if (!anyLoaded) {
            if (LogSupport.isLoggable())
                LogSupport.log("MailcapCommandMap: !anyLoaded");
            MailcapFile mf = loadResource("/" + name);
            if (mf != null)
                v.add(mf);
        }
    }

    /**
     * Load from the named file.
     */
    private MailcapFile loadFile(String name) {
        MailcapFile mtf = null;

        try {
            mtf = new MailcapFile(name);
        } catch (IOException e) {
            //   e.printStackTrace();
        }
        return mtf;
    }

    /**
     * Constructor that allows the caller to specify the path
     * of a <i>mailcap</i> file.
     *
     * @param fileName The name of the <i>mailcap</i> file to open
     * @exception   IOException   if the file can't be accessed
     */
    public MailcapCommandMap(String fileName) throws IOException {
        this();

        if (LogSupport.isLoggable())
            LogSupport.log("MailcapCommandMap: load PROG from " + fileName);
        if (DB[PROG] == null) {
            DB[PROG] = new MailcapFile(fileName);
        }
    }

    /**
     * Constructor that allows the caller to specify an <i>InputStream</i>
     * containing a mailcap file.
     *
     * @param is   InputStream of the <i>mailcap</i> file to open
     */
    public MailcapCommandMap(InputStream is) {
        this();

        LogSupport.log("MailcapCommandMap: load PROG");
        if (DB[PROG] == null) {
            try {
                DB[PROG] = new MailcapFile(is);
            } catch (IOException ex) {
                // XXX - should throw it
            }
        }
    }

    /**
     * Get the preferred command list for a MIME Type. The MailcapCommandMap
     * searches the mailcap files as described above under
     * <i>Mailcap file search order</i>.<p>
     *
     * The result of the search is a proper subset of available
     * commands in all mailcap files known to this instance of 
     * MailcapCommandMap.  The first entry for a particular command
     * is considered the preferred command.
     *
     * @param mimeType   the MIME type
     * @return the CommandInfo objects representing the preferred commands.
     */
    public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
        List cmdList = new ArrayList();
        if (mimeType != null)
            mimeType = mimeType.toLowerCase(Locale.ENGLISH);

        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            Map cmdMap = DB[i].getMailcapList(mimeType);
            if (cmdMap != null)
                appendPrefCmdsToList(cmdMap, cmdList);
        }

        // now add the fallback commands
        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
            if (cmdMap != null)
                appendPrefCmdsToList(cmdMap, cmdList);
        }

        CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
        cmdInfos = (CommandInfo[]) cmdList.toArray(cmdInfos);

        return cmdInfos;
    }

    /**
     * Put the commands that are in the hash table, into the list.
     */
    private void appendPrefCmdsToList(Map cmdHash, List cmdList) {
        Iterator verb_enum = cmdHash.keySet().iterator();

        while (verb_enum.hasNext()) {
            String verb = (String) verb_enum.next();
            if (!checkForVerb(cmdList, verb)) {
                List cmdList2 = (List) cmdHash.get(verb); // get the list
                String className = (String) cmdList2.get(0);
                cmdList.add(new CommandInfo(verb, className));
            }
        }
    }

    /**
     * Check the cmdList to see if this command exists, return
     * true if the verb is there.
     */
    private boolean checkForVerb(List cmdList, String verb) {
        Iterator ee = cmdList.iterator();
        while (ee.hasNext()) {
            String enum_verb = (String) ((CommandInfo) ee.next()).getCommandName();
            if (enum_verb.equals(verb))
                return true;
        }
        return false;
    }

    /**
     * Get all the available commands in all mailcap files known to
     * this instance of MailcapCommandMap for this MIME type.
     *
     * @param mimeType   the MIME type
     * @return the CommandInfo objects representing all the commands.
     */
    public synchronized CommandInfo[] getAllCommands(String mimeType) {
        List cmdList = new ArrayList();
        if (mimeType != null)
            mimeType = mimeType.toLowerCase(Locale.ENGLISH);

        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            Map cmdMap = DB[i].getMailcapList(mimeType);
            if (cmdMap != null)
                appendCmdsToList(cmdMap, cmdList);
        }

        // now add the fallback commands
        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
            if (cmdMap != null)
                appendCmdsToList(cmdMap, cmdList);
        }

        CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
        cmdInfos = (CommandInfo[]) cmdList.toArray(cmdInfos);

        return cmdInfos;
    }

    /**
     * Put the commands that are in the hash table, into the list.
     */
    private void appendCmdsToList(Map typeHash, List cmdList) {
        Iterator verb_enum = typeHash.keySet().iterator();

        while (verb_enum.hasNext()) {
            String verb = (String) verb_enum.next();
            List cmdList2 = (List) typeHash.get(verb);
            Iterator cmd_enum = ((List) cmdList2).iterator();

            while (cmd_enum.hasNext()) {
                String cmd = (String) cmd_enum.next();
                cmdList.add(new CommandInfo(verb, cmd));
                // cmdList.add(0, new CommandInfo(verb, cmd));
            }
        }
    }

    /**
     * Get the command corresponding to <code>cmdName</code> for the MIME type.
     *
     * @param mimeType   the MIME type
     * @param cmdName   the command name
     * @return the CommandInfo object corresponding to the command.
     */
    public synchronized CommandInfo getCommand(String mimeType, String cmdName) {
        if (mimeType != null)
            mimeType = mimeType.toLowerCase(Locale.ENGLISH);

        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            Map cmdMap = DB[i].getMailcapList(mimeType);
            if (cmdMap != null) {
                // get the cmd list for the cmd
                List v = (List) cmdMap.get(cmdName);
                if (v != null) {
                    String cmdClassName = (String) v.get(0);

                    if (cmdClassName != null)
                        return new CommandInfo(cmdName, cmdClassName);
                }
            }
        }

        // now try the fallback list
        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
            if (cmdMap != null) {
                // get the cmd list for the cmd
                List v = (List) cmdMap.get(cmdName);
                if (v != null) {
                    String cmdClassName = (String) v.get(0);

                    if (cmdClassName != null)
                        return new CommandInfo(cmdName, cmdClassName);
                }
            }
        }
        return null;
    }

    /**
     * Add entries to the registry.  Programmatically 
     * added entries are searched before other entries.<p>
     *
     * The string that is passed in should be in mailcap
     * format.
     *
     * @param mail_cap a correctly formatted mailcap string
     */
    public synchronized void addMailcap(String mail_cap) {
        // check to see if one exists
        LogSupport.log("MailcapCommandMap: add to PROG");
        if (DB[PROG] == null)
            DB[PROG] = new MailcapFile();

        DB[PROG].appendToMailcap(mail_cap);
    }

    /**
     * Return the DataContentHandler for the specified MIME type.
     *
     * @param mimeType   the MIME type
     * @return      the DataContentHandler
     */
    public synchronized DataContentHandler createDataContentHandler(String mimeType) {
        if (LogSupport.isLoggable())
            LogSupport.log("MailcapCommandMap: createDataContentHandler for " + mimeType);
        if (mimeType != null)
            mimeType = mimeType.toLowerCase(Locale.ENGLISH);

        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            if (LogSupport.isLoggable())
                LogSupport.log("  search DB #" + i);
            Map cmdMap = DB[i].getMailcapList(mimeType);
            if (cmdMap != null) {
                List v = (List) cmdMap.get("content-handler");
                if (v != null) {
                    String name = (String) v.get(0);
                    DataContentHandler dch = getDataContentHandler(name);
                    if (dch != null)
                        return dch;
                }
            }
        }

        // now try the fallback entries
        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            if (LogSupport.isLoggable())
                LogSupport.log("  search fallback DB #" + i);
            Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
            if (cmdMap != null) {
                List v = (List) cmdMap.get("content-handler");
                if (v != null) {
                    String name = (String) v.get(0);
                    DataContentHandler dch = getDataContentHandler(name);
                    if (dch != null)
                        return dch;
                }
            }
        }
        return null;
    }

    private DataContentHandler getDataContentHandler(String name) {
        if (LogSupport.isLoggable())
            LogSupport.log("    got content-handler");
        if (LogSupport.isLoggable())
            LogSupport.log("      class " + name);
        try {
            ClassLoader cld = null;
            // First try the "application's" class loader.
            cld = SecuritySupport.getContextClassLoader();
            if (cld == null)
                cld = this.getClass().getClassLoader();
            Class cl = null;
            try {
                cl = cld.loadClass(name);
            } catch (Exception ex) {
                // if anything goes wrong, do it the old way
                cl = Class.forName(name);
            }
            if (cl != null) // XXX - always true?
                return (DataContentHandler) cl.newInstance();
        } catch (IllegalAccessException e) {
            if (LogSupport.isLoggable())
                LogSupport.log("Can't load DCH " + name, e);
        } catch (ClassNotFoundException e) {
            if (LogSupport.isLoggable())
                LogSupport.log("Can't load DCH " + name, e);
        } catch (InstantiationException e) {
            if (LogSupport.isLoggable())
                LogSupport.log("Can't load DCH " + name, e);
        }
        return null;
    }

    /**
     * Get all the MIME types known to this command map.
     *
     * @return      array of MIME types as strings
     * @since   JAF 1.1
     */
    public synchronized String[] getMimeTypes() {
        List mtList = new ArrayList();

        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            String[] ts = DB[i].getMimeTypes();
            if (ts != null) {
                for (int j = 0; j < ts.length; j++) {
                    // eliminate duplicates
                    if (!mtList.contains(ts[j]))
                        mtList.add(ts[j]);
                }
            }
        }

        String[] mts = new String[mtList.size()];
        mts = (String[]) mtList.toArray(mts);

        return mts;
    }

    /**
     * Get the native commands for the given MIME type.
     * Returns an array of strings where each string is
     * an entire mailcap file entry.  The application
     * will need to parse the entry to extract the actual
     * command as well as any attributes it needs. See
     * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>
     * for details of the mailcap entry syntax.  Only mailcap
     * entries that specify a view command for the specified
     * MIME type are returned.
     *
     * @param   mimeType   the MIME type
     * @return      array of native command entries
     * @since   JAF 1.1
     */
    public synchronized String[] getNativeCommands(String mimeType) {
        List cmdList = new ArrayList();
        if (mimeType != null)
            mimeType = mimeType.toLowerCase(Locale.ENGLISH);

        for (int i = 0; i < DB.length; i++) {
            if (DB[i] == null)
                continue;
            String[] cmds = DB[i].getNativeCommands(mimeType);
            if (cmds != null) {
                for (int j = 0; j < cmds.length; j++) {
                    // eliminate duplicates
                    if (!cmdList.contains(cmds[j]))
                        cmdList.add(cmds[j]);
                }
            }
        }

        String[] cmds = new String[cmdList.size()];
        cmds = (String[]) cmdList.toArray(cmds);

        return cmds;
    }

    /**
     * for debugging...
     *
    public static void main(String[] argv) throws Exception {
    MailcapCommandMap map = new MailcapCommandMap();
    CommandInfo[] cmdInfo;
        
    cmdInfo = map.getPreferredCommands(argv[0]);
    System.out.println("Preferred Commands:");
    for (int i = 0; i < cmdInfo.length; i++)
       System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
               cmdInfo[i].getCommandClass() + "]");
    cmdInfo = map.getAllCommands(argv[0]);
    System.out.println();
    System.out.println("All Commands:");
    for (int i = 0; i < cmdInfo.length; i++)
       System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
               cmdInfo[i].getCommandClass() + "]");
    DataContentHandler dch = map.createDataContentHandler(argv[0]);
    if (dch != null)
       System.out.println("DataContentHandler " +
              dch.getClass().toString());
    System.exit(0);
    }
    */
}