libjava/ChangeLog:
[official-gcc.git] / libjava / classpath / javax / activation / MailcapCommandMap.java
blobcca5f04aafe18fd1ff65ea1cc56e620ae3c88b74
1 /* MailcapCommandMap.java -- Command map implementation using a mailcap file.
2 Copyright (C) 2004 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 02110-1301 USA.
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
24 combination.
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
38 package javax.activation;
40 import gnu.java.lang.CPStringBuilder;
42 import java.io.BufferedReader;
43 import java.io.File;
44 import java.io.FileReader;
45 import java.io.InputStream;
46 import java.io.InputStreamReader;
47 import java.io.IOException;
48 import java.io.Reader;
49 import java.io.StringReader;
50 import java.net.URL;
51 import java.util.ArrayList;
52 import java.util.Enumeration;
53 import java.util.LinkedHashMap;
54 import java.util.Iterator;
55 import java.util.List;
56 import java.util.Map;
58 /**
59 * Implementation of a command map using a <code>mailcap</code> file (RFC
60 * 1524). Mailcap files are searched for in the following places:
61 * <ol>
62 * <li>Programmatically added entries to this interface</li>
63 * <li>the file <tt>.mailcap</tt> in the user's home directory</li>
64 * <li>the file <i>&lt;java.home&gt;</i><tt>/lib/mailcap</tt></li>
65 * <li>the resource <tt>META-INF/mailcap</tt></li>
66 * <li>the resource <tt>META-INF/mailcap.default</tt> in the JAF
67 * distribution</li>
68 * </ol>
70 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
71 * @version 1.1
73 public class MailcapCommandMap
74 extends CommandMap
77 private static final int PROG = 0;
78 private static final int HOME = 1;
79 private static final int SYS = 2;
80 private static final int JAR = 3;
81 private static final int DEF = 4;
82 private static boolean debug = false;
83 private static final int NORMAL = 0;
84 private static final int FALLBACK = 1;
86 static
88 try
90 String d = System.getProperty("javax.activation.debug");
91 debug = Boolean.valueOf(d).booleanValue();
93 catch (SecurityException e)
98 private Map<String,Map<String,List<String>>>[][] mailcaps;
101 * Default constructor.
103 public MailcapCommandMap()
105 init(null);
109 * Constructor specifying a filename.
110 * @param fileName the name of the file to read mailcap entries from
112 public MailcapCommandMap(String fileName)
113 throws IOException
115 Reader in = null;
118 in = new FileReader(fileName);
120 catch (IOException e)
123 init(in);
124 if (in != null)
128 in.close();
130 catch (IOException e)
137 * Constructor specifying an input stream.
138 * @param is the input stream to read mailcap entries from
140 public MailcapCommandMap(InputStream is)
142 init(new InputStreamReader(is));
145 private void init(Reader in)
147 mailcaps = new Map[5][2];
148 for (int i = 0; i < 5; i++)
150 for (int j = 0; j < 2; j++)
152 mailcaps[i][j] =
153 new LinkedHashMap<String,Map<String,List<String>>>();
156 if (in != null)
158 if (debug)
160 System.out.println("MailcapCommandMap: load PROG");
164 parse(PROG, in);
166 catch (IOException e)
171 if (debug)
173 System.out.println("MailcapCommandMap: load HOME");
177 String home = System.getProperty("user.home");
178 if (home != null)
180 parseFile(HOME, new CPStringBuilder(home)
181 .append(File.separatorChar)
182 .append(".mailcap")
183 .toString());
186 catch (SecurityException e)
190 if (debug)
192 System.out.println("MailcapCommandMap: load SYS");
196 parseFile(SYS,
197 new CPStringBuilder(System.getProperty("java.home"))
198 .append(File.separatorChar)
199 .append("lib")
200 .append(File.separatorChar)
201 .append("mailcap")
202 .toString());
204 catch (SecurityException e)
208 if (debug)
210 System.out.println("MailcapCommandMap: load JAR");
212 List<URL> systemResources = getSystemResources("META-INF/mailcap");
213 int len = systemResources.size();
214 if (len > 0)
216 for (int i = 0; i < len ; i++)
218 Reader urlIn = null;
219 URL url = systemResources.get(i);
222 if (debug)
224 System.out.println("\t" + url.toString());
226 urlIn = new InputStreamReader(url.openStream());
227 parse(JAR, urlIn);
229 catch (IOException e)
231 if (debug)
233 System.out.println(e.getClass().getName() + ": " +
234 e.getMessage());
237 finally
239 if (urlIn != null)
243 urlIn.close();
245 catch (IOException e)
252 else
254 parseResource(JAR, "/META-INF/mailcap");
257 if (debug)
259 System.out.println("MailcapCommandMap: load DEF");
261 parseResource(DEF, "/META-INF/mailcap.default");
265 * Returns the list of preferred commands for a given MIME type.
266 * @param mimeType the MIME type
268 public synchronized CommandInfo[] getPreferredCommands(String mimeType)
270 List<CommandInfo> cmdList = new ArrayList<CommandInfo>();
271 List<String> verbList = new ArrayList<String>();
272 for (int i = 0; i < 2; i++)
274 for (int j = 0; j < 5; j++)
276 Map<String,List<String>> map = getCommands(mailcaps[j][i], mimeType);
277 if (map != null)
279 for (Map.Entry<String,List<String>> entry : map.entrySet())
281 String verb = entry.getKey();
282 if (!verbList.contains(verb))
284 List<String> classNames = entry.getValue();
285 String className = classNames.get(0);
286 CommandInfo cmd = new CommandInfo(verb, className);
287 cmdList.add(cmd);
288 verbList.add(verb);
294 CommandInfo[] cmds = new CommandInfo[cmdList.size()];
295 cmdList.toArray(cmds);
296 return cmds;
300 * Returns all commands for the given MIME type.
301 * @param mimeType the MIME type
303 public synchronized CommandInfo[] getAllCommands(String mimeType)
305 List<CommandInfo> cmdList = new ArrayList<CommandInfo>();
306 for (int i = 0; i < 2; i++)
308 for (int j = 0; j < 5; j++)
310 Map<String,List<String>> map = getCommands(mailcaps[j][i], mimeType);
311 if (map != null)
313 for (Map.Entry<String,List<String>> entry : map.entrySet())
315 String verb = entry.getKey();
316 List<String> classNames = entry.getValue();
317 int len = classNames.size();
318 for (int l = 0; l < len; l++)
320 String className = classNames.get(l);
321 CommandInfo cmd = new CommandInfo(verb, className);
322 cmdList.add(cmd);
328 CommandInfo[] cmds = new CommandInfo[cmdList.size()];
329 cmdList.toArray(cmds);
330 return cmds;
334 * Returns the command with the specified name for the given MIME type.
335 * @param mimeType the MIME type
336 * @param cmdName the command verb
338 public synchronized CommandInfo getCommand(String mimeType,
339 String cmdName)
341 for (int i = 0; i < 2; i++)
343 for (int j = 0; j < 5; j++)
345 Map<String,List<String>> map =
346 getCommands(mailcaps[j][i], mimeType);
347 if (map != null)
349 List<String> classNames = map.get(cmdName);
350 if (classNames == null)
352 classNames = map.get("x-java-" + cmdName);
354 if (classNames != null)
356 String className = classNames.get(0);
357 return new CommandInfo(cmdName, className);
362 return null;
366 * Adds entries programmatically to the registry.
367 * @param mailcap a mailcap string
369 public synchronized void addMailcap(String mailcap)
371 if (debug)
373 System.out.println("MailcapCommandMap: add to PROG");
377 parse(PROG, new StringReader(mailcap));
379 catch (IOException e)
385 * Returns the DCH for the specified MIME type.
386 * @param mimeType the MIME type
388 public synchronized DataContentHandler
389 createDataContentHandler(String mimeType)
391 if (debug)
393 System.out.println("MailcapCommandMap: " +
394 "createDataContentHandler for " + mimeType);
396 for (int i = 0; i < 2; i++)
398 for (int j = 0; j < 5; j++)
400 if (debug)
402 System.out.println(" search DB #" + i);
404 Map<String,List<String>> map = getCommands(mailcaps[j][i], mimeType);
405 if (map != null)
407 List<String> classNames = map.get("content-handler");
408 if (classNames == null)
410 classNames = map.get("x-java-content-handler");
412 if (classNames != null)
414 String className = classNames.get(0);
415 if (debug)
417 System.out.println(" In " + nameOf(j) +
418 ", content-handler=" + className);
422 Class<?> clazz = Class.forName(className);
423 return (DataContentHandler)clazz.newInstance();
425 catch (IllegalAccessException e)
427 if (debug)
429 e.printStackTrace();
432 catch (ClassNotFoundException e)
434 if (debug)
436 e.printStackTrace();
439 catch (InstantiationException e)
441 if (debug)
443 e.printStackTrace();
450 return null;
454 * Get the native commands for the given MIME type.
455 * Returns an array of strings where each string is
456 * an entire mailcap file entry. The application
457 * will need to parse the entry to extract the actual
458 * command as well as any attributes it needs. See
459 * <a href="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</a>
460 * for details of the mailcap entry syntax. Only mailcap
461 * entries that specify a view command for the specified
462 * MIME type are returned.
463 * @return array of native command entries
464 * @since JAF 1.1
466 public String[] getNativeCommands(String mimeType)
468 List<String> acc = new ArrayList<String>();
469 for (int i = 0; i < 2; i++)
471 for (int j = 0; j < 5; j++)
473 addNativeCommands(acc, mailcaps[j][i], mimeType);
476 String[] ret = new String[acc.size()];
477 acc.toArray(ret);
478 return ret;
481 private void addNativeCommands(List<String> acc,
482 Map<String,Map<String,List<String>>> mailcap,
483 String mimeType)
485 for (Map.Entry<String,Map<String,List<String>>> mEntry : mailcap.entrySet())
487 String entryMimeType = mEntry.getKey();
488 if (!entryMimeType.equals(mimeType))
490 continue;
492 Map<String,List<String>> commands = mEntry.getValue();
493 String viewCommand = commands.get("view-command").get(0);
494 if (viewCommand == null)
496 continue;
498 CPStringBuilder buf = new CPStringBuilder();
499 buf.append(mimeType);
500 buf.append(';');
501 buf.append(' ');
502 buf.append(viewCommand);
503 for (Map.Entry<String,List<String>> cEntry : commands.entrySet())
505 String verb = cEntry.getKey();
506 List<String> classNames = cEntry.getValue();
507 if (!"view-command".equals(verb))
509 for (String command : classNames)
511 buf.append(';');
512 buf.append(' ');
513 buf.append(verb);
514 buf.append('=');
515 buf.append(command);
519 if (buf.length() > 0)
521 acc.add(buf.toString());
526 private static String nameOf(int mailcap)
528 switch (mailcap)
530 case PROG:
531 return "PROG";
532 case HOME:
533 return "HOME";
534 case SYS:
535 return "SYS";
536 case JAR:
537 return "JAR";
538 case DEF:
539 return "DEF";
540 default:
541 return "ERR";
545 private void parseFile(int index, String filename)
547 Reader in = null;
550 if (debug)
552 System.out.println("\t" + filename);
554 in = new FileReader(filename);
555 parse(index, in);
557 catch (IOException e)
559 if (debug)
561 System.out.println(e.getClass().getName() + ": " +
562 e.getMessage());
565 finally
567 if (in != null)
571 in.close();
573 catch (IOException e)
580 private void parseResource(int index, String name)
582 Reader in = null;
585 InputStream is = getClass().getResourceAsStream(name);
586 if (is != null)
588 if (debug)
590 System.out.println("\t" + name);
592 in = new InputStreamReader(is);
593 parse(index, in);
596 catch (IOException e)
598 if (debug)
600 System.out.println(e.getClass().getName() + ": " +
601 e.getMessage());
604 finally
606 if (in != null)
610 in.close();
612 catch (IOException e)
619 private void parse(int index, Reader in)
620 throws IOException
622 BufferedReader br = new BufferedReader(in);
623 CPStringBuilder buf = null;
624 for (String line = br.readLine(); line != null; line = br.readLine())
626 line = line.trim();
627 int len = line.length();
628 if (len == 0 || line.charAt(0) == '#')
630 continue; // Comment
632 if (line.charAt(len - 1) == '\\')
634 if (buf == null)
636 buf = new CPStringBuilder();
638 buf.append(line.substring(0, len - 1));
640 else if (buf != null)
642 buf.append(line);
643 parseEntry(index, buf.toString());
644 buf = null;
646 else
648 parseEntry(index, line);
653 private void parseEntry(int index, String line)
655 // Tokenize entry into fields
656 char[] chars = line.toCharArray();
657 int len = chars.length;
658 boolean inQuotedString = false;
659 boolean fallback = false;
660 CPStringBuilder buffer = new CPStringBuilder();
661 List<String> fields = new ArrayList<String>();
662 for (int i = 0; i < len; i++)
664 char c = chars[i];
665 if (c == '\\')
667 c = chars[++i]; // qchar
669 if (c == ';' && !inQuotedString)
671 String field = buffer.toString().trim();
672 if ("x-java-fallback-entry".equals(field))
674 fallback = true;
676 fields.add(field);
677 buffer.setLength(0);
679 else
681 if (c == '"')
683 inQuotedString = !inQuotedString;
685 buffer.append(c);
688 String field = buffer.toString().trim();
689 if ("x-java-fallback-entry".equals(field))
691 fallback = true;
693 fields.add(field);
695 len = fields.size();
696 if (len < 2)
698 if (debug)
700 System.err.println("Invalid mailcap entry: " + line);
702 return;
705 Map<String,Map<String,List<String>>> mailcap =
706 fallback ? mailcaps[index][FALLBACK] : mailcaps[index][NORMAL];
707 String mimeType = fields.get(0);
708 addField(mailcap, mimeType, "view-command", (String) fields.get(1));
709 for (int i = 2; i < len; i++)
711 addField(mailcap, mimeType, null, (String) fields.get(i));
715 private void addField(Map<String,Map<String,List<String>>> mailcap,
716 String mimeType, String verb, String command)
718 if (verb == null)
720 int ei = command.indexOf('=');
721 if (ei != -1)
723 verb = command.substring(0, ei);
724 command = command.substring(ei + 1);
727 if (command.length() == 0 || verb == null || verb.length() == 0)
729 return; // Invalid field or flag
732 Map<String,List<String>> commands = mailcap.get(mimeType);
733 if (commands == null)
735 commands = new LinkedHashMap<String,List<String>>();
736 mailcap.put(mimeType, commands);
738 List<String> classNames = commands.get(verb);
739 if (classNames == null)
741 classNames = new ArrayList<String>();
742 commands.put(verb, classNames);
744 classNames.add(command);
747 private Map<String,List<String>>
748 getCommands(Map<String,Map<String,List<String>>> mailcap,
749 String mimeType)
751 int si = mimeType.indexOf('/');
752 String genericMimeType = new CPStringBuilder(mimeType.substring(0, si))
753 .append('/')
754 .append('*')
755 .toString();
756 Map<String,List<String>> specific = mailcap.get(mimeType);
757 Map<String,List<String>> generic = mailcap.get(genericMimeType);
758 if (generic == null)
760 return specific;
762 if (specific == null)
764 return generic;
766 Map<String,List<String>> combined = new LinkedHashMap<String,List<String>>();
767 combined.putAll(specific);
768 for (String verb : generic.keySet())
770 List<String> genericClassNames = generic.get(verb);
771 List<String> classNames = combined.get(verb);
772 if (classNames == null)
774 combined.put(verb, genericClassNames);
776 else
778 classNames.addAll(genericClassNames);
781 return combined;
784 // -- Utility methods --
786 private List<URL> getSystemResources(String name)
788 List<URL> acc = new ArrayList<URL>();
791 for (Enumeration<URL> i = ClassLoader.getSystemResources(name);
792 i.hasMoreElements(); )
794 acc.add(i.nextElement());
797 catch (IOException e)
800 return acc;