///////////////////////////////////////////////////////////////////////////////
//                                                      
// JTOpen (IBM Toolbox for Java - OSS version)                              
//                                                                             
// Filename: ToolboxJarMaker.java
//                                                                             
// The source code contained herein is licensed under the IBM Public License   
// Version 1.0, which has been approved by the Open Source Initiative.         
// Copyright (C) 2006-2006 International Business Machines Corporation and     
// others. All rights reserved.                                                
//                                                                             
///////////////////////////////////////////////////////////////////////////////

package utilities;

import com.ibm.as400.access.CommandLineArguments;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.Arrays;


/**
Generates a smaller JAR or ZIP file from a shipped <i>IBM Toolbox for Java</i>
JAR or ZIP file, based on the user's requirements.
<p>
In addition, ToolboxJarMaker can also be used to:
<ul compact>
<li> <em>extract</em> desired files from a JAR or ZIP file; or
<li> <em>split</em> a JAR or ZIP file into smaller JAR or ZIP files.
</ul>
<p>
An ToolboxJarMaker object can be included in the user's program,
or ToolboxJarMaker can be run as a command line program, as follows:
<blockquote>
<pre>
<strong>java utilities.ToolboxJarMaker</strong> [ options ]
</pre>
</blockquote>
ToolboxJarMaker extends the functionality of JarMaker by
additionally allowing the user to specify one or more
<i>IBM Toolbox for Java</i> components,
languages, and/or CCSIDs, and whether or not to include
Java Beans files that are associated with the specified components.

<p>
<b><a name="asoptions">Options</a></b>

<p>
<dl>
<dt><b><code>-source </b></code><var>sourceJarFile</var>
<dd>
Specifies the source JAR or ZIP file from which to derive
the destination JAR or ZIP file.  The source file is expected to contain the IBM Toolbox for Java product, and is typically named <code>jt400.jar</code>.
To process a JAR or ZIP other than the Toolbox, use {@link JarMaker JarMaker}.
If a relative path is specified, the path is assumed to be relative to
the current directory.
If this option is specified as the first positional argument,
the tag (<code>-source</code>) is optional. 
The -source option may be abbreviated to -s.
The default is <code>jt400.jar</code>, in the current directory.

<p>
<dt><b><code>-destination </b></code><var>destinationJarFile</var>
<dd>
Specifies the destination JAR or ZIP file, which will contain
the desired subset of the files in the source JAR or ZIP file.
If a pathname is not specified, the file is created in the current directory.
The -destination option may be abbreviated to -d.
The default name is generated by appending <code>"Small"</code> to the
source file name.  For example, if the source file is <code>jt400.jar</code>,
then the default destination file would be <code>jt400Small.jar</code>.

<p>
<dt><b><code>-fileRequired </b></code><var>jarEntry1[,jarEntry2[...] ] </var>
<dd>
The files in the source JAR or ZIP file that are to be copied to the destination.
Entries are separated by commas (no spaces).
The specified files, along with all of their dependencies,
will be considered required.
Files are specified in JAR entry name syntax, such as <code>com/ibm/as400/access/DataQueue.class</code>.
The -fileRequired option may be abbreviated to -fr.

<p>
<dt><b><code>-fileExcluded </b></code><var>jarEntry1[,jarEntry2[...] ] </var>
<dd>
The files in the source JAR or ZIP file that are to be excluded from the destination,
and from dependency analysis.
Entries are separated by commas (no spaces).
Files are specified in JAR entry name syntax, such as <code>com/ibm/as400/access/DataQueue.class</code>.
The -fileExcluded option may be abbreviated to -fx.

<p>
<dt><b><code>-additionalFile </b></code><var>file1[,file2[...] ] </var>
<dd>
Specifies additional files (not included in the source jar) which are
to be copied to the destination.
Entries are separated by commas (no spaces).
Files are specified by either their
absolute path, or their path relative to the current directory.
<br>The specified files will be included, regardless of the settings of
other options.
The -additionalFile option may be abbreviated to -af.

<p>
<dt><b><code>-additionalFilesDirectory </b></code><var>baseDirectory</var>
<dd>
Specifies the base directory for additional files.
This should be the parent directory of the
directory where the package path starts.  For example, if file <code>foo.class</code>
in package <code>com.ibm.mypackage</code> is located in directory
<code>C:\dir1\subdir2\com\ibm\mypackage\</code>, then specify base directory <code>C:\dir1\subdir2</code>.
<br>The -additionalFilesDirectory option may be abbreviated to -afd.
The default is the current directory.

<p>
<dt><b><code>-package </b></code><var>package1[,package2[...] ] </var>
<dd>
The packages that are required.
Entries are separated by commas (no spaces).
The -package option may be abbreviated to -p.
Package names are specified in standard syntax, such as <code>com.ibm.component</code>.
<br>Note: The specified packages are simply included in the output.
No additional dependency analysis is done on the files in a package,
unless they are explicitly specified as required files.

<p>
<dt><b><code>-packageExcluded </b></code><var>package1[,package2[...] ] </var>
<dd>
The packages that are to be excluded.
Entries are separated by commas (no spaces).
The -packageExcluded option may be abbreviated to -px.
Package names are specified in standard syntax, such as <code>com.ibm.component</code>.

<p>
<dt><b><code>-extract </b></code><var>[baseDirectory]</var>
<dd>
Extracts the desired entries of the source JAR into the specified base directory,
without generating a new JAR or ZIP file.  This option enables the
user to build up a customized JAR or ZIP file empirically,
based on the requirements of their particular application.
When this option is specified, <code>-additionalFile</code>,
<code>-additionalFilesDirectory</code>, and <code>-destination</code>
are ignored.
The -extract option may be abbreviated to -x.
By default, no extraction is done.
The default base directory is the current directory.

<p>
<dt><b><code>-split </b></code><var>[splitSize]</var>
<dd>
Splits the source JAR or ZIP file into smaller JAR or ZIP files.
No ZIP entries are added or removed;
the entries in the source JAR or ZIP file are simply distributed
among the destination JAR or ZIP files.
The split size is in units of kilobytes (1024 bytes),
and specifies the maximum size for the destination files.
The destination files are created in the current directory,
and are named by appending integers to the source file name;
any existing files by the same name are overwritten.
For example, if the source JAR file is <code>myfile.jar</code>,
then the destination JAR files would be
<code>myfile0.jar</code>, <code>myfile1.jar</code>, and so on.
When this option is specified, all other options except
<code>-source</code> and <code>-verbose</code> are ignored.
The -split option may be abbreviated to -sp.
The default split size is 2 megabytes (2048 kilobytes).

<p>
<dt><b><code>-component </b></code><var>componentID1[,componentID2[...] ] </var>
<dd>
The <i>IBM Toolbox for Java</i> components that are required.
Entries are separated by commas (no spaces), and are case insensitive.
The -component option may be abbreviated to -c.
<br>
See the list of <a href="doc-files/componentList.html">components</a>
that are supported by <i>IBM Toolbox for Java</i>.

<p>
<dt><b><code>-beans </b></code>
<dd>
Causes inclusion of all Java Beans files (classes, gifs) that are
directly associated with the specified components.
This option is valid only if <code>-component</code> is also specified.
The -beans option may be abbreviated to -b.
The default is no Beans.

<p>
<dt><b><code>-language </b></code><var>language1[,language2[...]]</var>
<dd>
Specifies the desired languages for the messages produced by the Toolbox classes.
Entries are separated by commas (no spaces).
The languages are identified by their Java locale name, such as fr_CA
(for Canadian French).
<br>Note: The shipped jt400.jar file contains only English messages.
The -language option may be abbreviated to -l.
By default, only English messages are included.

<p>
<dt><b><code>-languageDirectory </b></code><var>baseDirectory</var>
<dd>
Specifies the base directory for additional Toolbox language files.
The path below this directory should correspond to the package name
the language files.
For example, if the desired MRI files are located in directories
<code>/usr/myDir/com/ibm/as400/access/</code> and
<code>/usr/myDir/com/ibm/as400/vaccess/</code>,
then the base directory should be set to <code>/usr/myDir</code>.
<br>The -languageDirectory option may be abbreviated to -ld.
By default, language files are searched for relative to the
current directory.

<p>
<dt><b><code>-ccsid </b></code><var>ccsid1[,ccsid2[...]]</var>
<dd>
Specifies the CCSIDs whose conversion tables should be included.
Conversion tables for other CCSIDs will not be included.
Entries are separated by commas (no spaces).
The -ccsid option may be abbreviated to -cc.
By default, all CCSIDs are included.
See the list of <a href="doc-files/ccsidList.html">CCSIDs and encodings</a>
that are specifically supported by <i>IBM Toolbox for Java</i>.

<p>
<dt><b><code>-ccsidExcluded </b></code><var>ccsid1[,ccsid2[...]]</var>
<dd>
Specifies the CCSIDs whose conversion tables should <em>not</em> be included.
Entries are separated by commas (no spaces).  If a CCSID is specified on
both the <code>-ccsid</code> and <code>-ccsidExcluded</code>, it is included,
and a warning message is sent to <code>System.err</code>.
The -ccsidExcluded option may be abbreviated to -cx or -ccx.
By default, all CCSIDs are included.
See the list of <a href="doc-files/ccsidList.html">CCSIDs and encodings</a>
that are specifically supported by <i>IBM Toolbox for Java</i>.

<p>
<dt><b><code>-excludeSomeDependencies </b></code>
<dd>
Indicates that ToolboxJarMaker is allowed to selectively limit
dependency expansion, and exclude certain classes, components and packages
that are unlikely to be needed.
By default, this option is not in effect; that is, all directly- and
indirectly-referenced files in the source JAR file are included.

<p>
<dt><b><code>-noProxy </b></code>
<dd>
Specifies that proxy-related class files should <em>not</em> be included.
Proxy files are used internally by the Toolbox's Proxy Service.
If the Proxy Service will not be used, the proxy files are not needed.
The -noProxy option may be abbreviated to -np.
By default, proxy files in the source JAR file are included.

<p>
<dt><b><code>-verbose </b></code>
<dd>
Causes progress messages to be displayed.
The -verbose option may be abbreviated to -v.
The default is non-verbose.

<p>
<dt><b><code>-help </b></code>
<dd>
Displays the help text.
The -help option may be abbreviated to -h.
The default is non-verbose.

</dl>

<p>
At least one of the following options must be specified:
<ul compact>
<li>-fileRequired
<li>-fileExcluded
<li>-additionalFile
<li>-package
<li>-packageExcluded
<li>-extract
<li>-split
<li>-component
<li>-language
<li>-ccsid
<li>-ccsidExcluded
<li>-noProxy
<li>-excludeSomeDependencies
</ul>

<p>
If the following options are specified multiple times in a single command
string, only the final specification applies:
<ul compact>
<li>-source
<li>-destination
<li>-additionalFilesDirectory
<li>-extract
<li>-split
<li>-languageDirectory
</ul>
Other options have a cumulative effect when specified multiple times
in a single command string.

<p>
<b><a name="exam">Example usage</a></b>
<p>
<p>
To create a JAR file that contains only those files needed
by the <code>CommandCall</code> and <code>ProgramCall</code> components,
do the following:
<ol>
<li>Go to the directory where the <code>jt400.jar</code> file is located.
<li>Type:
<pre>
java utilities.ToolboxJarMaker -component CommandCall,ProgramCall
</pre>
<li>Save a copy of the original <code>jt400.jar</code> for future reference.
<li>Rename <code>jt400Small.jar</code> to <code>jt400.jar</code>.
</ol>
<p>
To extract from jt400.jar only those files needed
by class <code>com.ibm.as400.access.JDSQLStatement</code>,
using the current directory as the base directory for output, do the following:
<pre>
java utilities.ToolboxJarMaker -extract -s jt400.jar
     -fr com/ibm/as400/access/JDSQLStatement.class
</pre>
Any additional files could then be manually copied into appropriate locations
in the directory tree, and a customized JAR file could be generated using the
<code>jar</code> tool provided as part of the JDK.

</em>
**/

// Design notes
/*
Assumptions:

This tool will be used only by programmers
and administrators for whom English messages are acceptable.
Hence, no translated MRI is provided with this tool.

This tool will be used on JDK 1.1 or later.
When we move to JDK 1.2, the implementation of this tool can take
advantage of the utilities in the new java.util.jar package.

The manifest will be altered based on the list of functions the user specifies.

We need to generate and put our own Javadoc for this tool.  The build process does
not run Javadoc twice because the 'extra' output files (index, table of contents, ...)
would get overlayed.  It is easier to run javadoc locally then just put the output file.
(As an alternative in this release, we could ask the builders if they could generate
this javadoc at the same time as our access and vaccess stuff.)

We are going to include only run-time gifs.  Compile time gifs needed by visual builders
will not be put in the JAR unless explicitly specified.
*/

