| /* MailcapCommandMap.java -- Command map implementation using a mailcap file. |
| Copyright (C) 2004 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| package javax.activation; |
| |
| import gnu.java.lang.CPStringBuilder; |
| |
| import java.io.BufferedReader; |
| import java.io.File; |
| import java.io.FileReader; |
| import java.io.InputStream; |
| import java.io.InputStreamReader; |
| import java.io.IOException; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Enumeration; |
| import java.util.LinkedHashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * Implementation of a command map using a <code>mailcap</code> file (RFC |
| * 1524). Mailcap files are searched for in the following places: |
| * <ol> |
| * <li>Programmatically added entries to this interface</li> |
| * <li>the file <tt>.mailcap</tt> in the user's home directory</li> |
| * <li>the file <i><java.home></i><tt>/lib/mailcap</tt></li> |
| * <li>the resource <tt>META-INF/mailcap</tt></li> |
| * <li>the resource <tt>META-INF/mailcap.default</tt> in the JAF |
| * distribution</li> |
| * </ol> |
| * |
| * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a> |
| * @version 1.1 |
| */ |
| public class MailcapCommandMap |
| extends CommandMap |
| { |
| |
| private static final int PROG = 0; |
| private static final int HOME = 1; |
| private static final int SYS = 2; |
| private static final int JAR = 3; |
| private static final int DEF = 4; |
| private static boolean debug = false; |
| private static final int NORMAL = 0; |
| private static final int FALLBACK = 1; |
| |
| static |
| { |
| try |
| { |
| String d = System.getProperty("javax.activation.debug"); |
| debug = Boolean.valueOf(d).booleanValue(); |
| } |
| catch (SecurityException e) |
| { |
| } |
| } |
| |
| private Map<String,Map<String,List<String>>>[][] mailcaps; |
| |
| /** |
| * Default constructor. |
| */ |
| public MailcapCommandMap() |
| { |
| init(null); |
| } |
| |
| /** |
| * Constructor specifying a filename. |
| * @param fileName the name of the file to read mailcap entries from |
| */ |
| public MailcapCommandMap(String fileName) |
| throws IOException |
| { |
| Reader in = null; |
| try |
| { |
| in = new FileReader(fileName); |
| } |
| catch (IOException e) |
| { |
| } |
| init(in); |
| if (in != null) |
| { |
| try |
| { |
| in.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| |
| /** |
| * Constructor specifying an input stream. |
| * @param is the input stream to read mailcap entries from |
| */ |
| public MailcapCommandMap(InputStream is) |
| { |
| init(new InputStreamReader(is)); |
| } |
| |
| private void init(Reader in) |
| { |
| mailcaps = new Map[5][2]; |
| for (int i = 0; i < 5; i++) |
| { |
| for (int j = 0; j < 2; j++) |
| { |
| mailcaps[i][j] = |
| new LinkedHashMap<String,Map<String,List<String>>>(); |
| } |
| } |
| if (in != null) |
| { |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: load PROG"); |
| } |
| try |
| { |
| parse(PROG, in); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: load HOME"); |
| } |
| try |
| { |
| String home = System.getProperty("user.home"); |
| if (home != null) |
| { |
| parseFile(HOME, new CPStringBuilder(home) |
| .append(File.separatorChar) |
| .append(".mailcap") |
| .toString()); |
| } |
| } |
| catch (SecurityException e) |
| { |
| } |
| |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: load SYS"); |
| } |
| try |
| { |
| parseFile(SYS, |
| new CPStringBuilder(System.getProperty("java.home")) |
| .append(File.separatorChar) |
| .append("lib") |
| .append(File.separatorChar) |
| .append("mailcap") |
| .toString()); |
| } |
| catch (SecurityException e) |
| { |
| } |
| |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: load JAR"); |
| } |
| List<URL> systemResources = getSystemResources("META-INF/mailcap"); |
| int len = systemResources.size(); |
| if (len > 0) |
| { |
| for (int i = 0; i < len ; i++) |
| { |
| Reader urlIn = null; |
| URL url = systemResources.get(i); |
| try |
| { |
| if (debug) |
| { |
| System.out.println("\t" + url.toString()); |
| } |
| urlIn = new InputStreamReader(url.openStream()); |
| parse(JAR, urlIn); |
| } |
| catch (IOException e) |
| { |
| if (debug) |
| { |
| System.out.println(e.getClass().getName() + ": " + |
| e.getMessage()); |
| } |
| } |
| finally |
| { |
| if (urlIn != null) |
| { |
| try |
| { |
| urlIn.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| } |
| } |
| else |
| { |
| parseResource(JAR, "/META-INF/mailcap"); |
| } |
| |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: load DEF"); |
| } |
| parseResource(DEF, "/META-INF/mailcap.default"); |
| } |
| |
| /** |
| * Returns the list of preferred commands for a given MIME type. |
| * @param mimeType the MIME type |
| */ |
| public synchronized CommandInfo[] getPreferredCommands(String mimeType) |
| { |
| List<CommandInfo> cmdList = new ArrayList<CommandInfo>(); |
| List<String> verbList = new ArrayList<String>(); |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 5; j++) |
| { |
| Map<String,List<String>> map = getCommands(mailcaps[j][i], mimeType); |
| if (map != null) |
| { |
| for (Map.Entry<String,List<String>> entry : map.entrySet()) |
| { |
| String verb = entry.getKey(); |
| if (!verbList.contains(verb)) |
| { |
| List<String> classNames = entry.getValue(); |
| String className = classNames.get(0); |
| CommandInfo cmd = new CommandInfo(verb, className); |
| cmdList.add(cmd); |
| verbList.add(verb); |
| } |
| } |
| } |
| } |
| } |
| CommandInfo[] cmds = new CommandInfo[cmdList.size()]; |
| cmdList.toArray(cmds); |
| return cmds; |
| } |
| |
| /** |
| * Returns all commands for the given MIME type. |
| * @param mimeType the MIME type |
| */ |
| public synchronized CommandInfo[] getAllCommands(String mimeType) |
| { |
| List<CommandInfo> cmdList = new ArrayList<CommandInfo>(); |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 5; j++) |
| { |
| Map<String,List<String>> map = getCommands(mailcaps[j][i], mimeType); |
| if (map != null) |
| { |
| for (Map.Entry<String,List<String>> entry : map.entrySet()) |
| { |
| String verb = entry.getKey(); |
| List<String> classNames = entry.getValue(); |
| int len = classNames.size(); |
| for (int l = 0; l < len; l++) |
| { |
| String className = classNames.get(l); |
| CommandInfo cmd = new CommandInfo(verb, className); |
| cmdList.add(cmd); |
| } |
| } |
| } |
| } |
| } |
| CommandInfo[] cmds = new CommandInfo[cmdList.size()]; |
| cmdList.toArray(cmds); |
| return cmds; |
| } |
| |
| /** |
| * Returns the command with the specified name for the given MIME type. |
| * @param mimeType the MIME type |
| * @param cmdName the command verb |
| */ |
| public synchronized CommandInfo getCommand(String mimeType, |
| String cmdName) |
| { |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 5; j++) |
| { |
| Map<String,List<String>> map = |
| getCommands(mailcaps[j][i], mimeType); |
| if (map != null) |
| { |
| List<String> classNames = map.get(cmdName); |
| if (classNames == null) |
| { |
| classNames = map.get("x-java-" + cmdName); |
| } |
| if (classNames != null) |
| { |
| String className = classNames.get(0); |
| return new CommandInfo(cmdName, className); |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Adds entries programmatically to the registry. |
| * @param mailcap a mailcap string |
| */ |
| public synchronized void addMailcap(String mailcap) |
| { |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: add to PROG"); |
| } |
| try |
| { |
| parse(PROG, new StringReader(mailcap)); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| |
| /** |
| * Returns the DCH for the specified MIME type. |
| * @param mimeType the MIME type |
| */ |
| public synchronized DataContentHandler |
| createDataContentHandler(String mimeType) |
| { |
| if (debug) |
| { |
| System.out.println("MailcapCommandMap: " + |
| "createDataContentHandler for " + mimeType); |
| } |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 5; j++) |
| { |
| if (debug) |
| { |
| System.out.println(" search DB #" + i); |
| } |
| Map<String,List<String>> map = getCommands(mailcaps[j][i], mimeType); |
| if (map != null) |
| { |
| List<String> classNames = map.get("content-handler"); |
| if (classNames == null) |
| { |
| classNames = map.get("x-java-content-handler"); |
| } |
| if (classNames != null) |
| { |
| String className = classNames.get(0); |
| if (debug) |
| { |
| System.out.println(" In " + nameOf(j) + |
| ", content-handler=" + className); |
| } |
| try |
| { |
| Class<?> clazz = Class.forName(className); |
| return (DataContentHandler)clazz.newInstance(); |
| } |
| catch (IllegalAccessException e) |
| { |
| if (debug) |
| { |
| e.printStackTrace(); |
| } |
| } |
| catch (ClassNotFoundException e) |
| { |
| if (debug) |
| { |
| e.printStackTrace(); |
| } |
| } |
| catch (InstantiationException e) |
| { |
| if (debug) |
| { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * 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) |
| { |
| List<String> acc = new ArrayList<String>(); |
| for (int i = 0; i < 2; i++) |
| { |
| for (int j = 0; j < 5; j++) |
| { |
| addNativeCommands(acc, mailcaps[j][i], mimeType); |
| } |
| } |
| String[] ret = new String[acc.size()]; |
| acc.toArray(ret); |
| return ret; |
| } |
| |
| private void addNativeCommands(List<String> acc, |
| Map<String,Map<String,List<String>>> mailcap, |
| String mimeType) |
| { |
| for (Map.Entry<String,Map<String,List<String>>> mEntry : mailcap.entrySet()) |
| { |
| String entryMimeType = mEntry.getKey(); |
| if (!entryMimeType.equals(mimeType)) |
| { |
| continue; |
| } |
| Map<String,List<String>> commands = mEntry.getValue(); |
| String viewCommand = commands.get("view-command").get(0); |
| if (viewCommand == null) |
| { |
| continue; |
| } |
| CPStringBuilder buf = new CPStringBuilder(); |
| buf.append(mimeType); |
| buf.append(';'); |
| buf.append(' '); |
| buf.append(viewCommand); |
| for (Map.Entry<String,List<String>> cEntry : commands.entrySet()) |
| { |
| String verb = cEntry.getKey(); |
| List<String> classNames = cEntry.getValue(); |
| if (!"view-command".equals(verb)) |
| { |
| for (String command : classNames) |
| { |
| buf.append(';'); |
| buf.append(' '); |
| buf.append(verb); |
| buf.append('='); |
| buf.append(command); |
| } |
| } |
| } |
| if (buf.length() > 0) |
| { |
| acc.add(buf.toString()); |
| } |
| } |
| } |
| |
| private static String nameOf(int mailcap) |
| { |
| switch (mailcap) |
| { |
| case PROG: |
| return "PROG"; |
| case HOME: |
| return "HOME"; |
| case SYS: |
| return "SYS"; |
| case JAR: |
| return "JAR"; |
| case DEF: |
| return "DEF"; |
| default: |
| return "ERR"; |
| } |
| } |
| |
| private void parseFile(int index, String filename) |
| { |
| Reader in = null; |
| try |
| { |
| if (debug) |
| { |
| System.out.println("\t" + filename); |
| } |
| in = new FileReader(filename); |
| parse(index, in); |
| } |
| catch (IOException e) |
| { |
| if (debug) |
| { |
| System.out.println(e.getClass().getName() + ": " + |
| e.getMessage()); |
| } |
| } |
| finally |
| { |
| if (in != null) |
| { |
| try |
| { |
| in.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| } |
| |
| private void parseResource(int index, String name) |
| { |
| Reader in = null; |
| try |
| { |
| InputStream is = getClass().getResourceAsStream(name); |
| if (is != null) |
| { |
| if (debug) |
| { |
| System.out.println("\t" + name); |
| } |
| in = new InputStreamReader(is); |
| parse(index, in); |
| } |
| } |
| catch (IOException e) |
| { |
| if (debug) |
| { |
| System.out.println(e.getClass().getName() + ": " + |
| e.getMessage()); |
| } |
| } |
| finally |
| { |
| if (in != null) |
| { |
| try |
| { |
| in.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| } |
| |
| private void parse(int index, Reader in) |
| throws IOException |
| { |
| BufferedReader br = new BufferedReader(in); |
| CPStringBuilder buf = null; |
| for (String line = br.readLine(); line != null; line = br.readLine()) |
| { |
| line = line.trim(); |
| int len = line.length(); |
| if (len == 0 || line.charAt(0) == '#') |
| { |
| continue; // Comment |
| } |
| if (line.charAt(len - 1) == '\\') |
| { |
| if (buf == null) |
| { |
| buf = new CPStringBuilder(); |
| } |
| buf.append(line.substring(0, len - 1)); |
| } |
| else if (buf != null) |
| { |
| buf.append(line); |
| parseEntry(index, buf.toString()); |
| buf = null; |
| } |
| else |
| { |
| parseEntry(index, line); |
| } |
| } |
| } |
| |
| private void parseEntry(int index, String line) |
| { |
| // Tokenize entry into fields |
| char[] chars = line.toCharArray(); |
| int len = chars.length; |
| boolean inQuotedString = false; |
| boolean fallback = false; |
| CPStringBuilder buffer = new CPStringBuilder(); |
| List<String> fields = new ArrayList<String>(); |
| for (int i = 0; i < len; i++) |
| { |
| char c = chars[i]; |
| if (c == '\\') |
| { |
| c = chars[++i]; // qchar |
| } |
| if (c == ';' && !inQuotedString) |
| { |
| String field = buffer.toString().trim(); |
| if ("x-java-fallback-entry".equals(field)) |
| { |
| fallback = true; |
| } |
| fields.add(field); |
| buffer.setLength(0); |
| } |
| else |
| { |
| if (c == '"') |
| { |
| inQuotedString = !inQuotedString; |
| } |
| buffer.append(c); |
| } |
| } |
| String field = buffer.toString().trim(); |
| if ("x-java-fallback-entry".equals(field)) |
| { |
| fallback = true; |
| } |
| fields.add(field); |
| |
| len = fields.size(); |
| if (len < 2) |
| { |
| if (debug) |
| { |
| System.err.println("Invalid mailcap entry: " + line); |
| } |
| return; |
| } |
| |
| Map<String,Map<String,List<String>>> mailcap = |
| fallback ? mailcaps[index][FALLBACK] : mailcaps[index][NORMAL]; |
| String mimeType = fields.get(0); |
| addField(mailcap, mimeType, "view-command", (String) fields.get(1)); |
| for (int i = 2; i < len; i++) |
| { |
| addField(mailcap, mimeType, null, (String) fields.get(i)); |
| } |
| } |
| |
| private void addField(Map<String,Map<String,List<String>>> mailcap, |
| String mimeType, String verb, String command) |
| { |
| if (verb == null) |
| { |
| int ei = command.indexOf('='); |
| if (ei != -1) |
| { |
| verb = command.substring(0, ei); |
| command = command.substring(ei + 1); |
| } |
| } |
| if (command.length() == 0 || verb == null || verb.length() == 0) |
| { |
| return; // Invalid field or flag |
| } |
| |
| Map<String,List<String>> commands = mailcap.get(mimeType); |
| if (commands == null) |
| { |
| commands = new LinkedHashMap<String,List<String>>(); |
| mailcap.put(mimeType, commands); |
| } |
| List<String> classNames = commands.get(verb); |
| if (classNames == null) |
| { |
| classNames = new ArrayList<String>(); |
| commands.put(verb, classNames); |
| } |
| classNames.add(command); |
| } |
| |
| private Map<String,List<String>> |
| getCommands(Map<String,Map<String,List<String>>> mailcap, |
| String mimeType) |
| { |
| int si = mimeType.indexOf('/'); |
| String genericMimeType = new CPStringBuilder(mimeType.substring(0, si)) |
| .append('/') |
| .append('*') |
| .toString(); |
| Map<String,List<String>> specific = mailcap.get(mimeType); |
| Map<String,List<String>> generic = mailcap.get(genericMimeType); |
| if (generic == null) |
| { |
| return specific; |
| } |
| if (specific == null) |
| { |
| return generic; |
| } |
| Map<String,List<String>> combined = new LinkedHashMap<String,List<String>>(); |
| combined.putAll(specific); |
| for (String verb : generic.keySet()) |
| { |
| List<String> genericClassNames = generic.get(verb); |
| List<String> classNames = combined.get(verb); |
| if (classNames == null) |
| { |
| combined.put(verb, genericClassNames); |
| } |
| else |
| { |
| classNames.addAll(genericClassNames); |
| } |
| } |
| return combined; |
| } |
| |
| // -- Utility methods -- |
| |
| private List<URL> getSystemResources(String name) |
| { |
| List<URL> acc = new ArrayList<URL>(); |
| try |
| { |
| for (Enumeration<URL> i = ClassLoader.getSystemResources(name); |
| i.hasMoreElements(); ) |
| { |
| acc.add(i.nextElement()); |
| } |
| } |
| catch (IOException e) |
| { |
| } |
| return acc; |
| } |
| |
| } |