1. /*
  2. * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
  3. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  4. */
  5. package javax.activation;
  6. import java.util.*;
  7. import java.io.*;
  8. import com.sun.activation.registries.MailcapFile;
  9. import com.sun.activation.registries.MailcapEntry;
  10. /**
  11. * MailcapCommandMap extends the CommandMap
  12. * abstract class. It implements a CommandMap whose configuration
  13. * is based on mailcap files (RFC 1524). The MailcapCommandMap can
  14. * be configured both programmatically and via configuration
  15. * files.
  16. * <p>
  17. * <b>Mailcap file search order:</b><p>
  18. * The MailcapCommandMap looks in various places in the user's
  19. * system for mailcap file entries. When requests are made
  20. * to search for commands in the MailcapCommandMap, it searches
  21. * mailcap files in the following order:
  22. * <p>
  23. * <ol>
  24. * <li> Programatically added entries to the MailcapCommandMap instance.
  25. * <li> The file <code>.mailcap</code> in the user's home directory.
  26. * <li> The file <<i>java.home</i>><code>/lib/mailcap</code>.
  27. * <li> The file or resource named <code>META-INF/mailcap</code>.
  28. * <li> The file or resource named <code>META-INF/mailcap.default</code>
  29. * (usually found only in the <code>activation.jar</code> file).
  30. * </ol>
  31. * <p>
  32. * <b>Mailcap file format:</b><p>
  33. *
  34. * Mailcap files must conform to the mailcap
  35. * file specification (RFC 1524, <i>A User Agent Configuration Mechanism
  36. * For Multimedia Mail Format Information</i>).
  37. * The file format consists of entries corresponding to
  38. * particular MIME types. In general, the specification
  39. * specifies <i>applications</i> for clients to use when they
  40. * themselves cannot operate on the specified MIME type. The
  41. * MailcapCommandMap extends this specification by using a parameter mechanism
  42. * in mailcap files that allows JavaBeans(tm) components to be specified as
  43. * corresponding to particular commands for a MIME type.<p>
  44. *
  45. * When a mailcap file is
  46. * parsed, the MailcapCommandMap recognizes certain parameter signatures,
  47. * specifically those parameter names that begin with <code>x-java-</code>.
  48. * The MailcapCommandMap uses this signature to find
  49. * command entries for inclusion into its registries.
  50. * Parameter names with the form <code>x-java-<name></code>
  51. * are read by the MailcapCommandMap as identifying a command
  52. * with the name <i>name</i>. When the <i>name</i> is <code>
  53. * content-handler</code> the MailcapCommandMap recognizes the class
  54. * signified by this parameter as a <i>DataContentHandler</i>.
  55. * All other commands are handled generically regardless of command
  56. * name. The command implementation is specified by a fully qualified
  57. * class name of a JavaBean(tm) component. For example; a command for viewing
  58. * some data can be specified as: <code>x-java-view=com.foo.ViewBean</code>.<p>
  59. *
  60. * MailcapCommandMap aware mailcap files have the
  61. * following general form:<p>
  62. * <code>
  63. * # Comments begin with a '#' and continue to the end of the line.<br>
  64. * <mime type> ; <parameter list><br>
  65. * # Where a parameter list consists of one or more parameters,<br>
  66. * # where parameters look like: x-java-view=com.sun.TextViewer<br>
  67. * # and a parameter list looks like: <br>
  68. * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
  69. * <br>
  70. * # Note that mailcap entries that do not contain 'x-java' parameters<br>
  71. * # and comply to RFC 1524 are simply ignored:<br>
  72. * image/gif; /usr/dt/bin/sdtimage %s<br>
  73. *
  74. * </code>
  75. * <p>
  76. *
  77. * @author Bart Calder
  78. * @author Bill Shannon
  79. */
  80. public class MailcapCommandMap extends CommandMap {
  81. /*
  82. * We manage up to 5 databases, searched in order.
  83. * The default database is shared between all instances
  84. * of this class.
  85. * XXX - Can we safely share more databases between instances?
  86. */
  87. private static MailcapFile defDB = null;
  88. private MailcapFile[] DB = new MailcapFile[5];
  89. private static final int PROG = 0; // programmatically added entries
  90. private static final int HOME = 1; // the user's home directory
  91. private static final int SYS = 2; // the system file
  92. private static final int JAR = 3; // the app's entry in the jar file
  93. private static final int DEF = 4; // the default entry in our jar file
  94. private static boolean debug = false;
  95. static {
  96. try {
  97. debug = Boolean.getBoolean("javax.activation.debug");
  98. } catch (Throwable t) {
  99. // ignore any errors
  100. }
  101. }
  102. /**
  103. * The default Constructor.
  104. */
  105. public MailcapCommandMap() {
  106. super();
  107. if (debug)
  108. System.out.println("MailcapCommandMap: load HOME");
  109. try {
  110. String user_home = System.getProperty("user.home");
  111. if (user_home != null) {
  112. String path = user_home + File.separator + ".mailcap";
  113. DB[HOME] = loadFile(path);
  114. }
  115. } catch (SecurityException ex) {}
  116. if (debug)
  117. System.out.println("MailcapCommandMap: load SYS");
  118. try {
  119. // check system's home
  120. String system_mailcap = System.getProperty("java.home") +
  121. File.separator + "lib" + File.separator + "mailcap";
  122. DB[SYS] = loadFile(system_mailcap);
  123. } catch (SecurityException ex) {}
  124. if (debug)
  125. System.out.println("MailcapCommandMap: load JAR");
  126. // load from the app's jar file
  127. DB[JAR] = loadResource("/META-INF/mailcap");
  128. if (debug)
  129. System.out.println("MailcapCommandMap: load DEF");
  130. synchronized (MailcapCommandMap.class) {
  131. // see if another instance has created this yet.
  132. if (defDB == null)
  133. defDB = loadResource("/META-INF/mailcap.default");
  134. }
  135. DB[DEF] = defDB;
  136. }
  137. /**
  138. * Load from the named resource.
  139. */
  140. private MailcapFile loadResource(String name) {
  141. try {
  142. InputStream clis = this.getClass().getResourceAsStream(name);
  143. if (clis != null)
  144. return new MailcapFile(clis);
  145. } catch (IOException e) {
  146. // e.printStackTrace();
  147. }
  148. return null;
  149. }
  150. /**
  151. * Load from the named file.
  152. */
  153. private MailcapFile loadFile(String name) {
  154. MailcapFile mtf = null;
  155. try {
  156. mtf = new MailcapFile(name);
  157. } catch (IOException e) {
  158. // e.printStackTrace();
  159. }
  160. return mtf;
  161. }
  162. /**
  163. * Constructor that allows the caller to specify the path
  164. * of a <i>mailcap</i> file.
  165. *
  166. * @param fileName The name of the <i>mailcap</i> file to open
  167. */
  168. public MailcapCommandMap(String fileName) throws IOException {
  169. this();
  170. if (debug)
  171. System.out.println("MailcapCommandMap: load PROG from " + fileName);
  172. if (DB[PROG] == null) {
  173. DB[PROG] = new MailcapFile(fileName);
  174. }
  175. }
  176. /**
  177. * Constructor that allows the caller to specify an <i>InputStream</i>
  178. * containing a mailcap file.
  179. *
  180. * @param is InputStream of the <i>mailcap</i> file to open
  181. */
  182. public MailcapCommandMap(InputStream is) {
  183. this();
  184. if (debug)
  185. System.out.println("MailcapCommandMap: load PROG");
  186. if (DB[PROG] == null) {
  187. try {
  188. DB[PROG] = new MailcapFile(is);
  189. } catch (IOException ex) {
  190. // XXX - should throw it
  191. }
  192. }
  193. }
  194. /**
  195. * Get the preferred command list for a MIME Type. The MailcapCommandMap
  196. * searches the mailcap files as described above under
  197. * <i>Mailcap file search order</i>.<p>
  198. *
  199. * The result of the search is a proper subset of available
  200. * commands in all mailcap files known to this instance of
  201. * MailcapCommandMap. The first entry for a particular command
  202. * is considered the preferred command.
  203. *
  204. * @param mimeType the MIME type
  205. * @return the CommandInfo objects representing the preferred commands.
  206. */
  207. public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
  208. Vector cmdVector = new Vector();
  209. for (int i = 0; i < DB.length; i++) {
  210. if (DB[i] == null)
  211. continue;
  212. Hashtable cmdList = DB[i].getMailcapList(mimeType);
  213. if (cmdList != null)
  214. appendPrefCmdsToVector(cmdList, cmdVector);
  215. }
  216. CommandInfo[] cmdInfos = new CommandInfo[cmdVector.size()];
  217. cmdVector.copyInto(cmdInfos);
  218. return cmdInfos;
  219. }
  220. /**
  221. * Put the commands that are in the hash table, into the vector.
  222. */
  223. private void appendPrefCmdsToVector(Hashtable typeHash, Vector cmdList) {
  224. Enumeration verb_enum = typeHash.keys();
  225. while (verb_enum.hasMoreElements()) {
  226. String verb = (String)verb_enum.nextElement();
  227. if (!checkForVerb(cmdList, verb)) {
  228. Vector cmdVector = (Vector)typeHash.get(verb); // get the list
  229. String className = (String)cmdVector.firstElement();
  230. cmdList.addElement(new CommandInfo(verb, className));
  231. }
  232. }
  233. }
  234. /**
  235. * Check the cmdVector to see if this command exists, return
  236. * true if the verb is there.
  237. */
  238. private boolean checkForVerb(Vector cmdVector, String verb) {
  239. Enumeration enum = cmdVector.elements();
  240. while (enum.hasMoreElements()) {
  241. String enum_verb =
  242. (String)((CommandInfo)enum.nextElement()).getCommandName();
  243. if (enum_verb.equals(verb))
  244. return true;
  245. }
  246. return false;
  247. }
  248. /**
  249. * Get all the available commands in all mailcap files known to
  250. * this instance of MailcapCommandMap for this MIME type.
  251. *
  252. * @param mimeType the MIME type
  253. * @return the CommandInfo objects representing all the commands.
  254. */
  255. public synchronized CommandInfo[] getAllCommands(String mimeType) {
  256. Vector cmdVector = new Vector();
  257. for (int i = 0; i < DB.length; i++) {
  258. if (DB[i] == null)
  259. continue;
  260. Hashtable cmdList = DB[i].getMailcapList(mimeType);
  261. if (cmdList != null)
  262. appendCmdsToVector(cmdList, cmdVector);
  263. }
  264. CommandInfo[] cmdInfos = new CommandInfo[cmdVector.size()];
  265. cmdVector.copyInto(cmdInfos);
  266. return cmdInfos;
  267. }
  268. /**
  269. * Put the commands that are in the hash table, into the vector.
  270. */
  271. private void appendCmdsToVector(Hashtable typeHash, Vector cmdList) {
  272. Enumeration verb_enum = typeHash.keys();
  273. while (verb_enum.hasMoreElements()) {
  274. String verb = (String)verb_enum.nextElement();
  275. Vector cmdVector = (Vector)typeHash.get(verb);
  276. Enumeration cmd_enum = ((Vector)cmdVector).elements();
  277. while (cmd_enum.hasMoreElements()) {
  278. String cmd = (String)cmd_enum.nextElement();
  279. // cmdList.addElement(new CommandInfo(verb, cmd));
  280. cmdList.insertElementAt(new CommandInfo(verb, cmd), 0);
  281. }
  282. }
  283. }
  284. /**
  285. * Get the command corresponding to <code>cmdName</code> for the MIME type.
  286. *
  287. * @param mimeType the MIME type
  288. * @param cmdName the command name
  289. * @return the CommandInfo object corresponding to the command.
  290. */
  291. public synchronized CommandInfo getCommand(String mimeType,
  292. String cmdName) {
  293. for (int i = 0; i < DB.length; i++) {
  294. if (DB[i] == null)
  295. continue;
  296. Hashtable cmdList = DB[i].getMailcapList(mimeType);
  297. if (cmdList != null) {
  298. // get the cmd list for the cmd
  299. Vector v = (Vector)cmdList.get(cmdName);
  300. if (v != null) {
  301. String cmdClassName = (String)v.firstElement();
  302. if (cmdClassName != null)
  303. return new CommandInfo(cmdName, cmdClassName);
  304. }
  305. }
  306. }
  307. return null;
  308. }
  309. /**
  310. * Add entries to the registry. Programmatically
  311. * added entries are searched before other entries.<p>
  312. *
  313. * The string that is passed in should be in mailcap
  314. * format.
  315. *
  316. * @param mail_cap a correctly formatted mailcap string
  317. */
  318. public synchronized void addMailcap(String mail_cap) {
  319. // check to see if one exists
  320. if (debug)
  321. System.out.println("MailcapCommandMap: add to PROG");
  322. if (DB[PROG] == null)
  323. DB[PROG] = new MailcapFile();
  324. DB[PROG].appendToMailcap(mail_cap);
  325. }
  326. /**
  327. * Return the DataContentHandler for the specified MIME type.
  328. *
  329. * @param mimeType the MIME type
  330. * @return the DataContentHandler
  331. */
  332. public synchronized DataContentHandler createDataContentHandler(
  333. String mimeType) {
  334. if (debug)
  335. System.out.println(
  336. "MailcapCommandMap: createDataContentHandler for " + mimeType);
  337. for (int i = 0; i < DB.length; i++) {
  338. if (DB[i] == null)
  339. continue;
  340. if (debug)
  341. System.out.println(" search DB #" + i);
  342. Hashtable cmdList = DB[i].getMailcapList(mimeType);
  343. if (cmdList != null) {
  344. Vector v = (Vector)cmdList.get("content-handler");
  345. if (v != null) {
  346. if (debug)
  347. System.out.println(" got content-handler");
  348. try {
  349. if (debug)
  350. System.out.println(" class " +
  351. (String)v.firstElement());
  352. return (DataContentHandler)Class.forName(
  353. (String)v.firstElement()).newInstance();
  354. } catch (IllegalAccessException e) {
  355. } catch (ClassNotFoundException e) {
  356. } catch (InstantiationException e) {
  357. }
  358. }
  359. }
  360. }
  361. return null;
  362. }
  363. /**
  364. * for debugging...
  365. *
  366. public static void main(String[] argv) throws Exception {
  367. MailcapCommandMap map = new MailcapCommandMap();
  368. CommandInfo[] cmdInfo;
  369. cmdInfo = map.getPreferredCommands(argv[0]);
  370. System.out.println("Preferred Commands:");
  371. for (int i = 0; i < cmdInfo.length; i++)
  372. System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
  373. cmdInfo[i].getCommandClass() + "]");
  374. cmdInfo = map.getAllCommands(argv[0]);
  375. System.out.println();
  376. System.out.println("All Commands:");
  377. for (int i = 0; i < cmdInfo.length; i++)
  378. System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
  379. cmdInfo[i].getCommandClass() + "]");
  380. DataContentHandler dch = map.createDataContentHandler(argv[0]);
  381. if (dch != null)
  382. System.out.println("DataContentHandler " +
  383. dch.getClass().toString());
  384. System.exit(0);
  385. }
  386. */
  387. }