// * On-going TBDs:
// * Implementation:   Add new components as they are added to Toolbox.
//
// * Implementation:   Add new language MRI suffixes as they are supported.
//
// * Implementation:  When we move to JDK 1.2, 
//                    we can take advantage of the java.util.jar support.
//

public class ToolboxJarMaker extends JarMaker
{
  // Constants.

  // Component names that can be specified on the -component option.
  static final String[] VALID_COMPONENTS = {
    "AS400", "CommandCall", "ConnectionPool",
    "DataArea", "DataDescription", "DataQueue", "DigitalCertificate",
    "FTP", "IntegratedFileSystem", "JAAS",
    "JavaApplicationCall",
    "JDBC", "Job", "Message",
    "NetServer", "NumericDataTypes",
    "PCML", "Print", "ProgramCall", "RecordLevelAccess", "SecureAS400",
    "ServiceProgramCall", "SystemStatus", "SystemValue",
    "Trace", "User", "UserSpace",
    "AS400Visual", "CommandCallVisual", "DataQueueVisual",
    "IntegratedFileSystemVisual", "JavaApplicationCallVisual",
    "JDBCVisual", "JobVisual",
    "MessageVisual", "PrintVisual", "ProgramCallVisual",
    "RecordLevelAccessVisual", "UserVisual"
    };

  // Component abbreviations can be specified on the -component option.
  static final String[] VALID_COMPONENT_ABBREVS = {
    "AS400", "CmdCall", "ConnPool",
    "DA", "DD", "DQ", "DigCert",
    "FTP", "IFS", "JAAS",
    "JavaApp",
    "JDBC", "Job", "Msg",
    "NetS", "DataType",
    "PCML", "Prt", "PgmCall", "RLA", "Secure",
    "SvcPgmCall", "SysStat", "SysVal",
    "Trace", "User", "UserSpc",
    "AS400V", "CmdCallV", "DQV",
    "IFSV", "JavaAppV",
    "JDBCV", "JobV",
    "MsgV", "PrtV", "PgmCallV",
    "RLAV", "UserV",
    };
  // Note: The following list must be kept in sync with VALID_COMPONENTS.

  /** Specifies the <b>AS400</b> component. **/
  public static final Integer AS400 = new Integer(0);
  /** Specifies the <b>Command Call</b> component. **/
  public static final Integer COMMAND_CALL = new Integer(1);
  /** Specifies the <b>Connection Pool</b> component. **/
  public static final Integer CONNECTION_POOL = new Integer(2);
  /** Specifies the <b>Data Area</b> component. **/
  public static final Integer DATA_AREA = new Integer(3);
  /** Specifies the <b>Data Description</b> component. **/
  public static final Integer DATA_DESCRIPTION = new Integer(4);
  /** Specifies the <b>Data Queue</b> component. **/
  public static final Integer DATA_QUEUE = new Integer(5);
  /** Specifies the <b>Digital Certificate</b> component. **/
  public static final Integer DIGITAL_CERTIFICATE = new Integer(6);
  /** Specifies the <b>FTP</b> component. **/
  public static final Integer FTP = new Integer(7);
  /** Specifies the <b>Integrated File System</b> component. **/
  public static final Integer INTEGRATED_FILE_SYSTEM = new Integer(8);
  /** Specifies the <b>JAAS</b> component. **/
  public static final Integer JAAS = new Integer(9);
  /** Specifies the <b>Java Application Call</b> component. **/
  public static final Integer JAVA_APPLICATION_CALL = new Integer(10);
  /** Specifies the <b>JDBC</b> component. **/
  public static final Integer JDBC = new Integer(11);
  /** Specifies the <b>Job</b> component. **/
  public static final Integer JOB = new Integer(12);
  /** Specifies the <b>Message Queue</b> component. **/
  public static final Integer MESSAGE = new Integer(13);
  /** Specifies the <b>NetServer</b> component. **/
  public static final Integer NETSERVER = new Integer(14);
  /** Specifies the <b>Numeric Data Types</b> component. **/
  public static final Integer NUMERIC_DATA_TYPES = new Integer(15);
  /** Specifies the <b>PCML</b> component. **/
  public static final Integer PCML = new Integer(16);
  /** Specifies the <b>Network Print</b> component. **/
  public static final Integer PRINT = new Integer(17);
  /** Specifies the <b>Program Call</b> component. **/
  public static final Integer PROGRAM_CALL = new Integer(18);
  /** Specifies the <b>Record Level Access</b> component. **/
  public static final Integer RECORD_LEVEL_ACCESS = new Integer(19);
  /** Specifies the <b>Secure AS400</b> component.
   This component performs SSL (Secure Sockets Layer) processing. **/
  public static final Integer SECURE_AS400 = new Integer(20);
  /** Specifies the <b>Service Program Call</b> component. **/
  public static final Integer SERVICE_PROGRAM_CALL = new Integer(21);
  /** Specifies the <b>System Status</b> component. **/
  public static final Integer SYSTEM_STATUS = new Integer(22);
  /** Specifies the <b>System Value</b> component. **/
  public static final Integer SYSTEM_VALUE = new Integer(23);
  /** Specifies the <b>Trace</b> component. **/
  public static final Integer TRACE = new Integer(24);
  /** Specifies the <b>User</b> component. **/
  public static final Integer USER = new Integer(25);
  /** Specifies the <b>User Space</b> component. **/
  public static final Integer USER_SPACE = new Integer(26);
  /** Specifies the <b>Visual AS400</b> component. **/
  public static final Integer AS400_VISUAL = new Integer(27);
  /** Specifies the <b>Visual Command Call</b> component. **/
  public static final Integer COMMAND_CALL_VISUAL = new Integer(28);
  /** Specifies the <b>Visual Data Queue</b> component. **/
  public static final Integer DATA_QUEUE_VISUAL = new Integer(29);
  /** Specifies the <b>Visual Integrated File System </b> component. **/
  public static final Integer INTEGRATED_FILE_SYSTEM_VISUAL = new Integer(30);
  /** Specifies the <b>Visual Java Application Call</b> component. **/
  public static final Integer JAVA_APPLICATION_CALL_VISUAL = new Integer(31);
  /** Specifies the <b>Visual JDBC</b> component. **/
  public static final Integer JDBC_VISUAL = new Integer(32);  // and Visual SQL
  /** Specifies the <b>Visual Job</b> component. **/
  public static final Integer JOB_VISUAL = new Integer(33);
  /** Specifies the <b>Visual Message Queue</b> component. **/
  public static final Integer MESSAGE_VISUAL = new Integer(34);
  /** Specifies the <b>Visual Network Print</b> component. **/
  public static final Integer PRINT_VISUAL = new Integer(35);
  /** Specifies the <b>Visual Program Call</b> component. **/
  public static final Integer PROGRAM_CALL_VISUAL = new Integer(36);
  /** Specifies the <b>Visual Record Level Access</b> component. **/
  public static final Integer RECORD_LEVEL_ACCESS_VISUAL = new Integer(37);
  /** Specifies the <b>Visual User</b> component. **/
  public static final Integer USER_VISUAL = new Integer(38);


  private static final String CIAA  = "com/ibm/as400/access/";  // for convenience
  private static final String CIAD  = "com/ibm/as400/data/";
  private static final String CIAV  = "com/ibm/as400/vaccess/";
  private static final String CIASA = "com/ibm/as400/security/auth/";

  private static final String COPYRIGHT_ENTRY_NAME = CIAA + "Copyright.class";

  static final String DEFAULT_SOURCE_JAR_NAME = "jt400.jar";

  // The components specified by the user (Integer's).
  private Vector components_ = new Vector();  // Never null.

  // The languages (locales) specified by the user (String's).
  private Vector languages_ = new Vector();  // Never null.

  // Directory where language files (if languages are specified).
  // are to be searched for.  Default is the current directory.
  private File languageDirectory_ = new File(System.getProperty("user.dir"));

  // The CCSIDs specified by the user (Integer's).
  private Vector ccsids_= new Vector();  // Never null.
  // The excluded CCSIDs specified by the user (Integer's).
  private Vector ccsidsExcluded_= new Vector();  // Never null.

  private boolean noProxy_;  // If true, exclude proxy files.

  private boolean excludeResource_; // exclude resource pkg
  private boolean excludeJDBC_;     // exclude JDBC classes
  private boolean excludeRLA_;      // exclude RLA classes
  private boolean excludeSysVal_;   // exclude SystemValue classes
  private boolean excludeDirEntryClass_; // exclude DirectoryEntry* classes
  private boolean excludeSecAuth_; // exclude security.auth dependencies
  private boolean excludeValidationList_; // exclude ValidationList classes
  private boolean excludePathOptimizer_;  // exclude AS400ClassPathOptimizer class
  private boolean excludeBidi_;  // exclude some BIDI classes

  // Invocation arguments.  This is used only when ToolboxJarMaker
  // is invoked from the command line.
  private Arguments arguments_ = new Arguments(); // class defined in JarMaker


  /**
   Constructs an ToolboxJarMaker object.
   **/
  public ToolboxJarMaker()
  {
    super("com/ibm/as400"); // Default entry-name prefix.
    File defaultSourceJar = new File(DEFAULT_SOURCE_JAR_NAME);
    setSourceJar(defaultSourceJar);
  }


  /**
   Adds MRI files to the additional files list.
   <br>Note: This augments any previously specified language files.

   @param baseDirectory The base directory for the language files.
   @param neededJarEntries The current list of required files
   and their dependencies.
   The list should contain only <code>String</code> objects.
   @param jarMap A map of the source JAR or ZIP file.
   **/
  private void addLanguageFiles(File baseDirectory,
                                 Vector neededJarEntries, JarMap jarMap)
  {
    // Note: If any of the arguments is null, it is due to an
    // internal programming error.
    if (baseDirectory == null)
      throw new NullPointerException("baseDirectory");
    if (neededJarEntries == null)
      throw new NullPointerException("neededJarEntries");
    if (jarMap == null)
      throw new NullPointerException("jarMap");

    // Get a list of the MRI files in the source jar.
    // The list should look something like:
    //    com/ibm/as400/access/MRI.properties,
    //    com/ibm/as400/access/JDMRI.properties, ...
    // (Note: Beginning with V4R4, MRI's are "*.class"
    //        rather than "*.properties".  This design handles that.)

    // Make a list of the MRI files in the "needed JAR entries" list.
    Vector neededLanguageEntries = new Vector();  // String's
    Enumeration e = neededJarEntries.elements();
    while (e.hasMoreElements())
    {
      String entryName = (String)e.nextElement();
      // See if the filename has "MRI" in it.
      if (entryName.lastIndexOf("MRI") > entryName.lastIndexOf('/'))
        neededLanguageEntries.addElement(entryName);
    }

    if (languages_.size() != 0)
    {
      // Add the language-specific *MRI_xx_XX.[properties|class] files to
      // the "additional files" list.
      Vector filesToAdd = new Vector();
      Enumeration e1 = languages_.elements();
      while (e1.hasMoreElements())
      {
        String languageSuffix = (String)e1.nextElement();
        Enumeration e2 = neededLanguageEntries.elements();
        while (e2.hasMoreElements())
        {
          String entryName = (String)e2.nextElement();
          // Insert the language ID after the "MRI".
          String newEntryName = addMriSuffix(entryName, languageSuffix);
          String filePath = generateFilePath(languageDirectory_, newEntryName);
          if (DEBUG) System.out.println("Debug: Adding MRI file " + filePath);
          filesToAdd.addElement(new File(filePath));
        }
      }

      // Add the list of language files to the additional files list.
      setAdditionalFiles(filesToAdd, baseDirectory);
    }
    else // User specified no specific languages.
    {
      // Go through the list of referenced MRI files,
      // and mark as "needed" the corresponding _en and _en_US
      // entries if they exist.
      Enumeration e1 = neededLanguageEntries.elements();
      while (e1.hasMoreElements())
      {
        String entryName = (String)e1.nextElement();
        String entryName_en = addMriSuffix(entryName, "en");
        String entryName_en_US = addMriSuffix(entryName, "en_US");
        if (DEBUG) {
          System.out.println("Debug: entryName_en    = " + entryName_en);
          System.out.println("       entryName_en_US = " + entryName_en_US);
        }
        if (jarMap.contains(entryName_en) &&
            !neededJarEntries.contains(entryName_en))
          neededJarEntries.addElement(entryName_en);
        if (jarMap.contains(entryName_en_US) &&
            !neededJarEntries.contains(entryName_en_US))
          neededJarEntries.addElement(entryName_en_US);
      }
    }
  }

