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