1 /*
   2  * Copyright (c) 1997, 2015, 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             return (DataContentHandler) cl.newInstance();
 584         } catch (IllegalAccessException e) {
 585             if (LogSupport.isLoggable())
 586                 LogSupport.log("Can't load DCH " + name, e);
 587         } catch (ClassNotFoundException e) {
 588             if (LogSupport.isLoggable())
 589                 LogSupport.log("Can't load DCH " + name, e);
 590         } catch (InstantiationException e) {
 591             if (LogSupport.isLoggable())
 592                 LogSupport.log("Can't load DCH " + name, e);
 593         }
 594         return null;
 595     }
 596 
 597     /**
 598      * Get all the MIME types known to this command map.
 599      *
 600      * @return          array of MIME types as strings
 601      * @since   1.6, JAF 1.1
 602      */
 603     public synchronized String[] getMimeTypes() {
 604         List mtList = new ArrayList();
 605 
 606         for (int i = 0; i < DB.length; i++) {
 607             if (DB[i] == null)
 608                 continue;
 609             String[] ts = DB[i].getMimeTypes();
 610             if (ts != null) {
 611                 for (int j = 0; j < ts.length; j++) {
 612                     // eliminate duplicates
 613                     if (!mtList.contains(ts[j]))
 614                         mtList.add(ts[j]);
 615                 }
 616             }
 617         }
 618 
 619         String[] mts = new String[mtList.size()];
 620         mts = (String[])mtList.toArray(mts);
 621 
 622         return mts;
 623     }
 624 
 625     /**
 626      * Get the native commands for the given MIME type.
 627      * Returns an array of strings where each string is
 628      * an entire mailcap file entry.  The application
 629      * will need to parse the entry to extract the actual
 630      * command as well as any attributes it needs. See
 631      * <A HREF="http://www.ietf.org/rfc/rfc1524.txt">RFC 1524</A>
 632      * for details of the mailcap entry syntax.  Only mailcap
 633      * entries that specify a view command for the specified
 634      * MIME type are returned.
 635      *
 636      * @return          array of native command entries
 637      * @since   1.6, JAF 1.1
 638      */
 639     public synchronized String[] getNativeCommands(String mimeType) {
 640         List cmdList = new ArrayList();
 641         if (mimeType != null)
 642             mimeType = mimeType.toLowerCase(Locale.ENGLISH);
 643 
 644         for (int i = 0; i < DB.length; i++) {
 645             if (DB[i] == null)
 646                 continue;
 647             String[] cmds = DB[i].getNativeCommands(mimeType);
 648             if (cmds != null) {
 649                 for (int j = 0; j < cmds.length; j++) {
 650                     // eliminate duplicates
 651                     if (!cmdList.contains(cmds[j]))
 652                         cmdList.add(cmds[j]);
 653                 }
 654             }
 655         }
 656 
 657         String[] cmds = new String[cmdList.size()];
 658         cmds = (String[])cmdList.toArray(cmds);
 659 
 660         return cmds;
 661     }
 662 
 663     /**
 664      * for debugging...
 665      *
 666     public static void main(String[] argv) throws Exception {
 667         MailcapCommandMap map = new MailcapCommandMap();
 668         CommandInfo[] cmdInfo;
 669 
 670         cmdInfo = map.getPreferredCommands(argv[0]);
 671         System.out.println("Preferred Commands:");
 672         for (int i = 0; i < cmdInfo.length; i++)
 673             System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
 674                                             cmdInfo[i].getCommandClass() + "]");
 675         cmdInfo = map.getAllCommands(argv[0]);
 676         System.out.println();
 677         System.out.println("All Commands:");
 678         for (int i = 0; i < cmdInfo.length; i++)
 679             System.out.println("Command " + cmdInfo[i].getCommandName() + " [" +
 680                                             cmdInfo[i].getCommandClass() + "]");
 681         DataContentHandler dch = map.createDataContentHandler(argv[0]);
 682         if (dch != null)
 683             System.out.println("DataContentHandler " +
 684                                                 dch.getClass().toString());
 685         System.exit(0);
 686     }
 687     */
 688 }