  /**
   Constructs a new ZIP entry name by inserting the specified
   language suffix into the specified entry name.
   @param entryName The ZIP entry name into which to insert the suffix.
   @param languageSuffix The language suffix to insert.  For example, "en_US".
   @return The constructed entry name, with suffix inserted.
   **/
  private static String addMriSuffix(String entryName, String languageSuffix)
  {
    // Insert the language ID into the entry name after the "MRI".
    // Note that MRI entries are named "com/ibm/as400/xxx/xxxMRIxxx.class".
    int insertionPosition = entryName.lastIndexOf('.');
    // If no '.' found, then simply append suffix to end.
    if (insertionPosition == -1) insertionPosition = entryName.length();
    StringBuffer buf = new StringBuffer(entryName);
    buf.insert(insertionPosition, "_"+languageSuffix);
    return buf.toString();
  }


  /**
   Adds any needed PCML files to the additional files list.
   <br>Note: This augments any previously specified PCML files.

   @param neededJarEntries The current list of required files
   and their dependencies.
   The list should contain only <code>String</code> objects.
   @param jarMap A map of the source JAR or ZIP file.
   **/
  private static void addPcmlFiles(Vector neededJarEntries, JarMap jarMap)
  {
    // Note: If any of the arguments is null, it is due to an
    // internal programming error.
    if (neededJarEntries == null)
      throw new NullPointerException("neededJarEntries");
    if (jarMap == null)
      throw new NullPointerException("jarMap");

    // Get a list of the PCML files in the source jar.
    // The list should look something like:
    //    com/ibm/as400/resource/RJob.pcml,
    //    com/ibm/as400/resource/RJob.pcml.ser, ...
    // That is, look for any files with names ending ".pcml" or ".pcml.ser".

    // Make a list of the PCML files in the source jar file.
    Enumeration e = jarMap.elements();
    Vector neededPcmlEntries = new Vector();
    while (e.hasMoreElements())
    {
      String entryName = (String)e.nextElement();
      // See if the filename ends with ".pcml" or ".pcml.ser".
      if (entryName.endsWith(".pcml") || entryName.endsWith(".pcml.ser"))
        neededPcmlEntries.addElement(entryName);
    }

    // Go through the list of PCML files contained in the source jar file,
    // and mark as "needed" any that have a corresponding class file in the
    // "needed files" list.
    Enumeration e1 = neededPcmlEntries.elements();
    while (e1.hasMoreElements())
    {
      String pcmlEntryName = (String)e1.nextElement();
      // Get the associated class file entry name.
      String classEntryName = getClassEntryForPcml(pcmlEntryName);
      if (neededJarEntries.contains(classEntryName) &&
          !neededJarEntries.contains(pcmlEntryName))
        neededJarEntries.addElement(pcmlEntryName);
    }
  }


  /**
   Adds or removes ZIP entry names from the "required files" list,
   prior to initial generation of the dependencies list.
   This method is provided so that subclasses of JarMaker can override it.
   <br><em>This method is meant to be called by the JarMaker class only.</em>

   @param neededJarEntries An unsorted list of names of ZIP entries
   that should be included in the output.
   @param jarMap A map of the source JAR or ZIP file.
   @return The modified list of ZIP entry names
   (must never be <code>null</code>.
   This should be a Vector of Strings.
   @exception IOException If an error occurs when reading the source file.
   **/
  Vector adjustDependencies1(Vector neededJarEntries, JarMap jarMap)
    throws IOException
  {
    // Add selected ConvTableXXX files, per -ccsid list.
    if (ccsids_.size() == 0)
    { // User specified no particular ccsids.
      // We will add *all* of the ccsids (if appropriate) in adjustDependencies2().
    }
    else // User specified one or more particular ccsids.
    {
      // If the IFS component was specified, be sure to include
      // the "old Unicode" ccsid.  @A0a
      if (components_.contains(INTEGRATED_FILE_SYSTEM))
        addElement(ccsids_, new Integer(61952));

      // In all cases, be sure to include the "new Unicode" ccsid.  @A2a
      addElement(ccsids_, new Integer(13488));

      // Add the ConvTableXXX's for the selected CCSIDs.
      Enumeration e3 = ccsids_.elements();
      while (e3.hasMoreElements())
      {
        Integer ccsid = (Integer)e3.nextElement();
        String entry = CIAA + "ConvTable" + ccsid.toString() +
          CLASS_SUFFIX;
        if (!neededJarEntries.contains(entry))
        {
          if (jarMap.contains(entry))
          {
            neededJarEntries.addElement(entry);
          }
          else
            System.err.println("Warning: No entry in source file for " +
                                "CCSID " + ccsid.toString() + ".");
        }
      }

    }

    if (noProxy_)
    {
      addElement(dependenciesToExclude_, "com/ibm/as400/access/ProxyClientConnection.class");
      addElement(dependenciesToExclude_, "com/ibm/as400/access/ProxyReturnValue.class");
      if (verbose_ || DEBUG) {
        System.out.println("Excluding Proxy* class dependencies.");
      }
    }

    // See if user gave the OK to selectively limit dependency expansion.  @A4a
    if (excludeSomeDependencies_)
    {
      // Note: As of V5R2, AS400ThreadedServer references DBReplyRequestedDS.
      // We can't just exclude DBReplyRequestedDS, nor can we exclude its
      // direct dependents (DBBaseReplyDS and DBDSPool), but we *can* exclude
      // the files that *those* classes depend on.                      @A4a

      // If neither JDBC nor Visual JDBC is on the components list,
      // and DBReplyRequestedDS isn't on the required files list,
      // then exclude a couple of JDBC files.                           @A4a
      boolean accessPackageWasSpecified = getPackages().contains("com.ibm.as400.access");
      if (!accessPackageWasSpecified &&
          !(components_.isEmpty()) &&
          !(components_.contains(JDBC)) &&
          !(components_.contains(JDBC_VISUAL)))
      {
        excludeJDBC_ = true;
        if (verbose_ || DEBUG) {
          System.out.println("Excluding JDBC classes.");
        }
        if (!getFilesRequired().contains("com/ibm/as400/access/DBBaseReplyDS.class"))
        {
          addElement(dependenciesToExclude_, "com/ibm/as400/access/DBBaseReplyDS.class");
          if (verbose_ || DEBUG) {
            System.out.println("Excluding dependency: " + "com/ibm/as400/access/DBBaseReplyDS.class");
          }

          if (!getFilesRequired().contains("com/ibm/as400/access/DBDSPool.class"))
          {
            addElement(dependenciesToExclude_, "com/ibm/as400/access/DBDSPool.class");
            if (verbose_ || DEBUG) {
              System.out.println("Excluding dependency: " + "com/ibm/as400/access/DBDSPool.class");
            }
          }
        }
      }

      // Note: In V5R1, we added a getJob() method to CommandCall and ProgramCall,
      // that returns an RJob object.  Unfortunately this introduced a new
      // dependency on the 'resource' package.
      // In V5R2 we added method getServerJob() that returns a Job object,
      // and we intend to deprecate getJob().                           @A4a

      // Note: If we're dealing with a pre-V5R2 jt400.jar file, the user will need to specify RJob as a "required file" if they want to use it.
      // Note: The getJob() method was eliminated from CommandCall and ProgramCall in JTOpen 6.5.

      // If the 'resource' package isn't on the packages list,
      // and neither RJob nor JobLog is on the required files list,
      // and neither Job nor Visual Job is on the components list,
      // then exclude the 'resource' package.
      if (!(getPackages().contains("com.ibm.as400.resource")) &&
          !(getFilesRequired().contains("com/ibm/as400/resource/RJob.class")) &&
          !(getFilesRequired().contains("com/ibm/as400/access/JobLog.class")) &&
          !(components_.isEmpty()) &&
          !(components_.contains(JOB)) &&
          !(components_.contains(JOB_VISUAL)))
      {
        excludeResource_ = true;
        addElement(dependenciesToExclude_, "com/ibm/as400/resource/RJob.class");
        if (verbose_ || DEBUG) {
          System.out.println("Excluding package com.ibm.as400.resource");
        }
      }

      // If RecordLevelAccess and RecordLevelAccessVisual aren't on the components list,
      // and the required files list contains no file named com/ibm/as400/access/DDM*,
      // then exclude DDM* classes.
      if (!accessPackageWasSpecified &&
          !(containsPattern(getFilesRequired(), "com/ibm/as400/access/DDM")) &&
          !(containsPattern(getFilesRequired(), "com/ibm/as400/access/Record")) &&
          !(components_.isEmpty()) &&
          !(components_.contains(RECORD_LEVEL_ACCESS)) &&
          !(components_.contains(RECORD_LEVEL_ACCESS_VISUAL)) &&
          !(components_.contains(DATA_DESCRIPTION)))
      {
        excludeRLA_ = true;
        addElement(dependenciesToExclude_, "com/ibm/as400/access/Record.class");
        addElement(dependenciesToExclude_, "com/ibm/as400/access/CharacterFieldDescription.class");
        if (verbose_ || DEBUG) {
          System.out.println("Excluding Record Level Access classes.");
        }
      }

      if (excludeJDBC_)
      {
        // referenced by AS400BidiTransform:
        addElement(dependenciesToExclude_, "com/ibm/as400/access/AS400JDBCConnection.class");
        addElement(dependenciesToExclude_, "com/ibm/as400/access/JDProperties.class");
        if (verbose_ || DEBUG) {
          System.out.println("Excluding dependencies: " + "com/ibm/as400/access/AS400JDBCConnection.class, " +
              "com/ibm/as400/access/JDProperties.class");
        }
      }

      if (excludeJDBC_ && excludeRLA_)
      {
        addElement(dependenciesToExclude_, "com/ibm/as400/access/ClassDecoupler.class");
        if (verbose_ || DEBUG) {
          System.out.println("Excluding dependency: " + "com/ibm/as400/access/ClassDecoupler.class");
        }
      }

      // If SystemValue isn't on the components list,
      // and the required files list contains no file named com/ibm/as400/access/SystemValue*,
      // then exclude SV* classes.
      if (!accessPackageWasSpecified &&
          !(containsPattern(getFilesRequired(), "com/ibm/as400/access/SystemValue")) &&
          !(containsPattern(getFilesRequired(), "com/ibm/as400/access/SV")) &&
          !(components_.isEmpty()) &&
          !(components_.contains(SYSTEM_VALUE)))
      {
        excludeSysVal_ = true;
        if (verbose_ || DEBUG) {
          System.out.println("Excluding System Value classes.");
        }
      }


      // If the required files list doesn't contain com.ibm.as400.security.auth.ProfileTokenCredential,
      // then exclude that class.
      if (!(getPackages().contains("com.ibm.as400.security.auth")) &&
          !(getFilesRequired().contains("com/ibm/as400/security/auth/ProfileTokenCredential.class")))
      {
        excludeSecAuth_ = true;
        addElement(dependenciesToExclude_, "com/ibm/as400/security/auth/ProfileTokenCredential.class");
        addElement(dependenciesToExclude_, "com/ibm/as400/security/auth/AS400Credential.class");
        if (verbose_ || DEBUG) {
          System.out.println("Excluding ProfileTokenCredential dependencies.");
        }
      }


      // If User and UserVisual aren't on the components list,
      // and the required files list contains no file named com/ibm/as400/access/Directory*,
      // then exclude Directory* classes.
      if (!accessPackageWasSpecified &&
          !(containsPattern(getFilesRequired(), "com/ibm/as400/access/Directory")) &&
          !(components_.isEmpty()) &&
          !(components_.contains(USER)) &&
          !(components_.contains(USER_VISUAL)))
      {
        excludeDirEntryClass_ = true;
        if (verbose_ || DEBUG) {
          System.out.println("Excluding DirectoryEntry classes.");
        }
      }

      // If components list doesn't include Job or Visual Job,
      // and Job.class isn't on the required files list,
      // then exclude Job.class from dependency analysis.
      if (!accessPackageWasSpecified &&
          !(components_.isEmpty()) &&
          !(components_.contains(JOB)) &&
          !(components_.contains(JOB_VISUAL)))
      {
        if (verbose_ || DEBUG) {
          System.out.println("Excluding Job classes from dependency analysis.");
        }
        if (!getFilesRequired().contains("com/ibm/as400/access/Job.class"))
        {
          addElement(dependenciesToExclude_, "com/ibm/as400/access/Job.class");
          if (verbose_ || DEBUG) {
            System.out.println("Excluding dependency: " + "com/ibm/as400/access/Job.class");
          }
        }
      }


      // If the required files list contains no file named com/ibm/as400/access/ValidationList*,
      // then exclude ValidationList* classes.
      if (!accessPackageWasSpecified &&
          !(containsPattern(getFilesRequired(), "com/ibm/as400/access/ValidationList")))
      {
        excludeValidationList_ = true;
        addElement(dependenciesToExclude_, "com/ibm/as400/access/ValidationList.class");
        if (verbose_ || DEBUG) {
          System.out.println("Excluding ValidationList classes.");
        }
      }


      // If the required files list doesn't contain com/ibm/as400/util/AS400ClassPathOptimizer,
      // then exclude it.
      if (!(getPackages().contains("com.ibm.as400.util")) &&
          !(getFilesRequired().contains("com/ibm/as400/util/AS400ClassPathOptimizer.class")))
      {
        excludePathOptimizer_ = true;
        // This class is small, no need to add it to the exclusions list.
        if (verbose_ || DEBUG) {
          System.out.println("Excluding AS400ClassPathOptimizer.");
        }
      }

      // If the specified languages don't include 'he' and 'ar', and no BIDI CCSID was specified, exclude some BIDI classes.
      if (isOkToExcludeBidi(components_, languages_, ccsids_))
      {
        excludeBidi_ = true;
      }

    }  // if excludeSomeDependencies_

    return neededJarEntries;
  }

