src/share/jaf_classes/javax/activation/MailcapCommandMap.java

changeset 0
373ffda63c9a
child 637
9c07ef4934dd
equal deleted inserted replaced
-1:000000000000 0:373ffda63c9a
1 /*
2 * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26
27 package javax.activation;
28
29 import java.util.*;
30 import java.io.*;
31 import java.net.*;
32 import com.sun.activation.registries.MailcapFile;
33 import com.sun.activation.registries.LogSupport;
34
35 /**
36 * MailcapCommandMap extends the CommandMap
37 * abstract class. It implements a CommandMap whose configuration
38 * is based on mailcap files
39 * (<A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>).
40 * The MailcapCommandMap can be configured both programmatically
41 * and via configuration files.
42 * <p>
43 * <b>Mailcap file search order:</b><p>
44 * The MailcapCommandMap looks in various places in the user's
45 * system for mailcap file entries. When requests are made
46 * to search for commands in the MailcapCommandMap, it searches
47 * mailcap files in the following order:
48 * <p>
49 * <ol>
50 * <li> Programatically added entries to the MailcapCommandMap instance.
51 * <li> The file <code>.mailcap</code> in the user's home directory.
52 * <li> The file &lt;<i>java.home</i>&gt;<code>/lib/mailcap</code>.
53 * <li> The file or resources named <code>META-INF/mailcap</code>.
54 * <li> The file or resource named <code>META-INF/mailcap.default</code>
55 * (usually found only in the <code>activation.jar</code> file).
56 * </ol>
57 * <p>
58 * <b>Mailcap file format:</b><p>
59 *
60 * Mailcap files must conform to the mailcap
61 * file specification (RFC 1524, <i>A User Agent Configuration Mechanism
62 * For Multimedia Mail Format Information</i>).
63 * The file format consists of entries corresponding to
64 * particular MIME types. In general, the specification
65 * specifies <i>applications</i> for clients to use when they
66 * themselves cannot operate on the specified MIME type. The
67 * MailcapCommandMap extends this specification by using a parameter mechanism
68 * in mailcap files that allows JavaBeans(tm) components to be specified as
69 * corresponding to particular commands for a MIME type.<p>
70 *
71 * When a mailcap file is
72 * parsed, the MailcapCommandMap recognizes certain parameter signatures,
73 * specifically those parameter names that begin with <code>x-java-</code>.
74 * The MailcapCommandMap uses this signature to find
75 * command entries for inclusion into its registries.
76 * Parameter names with the form <code>x-java-&lt;name></code>
77 * are read by the MailcapCommandMap as identifying a command
78 * with the name <i>name</i>. When the <i>name</i> is <code>
79 * content-handler</code> the MailcapCommandMap recognizes the class
80 * signified by this parameter as a <i>DataContentHandler</i>.
81 * All other commands are handled generically regardless of command
82 * name. The command implementation is specified by a fully qualified
83 * class name of a JavaBean(tm) component. For example; a command for viewing
84 * some data can be specified as: <code>x-java-view=com.foo.ViewBean</code>.<p>
85 *
86 * When the command name is <code>fallback-entry</code>, the value of
87 * the command may be <code>true</code> or <code>false</code>. An
88 * entry for a MIME type that includes a parameter of
89 * <code>x-java-fallback-entry=true</code> defines fallback commands
90 * for that MIME type that will only be used if no non-fallback entry
91 * can be found. For example, an entry of the form <code>text/*; ;
92 * x-java-fallback-entry=true; x-java-view=com.sun.TextViewer</code>
93 * specifies a view command to be used for any text MIME type. This
94 * view command would only be used if a non-fallback view command for
95 * the MIME type could not be found.<p>
96 *
97 * MailcapCommandMap aware mailcap files have the
98 * following general form:<p>
99 * <code>
100 * # Comments begin with a '#' and continue to the end of the line.<br>
101 * &lt;mime type>; ; &lt;parameter list><br>
102 * # Where a parameter list consists of one or more parameters,<br>
103 * # where parameters look like: x-java-view=com.sun.TextViewer<br>
104 * # and a parameter list looks like: <br>
105 * text/plain; ; x-java-view=com.sun.TextViewer; x-java-edit=com.sun.TextEdit
106 * <br>
107 * # Note that mailcap entries that do not contain 'x-java' parameters<br>
108 * # and comply to RFC 1524 are simply ignored:<br>
109 * image/gif; /usr/dt/bin/sdtimage %s<br>
110 *
111 * </code>
112 * <p>
113 *
114 * @author Bart Calder
115 * @author Bill Shannon
116 *
117 * @since 1.6
118 */
119
120 public class MailcapCommandMap extends CommandMap {
121 /*
122 * We manage a collection of databases, searched in order.
123 */
124 private MailcapFile[] DB;
125 private static final int PROG = 0; // programmatically added entries
126
127 /**
128 * The default Constructor.
129 */
130 public MailcapCommandMap() {
131 super();
132 List dbv = new ArrayList(5); // usually 5 or less databases
133 MailcapFile mf = null;
134 dbv.add(null); // place holder for PROG entry
135
136 LogSupport.log("MailcapCommandMap: load HOME");
137 try {
138 String user_home = System.getProperty("user.home");
139
140 if (user_home != null) {
141 String path = user_home + File.separator + ".mailcap";
142 mf = loadFile(path);
143 if (mf != null)
144 dbv.add(mf);
145 }
146 } catch (SecurityException ex) {}
147
148 LogSupport.log("MailcapCommandMap: load SYS");
149 try {
150 // check system's home
151 String system_mailcap = System.getProperty("java.home") +
152 File.separator + "lib" + File.separator + "mailcap";
153 mf = loadFile(system_mailcap);
154 if (mf != null)
155 dbv.add(mf);
156 } catch (SecurityException ex) {}
157
158 LogSupport.log("MailcapCommandMap: load JAR");
159 // load from the app's jar file
160 loadAllResources(dbv, "META-INF/mailcap");
161
162 LogSupport.log("MailcapCommandMap: load DEF");
163 mf = loadResource("/META-INF/mailcap.default");
164
165 if (mf != null)
166 dbv.add(mf);
167
168 DB = new MailcapFile[dbv.size()];
169 DB = (MailcapFile[])dbv.toArray(DB);
170 }
171
172 /**
173 * Load from the named resource.
174 */
175 private MailcapFile loadResource(String name) {
176 InputStream clis = null;
177 try {
178 clis = SecuritySupport.getResourceAsStream(this.getClass(), name);
179 if (clis != null) {
180 MailcapFile mf = new MailcapFile(clis);
181 if (LogSupport.isLoggable())
182 LogSupport.log("MailcapCommandMap: successfully loaded " +
183 "mailcap file: " + name);
184 return mf;
185 } else {
186 if (LogSupport.isLoggable())
187 LogSupport.log("MailcapCommandMap: not loading " +
188 "mailcap file: " + name);
189 }
190 } catch (IOException e) {
191 if (LogSupport.isLoggable())
192 LogSupport.log("MailcapCommandMap: can't load " + name, e);
193 } catch (SecurityException sex) {
194 if (LogSupport.isLoggable())
195 LogSupport.log("MailcapCommandMap: can't load " + name, sex);
196 } finally {
197 try {
198 if (clis != null)
199 clis.close();
200 } catch (IOException ex) { } // ignore it
201 }
202 return null;
203 }
204
205 /**
206 * Load all of the named resource.
207 */
208 private void loadAllResources(List v, String name) {
209 boolean anyLoaded = false;
210 try {
211 URL[] urls;
212 ClassLoader cld = null;
213 // First try the "application's" class loader.
214 cld = SecuritySupport.getContextClassLoader();
215 if (cld == null)
216 cld = this.getClass().getClassLoader();
217 if (cld != null)
218 urls = SecuritySupport.getResources(cld, name);
219 else
220 urls = SecuritySupport.getSystemResources(name);
221 if (urls != null) {
222 if (LogSupport.isLoggable())
223 LogSupport.log("MailcapCommandMap: getResources");
224 for (int i = 0; i < urls.length; i++) {
225 URL url = urls[i];
226 InputStream clis = null;
227 if (LogSupport.isLoggable())
228 LogSupport.log("MailcapCommandMap: URL " + url);
229 try {
230 clis = SecuritySupport.openStream(url);
231 if (clis != null) {
232 v.add(new MailcapFile(clis));
233 anyLoaded = true;
234 if (LogSupport.isLoggable())
235 LogSupport.log("MailcapCommandMap: " +
236 "successfully loaded " +
237 "mailcap file from URL: " +
238 url);
239 } else {
240 if (LogSupport.isLoggable())
241 LogSupport.log("MailcapCommandMap: " +
242 "not loading mailcap " +
243 "file from URL: " + url);
244 }
245 } catch (IOException ioex) {
246 if (LogSupport.isLoggable())
247 LogSupport.log("MailcapCommandMap: can't load " +
248 url, ioex);
249 } catch (SecurityException sex) {
250 if (LogSupport.isLoggable())
251 LogSupport.log("MailcapCommandMap: can't load " +
252 url, sex);
253 } finally {
254 try {
255 if (clis != null)
256 clis.close();
257 } catch (IOException cex) { }
258 }
259 }
260 }
261 } catch (Exception ex) {
262 if (LogSupport.isLoggable())
263 LogSupport.log("MailcapCommandMap: can't load " + name, ex);
264 }
265
266 // if failed to load anything, fall back to old technique, just in case
267 if (!anyLoaded) {
268 if (LogSupport.isLoggable())
269 LogSupport.log("MailcapCommandMap: !anyLoaded");
270 MailcapFile mf = loadResource("/" + name);
271 if (mf != null)
272 v.add(mf);
273 }
274 }
275
276 /**
277 * Load from the named file.
278 */
279 private MailcapFile loadFile(String name) {
280 MailcapFile mtf = null;
281
282 try {
283 mtf = new MailcapFile(name);
284 } catch (IOException e) {
285 // e.printStackTrace();
286 }
287 return mtf;
288 }
289
290 /**
291 * Constructor that allows the caller to specify the path
292 * of a <i>mailcap</i> file.
293 *
294 * @param fileName The name of the <i>mailcap</i> file to open
295 * @exception IOException if the file can't be accessed
296 */
297 public MailcapCommandMap(String fileName) throws IOException {
298 this();
299
300 if (LogSupport.isLoggable())
301 LogSupport.log("MailcapCommandMap: load PROG from " + fileName);
302 if (DB[PROG] == null) {
303 DB[PROG] = new MailcapFile(fileName);
304 }
305 }
306
307
308 /**
309 * Constructor that allows the caller to specify an <i>InputStream</i>
310 * containing a mailcap file.
311 *
312 * @param is InputStream of the <i>mailcap</i> file to open
313 */
314 public MailcapCommandMap(InputStream is) {
315 this();
316
317 LogSupport.log("MailcapCommandMap: load PROG");
318 if (DB[PROG] == null) {
319 try {
320 DB[PROG] = new MailcapFile(is);
321 } catch (IOException ex) {
322 // XXX - should throw it
323 }
324 }
325 }
326
327 /**
328 * Get the preferred command list for a MIME Type. The MailcapCommandMap
329 * searches the mailcap files as described above under
330 * <i>Mailcap file search order</i>.<p>
331 *
332 * The result of the search is a proper subset of available
333 * commands in all mailcap files known to this instance of
334 * MailcapCommandMap. The first entry for a particular command
335 * is considered the preferred command.
336 *
337 * @param mimeType the MIME type
338 * @return the CommandInfo objects representing the preferred commands.
339 */
340 public synchronized CommandInfo[] getPreferredCommands(String mimeType) {
341 List cmdList = new ArrayList();
342 if (mimeType != null)
343 mimeType = mimeType.toLowerCase(Locale.ENGLISH);
344
345 for (int i = 0; i < DB.length; i++) {
346 if (DB[i] == null)
347 continue;
348 Map cmdMap = DB[i].getMailcapList(mimeType);
349 if (cmdMap != null)
350 appendPrefCmdsToList(cmdMap, cmdList);
351 }
352
353 // now add the fallback commands
354 for (int i = 0; i < DB.length; i++) {
355 if (DB[i] == null)
356 continue;
357 Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
358 if (cmdMap != null)
359 appendPrefCmdsToList(cmdMap, cmdList);
360 }
361
362 CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
363 cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
364
365 return cmdInfos;
366 }
367
368 /**
369 * Put the commands that are in the hash table, into the list.
370 */
371 private void appendPrefCmdsToList(Map cmdHash, List cmdList) {
372 Iterator verb_enum = cmdHash.keySet().iterator();
373
374 while (verb_enum.hasNext()) {
375 String verb = (String)verb_enum.next();
376 if (!checkForVerb(cmdList, verb)) {
377 List cmdList2 = (List)cmdHash.get(verb); // get the list
378 String className = (String)cmdList2.get(0);
379 cmdList.add(new CommandInfo(verb, className));
380 }
381 }
382 }
383
384 /**
385 * Check the cmdList to see if this command exists, return
386 * true if the verb is there.
387 */
388 private boolean checkForVerb(List cmdList, String verb) {
389 Iterator ee = cmdList.iterator();
390 while (ee.hasNext()) {
391 String enum_verb =
392 (String)((CommandInfo)ee.next()).getCommandName();
393 if (enum_verb.equals(verb))
394 return true;
395 }
396 return false;
397 }
398
399 /**
400 * Get all the available commands in all mailcap files known to
401 * this instance of MailcapCommandMap for this MIME type.
402 *
403 * @param mimeType the MIME type
404 * @return the CommandInfo objects representing all the commands.
405 */
406 public synchronized CommandInfo[] getAllCommands(String mimeType) {
407 List cmdList = new ArrayList();
408 if (mimeType != null)
409 mimeType = mimeType.toLowerCase(Locale.ENGLISH);
410
411 for (int i = 0; i < DB.length; i++) {
412 if (DB[i] == null)
413 continue;
414 Map cmdMap = DB[i].getMailcapList(mimeType);
415 if (cmdMap != null)
416 appendCmdsToList(cmdMap, cmdList);
417 }
418
419 // now add the fallback commands
420 for (int i = 0; i < DB.length; i++) {
421 if (DB[i] == null)
422 continue;
423 Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
424 if (cmdMap != null)
425 appendCmdsToList(cmdMap, cmdList);
426 }
427
428 CommandInfo[] cmdInfos = new CommandInfo[cmdList.size()];
429 cmdInfos = (CommandInfo[])cmdList.toArray(cmdInfos);
430
431 return cmdInfos;
432 }
433
434 /**
435 * Put the commands that are in the hash table, into the list.
436 */
437 private void appendCmdsToList(Map typeHash, List cmdList) {
438 Iterator verb_enum = typeHash.keySet().iterator();
439
440 while (verb_enum.hasNext()) {
441 String verb = (String)verb_enum.next();
442 List cmdList2 = (List)typeHash.get(verb);
443 Iterator cmd_enum = ((List)cmdList2).iterator();
444
445 while (cmd_enum.hasNext()) {
446 String cmd = (String)cmd_enum.next();
447 cmdList.add(new CommandInfo(verb, cmd));
448 // cmdList.add(0, new CommandInfo(verb, cmd));
449 }
450 }
451 }
452
453 /**
454 * Get the command corresponding to <code>cmdName</code> for the MIME type.
455 *
456 * @param mimeType the MIME type
457 * @param cmdName the command name
458 * @return the CommandInfo object corresponding to the command.
459 */
460 public synchronized CommandInfo getCommand(String mimeType,
461 String cmdName) {
462 if (mimeType != null)
463 mimeType = mimeType.toLowerCase(Locale.ENGLISH);
464
465 for (int i = 0; i < DB.length; i++) {
466 if (DB[i] == null)
467 continue;
468 Map cmdMap = DB[i].getMailcapList(mimeType);
469 if (cmdMap != null) {
470 // get the cmd list for the cmd
471 List v = (List)cmdMap.get(cmdName);
472 if (v != null) {
473 String cmdClassName = (String)v.get(0);
474
475 if (cmdClassName != null)
476 return new CommandInfo(cmdName, cmdClassName);
477 }
478 }
479 }
480
481 // now try the fallback list
482 for (int i = 0; i < DB.length; i++) {
483 if (DB[i] == null)
484 continue;
485 Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
486 if (cmdMap != null) {
487 // get the cmd list for the cmd
488 List v = (List)cmdMap.get(cmdName);
489 if (v != null) {
490 String cmdClassName = (String)v.get(0);
491
492 if (cmdClassName != null)
493 return new CommandInfo(cmdName, cmdClassName);
494 }
495 }
496 }
497 return null;
498 }
499
500 /**
501 * Add entries to the registry. Programmatically
502 * added entries are searched before other entries.<p>
503 *
504 * The string that is passed in should be in mailcap
505 * format.
506 *
507 * @param mail_cap a correctly formatted mailcap string
508 */
509 public synchronized void addMailcap(String mail_cap) {
510 // check to see if one exists
511 LogSupport.log("MailcapCommandMap: add to PROG");
512 if (DB[PROG] == null)
513 DB[PROG] = new MailcapFile();
514
515 DB[PROG].appendToMailcap(mail_cap);
516 }
517
518 /**
519 * Return the DataContentHandler for the specified MIME type.
520 *
521 * @param mimeType the MIME type
522 * @return the DataContentHandler
523 */
524 public synchronized DataContentHandler createDataContentHandler(
525 String mimeType) {
526 if (LogSupport.isLoggable())
527 LogSupport.log(
528 "MailcapCommandMap: createDataContentHandler for " + mimeType);
529 if (mimeType != null)
530 mimeType = mimeType.toLowerCase(Locale.ENGLISH);
531
532 for (int i = 0; i < DB.length; i++) {
533 if (DB[i] == null)
534 continue;
535 if (LogSupport.isLoggable())
536 LogSupport.log(" search DB #" + i);
537 Map cmdMap = DB[i].getMailcapList(mimeType);
538 if (cmdMap != null) {
539 List v = (List)cmdMap.get("content-handler");
540 if (v != null) {
541 String name = (String)v.get(0);
542 DataContentHandler dch = getDataContentHandler(name);
543 if (dch != null)
544 return dch;
545 }
546 }
547 }
548
549 // now try the fallback entries
550 for (int i = 0; i < DB.length; i++) {
551 if (DB[i] == null)
552 continue;
553 if (LogSupport.isLoggable())
554 LogSupport.log(" search fallback DB #" + i);
555 Map cmdMap = DB[i].getMailcapFallbackList(mimeType);
556 if (cmdMap != null) {
557 List v = (List)cmdMap.get("content-handler");
558 if (v != null) {
559 String name = (String)v.get(0);
560 DataContentHandler dch = getDataContentHandler(name);
561 if (dch != null)
562 return dch;
563 }
564 }
565 }
566 return null;
567 }
568
569 private DataContentHandler getDataContentHandler(String name) {
570 if (LogSupport.isLoggable())
571 LogSupport.log(" got content-handler");
572 if (LogSupport.isLoggable())
573 LogSupport.log(" class " + name);
574 try {
575 ClassLoader cld = null;
576 // First try the "application's" class loader.
577 cld = SecuritySupport.getContextClassLoader();
578 if (cld == null)
579 cld = this.getClass().getClassLoader();
580 Class cl = null;
581 try {
582 cl = cld.loadClass(name);
583 } catch (Exception ex) {
584 // if anything goes wrong, do it the old way
585 cl = Class.forName(name);
586 }
587 if (cl != null) // XXX - always true?
588 return (DataContentHandler)cl.newInstance();
589 } catch (IllegalAccessException e) {
590 if (LogSupport.isLoggable())
591 LogSupport.log("Can't load DCH " + name, e);
592 } catch (ClassNotFoundException e) {
593 if (LogSupport.isLoggable())
594 LogSupport.log("Can't load DCH " + name, e);
595 } catch (InstantiationException e) {
596 if (LogSupport.isLoggable())
597 LogSupport.log("Can't load DCH " + name, e);
598 }
599 return null;
600 }
601
602 /**
603 * Get all the MIME types known to this command map.
604 *
605 * @return array of MIME types as strings
606 * @since JAF 1.1
607 */
608 public synchronized String[] getMimeTypes() {
609 List mtList = new ArrayList();
610
611 for (int i = 0; i < DB.length; i++) {
612 if (DB[i] == null)
613 continue;
614 String[] ts = DB[i].getMimeTypes();
615 if (ts != null) {
616 for (int j = 0; j < ts.length; j++) {
617 // eliminate duplicates
618 if (!mtList.contains(ts[j]))
619 mtList.add(ts[j]);
620 }
621 }
622 }
623
624 String[] mts = new String[mtList.size()];
625 mts = (String[])mtList.toArray(mts);
626
627 return mts;
628 }
629
630 /**
631 * Get the native commands for the given MIME type.
632 * Returns an array of strings where each string is
633 * an entire mailcap file entry. The application
634 * will need to parse the entry to extract the actual
635 * command as well as any attributes it needs. See
636 * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>
637 * for details of the mailcap entry syntax. Only mailcap
638 * entries that specify a view command for the specified
639 * MIME type are returned.
640 *
641 * @return array of native command entries
642 * @since JAF 1.1
643 */
644 public synchronized String[] getNativeCommands(String mimeType) {
645 List cmdList = new ArrayList();
646 if (mimeType != null)
647 mimeType = mimeType.toLowerCase(Locale.ENGLISH);
648
649 for (int i = 0; i < DB.length; i++) {
650 if (DB[i] == null)
651 continue;
652 String[] cmds = DB[i].getNativeCommands(mimeType);
653 if (cmds != null) {
654 for (int j = 0; j < cmds.length; j++) {
655 // eliminate duplicates
656 if (!cmdList.contains(cmds[j]))
657 cmdList.add(cmds[j]);
658 }
659 }
660 }
661
662 String[] cmds = new String[cmdList.size()];
663 cmds = (String[])cmdList.toArray(cmds);
664
665 return cmds;
666 }
667
668 /**
669 * for debugging...
670 *
671 public static void main(String[] argv) throws Exception {
672 MailcapCommandMap map = new MailcapCommandMap();
673 CommandInfo[] cmdInfo;
674
675 cmdInfo = map.getPreferredCommands(argv[0]);
676 System.out.println("Preferred Commands:");
677 for (int i = 0; i < cmdInfo.length; i++)
678 System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
679 cmdInfo[i].getCommandClass() + "]");
680 cmdInfo = map.getAllCommands(argv[0]);
681 System.out.println();
682 System.out.println("All Commands:");
683 for (int i = 0; i < cmdInfo.length; i++)
684 System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
685 cmdInfo[i].getCommandClass() + "]");
686 DataContentHandler dch = map.createDataContentHandler(argv[0]);
687 if (dch != null)
688 System.out.println("DataContentHandler " +
689 dch.getClass().toString());
690 System.exit(0);
691 }
692 */
693 }

mercurial