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)
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
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
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
;
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
;
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
;
59 * Implementation of a command map using a <code>mailcap</code> file (RFC
60 * 1524). Mailcap files are searched for in the following places:
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><java.home></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
70 * @author <a href='mailto:dog@gnu.org'>Chris Burdess</a>
73 public class MailcapCommandMap
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;
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()
109 * Constructor specifying a filename.
110 * @param fileName the name of the file to read mailcap entries from
112 public MailcapCommandMap(String fileName
)
118 in
= new FileReader(fileName
);
120 catch (IOException e
)
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
++)
153 new LinkedHashMap
<String
,Map
<String
,List
<String
>>>();
160 System
.out
.println("MailcapCommandMap: load PROG");
166 catch (IOException e
)
173 System
.out
.println("MailcapCommandMap: load HOME");
177 String home
= System
.getProperty("user.home");
180 parseFile(HOME
, new CPStringBuilder(home
)
181 .append(File
.separatorChar
)
186 catch (SecurityException e
)
192 System
.out
.println("MailcapCommandMap: load SYS");
197 new CPStringBuilder(System
.getProperty("java.home"))
198 .append(File
.separatorChar
)
200 .append(File
.separatorChar
)
204 catch (SecurityException e
)
210 System
.out
.println("MailcapCommandMap: load JAR");
212 List
<URL
> systemResources
= getSystemResources("META-INF/mailcap");
213 int len
= systemResources
.size();
216 for (int i
= 0; i
< len
; i
++)
219 URL url
= systemResources
.get(i
);
224 System
.out
.println("\t" + url
.toString());
226 urlIn
= new InputStreamReader(url
.openStream());
229 catch (IOException e
)
233 System
.out
.println(e
.getClass().getName() + ": " +
245 catch (IOException e
)
254 parseResource(JAR
, "/META-INF/mailcap");
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
);
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
);
294 CommandInfo
[] cmds
= new CommandInfo
[cmdList
.size()];
295 cmdList
.toArray(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
);
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
);
328 CommandInfo
[] cmds
= new CommandInfo
[cmdList
.size()];
329 cmdList
.toArray(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
,
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
);
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
);
366 * Adds entries programmatically to the registry.
367 * @param mailcap a mailcap string
369 public synchronized void addMailcap(String mailcap
)
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
)
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
++)
402 System
.out
.println(" search DB #" + i
);
404 Map
<String
,List
<String
>> map
= getCommands(mailcaps
[j
][i
], mimeType
);
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);
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
)
432 catch (ClassNotFoundException e
)
439 catch (InstantiationException e
)
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
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()];
481 private void addNativeCommands(List
<String
> acc
,
482 Map
<String
,Map
<String
,List
<String
>>> mailcap
,
485 for (Map
.Entry
<String
,Map
<String
,List
<String
>>> mEntry
: mailcap
.entrySet())
487 String entryMimeType
= mEntry
.getKey();
488 if (!entryMimeType
.equals(mimeType
))
492 Map
<String
,List
<String
>> commands
= mEntry
.getValue();
493 String viewCommand
= commands
.get("view-command").get(0);
494 if (viewCommand
== null)
498 CPStringBuilder buf
= new CPStringBuilder();
499 buf
.append(mimeType
);
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
)
519 if (buf
.length() > 0)
521 acc
.add(buf
.toString());
526 private static String
nameOf(int mailcap
)
545 private void parseFile(int index
, String filename
)
552 System
.out
.println("\t" + filename
);
554 in
= new FileReader(filename
);
557 catch (IOException e
)
561 System
.out
.println(e
.getClass().getName() + ": " +
573 catch (IOException e
)
580 private void parseResource(int index
, String name
)
585 InputStream is
= getClass().getResourceAsStream(name
);
590 System
.out
.println("\t" + name
);
592 in
= new InputStreamReader(is
);
596 catch (IOException e
)
600 System
.out
.println(e
.getClass().getName() + ": " +
612 catch (IOException e
)
619 private void parse(int index
, Reader in
)
622 BufferedReader br
= new BufferedReader(in
);
623 CPStringBuilder buf
= null;
624 for (String line
= br
.readLine(); line
!= null; line
= br
.readLine())
627 int len
= line
.length();
628 if (len
== 0 || line
.charAt(0) == '#')
632 if (line
.charAt(len
- 1) == '\\')
636 buf
= new CPStringBuilder();
638 buf
.append(line
.substring(0, len
- 1));
640 else if (buf
!= null)
643 parseEntry(index
, buf
.toString());
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
++)
667 c
= chars
[++i
]; // qchar
669 if (c
== ';' && !inQuotedString
)
671 String field
= buffer
.toString().trim();
672 if ("x-java-fallback-entry".equals(field
))
683 inQuotedString
= !inQuotedString
;
688 String field
= buffer
.toString().trim();
689 if ("x-java-fallback-entry".equals(field
))
700 System
.err
.println("Invalid mailcap entry: " + line
);
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
)
720 int ei
= command
.indexOf('=');
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
,
751 int si
= mimeType
.indexOf('/');
752 String genericMimeType
= new CPStringBuilder(mimeType
.substring(0, si
))
756 Map
<String
,List
<String
>> specific
= mailcap
.get(mimeType
);
757 Map
<String
,List
<String
>> generic
= mailcap
.get(genericMimeType
);
762 if (specific
== null)
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
);
778 classNames
.addAll(genericClassNames
);
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
)