  private final static boolean isOkToExcludeBidi(Vector components, Vector languages, Vector ccsidList)
  {
    // If caller indicated no specific language or CCSID, assume they want all languages and CCSIDs.
    if (languages.isEmpty() && ccsidList.isEmpty()) return false;

    // If caller specified either Arabic nor Hebrew, we'll definitely need Bidi classes.
    if (languages.contains("ar") || 
        languages.contains("he"))
      return false;

    // Caller specified a language, neither Arabic nor Hebrew.
    // If no bidi CCSID was specified, then exclude *Bidi* and Arabic* classes.

    if (ccsidList.isEmpty()) return true;

    // Convert CCSIDs vector to an array of int, for quick searching.
    int[] ccsids = new int[ccsidList.size()];
    for (int i=0; i<ccsids.length; i++) {
      ccsids[i] = ((Integer)ccsidList.get(i)).intValue();
    }
    Arrays.sort(ccsids);  // sort into ascending order for quick searching

    // See if a bidi CCSID was specified.
    if (contains(ccsids, 1046) ||
        contains(ccsids, 1089) ||
        contains(ccsids, 1255) ||
        contains(ccsids, 1256) ||
        contains(ccsids, 12708) ||
        contains(ccsids, 420) ||
        contains(ccsids, 424) ||
        contains(ccsids, 425) ||
        contains(ccsids, 5351) ||
        contains(ccsids, 62211) ||
        contains(ccsids, 62224) ||
        contains(ccsids, 62235) ||
        contains(ccsids, 62245) ||
        contains(ccsids, 62251) ||
        contains(ccsids, 8612) ||
        contains(ccsids, 862) ||
        contains(ccsids, 864) ||
        contains(ccsids, 916))
    {
      return false;  // a bidi CCSID was specified, so don't exclude bidi classes
    }
    else return true; // OK to exclude
  }

  /**
   Adds or removes ZIP entry names from the dependencies list,
   prior to final presentation of the dependencies list.
   This method is provided so that subclasses of JarMaker can override it.
   <br><em>This method is meant to be called by the JarMaker class only.</em>

   @param neededJarEntries An unsorted list of names of ZIP entries
   that should be included in the output.
   @param jarMap A map of the source JAR or ZIP file.
   @return The modified list of ZIP entry names
   (must never be <code>null</code>.
   This should be a Vector of Strings.
   @exception IOException If an error occurs when reading the source file.
   **/
  Vector adjustDependencies2(Vector neededJarEntries, JarMap jarMap)
    throws IOException
  {

    // If the source jar file contains ConvTableJavaMap.class, it is Mod4 or later.
    // Otherwise it is pre-Mod4.
    boolean jarIsPreMod4;
    String entry1 = CIAA + "ConvTableJavaMap" + CLASS_SUFFIX;
    if (jarMap.contains(entry1))
      jarIsPreMod4 = false;
    else
      jarIsPreMod4 = true;

    // See if ConvTable.class was referenced.  If so, make sure we end up with
    // exactly the ConvTableXXX entries that we need, no more and no less.
    if (neededJarEntries.contains(CIAA + "ConvTable" + CLASS_SUFFIX))
    {  // Some or all of the ConvTableXXX files are needed, so include the right ones.

      if (ccsids_.size() == 0)
      { // User specified no particular ccsids, so include them all.

        if (jarIsPreMod4)
        {  // Nothing to do, all the ConvTableXXX files are all referenced in ConvTable.java.
        }
        else
        {  // The ConvTableXXX files need to be added explicitly.
          Vector entriesToAdd = new Vector();
          Enumeration e = jarMap.elements();
          while (e.hasMoreElements())
          {
            String entry = (String)e.nextElement();
            // We are looking for entries like "..../ConvTableXXX.yyyy",
            // where xxx is an integer greater than 0.
            String className = entry.substring(1 + entry.lastIndexOf('/'));
            if (className.startsWith("ConvTable") &&
                Character.isDigit(className.charAt(9))) // char after "ConvTable"
              entriesToAdd.addElement(entry);
          }

          // Determine the dependencies for the ConvTableXXX's.
          Enumeration e1 = entriesToAdd.elements();
          Vector unanalyzedEntries = jarMap.getEntryNames();
          Vector referencedJarEntries = new Vector();
          while (e1.hasMoreElements())
          {
            String entryName = (String)e1.nextElement();
            if (unanalyzedEntries.contains(entryName))
            {
              unanalyzedEntries.removeElement(entryName);
              analyzeJarEntry(entryName, unanalyzedEntries,
                               referencedJarEntries, jarMap);
              addElement(referencedJarEntries, entryName);
            }
          }
          copyVector(referencedJarEntries, neededJarEntries, CHECK_DUPS);
        }
      }  // ... no particular ccsids were specified.

      else // User specified one or more particular ccsids.
      {

        if (!jarIsPreMod4)
        {
          // We are dealing with a Mod4 or later Toolbox jar file.
          // No special action is required.
        }
        else
          // We are dealing with a pre-Mod4 Toolbox jar file.
          // Weed out any unneeded ConvTableXXX entries that get dragged in
          // (because their names are all referenced in ConvTable.java).
        {
          if (ccsids_.size() != 0)  // Did the user specify any particular CCSIDs.
          {
            // First clear all ConvTableXXX entries from the dependencies list.
            Vector entriesToRemove = new Vector();
            Enumeration e = neededJarEntries.elements();
            while (e.hasMoreElements())
            {
              String entry = (String)e.nextElement();
              // We are looking for entries like "..../ConvTableXXX.yyyy",
              // where xxx is an integer greater than 0.
              String className = entry.substring(1 + entry.lastIndexOf('/'));
              if (className.startsWith("ConvTable") &&
                  Character.isDigit(className.charAt(9))) // char after "ConvTable"
                entriesToRemove.addElement(entry);
            }
            Enumeration e2 = entriesToRemove.elements();
            while (e2.hasMoreElements())
            {
              neededJarEntries.removeElement((String)e2.nextElement());
            }

            // If the IFS component was specified, be sure to include
            // the "old Unicode" ccsid.  @A0a
            if (components_.contains(INTEGRATED_FILE_SYSTEM))
              addElement(ccsids_, new Integer(61952));

            // In all cases, be sure to include the "new Unicode" ccsid.  @A2a
            addElement(ccsids_, new Integer(13488));

            // Now add back in the ConvTableXXX's for the selected CCSIDs.
            Enumeration e3 = ccsids_.elements();
            while (e3.hasMoreElements())
            {
              Integer ccsid = (Integer)e3.nextElement();
              String entry = CIAA + "ConvTable" + ccsid.toString() +
                CLASS_SUFFIX;
              if (!neededJarEntries.contains(entry))
              {
                if (jarMap.contains(entry))
                {
                  neededJarEntries.addElement(entry);
                }
                else
                  System.err.println("Warning: No entry in source file for " +
                                      "CCSID " + ccsid.toString() + ".");
              }
            }
          }
        }
      }
    }  // ... include the right ConvTableXXX files, per the -ccsid list.

    // Remove selected ConvTableXXX files, per -ccsidExcluded list.
    if (ccsidsExcluded_.size() != 0)
    {
      Enumeration e = ccsidsExcluded_.elements();
      while (e.hasMoreElements())
      {
        Integer ccsid = (Integer)e.nextElement();
        String entry = CIAA + "ConvTable" + ccsid.toString() +
          CLASS_SUFFIX;
        neededJarEntries.removeElement(entry);
        if (!jarMap.contains(entry))
          System.err.println("Warning: No entry in source file for " +
                              "excluded CCSID " + ccsid.toString() + ".");
      }
    }


    // Remove excluded packages.
    removePackageFiles(neededJarEntries, jarMap, getPackagesExcluded());

    // Load the entry names associated with any required packages.
    // Note that these files will not be explicitly analyzed.
    addPackageFiles(neededJarEntries, jarMap, getPackages());

    // Add any needed MRI files to the "additional files" list.
    // Note: Regardless of language selection, don't remove the base MRI files.
    // That way, we always have a last-ditch default for messages (English),
    // no matter what locale the JAR file is used in.
    addLanguageFiles(languageDirectory_, neededJarEntries, jarMap);

    // Add any needed PCML files to the "additional files" list.
    addPcmlFiles(neededJarEntries, jarMap);

    // If -noproxy option was specified, remove proxy files.
    if (noProxy_)
    {
      Vector entriesToRemove = new Vector();
      Enumeration e = neededJarEntries.elements();
      while (e.hasMoreElements())
      {
        String entry = (String)e.nextElement();
        // We are looking for entries like:
        //    "..../###Proxy.class",
        //    "..../###ProxyImpl.class",
        //    "..../Proxy###.class",
        //    "..../Px###.class",
        String className = entry.substring(1 + entry.lastIndexOf('/'));
        if (className.endsWith(CLASS_SUFFIX))
        {
          className = className.substring(0,className.lastIndexOf(CLASS_SUFFIX));
          if ((className.endsWith("Proxy")) ||
              (className.endsWith("ProxyImpl")) ||
              (className.startsWith("Proxy")) ||
              (className.startsWith("Px")) )
          {
            entriesToRemove.addElement(entry);
            if (verbose_ || DEBUG) {
              System.out.println("Excluding proxy class: " + className);
            }
          }
        }
      }
      Enumeration e2 = entriesToRemove.elements();
      while (e2.hasMoreElements())
        neededJarEntries.removeElement((String)e2.nextElement());
    }

    if (excludeJDBC_)
    {
      neededJarEntries.removeElement("com/ibm/as400/access/DBDSPool.class");
      // Note: The AS400 class has dependencies on some DB* classes.
    }

    if (excludeResource_)
    {
      removeElements(neededJarEntries, "com/ibm/as400/resource/", STARTS_WITH);
    }

    if (excludeRLA_)
    {
      removeElements(neededJarEntries, "com/ibm/as400/access/Record", STARTS_WITH);
      removeElements(neededJarEntries, "FieldDescription.class", ENDS_WITH);
    }

    if (excludeSysVal_)
    {
      removeElements(neededJarEntries, "com/ibm/as400/access/SV", STARTS_WITH);
    }

    if (excludeSecAuth_)
    {
      // The AS400 class needs ProfileTokenCredential, which needs some other security.auth classes, so add them back in.
      addElement(neededJarEntries, "com/ibm/as400/security/auth/ProfileTokenCredential.class");
      addElement(neededJarEntries, "com/ibm/as400/security/auth/AS400Credential.class");
      addElement(neededJarEntries, "com/ibm/as400/security/auth/AS400BasicAuthenticationCredential.class");
      addElement(neededJarEntries, "com/ibm/as400/security/auth/AS400SwappableCredential.class");
    }

    if (excludeDirEntryClass_)
    {
      removeElements(neededJarEntries, "com/ibm/as400/access/DirectoryEntry", STARTS_WITH);
    }

    if (excludeValidationList_)
    {
      neededJarEntries.removeElement("com/ibm/as400/access/PersistenceException.class");
      removeElements(neededJarEntries, "com/ibm/as400/access/ValidationList", STARTS_WITH);
    }

    if (excludePathOptimizer_)
    {
      neededJarEntries.removeElement("com/ibm/as400/util/AS400ClassPathOptimizer.class");
    }

    if (excludeBidi_)
    {
      // Note: ConverterImplRemote needs several Bidi* classes regardless.
      removeElements(neededJarEntries, "Arabic", CONTAINS);
    }

    // Make sure that the copyright gets included.
    if (jarMap.contains(COPYRIGHT_ENTRY_NAME))
    {
      addElement(neededJarEntries, COPYRIGHT_ENTRY_NAME);
    }

    return neededJarEntries;
  }

