    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.ftp;
    019
    020import java.text.DateFormatSymbols;
    021import java.util.Collection;
    022import java.util.Locale;
    023import java.util.Map;
    024import java.util.StringTokenizer;
    025import java.util.TreeMap;
    026
    027/**
    028 * <p>
    029 * This class implements an alternate means of configuring the
    030 * {@link  org.apache.commons.net.ftp.FTPClient  FTPClient} object and
    031 * also subordinate objects which it uses.  Any class implementing the
    032 * {@link  org.apache.commons.net.ftp.Configurable  Configurable }
    033 * interface can be configured by this object.
    034 * </p><p>
    035 * In particular this class was designed primarily to support configuration
    036 * of FTP servers which express file timestamps in formats and languages
    037 * other than those for the US locale, which although it is the most common
    038 * is not universal.  Unfortunately, nothing in the FTP spec allows this to
    039 * be determined in an automated way, so manual configuration such as this
    040 * is necessary.
    041 * </p><p>
    042 * This functionality was designed to allow existing clients to work exactly
    043 * as before without requiring use of this component.  This component should
    044 * only need to be explicitly invoked by the user of this package for problem
    045 * cases that previous implementations could not solve.
    046 * </p>
    047 * <h2>Examples of use of FTPClientConfig</h2>
    048 * Use cases:
    049 * You are trying to access a server that
    050 * <ul>
    051 * <li>lists files with timestamps that use month names in languages other
    052 * than English</li>
    053 * <li>lists files with timestamps that use date formats other
    054 * than the American English "standard" <code>MM dd yyyy</code></li>
    055 * <li>is in different timezone and you need accurate timestamps for
    056 * dependency checking as in Ant</li>
    057 * </ul>
    058 * <p>
    059 * Unpaged (whole list) access on a UNIX server that uses French month names
    060 * but uses the "standard" <code>MMM d yyyy</code> date formatting
    061 * <pre>
    062 *    FTPClient f=FTPClient();
    063 *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
    064 *    conf.setServerLanguageCode("fr");
    065 *    f.configure(conf);
    066 *    f.connect(server);
    067 *    f.login(username, password);
    068 *    FTPFile[] files = listFiles(directory);
    069 * </pre>
    070 * <p>
    071 * Paged access on a UNIX server that uses Danish month names
    072 * and "European" date formatting in Denmark's time zone, when you
    073 * are in some other time zone.
    074 * <pre>
    075 *    FTPClient f=FTPClient();
    076 *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
    077 *    conf.setServerLanguageCode("da");
    078 *    conf.setDefaultDateFormat("d MMM yyyy");
    079 *    conf.setRecentDateFormat("d MMM HH:mm");
    080 *    conf.setTimeZoneId("Europe/Copenhagen");
    081 *    f.configure(conf);
    082 *    f.connect(server);
    083 *    f.login(username, password);
    084 *    FTPListParseEngine engine =
    085 *       f.initiateListParsing("com.whatever.YourOwnParser", directory);
    086 *
    087 *    while (engine.hasNext()) {
    088 *       FTPFile[] files = engine.getNext(25);  // "page size" you want
    089 *       //do whatever you want with these files, display them, etc.
    090 *       //expensive FTPFile objects not created until needed.
    091 *    }
    092 * </pre>
    093 * <p>
    094 * Unpaged (whole list) access on a VMS server that uses month names
    095 * in a language not {@link #getSupportedLanguageCodes() supported} by the system.
    096 * but uses the "standard" <code>MMM d yyyy</code> date formatting
    097 * <pre>
    098 *    FTPClient f=FTPClient();
    099 *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS);
    100 *    conf.setShortMonthNames(
    101 *        "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des");
    102 *    f.configure(conf);
    103 *    f.connect(server);
    104 *    f.login(username, password);
    105 *    FTPFile[] files = listFiles(directory);
    106 * </pre>
    107 * <p>
    108 * Unpaged (whole list) access on a Windows-NT server in a different time zone.
    109 * (Note, since the NT Format uses numeric date formatting, language issues
    110 * are irrelevant here).
    111 * <pre>
    112 *    FTPClient f=FTPClient();
    113 *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT);
    114 *    conf.setTimeZoneId("America/Denver");
    115 *    f.configure(conf);
    116 *    f.connect(server);
    117 *    f.login(username, password);
    118 *    FTPFile[] files = listFiles(directory);
    119 * </pre>
    120 * Unpaged (whole list) access on a Windows-NT server in a different time zone
    121 * but which has been configured to use a unix-style listing format.
    122 * <pre>
    123 *    FTPClient f=FTPClient();
    124 *    FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
    125 *    conf.setTimeZoneId("America/Denver");
    126 *    f.configure(conf);
    127 *    f.connect(server);
    128 *    f.login(username, password);
    129 *    FTPFile[] files = listFiles(directory);
    130 * </pre>
    131 *
    132 * @since 1.4
    133 * @see org.apache.commons.net.ftp.Configurable
    134 * @see org.apache.commons.net.ftp.FTPClient
    135 * @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig)
    136 * @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl
    137 */
    138public class FTPClientConfig
    139{
    140
    141    /**
    142     * Identifier by which a unix-based ftp server is known throughout
    143     * the commons-net ftp system.
    144     */
    145    public static final String SYST_UNIX  = "UNIX";
    146
    147    /**
    148     * Identifier for alternate UNIX parser; same as {@link #SYST_UNIX} but leading spaces are
    149     * trimmed from file names. This is to maintain backwards compatibility with
    150     * the original behavior of the parser which ignored multiple spaces between the date
    151     * and the start of the file name.
    152     * @since 3.4
    153     */
    154    public static final String SYST_UNIX_TRIM_LEADING  = "UNIX_LTRIM";
    155
    156    /**
    157     * Identifier by which a vms-based ftp server is known throughout
    158     * the commons-net ftp system.
    159     */
    160    public static final String SYST_VMS   = "VMS";
    161
    162    /**
    163     * Identifier by which a WindowsNT-based ftp server is known throughout
    164     * the commons-net ftp system.
    165     */
    166    public static final String SYST_NT    = "WINDOWS";
    167
    168    /**
    169     * Identifier by which an OS/2-based ftp server is known throughout
    170     * the commons-net ftp system.
    171     */
    172    public static final String SYST_OS2   = "OS/2";
    173
    174    /**
    175     * Identifier by which an OS/400-based ftp server is known throughout
    176     * the commons-net ftp system.
    177     */
    178    public static final String SYST_OS400 = "OS/400";
    179
    180    /**
    181     * Identifier by which an AS/400-based ftp server is known throughout
    182     * the commons-net ftp system.
    183     */
    184    public static final String SYST_AS400 = "AS/400";
    185
    186    /**
    187     * Identifier by which an MVS-based ftp server is known throughout
    188     * the commons-net ftp system.
    189     */
    190    public static final String SYST_MVS = "MVS";
    191
    192    /**
    193     * Some servers return an "UNKNOWN Type: L8" message
    194     * in response to the SYST command. We set these to be a Unix-type system.
    195     * This may happen if the ftpd in question was compiled without system
    196     * information.
    197     *
    198     * NET-230 - Updated to be UPPERCASE so that the check done in
    199     * createFileEntryParser will succeed.
    200     *
    201     * @since 1.5
    202     */
    203    public static final String SYST_L8 = "TYPE: L8";
    204
    205    /**
    206     * Identifier by which an Netware-based ftp server is known throughout
    207     * the commons-net ftp system.
    208     *
    209     * @since 1.5
    210     */
    211    public static final String SYST_NETWARE = "NETWARE";
    212
    213    /**
    214     * Identifier by which a Mac pre OS-X -based ftp server is known throughout
    215     * the commons-net ftp system.
    216     *
    217     * @since 3.1
    218     */
    219    // Full string is "MACOS Peter's Server"; the substring below should be enough
    220    public static final String SYST_MACOS_PETER  = "MACOS PETER"; // NET-436
    221
    222    private final String serverSystemKey;
    223    private String defaultDateFormatStr = null;
    224    private String recentDateFormatStr = null;
    225    private boolean lenientFutureDates = true; // NET-407
    226    private String serverLanguageCode = null;
    227    private String shortMonthNames = null;
    228    private String serverTimeZoneId = null;
    229    private boolean saveUnparseableEntries = false;
    230
    231
    232    /**
    233     * The main constructor for an FTPClientConfig object
    234     * @param systemKey key representing system type of the  server being
    235     * connected to. See {@link #getServerSystemKey() serverSystemKey}
    236     * If set to the empty string, then FTPClient uses the system type returned by the server.
    237     * However this is not recommended for general use;
    238     * the correct system type should be set if it is known.
    239     */
    240    public FTPClientConfig(final String systemKey) {
    241        this.serverSystemKey = systemKey;
    242    }
    243
    244    /**
    245     * Convenience constructor mainly for use in testing.
    246     * Constructs a UNIX configuration.
    247     */
    248    public FTPClientConfig() {
    249        this(SYST_UNIX);
    250    }
    251
    252    /**
    253     * Constructor which allows setting of the format string member fields
    254     * @param systemKey key representing system type of the  server being
    255     * connected to. See
    256     *  {@link #getServerSystemKey() serverSystemKey}
    257     * @param defaultDateFormatStr See
    258     *  {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
    259     * @param recentDateFormatStr See
    260     *  {@link  #setRecentDateFormatStr(String)  recentDateFormatStr}
    261     *  @since 3.6
    262     */
    263    public FTPClientConfig(final String systemKey,
    264                           final String defaultDateFormatStr,
    265                           final String recentDateFormatStr)
    266    {
    267        this(systemKey);
    268        this.defaultDateFormatStr = defaultDateFormatStr;
    269        this.recentDateFormatStr = recentDateFormatStr;
    270    }
    271
    272    /**
    273     * Constructor which allows setting of most member fields
    274     * @param systemKey key representing system type of the  server being
    275     * connected to. See
    276     *  {@link #getServerSystemKey() serverSystemKey}
    277     * @param defaultDateFormatStr See
    278     *  {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
    279     * @param recentDateFormatStr See
    280     *  {@link  #setRecentDateFormatStr(String)  recentDateFormatStr}
    281     * @param serverLanguageCode See
    282     *  {@link  #setServerLanguageCode(String)  serverLanguageCode}
    283     * @param shortMonthNames See
    284     *  {@link  #setShortMonthNames(String)  shortMonthNames}
    285     * @param serverTimeZoneId See
    286     *  {@link  #setServerTimeZoneId(String)  serverTimeZoneId}
    287     */
    288    public FTPClientConfig(final String systemKey,
    289                           final String defaultDateFormatStr,
    290                           final String recentDateFormatStr,
    291                           final String serverLanguageCode,
    292                           final String shortMonthNames,
    293                           final String serverTimeZoneId)
    294    {
    295        this(systemKey);
    296        this.defaultDateFormatStr = defaultDateFormatStr;
    297        this.recentDateFormatStr = recentDateFormatStr;
    298        this.serverLanguageCode = serverLanguageCode;
    299        this.shortMonthNames = shortMonthNames;
    300        this.serverTimeZoneId = serverTimeZoneId;
    301    }
    302
    303    /**
    304     * Constructor which allows setting of all member fields
    305     * @param systemKey key representing system type of the  server being
    306     * connected to. See
    307     *  {@link #getServerSystemKey() serverSystemKey}
    308     * @param defaultDateFormatStr See
    309     *  {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
    310     * @param recentDateFormatStr See
    311     *  {@link  #setRecentDateFormatStr(String)  recentDateFormatStr}
    312     * @param serverLanguageCode See
    313     *  {@link  #setServerLanguageCode(String)  serverLanguageCode}
    314     * @param shortMonthNames See
    315     *  {@link  #setShortMonthNames(String)  shortMonthNames}
    316     * @param serverTimeZoneId See
    317     *  {@link  #setServerTimeZoneId(String)  serverTimeZoneId}
    318     * @param lenientFutureDates See
    319     * {@link  #setLenientFutureDates(boolean)  lenientFutureDates}
    320     * @param saveUnparseableEntries See
    321     * {@link  #setUnparseableEntries(boolean)  saveUnparseableEntries}
    322     */
    323    public FTPClientConfig(final String systemKey,
    324                           final String defaultDateFormatStr,
    325                           final String recentDateFormatStr,
    326                           final String serverLanguageCode,
    327                           final String shortMonthNames,
    328                           final String serverTimeZoneId,
    329                           final boolean lenientFutureDates,
    330                           final boolean saveUnparseableEntries)
    331    {
    332        this(systemKey);
    333        this.defaultDateFormatStr = defaultDateFormatStr;
    334        this.lenientFutureDates = lenientFutureDates;
    335        this.recentDateFormatStr = recentDateFormatStr;
    336        this.saveUnparseableEntries = saveUnparseableEntries;
    337        this.serverLanguageCode = serverLanguageCode;
    338        this.shortMonthNames = shortMonthNames;
    339        this.serverTimeZoneId = serverTimeZoneId;
    340    }
    341
    342    // Copy constructor, intended for use by FTPClient only
    343    FTPClientConfig(final String systemKey, final FTPClientConfig config) {
    344        this.serverSystemKey = systemKey;
    345        this.defaultDateFormatStr = config.defaultDateFormatStr;
    346        this.lenientFutureDates = config.lenientFutureDates;
    347        this.recentDateFormatStr = config.recentDateFormatStr;
    348        this.saveUnparseableEntries = config.saveUnparseableEntries;
    349        this.serverLanguageCode = config.serverLanguageCode;
    350        this.serverTimeZoneId = config.serverTimeZoneId;
    351        this.shortMonthNames = config.shortMonthNames;
    352    }
    353
    354    /**
    355     * Copy constructor
    356     * @param config source
    357     * @since 3.6
    358     */
    359    public FTPClientConfig(final FTPClientConfig config) {
    360        this.serverSystemKey = config.serverSystemKey;
    361        this.defaultDateFormatStr = config.defaultDateFormatStr;
    362        this.lenientFutureDates = config.lenientFutureDates;
    363        this.recentDateFormatStr = config.recentDateFormatStr;
    364        this.saveUnparseableEntries = config.saveUnparseableEntries;
    365        this.serverLanguageCode = config.serverLanguageCode;
    366        this.serverTimeZoneId = config.serverTimeZoneId;
    367        this.shortMonthNames = config.shortMonthNames;
    368    }
    369
    370    private static final Map<String, Object> LANGUAGE_CODE_MAP = new TreeMap<>();
    371    static {
    372
    373        // if there are other commonly used month name encodings which
    374        // correspond to particular locales, please add them here.
    375
    376
    377
    378        // many locales code short names for months as all three letters
    379        // these we handle simply.
    380        LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH);
    381        LANGUAGE_CODE_MAP.put("de",Locale.GERMAN);
    382        LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN);
    383        LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish
    384        LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese
    385        LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish
    386        LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish
    387        LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian
    388        LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch
    389        LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian
    390        LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian
    391        LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian
    392        LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak
    393        LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian
    394
    395
    396        // some don't
    397        LANGUAGE_CODE_MAP.put("fr",
    398                "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c");  //french
    399
    400    }
    401
    402    /**
    403     * Getter for the serverSystemKey property.  This property
    404     * specifies the general type of server to which the client connects.
    405     * Should be either one of the <code>FTPClientConfig.SYST_*</code> codes
    406     * or else the fully qualified class name of a parser implementing both
    407     * the <code>FTPFileEntryParser</code> and <code>Configurable</code>
    408     * interfaces.
    409     * @return Returns the serverSystemKey property.
    410     */
    411    public String getServerSystemKey() {
    412        return serverSystemKey;
    413    }
    414
    415    /**
    416     * getter for the {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
    417     * property.
    418     * @return Returns the defaultDateFormatStr property.
    419     */
    420    public String getDefaultDateFormatStr() {
    421        return defaultDateFormatStr;
    422    }
    423
    424    /**
    425     * getter for the {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property.
    426     * @return Returns the recentDateFormatStr property.
    427     */
    428
    429    public String getRecentDateFormatStr() {
    430        return recentDateFormatStr;
    431    }
    432
    433    /**
    434     * getter for the {@link  #setServerTimeZoneId(String)  serverTimeZoneId} property.
    435     * @return Returns the serverTimeZoneId property.
    436     */
    437    public String getServerTimeZoneId() {
    438        return serverTimeZoneId;
    439    }
    440
    441    /**
    442     * <p>
    443     * getter for the {@link  #setShortMonthNames(String)  shortMonthNames}
    444     * property.
    445     * </p>
    446     * @return Returns the shortMonthNames.
    447     */
    448    public String getShortMonthNames() {
    449        return shortMonthNames;
    450    }
    451
    452    /**
    453     * <p>
    454     * getter for the {@link  #setServerLanguageCode(String)  serverLanguageCode} property.
    455     * </p>
    456     * @return Returns the serverLanguageCode property.
    457     */
    458    public String getServerLanguageCode() {
    459        return serverLanguageCode;
    460    }
    461
    462    /**
    463     * <p>
    464     * getter for the {@link  #setLenientFutureDates(boolean)  lenientFutureDates} property.
    465     * </p>
    466     * @return Returns the lenientFutureDates (default true).
    467     * @since 1.5
    468     */
    469    public boolean isLenientFutureDates() {
    470        return lenientFutureDates;
    471    }
    472    /**
    473     * <p>
    474     * setter for the defaultDateFormatStr property.  This property
    475     * specifies the main date format that will be used by a parser configured
    476     * by this configuration to parse file timestamps.  If this is not
    477     * specified, such a parser will use as a default value, the most commonly
    478     * used format which will be in as used in <code>en_US</code> locales.
    479     * </p><p>
    480     * This should be in the format described for
    481     * <code>java.text.SimpleDateFormat</code>.
    482     * property.
    483     * </p>
    484     * @param defaultDateFormatStr The defaultDateFormatStr to set.
    485     */
    486    public void setDefaultDateFormatStr(final String defaultDateFormatStr) {
    487        this.defaultDateFormatStr = defaultDateFormatStr;
    488    }
    489
    490    /**
    491     * <p>
    492     * setter for the recentDateFormatStr property.  This property
    493     * specifies a secondary date format that will be used by a parser
    494     * configured by this configuration to parse file timestamps, typically
    495     * those less than a year old.  If this is  not specified, such a parser
    496     * will not attempt to parse using an alternate format.
    497     * </p>
    498     * <p>
    499     * This is used primarily in unix-based systems.
    500     * </p>
    501     * <p>
    502     * This should be in the format described for
    503     * <code>java.text.SimpleDateFormat</code>.
    504     * </p>
    505     * @param recentDateFormatStr The recentDateFormatStr to set.
    506     */
    507    public void setRecentDateFormatStr(final String recentDateFormatStr) {
    508        this.recentDateFormatStr = recentDateFormatStr;
    509    }
    510
    511    /**
    512     * <p>
    513     * setter for the lenientFutureDates property.  This boolean property
    514     * (default: true) only has meaning when a
    515     * {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} property
    516     * has been set.  In that case, if this property is set true, then the
    517     * parser, when it encounters a listing parseable with the recent date
    518     * format, will only consider a date to belong to the previous year if
    519     * it is more than one day in the future.  This will allow all
    520     * out-of-synch situations (whether based on "slop" - i.e. servers simply
    521     * out of synch with one another or because of time zone differences -
    522     * but in the latter case it is highly recommended to use the
    523     * {@link  #setServerTimeZoneId(String)  serverTimeZoneId} property
    524     * instead) to resolve correctly.
    525     * </p><p>
    526     * This is used primarily in unix-based systems.
    527     * </p>
    528     * @param lenientFutureDates set true to compensate for out-of-synch
    529     * conditions.
    530     */
    531    public void setLenientFutureDates(final boolean lenientFutureDates) {
    532        this.lenientFutureDates = lenientFutureDates;
    533    }
    534    /**
    535     * <p>
    536     * setter for the serverTimeZoneId property.  This property
    537     * allows a time zone to be specified corresponding to that known to be
    538     * used by an FTP server in file listings.  This might be particularly
    539     * useful to clients such as Ant that try to use these timestamps for
    540     * dependency checking.
    541     * </p><p>
    542     * This should be one of the identifiers used by
    543     * <code>java.util.TimeZone</code> to refer to time zones, for example,
    544     * <code>America/Chicago</code> or <code>Asia/Rangoon</code>.
    545     * </p>
    546     * @param serverTimeZoneId The serverTimeZoneId to set.
    547     */
    548    public void setServerTimeZoneId(final String serverTimeZoneId) {
    549        this.serverTimeZoneId = serverTimeZoneId;
    550    }
    551
    552    /**
    553     * <p>
    554     * setter for the shortMonthNames property.
    555     * This property allows the user to specify a set of month names
    556     * used by the server that is different from those that may be
    557     * specified using the {@link  #setServerLanguageCode(String)  serverLanguageCode}
    558     * property.
    559     * </p><p>
    560     * This should be a string containing twelve strings each composed of
    561     * three characters, delimited by pipe (|) characters.  Currently,
    562     * only 8-bit ASCII characters are known to be supported.  For example,
    563     * a set of month names used by a hypothetical Icelandic FTP server might
    564     * conceivably be specified as
    565     * <code>"jan|feb|mar|apr|ma&#xED;|j&#xFA;n|j&#xFA;l|&#xE1;g&#xFA;|sep|okt|n&#xF3;v|des"</code>.
    566     * </p>
    567     * @param shortMonthNames The value to set to the shortMonthNames property.
    568     */
    569    public void setShortMonthNames(final String shortMonthNames) {
    570        this.shortMonthNames = shortMonthNames;
    571    }
    572
    573    /**
    574     * <p>
    575     * setter for the serverLanguageCode property.  This property allows
    576     * user to specify a
    577     * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
    578     * two-letter ISO-639 language code</a> that will be used to
    579     * configure the set of month names used by the file timestamp parser.
    580     * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames}
    581     * is specified, parsing will assume English month names, which may or
    582     * may not be significant, depending on whether the date format(s)
    583     * specified via {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr}
    584     * and/or {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} are using
    585     * numeric or alphabetic month names.
    586     * </p>
    587     * <p>If the code supplied is not supported here, <code>en_US</code>
    588     * month names will be used.  We are supporting here those language
    589     * codes which, when a <code> java.util.Locale</code> is constucted
    590     * using it, and a <code>java.text.SimpleDateFormat</code> is
    591     * constructed using that Locale, the array returned by the
    592     * SimpleDateFormat's <code>getShortMonths()</code> method consists
    593     * solely of three 8-bit ASCII character strings.  Additionally,
    594     * languages which do not meet this requirement are included if a
    595     * common alternative set of short month names is known to be used.
    596     * This means that users who can tell us of additional such encodings
    597     * may get them added to the list of supported languages by contacting
    598     * the Apache Commons Net team.
    599     * </p>
    600     * <p><strong>
    601     * Please note that this attribute will NOT be used to determine a
    602     * locale-based date format for the language.  </strong>
    603     * Experience has shown that many if not most FTP servers outside the
    604     * United States employ the standard <code>en_US</code> date format
    605     * orderings of <code>MMM d yyyy</code> and <code>MMM d HH:mm</code>
    606     * and attempting to deduce this automatically here would cause more
    607     * problems than it would solve.  The date format must be changed
    608     * via the {@link  #setDefaultDateFormatStr(String)  defaultDateFormatStr} and/or
    609     * {@link  #setRecentDateFormatStr(String)  recentDateFormatStr} parameters.
    610     * </p>
    611     * @param serverLanguageCode The value to set to the serverLanguageCode property.
    612     */
    613    public void setServerLanguageCode(final String serverLanguageCode) {
    614        this.serverLanguageCode = serverLanguageCode;
    615    }
    616
    617    /**
    618     * Looks up the supplied language code in the internally maintained table of
    619     * language codes.  Returns a DateFormatSymbols object configured with
    620     * short month names corresponding to the code.  If there is no corresponding
    621     * entry in the table, the object returned will be that for
    622     * <code>Locale.US</code>
    623     * @param languageCode See {@link  #setServerLanguageCode(String)  serverLanguageCode}
    624     * @return a DateFormatSymbols object configured with short month names
    625     * corresponding to the supplied code, or with month names for
    626     * <code>Locale.US</code> if there is no corresponding entry in the internal
    627     * table.
    628     */
    629    public static DateFormatSymbols lookupDateFormatSymbols(final String languageCode)
    630    {
    631        final Object lang = LANGUAGE_CODE_MAP.get(languageCode);
    632        if (lang != null) {
    633            if (lang instanceof Locale) {
    634                return new DateFormatSymbols((Locale) lang);
    635            } else if (lang instanceof String){
    636                return getDateFormatSymbols((String) lang);
    637            }
    638        }
    639        return new DateFormatSymbols(Locale.US);
    640    }
    641
    642    /**
    643     * Returns a DateFormatSymbols object configured with short month names
    644     * as in the supplied string
    645     * @param shortmonths This  should be as described in
    646     *  {@link  #setShortMonthNames(String)  shortMonthNames}
    647     * @return a DateFormatSymbols object configured with short month names
    648     * as in the supplied string
    649     */
    650    public static DateFormatSymbols getDateFormatSymbols(final String shortmonths)
    651    {
    652        final String[] months = splitShortMonthString(shortmonths);
    653        final DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
    654        dfs.setShortMonths(months);
    655        return dfs;
    656    }
    657
    658    private static String[] splitShortMonthString(final String shortmonths) {
    659        final StringTokenizer st = new StringTokenizer(shortmonths, "|");
    660        final int monthcnt = st.countTokens();
    661        if (12 != monthcnt) {
    662            throw new IllegalArgumentException(
    663                    "expecting a pipe-delimited string containing 12 tokens");
    664        }
    665        final String[] months = new String[13];
    666        int pos = 0;
    667        while(st.hasMoreTokens()) {
    668            months[pos++] = st.nextToken();
    669        }
    670        months[pos]="";
    671        return months;
    672    }
    673
    674    /**
    675     * Returns a Collection of all the language codes currently supported
    676     * by this class. See {@link  #setServerLanguageCode(String)  serverLanguageCode}
    677     * for a functional descrption of language codes within this system.
    678     *
    679     * @return a Collection of all the language codes currently supported
    680     * by this class
    681     */
    682    public static Collection<String> getSupportedLanguageCodes() {
    683        return LANGUAGE_CODE_MAP.keySet();
    684    }
    685
    686    /**
    687     * Allow list parsing methods to create basic FTPFile entries if parsing fails.
    688     * <p>
    689     * In this case, the FTPFile will contain only the unparsed entry {@link FTPFile#getRawListing()}
    690     * and {@link FTPFile#isValid()} will return {@code false}
    691     * @param saveUnparseable if true, then create FTPFile entries if parsing fails
    692     * @since 3.4
    693     */
    694    public void setUnparseableEntries(final boolean saveUnparseable) {
    695        this.saveUnparseableEntries = saveUnparseable;
    696    }
    697
    698    /**
    699     * @return true if list parsing should return FTPFile entries even for unparseable response lines
    700     * <p>
    701     * If true, the FTPFile for any unparseable entries will contain only the unparsed entry
    702     * {@link FTPFile#getRawListing()} and {@link FTPFile#isValid()} will return {@code false}
    703     * @since 3.4
    704     */
    705    public boolean getUnparseableEntries() {
    706        return this.saveUnparseableEntries;
    707    }
    708
    709}