  private static boolean containsPattern(Vector list, String pattern)
  {
    Enumeration e = list.elements();
    while (e.hasMoreElements()) {
      String elem = (String)e.nextElement();
      if (elem.startsWith(pattern)) {
        return true;
      }
    }
    return false;
  }


  /**
   Returns the base dependencies for a component.

   @param comp The component ID.
   @return The names of the ZIP entries that comprise the "base dependency list"
           for the specified component.  (String's)
   **/
  private static Vector getBaseDependenciesForComponent(Integer comp,
                                                 boolean includeBeans)
  {
    // Implementation note:  These are the base dependency lists for the
    // various Toolbox components as of mod1 (v3r2m1).  The lists will need
    // to be updated in future releases, as components are added and
    // as component designs evolve.

    Vector deps = new Vector(); // base ZIP entries for component

    if (comp.equals(AS400))
    {
      deps.addElement   (CIAA+"AS400.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400BeanInfo.class");
      }
    }
    else if (comp.equals (COMMAND_CALL))
    {
      deps.addElement   (CIAA+"CommandCall.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"CommandCallBeanInfo.class");
      }
    }
    else if (comp.equals (CONNECTION_POOL))
    {
      deps.addElement   (CIAA+"AS400ConnectionPool.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400ConnectionPoolBeanInfo.class");
        deps.addElement (CIAA+"ConnectionPoolBeanInfo.class");
      }
    }
    else if (comp.equals (DATA_AREA))
    {
      deps.addElement   (CIAA+"CharacterDataArea.class");
      deps.addElement   (CIAA+"DecimalDataArea.class");
      deps.addElement   (CIAA+"LocalDataArea.class");
      deps.addElement   (CIAA+"LogicalDataArea.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"CharacterDataAreaBeanInfo.class");
        deps.addElement (CIAA+"DecimalDataAreaBeanInfo.class");
        deps.addElement (CIAA+"LocalDataAreaBeanInfo.class");
        deps.addElement (CIAA+"LogicalDataAreaBeanInfo.class");
      }
    }
    else if (comp.equals (DATA_DESCRIPTION))
    {
      // Field description classes:
      deps.addElement   (CIAA+"ArrayFieldDescription.class");
      deps.addElement   (CIAA+"BinaryFieldDescription.class");
      deps.addElement   (CIAA+"CharacterFieldDescription.class");
      deps.addElement   (CIAA+"DateFieldDescription.class");
      deps.addElement   (CIAA+"DBCSEitherFieldDescription.class");
      deps.addElement   (CIAA+"DBCSGraphicFieldDescription.class");
      deps.addElement   (CIAA+"DBCSOnlyFieldDescription.class");
      deps.addElement   (CIAA+"DBCSOpenFieldDescription.class");
      deps.addElement   (CIAA+"FloatFieldDescription.class");
      deps.addElement   (CIAA+"HexFieldDescription.class");
      deps.addElement   (CIAA+"PackedDecimalFieldDescription.class");
      deps.addElement   (CIAA+"TimeFieldDescription.class");
      deps.addElement   (CIAA+"TimestampFieldDescription.class");
      deps.addElement   (CIAA+"VariableLengthFieldDescription.class");
      deps.addElement   (CIAA+"ZonedDecimalFieldDescription.class");

      deps.addElement   (CIAA+"AS400Array.class");
      deps.addElement   (CIAA+"AS400Bin2.class");
      deps.addElement   (CIAA+"AS400Bin4.class");
      deps.addElement   (CIAA+"AS400ByteArray.class");
      deps.addElement   (CIAA+"AS400Float4.class");
      deps.addElement   (CIAA+"AS400Float8.class");
      deps.addElement   (CIAA+"AS400PackedDecimal.class");
      deps.addElement   (CIAA+"AS400Structure.class");
      deps.addElement   (CIAA+"AS400Text.class");
      deps.addElement   (CIAA+"AS400UnsignedBin2.class");
      deps.addElement   (CIAA+"AS400UnsignedBin4.class");
      deps.addElement   (CIAA+"AS400ZonedDecimal.class");
      deps.addElement   (CIAA+"Record.class"); // references RecordFormat
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400ArrayBeanInfo.class");
        deps.addElement (CIAA+"AS400StructureBeanInfo.class");
        deps.addElement (CIAA+"RecordBeanInfo.class");
        deps.addElement (CIAA+"RecordFormatBeanInfo.class");
      }
    }
    else if (comp.equals (DATA_QUEUE))
    {
      deps.addElement   (CIAA+"DataQueue.class");
      deps.addElement   (CIAA+"KeyedDataQueue.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"BaseDataQueueBeanInfo.class");
        deps.addElement (CIAA+"DataQueueAttributesBeanInfo.class");
        deps.addElement (CIAA+"DataQueueBeanInfo.class");
        deps.addElement (CIAA+"KeyedDataQueueBeanInfo.class");
      }
    }
    else if (comp.equals (DIGITAL_CERTIFICATE))
    {
      deps.addElement   (CIAA+"AS400Certificate.class");
      deps.addElement   (CIAA+"AS400CertificateAttribute.class");
      deps.addElement   (CIAA+"AS400CertificateUserProfileUtil.class");
      deps.addElement   (CIAA+"AS400CertificateVldlUtil.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400CertificateUserProfileUtilBeanInfo.class");
        deps.addElement (CIAA+"AS400CertificateUtilBeanInfo.class");
        deps.addElement (CIAA+"AS400CertificateVldlUtilBeanInfo.class");
      }
    }
    else if (comp.equals (FTP))
    {
      deps.addElement   (CIAA+"AS400FTP.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400FTPBeanInfo.class");
        deps.addElement (CIAA+"FTPBeanInfo.class");
      }
    }
    else if (comp.equals (INTEGRATED_FILE_SYSTEM))
    {
      deps.addElement   (CIAA+"IFSFile.class");
      deps.addElement   (CIAA+"IFSRandomAccessFile.class");
      deps.addElement   (CIAA+"IFSTextFileInputStream.class");  // deprecated
      deps.addElement   (CIAA+"IFSTextFileOutputStream.class"); // deprecated
      deps.addElement   (CIAA+"IFSFileReader.class");
      deps.addElement   (CIAA+"IFSFileWriter.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"IFSFileBeanInfo.class");
        deps.addElement (CIAA+"IFSRandomAccessFileBeanInfo.class");
        deps.addElement (CIAA+"IFSTextFileInputStreamBeanInfo.class");
        deps.addElement (CIAA+"IFSTextFileOutputStreamBeanInfo.class");
      }
    }
    else if (comp.equals (JAAS))
    {
      deps.addElement   (CIASA+"ProfileHandleCredential.class");
      deps.addElement   (CIASA+"ProfileTokenCredential.class");
      deps.addElement   (CIASA+"UserProfilePrincipal.class");
      if (includeBeans)
      {
        deps.addElement (CIASA+"ProfileHandleCredentialBeanInfo.class");
        deps.addElement (CIASA+"ProfileTokenCredentialBeanInfo.class");
        deps.addElement (CIASA+"UserProfilePrincipalBeanInfo.class");
      }
    }
    else if (comp.equals (JAVA_APPLICATION_CALL))
    {
      deps.addElement   (CIAA+"JavaApplicationCall.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"JavaApplicationCallBeanInfo.class");
      }
    }
    else if (comp.equals (JDBC))
    {
      deps.addElement   (CIAA+"AS400JDBCDriver.class");
      deps.addElement   (CIAA+"AS400JDBCRowSet.class");
      deps.addElement   (CIAA+"AS400JDBCXAConnection.class");
      deps.addElement   (CIAA+"AS400JDBCConnectionPool.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400JDBCRowSetBeanInfo.class");
        deps.addElement (CIAA+"AS400JDBCDataSourceBeanInfo.class");
        deps.addElement (CIAA+"AS400JDBCXADataSourceBeanInfo.class");
        deps.addElement (CIAA+"AS400JDBCConnectionPoolBeanInfo.class");
        deps.addElement (CIAA+"AS400JDBCConnectionPoolDataSourceBeanInfo.class");
        deps.addElement (CIAA+"ConnectionPoolBeanInfo.class");
      }
    }
    else if (comp.equals (JOB))
    {
      deps.addElement   (CIAA+"JobList.class");
      deps.addElement   (CIAA+"JobLog.class");
      if (includeBeans)
      {  // No beans for this component.
      }
    }
    else if (comp.equals (MESSAGE))
    {
      deps.addElement   (CIAA+"MessageQueue.class");
      if (includeBeans)
      {  // No beans for this component.
      }
    }
    else if (comp.equals (NETSERVER))
    {
      deps.addElement   (CIAA+"ISeriesNetServer.class");
      // TBD: The following NetServer* classes are deprecated in favor of ISeriesNetServer.  Delete them from JarMaker eventually.
      deps.addElement   (CIAA+"NetServer.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"NetServerBeanInfo.class");
        deps.addElement (CIAA+"NetServerFileShareBeanInfo.class");
        deps.addElement (CIAA+"NetServerPrintShareBeanInfo.class");
      }
    }
    else if (comp.equals (NUMERIC_DATA_TYPES))
    {  // All the DataType classes except for AS400Text.
      deps.addElement   (CIAA+"AS400Array.class");
      deps.addElement   (CIAA+"AS400Bin2.class");
      deps.addElement   (CIAA+"AS400Bin4.class");
      deps.addElement   (CIAA+"AS400ByteArray.class");
      deps.addElement   (CIAA+"AS400Float4.class");
      deps.addElement   (CIAA+"AS400Float8.class");
      deps.addElement   (CIAA+"AS400PackedDecimal.class");
      deps.addElement   (CIAA+"AS400Structure.class");
      deps.addElement   (CIAA+"AS400UnsignedBin2.class");
      deps.addElement   (CIAA+"AS400UnsignedBin4.class");
      deps.addElement   (CIAA+"AS400ZonedDecimal.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400ArrayBeanInfo.class");
        deps.addElement (CIAA+"AS400StructureBeanInfo.class");
      }
    }
    else if (comp.equals (PCML))
    {
      deps.addElement   (CIAD+"ProgramCallDocument.class");
      deps.addElement   (CIAD+"RecordFormatDocument.class");
      if (includeBeans)
      {  // No beans for this component.
      }
    }
    else if (comp.equals (PRINT))
    {
      deps.addElement   (CIAA+"AFPResourceList.class");
      deps.addElement   (CIAA+"OutputQueueList.class");
      deps.addElement   (CIAA+"PrinterList.class");
      deps.addElement   (CIAA+"PrintObjectInputStream.class");
      deps.addElement   (CIAA+"SpooledFileList.class");
      deps.addElement   (CIAA+"SpooledFileOutputStream.class");
      deps.addElement   (CIAA+"SCS3812Writer.class");
      deps.addElement   (CIAA+"SCS5553Writer.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AFPResourceBeanInfo.class");
        deps.addElement (CIAA+"AFPResourceListBeanInfo.class");
        deps.addElement (CIAA+"OutputQueueBeanInfo.class");
        deps.addElement (CIAA+"OutputQueueListBeanInfo.class");
        deps.addElement (CIAA+"PrintObjectBeanInfo.class");
        deps.addElement (CIAA+"PrintObjectListBeanInfo.class");
        deps.addElement (CIAA+"PrinterBeanInfo.class");
        deps.addElement (CIAA+"PrinterFileBeanInfo.class");
        deps.addElement (CIAA+"PrinterFileListBeanInfo.class");
        deps.addElement (CIAA+"PrinterListBeanInfo.class");
        deps.addElement (CIAA+"SpooledFileListBeanInfo.class");
        deps.addElement (CIAA+"WriterJobListBeanInfo.class");
      }
    }
    else if (comp.equals (PROGRAM_CALL))
    {
      deps.addElement   (CIAA+"ProgramCall.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"ProgramCallBeanInfo.class");
        deps.addElement (CIAA+"ProgramParameterBeanInfo.class");
      }
    }
    else if (comp.equals (RECORD_LEVEL_ACCESS))
    {
      deps.addElement   (CIAA+"AS400FileRecordDescription.class");
      deps.addElement   (CIAA+"KeyedFile.class");  // extends AS400File
      deps.addElement   (CIAA+"SequentialFile.class");
      deps.addElement   (CIAA+"DDMAS400MessageReply.class"); // extends DDMReplyDataStream
      deps.addElement   (CIAA+"DDMEndUnitOfWorkReply.class");
      deps.addElement   (CIAA+"DDMObjectDataStream.class"); // extends DDMDataStream
      deps.addElement   (CIAA+"DDMRecordCache.class");
      deps.addElement   (CIAA+"DDMRequestDataStream.class"); // extends DDMDataStream
      deps.addElement   (CIAA+"DDMS38IOFB.class");
      // Field description classes:
      deps.addElement   (CIAA+"ArrayFieldDescription.class");
      deps.addElement   (CIAA+"BinaryFieldDescription.class");
      deps.addElement   (CIAA+"CharacterFieldDescription.class");
      deps.addElement   (CIAA+"DBCSEitherFieldDescription.class");
      deps.addElement   (CIAA+"DBCSGraphicFieldDescription.class");
      deps.addElement   (CIAA+"DBCSOnlyFieldDescription.class");
      deps.addElement   (CIAA+"DBCSOpenFieldDescription.class");
      deps.addElement   (CIAA+"DateFieldDescription.class");
      deps.addElement   (CIAA+"FloatFieldDescription.class");
      deps.addElement   (CIAA+"HexFieldDescription.class");
      deps.addElement   (CIAA+"PackedDecimalFieldDescription.class");
      deps.addElement   (CIAA+"TimeFieldDescription.class");
      deps.addElement   (CIAA+"TimestampFieldDescription.class");
      deps.addElement   (CIAA+"ZonedDecimalFieldDescription.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"AS400FileBeanInfo.class");
        deps.addElement (CIAA+"AS400FileRecordDescriptionBeanInfo.class");
        deps.addElement (CIAA+"KeyedFileBeanInfo.class");
        deps.addElement (CIAA+"SequentialFileBeanInfo.class");
      }
    }
    else if (comp.equals (SECURE_AS400))
    {
      deps.addElement   (CIAA+"SecureAS400.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"SecureAS400BeanInfo.class");
      }
    }
    else if (comp.equals (SERVICE_PROGRAM_CALL))
    {
      deps.addElement   (CIAA+"ServiceProgramCall.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"ServiceProgramCallBeanInfo.class");
      }
    }
    else if (comp.equals (SYSTEM_STATUS))
    {
      deps.addElement   (CIAA+"SystemStatus.class");
      deps.addElement   (CIAA+"SystemPool.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"SystemStatusBeanInfo.class");
        deps.addElement (CIAA+"SystemPoolBeanInfo.class");
      }
    }
    else if (comp.equals (SYSTEM_VALUE))
    {
      deps.addElement   (CIAA+"SystemValueList.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"SystemValueListBeanInfo.class");
        deps.addElement (CIAA+"SystemValueBeanInfo.class");
      }
    }
    else if (comp.equals (TRACE))
    {
      deps.addElement   (CIAA+"Trace.class");
    }
    else if (comp.equals (USER))
    {
      deps.addElement   (CIAA+"UserList.class");
      if (includeBeans)
      {  // No beans for this component.
      }
    }
    else if (comp.equals (USER_SPACE))
    {
      deps.addElement   (CIAA+"UserSpace.class");
      if (includeBeans)
      {
        deps.addElement (CIAA+"UserSpaceBeanInfo.class");
      }
    }
    else if (comp.equals (AS400_VISUAL))
    {
      deps.addElement   (CIAV+"AS400ListPane.class");
      deps.addElement   (CIAV+"AS400ExplorerPane.class");
      deps.addElement   (CIAV+"ErrorDialogAdapter.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"AS400DetailsModelBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ExplorerPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ListModelBeanInfo.class");
        deps.addElement (CIAV+"AS400ListPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400TreeModelBeanInfo.class");
        deps.addElement (CIAV+"AS400TreePaneBeanInfo.class");
        deps.addElement (CIAV+"ErrorDialogAdapterBeanInfo.class");
        deps.addElement (CIAV+"VActionAdapterBeanInfo.class");
        deps.addElement (CIAV+"VPropertiesActionBeanInfo.class");
        deps.addElement (CIAV+"WorkingCursorAdapterBeanInfo.class");
      }
    }
    else if (comp.equals (COMMAND_CALL_VISUAL))
    {
      deps.addElement   (CIAV+"CommandCallButton.class");
      deps.addElement   (CIAV+"CommandCallMenuItem.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"CommandCallButtonBeanInfo.class");
        deps.addElement (CIAV+"CommandCallMenuItemBeanInfo.class");
      }
    }
    else if (comp.equals (DATA_QUEUE_VISUAL))
    {
      deps.addElement   (CIAV+"DataQueueDocument.class");
      deps.addElement   (CIAV+"KeyedDataQueueDocument.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"DataQueueDocumentBeanInfo.class");
        deps.addElement (CIAV+"KeyedDataQueueDocumentBeanInfo.class");
      }
    }
    else if (comp.equals (INTEGRATED_FILE_SYSTEM_VISUAL))
    {
      deps.addElement   (CIAV+"IFSFileDialog.class"); // not a bean
      deps.addElement   (CIAV+"IFSTextFileDocument.class");
      deps.addElement   (CIAV+"VIFSDirectory.class");
      deps.addElement   (CIAV+"AS400ExplorerPane.class");
      deps.addElement   (CIAV+"AS400ListPane.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"IFSTextFileDocumentBeanInfo.class");
        deps.addElement (CIAV+"VIFSDirectoryBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsModelBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ExplorerPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ListModelBeanInfo.class");
        deps.addElement (CIAV+"AS400ListPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400TreeModelBeanInfo.class");
        deps.addElement (CIAV+"AS400TreePaneBeanInfo.class");
      }
    }
    else if (comp.equals (JAVA_APPLICATION_CALL_VISUAL))
    {
      deps.addElement   (CIAV+"VJavaApplicationCall.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"VJavaApplicationCallBeanInfo.class");
      }
    }
    else if (comp.equals (JDBC_VISUAL))
    {
      deps.addElement   (CIAV+"SQLResultSetFormPane.class");
      deps.addElement   (CIAV+"SQLResultSetTablePane.class");
      deps.addElement   (CIAV+"SQLStatementButton.class");
      deps.addElement   (CIAV+"SQLStatementMenuItem.class");
      deps.addElement   (CIAV+"SQLStatementDocument.class");
      deps.addElement   (CIAV+"SQLQueryBuilderPane.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"SQLConnectionBeanInfo.class");
        deps.addElement (CIAV+"SQLQueryBuilderPaneBeanInfo.class");
        deps.addElement (CIAV+"SQLResultSetFormPaneBeanInfo.class");
        deps.addElement (CIAV+"SQLResultSetTableModelBeanInfo.class");
        deps.addElement (CIAV+"SQLResultSetTablePaneBeanInfo.class");
        deps.addElement (CIAV+"SQLStatementButtonBeanInfo.class");
        deps.addElement (CIAV+"SQLStatementDocumentBeanInfo.class");
        deps.addElement (CIAV+"SQLStatementMenuItemBeanInfo.class");
      }
    }
    else if (comp.equals (JOB_VISUAL))
    {
      deps.addElement   (CIAV+"VJobList.class");
      deps.addElement   (CIAV+"AS400ExplorerPane.class");
      deps.addElement   (CIAV+"AS400ListPane.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"VJobBeanInfo.class");
        deps.addElement (CIAV+"VJobListBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsModelBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ExplorerPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ListModelBeanInfo.class");
        deps.addElement (CIAV+"AS400ListPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400TreeModelBeanInfo.class");
        deps.addElement (CIAV+"AS400TreePaneBeanInfo.class");
      }
    }
    else if (comp.equals (MESSAGE_VISUAL))
    {
      deps.addElement   (CIAV+"VMessageList.class");
      deps.addElement   (CIAV+"VMessageQueue.class");
      deps.addElement   (CIAV+"AS400ExplorerPane.class");
      deps.addElement   (CIAV+"AS400ListPane.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"VMessageListBeanInfo.class");
        deps.addElement (CIAV+"VMessageQueueBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsModelBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ExplorerPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ListModelBeanInfo.class");
        deps.addElement (CIAV+"AS400ListPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400TreeModelBeanInfo.class");
        deps.addElement (CIAV+"AS400TreePaneBeanInfo.class");
      }
    }
    else if (comp.equals (PRINT_VISUAL))
    {
      deps.addElement   (CIAV+"VPrinters.class");
      deps.addElement   (CIAV+"AS400ExplorerPane.class");
      deps.addElement   (CIAV+"AS400ListPane.class");
      // Note - Pre-mod2 Toolbox erroneously refers to
      //        VOutput16.GIF, VOutput32.GIF (uppercase),
      //        therefore they won't get included.
      if (includeBeans)
      {
        deps.addElement (CIAV+"VPrinterBeanInfo.class");
        deps.addElement (CIAV+"VPrinterOutputBeanInfo.class");
        deps.addElement (CIAV+"VPrintersBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsModelBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ExplorerPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ListModelBeanInfo.class");
        deps.addElement (CIAV+"AS400ListPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400TreeModelBeanInfo.class");
        deps.addElement (CIAV+"AS400TreePaneBeanInfo.class");
      }
    }
    else if (comp.equals (PROGRAM_CALL_VISUAL))
    {
      deps.addElement   (CIAV+"ProgramCallButton.class");
      deps.addElement   (CIAV+"ProgramCallMenuItem.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"ProgramCallButtonBeanInfo.class");
        deps.addElement (CIAV+"ProgramCallMenuItemBeanInfo.class");
      }
    }
    else if (comp.equals (RECORD_LEVEL_ACCESS_VISUAL))
    {
      deps.addElement   (CIAV+"RecordListFormPane.class");
      deps.addElement   (CIAV+"RecordListTablePane.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"RecordListFormPaneBeanInfo.class");
        deps.addElement (CIAV+"RecordListTableModelBeanInfo.class");
        deps.addElement (CIAV+"RecordListTablePaneBeanInfo.class");
      }
    }
    else if (comp.equals (USER_VISUAL))
    {
      deps.addElement   (CIAV+"VUserList.class");
      deps.addElement   (CIAV+"AS400ExplorerPane.class");
      deps.addElement   (CIAV+"AS400ListPane.class");
      if (includeBeans)
      {
        deps.addElement (CIAV+"VUserListBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsModelBeanInfo.class");
        deps.addElement (CIAV+"AS400DetailsPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ExplorerPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400ListModelBeanInfo.class");
        deps.addElement (CIAV+"AS400ListPaneBeanInfo.class");
        deps.addElement (CIAV+"AS400TreeModelBeanInfo.class");
        deps.addElement (CIAV+"AS400TreePaneBeanInfo.class");
      }
    }

    else  // none of the above component values
      throw new IllegalArgumentException("component (" +
                                          comp.intValue() + ")");

    return deps;
  }


  /**
   Returns the CCSIDs explicitly to be included.

   @return The CCSIDs explicitly to be included.
   The list will be empty if none has been specified.
   The list will contain only <code>Integer</code> objects.
   **/
  public Vector getCCSIDs() { return ccsids_; }


  /**
   Returns CCSIDs to be excluded.

   @return The CCSIDs to be excluded.
   The list will be empty if none has been specified.
   The list will contain only <code>Integer</code> objects.
   **/
  public Vector getCCSIDsExcluded() { return ccsidsExcluded_; }



  /**
   Returns the name of the class file corresponding to a PCML file.

   @param pcmlEntryName The PCML entry name.
   @return The name of the class file corresponding to a PCML file.
   **/
  private static String getClassEntryForPcml(String pcmlEntryName)
  {
    int suffixPos = pcmlEntryName.lastIndexOf(".pcml");
    return (pcmlEntryName.substring(0,suffixPos) + ".class");
  }


  /**
   Returns the component ID for a component.

   @param componentName The component name.
   @return The associated ID for the component, or null
           if the component name is not recognized.
   **/
  private static Integer getComponentID(String componentName)
  {
    String comp = componentName.trim();
    Integer id = null;  // default

    for (int i=0; i<VALID_COMPONENTS.length; ++i) {
      if (comp.equalsIgnoreCase(VALID_COMPONENTS[i]))
      {
        id = new Integer(i);
        break;
      }
    }
    if (id == null) { // check abbreviations
      for (int i=0; i<VALID_COMPONENT_ABBREVS.length; ++i) {
        if (comp.equalsIgnoreCase(VALID_COMPONENT_ABBREVS[i]))
        {
          id = new Integer(i);
          break;
        }
      }
    }

    return id;
  }


  /**
   Returns the required components specified by the user.

   @return The components specified by the user.
   The list will be empty if none has been specified.
   The list will contain only <code>Integer</code> objects.
   See <a href="doc-files/componentList.html">component list</a> for
   a list of integer values and associated components.
   **/
  public Vector getComponents() { return components_; }


  /**
   Returns the base directory under which the Toolbox language files
   will be searched for.
   This value is set by <code>setLanguageDirectory()</code>.
   @return The base directory for the language files.
   **/
  public File getLanguageDirectory() { return languageDirectory_; }


  /**
   Returns the national languages that explicitly are to be supported.
   The languages are identified by their Java locale name, such as fr_CA
   (for Canadian French).
   Note: Locale <code>en</code> is always implicitly supported.

   @return The national languages that explicitly are to be supported.
   The list will be empty if none has been specified.
   The list will contain only <code>String</code> objects.
   **/
  public Vector getLanguages() { return languages_; }


  /**
   Returns the best-fit shipped MRI suffix (such as "_fr")
   for a given language specifier (such as "_fr_FR").

   @param langId The language ID specified by the user.
   @param verbose The verbose mode.
   @return The shipped suffix most closely matching that ID;
           <code>null</code> if the language is not supported.
   **/
  private static String getShippedLanguageSuffixFor(String langId,
                                                     boolean verbose)
  {
    // Shipped suffixes are: de, de_CH, es, fr, fr_BE, fr_CA, fr_CH,
    // it, it_CH, ja, ko, zh, zh_TW, cs, hu, pl.

    // Note: cs, hu, and pl were not supported prior to mod1.

    // This routine will need to be updated if any languages are
    // added or dropped in subsequent releases of the Toolbox.

    String result = null;
    String id = normalizeLanguageSuffix(langId);

    if      (id.startsWith("en")) {
      if (verbose)
        System.out.println("English MRI files are included by default.");
    }
    else if (id.startsWith("de_CH"))  result = "de_CH";
    else if (id.startsWith("de"))     result = "de";
    else if (id.startsWith("es"))     result = "es";
    else if (id.startsWith("fr_BE"))  result = "fr_BE";
    else if (id.startsWith("fr_CA"))  result = "fr_CA";
    else if (id.startsWith("fr_CH"))  result = "fr_CH";
    else if (id.startsWith("fr"))     result = "fr";
    else if (id.startsWith("it_CH"))  result = "it_CH";
    else if (id.startsWith("it"))     result = "it";
    else if (id.startsWith("ja"))     result = "ja";
    else if (id.startsWith("ko"))     result = "ko";
    else if (id.startsWith("zh_TW"))  result = "zh_TW";
    else if (id.startsWith("zh"))     result = "zh";
    else if (id.startsWith("cs"))     result = "cs";  // added in mod1
    else if (id.startsWith("hu"))     result = "hu";  // added in mod1
    else if (id.startsWith("pl"))     result = "pl";  // added in mod1

    else {
      System.err.println("Warning: Unsupported language ID specified: " +
                          langId + ".");
      result = id;  // try it anyway, it might be a new one
    }

    if (DEBUG)
      System.out.println("Debug: Specified language suffix " + langId +
                          " maps to " + result);
    return result;
  }


  /**
   Reformats a user-supplied language specifier so that we have the
   best chance of recognizing it.
   @param language The language suffix.
   @return The normalized version of the suffix.  Never returns null.
   **/
  private static String normalizeLanguageSuffix(String language)
  {
    String result;
    String languageID = language;

    // Strip off any leading or trailing blanks, underscores.
    String id = language.trim();
    while (id.charAt(0) == '_')
      id = id.substring(1);
    while (id.endsWith("_"))
      id = id.substring(0, id.length()-1);

    if (id.length() < 2)
      throw new IllegalArgumentException("language (" + language + ")");
    else if (id.indexOf("_") == 2)
      languageID = id;
    else {
      StringBuffer buf = new StringBuffer(id);
      buf.insert(2, '_');
      languageID = buf.toString();
    }

    // Lower-case the chars before the first underscore, and uppercase the rest.
    String part1 = languageID.substring(0,2).toLowerCase();
    String part2 = languageID.substring(2).toUpperCase();
    result = part1 + part2;
    if (DEBUG) System.out.println("Debug: Normalized language specifier: " +
                                    result);
    return result;
  }

  /**
   Parses and validates the arguments specified on the command line.

   @param args The command line arguments.
   @return An indication of whether the parse succeeded.
   **/
  private boolean parseArgs(String[] args)
  {
    boolean succeeded = false;

    // Wipe the slate clean, in case this ToolboxJarMaker object is
    // being recycled.
    reset();

    succeeded = arguments_.parse(args, this);
    return succeeded;
  }


  /**
   Resets the ToolboxJarMaker object to a clean, default state,
   to facilitate object reuse.
   **/
  public void reset()
  {
    super.reset();
    arguments_ = new Arguments();
    File defaultSourceJar = new File(DEFAULT_SOURCE_JAR_NAME);
    setSourceJar(defaultSourceJar);

    components_.removeAllElements();
    languages_.removeAllElements();
    languageDirectory_ = new File(System.getProperty("user.dir"));
    ccsids_.removeAllElements();
    ccsidsExcluded_.removeAllElements();
  }


  /**
   Specifies the CCSIDs explicitly to be included.
   The conversion tables for only these CCSIDs (and CCSIDs
   specified in any prior invocations) will be included in the output.
   Conversion tables for other CCSIDs will <em>not</em> be included.
   <br>Note: This augments any previously specified CCSIDs.
   This method does not verify the existence of the specified CCSIDs.

   @param ccsidList The CCSIDs explicitly to be included.
   The list should contain only <code>Integer</code> objects.
   See <a href="doc-files/ccsidList.html">CCSIDs and encodings</a>
   for valid values.
   **/
  public void setCCSIDs(Vector ccsidList)
  {
    if (ccsidList == null)
      throw new NullPointerException("ccsidList");
    // Check for nulls and for correct element type.
    ccsidList = validateList(ccsidList, "CCSID",
                              "java.lang.Integer", verbose_);
    Enumeration e = ccsidList.elements();
    while (e.hasMoreElements())
    {
      Integer ccsid = (Integer)e.nextElement();
      addElement(ccsids_, ccsid);
      if (ccsidsExcluded_.contains(ccsid))
      {
        System.err.println("Warning: CCSID " + ccsid.intValue() +
                            ", specified for both inclusion " +
                            "and exclusion, will be included.");
        ccsidsExcluded_.removeElement(ccsid);
      }

      // If adding an ebcdic mixed-byte ccsid, also add associated ccsids
      // for "single-byte portion" and "double-byte portion".
      switch (ccsid.intValue())
      {
        case  930:
        case 5026:
          addElement(ccsids_, new Integer(290));
          addElement(ccsids_, new Integer(300));
          break;

        case  933:
        case 1364:
          addElement(ccsids_, new Integer(833));
          addElement(ccsids_, new Integer(834));
          break;

        case  935:
        case 1388:
          addElement(ccsids_, new Integer(836));
          addElement(ccsids_, new Integer(837));
          break;

        case  937:
          addElement(ccsids_, new Integer( 37));
          addElement(ccsids_, new Integer(835));
          break;

        case  939:
        case 5035:
          addElement(ccsids_, new Integer(1027));
          addElement(ccsids_, new Integer( 300));
          break;

        case 1399:
          addElement(ccsids_, new Integer( 5123));
          addElement(ccsids_, new Integer(16684));
          break;

        default:
          // None of the above, so nothing special to do.
      }
    }
  }

  /**
   Specifies CCSIDs to be excluded.
   The conversion tables for these CCSIDs will not be included in the output.
   <br>Note: This augments any previously specified CCSIDs.
   This method does not verify the existence of the specified CCSIDs.

   @param ccsidList The CCSIDs to be excluded.
   The list should contain only <code>Integer</code> objects.
   **/
  public void setCCSIDsExcluded(Vector ccsidList)
  {
    if (ccsidList == null)
      throw new NullPointerException("ccsidList");
    // Check for nulls and for correct element type.
    ccsidList = validateList(ccsidList, "CCSID",
                              "java.lang.Integer", verbose_);
    Enumeration e = ccsidList.elements();
    while (e.hasMoreElements())
    {
      Integer ccsid = (Integer)e.nextElement();
      // See if the CCSID was also specified to be *included*.
      if (ccsids_.contains(ccsid))
        System.err.println("Warning: CCSID " + ccsid.intValue() +
                            ", specified for both exclusion " +
                            "and inclusion, will be included.");
      else
        addElement(ccsidsExcluded_, ccsid);
    }
  }


  /**
   Specifies required <i>IBM Toolbox for Java</i> components.
   Java Beans files associated with the components are <em>not</em>
   automatically included.
   <br>Note: This augments any previously specified components.
   If an unrecognized component is specified, a warning message is
   sent to <code>System.err</code>, and the component is ignored.

   @param components The required components.
   The list should contain only <code>Integer</code> objects.
   See <a href="doc-files/componentList.html">component list</a> for valid values.
   **/
  public void setComponents(Vector components)
  {
    setComponents(components, false); // do not include beans
  }


  /**
   Specifies required <i>IBM Toolbox for Java</i> components.
   <br>Note: This augments any previously specified components.
   If an unrecognized component is specified, a warning message is
   sent to <code>System.err</code>, and the component is ignored.

   @param components The required components.
   The list should contain only <code>Integer</code> objects.
   See <a href="doc-files/componentList.html">component list</a> for valid values.
   @param includeBeans Whether or not Java Beans files associated
   with the components are to be included.
   **/
  public void setComponents(Vector components, boolean includeBeans)
  {
    if (components == null)
      throw new NullPointerException("componentList");
    // Check for nulls and for correct element type.
    components = validateList(components, "component",
                               "java.lang.Integer", verbose_);
    Vector filesForComponents = new Vector();
    Enumeration e = components.elements();
    while (e.hasMoreElements())
    {
      Integer comp = (Integer)e.nextElement();
      if (addElement(components_, comp)) // if not already in list...
      {
        Vector filesForThisComp = getBaseDependenciesForComponent(comp, includeBeans);
        copyVector(filesForThisComp, filesForComponents, CHECK_DUPS);
      }
    }

    // Add the component files to list of required files.
    setFilesRequired(filesForComponents);
  }


  /**
   Specifies the base directory for additional Toolbox language files.
   The path below this directory should correspond to the package name
   the language files.
   <br>
   For example, if the MRI files are located in directories
   <code>/usr/myDir/com/ibm/as400/access</code> and
   <code>/usr/myDir/com/ibm/as400/vaccess</code>,
   then the base directory should be set to <code>/usr/myDir</code>.
   <br>This method does not verify the existence of the specified directory.
   <br>The default language directory is the current directory. 

   @param baseDirectory The base directory for the language files.
   **/
  public void setLanguageDirectory(File baseDirectory)
  {
    if (baseDirectory == null)
      throw new NullPointerException("baseDirectory");
    else
      languageDirectory_ = baseDirectory;
  }


  /**
   Specifies national languages that explicitly are to be supported.
   Languages are identified by their Java locale name, such as
   <code>fr_CA</code>.
   The language files for the specified languages will be searched for
   in the directory tree specified by <code>setLanguageDirectory</code>,
   or, if no directory has been specified, in the directory tree
   under the current directory.
   <br>Note: This augments any previously specified languages.
   This method does not verify the existence of files for the specified
   languages.
   Locale <code>en</code> is always implicitly supported.
   If an unrecognized language is specified, a warning message is
   sent to <code>System.err</code>, and the language is ignored.

   @param languages The languages to be supported.
   The list should contain only <code>String</code> objects.
   **/
  public void setLanguages(Vector languages)
  {
    if (languages == null)
      throw new NullPointerException("languageList");
    // Check for nulls and for correct element type.
    languages = validateList(languages, "language",
                              "java.lang.String", verbose_);

    // Map the specified languages to the actual shipped MRI name extensions.
    Enumeration e = languages.elements();
    while (e.hasMoreElements())
    {
      String specifiedLanguage = (String)e.nextElement();  // e.g. "fr_CA"
      String mriSuffix = getShippedLanguageSuffixFor(specifiedLanguage, verbose_);
      addElement(languages_, mriSuffix);
    }
    // Note: Postpone calling setAdditionalFiles() until we need to, since
    // it needs to read the source JAR file and therefore may throw exceptions.
  }


  /**
   Specifies whether proxy-only files are to be excluded.
   Proxy files are needed only if the Toolbox's Proxy Service
   will be used.
   By default, all proxy files in the source JAR file are included.

   @param excludeProxies Whether or not proxy-only files
   are to be excluded.
   **/
  public void setProxyFilesExcluded(boolean excludeProxies)
  {
    noProxy_ = excludeProxies;
  }



  /**
   Specifies whether ToolboxJarMaker is allowed to selectively limit
   dependency expansion, and exclude certain components and packages
   that are unlikely to be needed.
   By default, this option is not in effect; that is, all directly- and
   indirectly-referenced files in the source JAR file are included.
   Examples of files that may be excluded from the output:
   <ul>
   <li>JDBC classes, if component {@link #JDBC JDBC} has not been specified
   <li>class {@link com.ibm.as400.resource.RJob RJob}, if neither component {@link com.ibm.as400.access.Job Job} nor package <tt>com.ibm.as400.resource</tt> has been specified
   </ul>
   <em>Note: Dependency exclusion is not recommended for pre-V5R2 Toolbox JAR files.</em>

   @param excludeSomeDependencies Whether or not ToolboxJarMaker should
   selectively limit dependency expansion.
   **/
  public void setExcludeSomeDependencies(boolean excludeSomeDependencies)
  {
    excludeSomeDependencies_ = excludeSomeDependencies;
  }


  /**
   Performs the actions specified in the invocation arguments.

   @param args The command line arguments.
   **/
  public static void main(String[] args)
  {
    try
    {
      ToolboxJarMaker jm = new ToolboxJarMaker();

      if (jm.parseArgs(args))
      {
        if (jm.isSplit())  // -split overrides all other options
        {
          File srcJar = jm.getSourceJar();
          int splitSize = jm.getSplitSize();
          jm.split(srcJar, splitSize); // unpack required files
        }
        else if (jm.isExtract())
        {
          File srcJar = jm.getSourceJar();
          File outputDir = jm.getExtractionDirectory();
          jm.extract(srcJar, outputDir); // unpack required files
        }
        else
        {
          File srcJar = jm.getSourceJar();
          File destJar = jm.getDestinationJar();
          jm.makeJar(srcJar, destJar); // create a new JAR file
        }
      }
      else System.exit(1);
    }
    catch (Throwable e) {
      System.err.println(e.toString());
      e.printStackTrace(System.err);
      System.exit(1);
    }

    System.exit(0);
  }



  class Arguments
  {

    /**
     Parses and validates the arguments specified on the command line.

     @param arguments The command line arguments.
     @param jmaker The object to apply the arguments to.
     @return An indication of whether the parse succeeded.
     **/
    boolean parse(String[] argList, ToolboxJarMaker jmaker)
    {
      Vector components = null; // Strings
      Vector languages = null; // Strings
      Vector ccsids = null; // Integers
      Vector ccsidsExcluded = null; // Integers
      boolean includeBeans = false;
      boolean noProxySpecified = false;
      boolean succeeded = true;

      // Submit the arguments to the superclass first.
      if (!jmaker.parseArgs(argList, true)) // 2nd arg tells superclass
      {                                 // to tolerate unrecognized options.
        printUsage(System.err);
        return false;
      }

      // Get the args that the superclass didn't recognize.
      String[] args = jmaker.getUnrecognizedArgs();
      if (DEBUG)
      {
        System.out.print("ToolboxJarMaker.parse(): Arguments leftover from JarMaker parse: ");
        for (int i=0; i<args.length; i++)
          System.out.print(args[i] + " ");
        System.out.println();
      }

      if (args.length == 0) {
        if (requestedUsageInfo_) {
          printUsage(System.out);
          return false;
        }
        else
          return succeeded;  // the superclass handled all the arguments
      }

      Vector options = new Vector();
      options.addElement("-component");
      options.addElement("-beans");
      options.addElement("-language");
      options.addElement("-languageDirectory");
      options.addElement("-ccsid");
      options.addElement("-ccsidExcluded");
      options.addElement("-noProxy");
      options.addElement("-excludeSomeDependencies");

      Hashtable shortcuts = new Hashtable();
      shortcuts.put("-c",                  "-component");
      shortcuts.put("-comp",               "-component");
      shortcuts.put("-b",                  "-beans");
      shortcuts.put("-l",                  "-language");
      shortcuts.put("-lang",               "-language");
      shortcuts.put("-ld",                 "-languageDirectory");
      shortcuts.put("-langdir",            "-languageDirectory");
      shortcuts.put("-languagedir",        "-languageDirectory");
      shortcuts.put("-cc",                 "-ccsid");
      shortcuts.put("-cx",                 "-ccsidExcluded");
      shortcuts.put("-ccx",                "-ccsidExcluded");
      shortcuts.put("-ccsidex",            "-ccsidExcluded");
      shortcuts.put("-ccsidsex",           "-ccsidExcluded");
      shortcuts.put("-np",                 "-noProxy");
      shortcuts.put("-npprox",             "-noProxy");
      shortcuts.put("-xd",                 "-excludeSomeDependencies");
      shortcuts.put("-xsd",                "-excludeSomeDependencies");
      shortcuts.put("-excludesome",        "-excludeSomeDependencies");

      CommandLineArguments arguments = new CommandLineArguments(args, options, shortcuts);

      // Examine the arguments.

      String val;

      if (verbose_) {
        System.out.print("Arguments parsed by ToolboxJarMaker: ");
        String opts = JarMaker.listCommandOptions(arguments, JarMaker.OPTIONS_ALL);
        System.out.println(opts);

        opts = JarMaker.listCommandOptions(arguments, JarMaker.OPTIONS_UNRECOGNIZED);
        if (opts.length() != 0) {
          System.out.print("Arguments unrecognized by ToolboxJarMaker: ");
          System.out.println(opts);
        }
      }

      val = arguments.getOptionValue("-component");
      if (val != null) {
        if (val.length() != 0) {
          // Parse the list of components, separated by commas.
          StringTokenizer st = new StringTokenizer(val, ",");
          if (st.countTokens() != 0)
          {
            if (components == null)
              components = new Vector(st.countTokens());
            boolean badComponent = false;
            while (st.hasMoreTokens()) {
              String token = st.nextToken();
              Integer component = getComponentID(token);
              if (component == null)
              {
                System.err.println("Error: Unrecognized component name: " + token);
                badComponent = true;
                succeeded = false;
              }
              else
                addElement(components, component);
            }
            if (badComponent && jmaker.isVerbose())
            {
              System.err.println("The recognized components are:");
              int j;
              for (j=0; j<VALID_COMPONENTS.length-1; ++j)
                System.err.print(VALID_COMPONENTS[j] + ", ");
              System.err.println(VALID_COMPONENTS[j]);
            }
          }
        }
        else {
          System.err.println("Warning: No component specified after -component.");
        }
      }

      includeBeans = (arguments.isOptionSpecified("-beans"));

      val = arguments.getOptionValue("-language");
      if (val != null) {
        if (val.length() != 0) {
          // Parse the list of languages, separated by commas.
          StringTokenizer st = new StringTokenizer(val, ",");
          if (st.countTokens() != 0)
          {
            if (languages == null)
              languages = new Vector(st.countTokens());
            while (st.hasMoreTokens())
              addElement(languages, st.nextToken());
          }
        }
        else {
          System.err.println("Warning: No language specified after -language.");
        }
      }

      val = arguments.getOptionValue("-languageDirectory");
      if (val != null) {
        if (val.length() != 0) {
          jmaker.setLanguageDirectory(new File(val));
        }
        else {
          System.err.println("Warning: No directory specified after -languageDirectory.");
        }
      }

      val = arguments.getOptionValue("-ccsid");
      if (val != null) {
        if (val.length() != 0) {
          // Parse the list of CCSIDs, separated by commas.
          StringTokenizer st = new StringTokenizer(val, ",");
          if (st.countTokens() != 0)
          {
            if (ccsids == null)
              ccsids = new Vector(st.countTokens());
            while (st.hasMoreTokens())
            {
              String token = st.nextToken();
              Integer ccsid = null;
              try { ccsid = new Integer(token); }
              catch (NumberFormatException e) {
                System.err.println("Error: Non-integer CCSID value: " + token);
                succeeded = false;
                continue; // skip to next token in list
              }
              addElement(ccsids, ccsid);
            }
          }
        }
        else {
          System.err.println("Warning: No CCSID specified after -ccsid.");
        }
      }

      val = arguments.getOptionValue("-ccsidExcluded");
      if (val != null) {
        if (val.length() != 0) {
          // Parse the list of CCSIDs, separated by commas.
          StringTokenizer st = new StringTokenizer(val, ",");
          if (st.countTokens() != 0)
          {
            if (ccsidsExcluded == null)
              ccsidsExcluded = new Vector(st.countTokens());
            while (st.hasMoreTokens())
            {
              String token = st.nextToken();
              Integer ccsid = null;
              try {ccsid = new Integer(token);}
              catch (NumberFormatException e) {
                System.err.println("Error: Non-integer CCSID value: " + token);
                succeeded = false;
                continue; // skip to next token in list
              }
              addElement(ccsidsExcluded, ccsid);
            }
          }
        }
        else {
          System.err.println("Warning: No CCSID specified after -ccsidExcluded.");
        }
      }

      if (arguments.isOptionSpecified("-noproxy")) {
        noProxy_ = true;
        noProxySpecified = true;
      }

      excludeSomeDependencies_ = (arguments.isOptionSpecified("-excludeSomeDependencies"));


      // Check for any extra arguments.
      Enumeration enum1 = arguments.getExtraOptions();
      while (enum1.hasMoreElements()) {
        String optionName = (String)enum1.nextElement();
        String optionVal = arguments.getOptionValue(optionName);
        String optionWithVal = ( optionVal == null ? optionName : optionName + " " + optionVal );
        System.err.println("Error: Unrecognized option: " + optionWithVal);
        succeeded = false;
      }

      if (jmaker.isSplit())
      {
        if ((components != null) ||
            (includeBeans == true) ||
            (languages != null) ||
            (ccsids != null) ||
            (ccsidsExcluded != null))
        {
          System.err.println("Warning: When -split is specified, " +
                              "all other options are ignored, except " +
                              "-source and -verbose.");
        }
      }
      else  // not a split
      {
        // Check for sufficient options.
        if (!jmaker.isOptionInfoSufficient() && // enough options for superclass
            components == null &&
            languages == null &&
            ccsids == null &&
            ccsidsExcluded == null &&
            noProxySpecified == false &&
            excludeSomeDependencies_ == false)
        {
          System.err.println("Error: Need to specify more options.");
          succeeded = false;
        }

        // Set any languages.
        if (languages != null)
          setLanguages(languages);

        if (components != null)
        {
          if (components.size() != 0)
            setComponents(components, includeBeans);
          else succeeded = false;  // bogus components
        }
        if (ccsids != null)
          setCCSIDs(ccsids);
        if (ccsidsExcluded != null)
          setCCSIDsExcluded(ccsidsExcluded);
      }

      if (!succeeded)
        printUsage(System.err);

      return succeeded;
    }

    /**
     Prints the usage information.

     @param output   The output stream.
     **/
    private void printUsage(PrintStream output)
    {
      output.println();
      output.println("Usage: ");
      output.println();
      output.println("  ToolboxJarMaker [-source jarFile]");
      output.println("           [-destination jarFile]");
      output.println("           [-fileRequired entry1[,entry2[...]]]");
      output.println("           [-fileExcluded entry1[,entry2[...]]]");
      output.println("           [-additionalFile file1[,file2[...]]]");
      output.println("           [-additionalFilesDirectory directory");
      output.println("           [-package pkg1[,pkg2[...]]]");
      output.println("           [-packageExcluded pkg1[,pkg2[...]]]");
      output.println("           [-extract [directory]]");
      output.println("           [-split [kilobytes]]");
      output.println("           [-component comp1[,comp2[...]]]");
      output.println("           [-beans]");
      output.println("           [-language language1[,language2[...]]]");
      output.println("           [-languageDirectory directory");
      output.println("           [-ccsid ccsid1[,ccsid2[...]]]");
      output.println("           [-ccsidExcluded ccsid1[,ccsid2[...]]]");
      output.println("           [-noProxy]");
      output.println("           [-excludeSomeDependencies]");
      output.println("           [-verbose]");
      output.println("           [-help]");
      output.println();
      output.println("At least one of the following options must be specified: ");
      output.println("-fileRequired, -fileExcluded, -additionalFile, -package, -packageExcluded, -extract, -split, " +
                      "-component, -language, -ccsid, -ccsidExcluded, -noProxy");
      output.println();
      output.println("The -excludeSomeDependencies option is not recommended " +
                      "for pre-V5R2 Toolbox JAR files.");
    }

  }

}
