From 063284837c8c366e5502b1b0264b8eb807b61732 Mon Sep 17 00:00:00 2001
From: Joe Robinson
+ * This class implements an alternate means of configuring the
+ * {@link org.apache.commons.net.ftp.FTPClient FTPClient} object and
+ * also subordinate objects which it uses. Any class implementing the
+ * {@link org.apache.commons.net.ftp.Configurable Configurable }
+ * interface can be configured by this object.
+ *
+ * In particular this class was designed primarily to support configuration
+ * of FTP servers which express file timestamps in formats and languages
+ * other than those for the US locale, which although it is the most common
+ * is not universal. Unfortunately, nothing in the FTP spec allows this to
+ * be determined in an automated way, so manual configuration such as this
+ * is necessary.
+ *
+ * This functionality was designed to allow existing clients to work exactly
+ * as before without requiring use of this component. This component should
+ * only need to be explicitly invoked by the user of this package for problem
+ * cases that previous implementations could not solve.
+ *
+ * Unpaged (whole list) access on a UNIX server that uses French month names
+ * but uses the "standard" Examples of use of FTPClientConfig
+ * Use cases:
+ * You are trying to access a server that
+ *
+ *
+ * MM dd yyyy
MMM d yyyy
date formatting
+ *
+ * FTPClient f=FTPClient();
+ * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX);
+ * conf.setServerLanguageCode("fr");
+ * f.configure(conf);
+ * f.connect(server);
+ * f.login(username, password);
+ * FTPFile[] files = listFiles(directory);
+ *
+ *
+ * Paged access on a UNIX server that uses Danish month names + * and "European" date formatting in Denmark's time zone, when you + * are in some other time zone. + *
+ * FTPClient f=FTPClient(); + * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); + * conf.setServerLanguageCode("da"); + * conf.setDefaultDateFormat("d MMM yyyy"); + * conf.setRecentDateFormat("d MMM HH:mm"); + * conf.setTimeZoneId("Europe/Copenhagen"); + * f.configure(conf); + * f.connect(server); + * f.login(username, password); + * FTPListParseEngine engine = + * f.initiateListParsing("com.whatever.YourOwnParser", directory); + * + * while (engine.hasNext()) { + * FTPFile[] files = engine.getNext(25); // "page size" you want + * //do whatever you want with these files, display them, etc. + * //expensive FTPFile objects not created until needed. + * } + *+ * + *
+ * Unpaged (whole list) access on a VMS server that uses month names
+ * in a language not {@link #getSupportedLanguageCodes() supported} by the system.
+ * but uses the "standard" MMM d yyyy
date formatting
+ *
+ * FTPClient f=FTPClient(); + * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_VMS); + * conf.setShortMonthNames( + * "jan|feb|mar|apr|ma\u00ED|j\u00FAn|j\u00FAl|\u00e1g\u00FA|sep|okt|n\u00F3v|des"); + * f.configure(conf); + * f.connect(server); + * f.login(username, password); + * FTPFile[] files = listFiles(directory); + *+ * + *
+ * Unpaged (whole list) access on a Windows-NT server in a different time zone. + * (Note, since the NT Format uses numeric date formatting, language issues + * are irrelevant here). + *
+ * FTPClient f=FTPClient(); + * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT); + * conf.setTimeZoneId("America/Denver"); + * f.configure(conf); + * f.connect(server); + * f.login(username, password); + * FTPFile[] files = listFiles(directory); + *+ * + * Unpaged (whole list) access on a Windows-NT server in a different time zone + * but which has been configured to use a unix-style listing format. + *
+ * FTPClient f=FTPClient(); + * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); + * conf.setTimeZoneId("America/Denver"); + * f.configure(conf); + * f.connect(server); + * f.login(username, password); + * FTPFile[] files = listFiles(directory); + *+ * + * @since 1.4 + * @see org.apache.commons.net.ftp.Configurable + * @see org.apache.commons.net.ftp.FTPClient + * @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig) + * @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl + */ +public class FTPClientConfig +{ + + /** + * Identifier by which a unix-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_UNIX = "UNIX"; + + /** + * Identifier by which a vms-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_VMS = "VMS"; + + /** + * Identifier by which a WindowsNT-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_NT = "WINDOWS"; + + /** + * Identifier by which an OS/2-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_OS2 = "OS/2"; + + /** + * Identifier by which an OS/400-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_OS400 = "OS/400"; + + /** + * Identifier by which an AS/400-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_AS400 = "AS/400"; + + /** + * Identifier by which an MVS-based ftp server is known throughout + * the commons-net ftp system. + */ + public static final String SYST_MVS = "MVS"; + + /** + * Some servers return an "UNKNOWN Type: L8" message + * in response to the SYST command. We set these to be a Unix-type system. + * This may happen if the ftpd in question was compiled without system + * information. + * + * NET-230 - Updated to be UPPERCASE so that the check done in + * createFileEntryParser will succeed. + * + * @since 1.5 + */ + public static final String SYST_L8 = "TYPE: L8"; + + /** + * Identifier by which an Netware-based ftp server is known throughout + * the commons-net ftp system. + * + * @since 1.5 + */ + public static final String SYST_NETWARE = "NETWARE"; + + private final String serverSystemKey; + private String defaultDateFormatStr = null; + private String recentDateFormatStr = null; + private boolean lenientFutureDates = false; + private String serverLanguageCode = null; + private String shortMonthNames = null; + private String serverTimeZoneId = null; + + + /** + * The main constructor for an FTPClientConfig object + * @param systemKey key representing system type of the server being + * connected to. See {@link #getServerSystemKey() serverSystemKey} + */ + public FTPClientConfig(String systemKey) { + this.serverSystemKey = systemKey; + } + + /** + * Convenience constructor mainly for use in testing. + * Constructs a UNIX configuration. + */ + public FTPClientConfig() { + this(SYST_UNIX); + } + + /** + * Constructor which allows setting of all member fields + * @param systemKey key representing system type of the server being + * connected to. See + * {@link #getServerSystemKey() serverSystemKey} + * @param defaultDateFormatStr See + * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * @param recentDateFormatStr See + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} + * @param serverLanguageCode See + * {@link #setServerLanguageCode(String) serverLanguageCode} + * @param shortMonthNames See + * {@link #setShortMonthNames(String) shortMonthNames} + * @param serverTimeZoneId See + * {@link #setServerTimeZoneId(String) serverTimeZoneId} + */ + public FTPClientConfig(String systemKey, + String defaultDateFormatStr, + String recentDateFormatStr, + String serverLanguageCode, + String shortMonthNames, + String serverTimeZoneId) + { + this(systemKey); + this.defaultDateFormatStr = defaultDateFormatStr; + this.recentDateFormatStr = recentDateFormatStr; + this.serverLanguageCode = serverLanguageCode; + this.shortMonthNames = shortMonthNames; + this.serverTimeZoneId = serverTimeZoneId; + } + + private static Map
FTPClientConfig.SYST_*
codes
+ * or else the fully qualified class name of a parser implementing both
+ * the FTPFileEntryParser
and Configurable
+ * interfaces.
+ * @return Returns the serverSystemKey property.
+ */
+ public String getServerSystemKey() {
+ return serverSystemKey;
+ }
+
+ /**
+ * getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr}
+ * property.
+ * @return Returns the defaultDateFormatStr property.
+ */
+ public String getDefaultDateFormatStr() {
+ return defaultDateFormatStr;
+ }
+
+ /**
+ * getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property.
+ * @return Returns the recentDateFormatStr property.
+ */
+
+ public String getRecentDateFormatStr() {
+ return recentDateFormatStr;
+ }
+
+ /**
+ * getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property.
+ * @return Returns the serverTimeZoneId property.
+ */
+ public String getServerTimeZoneId() {
+ return serverTimeZoneId;
+ }
+
+ /**
+ * + * getter for the {@link #setShortMonthNames(String) shortMonthNames} + * property. + *
+ * @return Returns the shortMonthNames. + */ + public String getShortMonthNames() { + return shortMonthNames; + } + + /** + *+ * getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property. + *
+ * @return Returns the serverLanguageCode property. + */ + public String getServerLanguageCode() { + return serverLanguageCode; + } + + /** + *+ * getter for the {@link #setLenientFutureDates(boolean) lenientFutureDates} property. + *
+ * @return Returns the lenientFutureDates. + * @since 1.5 + */ + public boolean isLenientFutureDates() { + return lenientFutureDates; + } + /** + *
+ * setter for the defaultDateFormatStr property. This property
+ * specifies the main date format that will be used by a parser configured
+ * by this configuration to parse file timestamps. If this is not
+ * specified, such a parser will use as a default value, the most commonly
+ * used format which will be in as used in en_US
locales.
+ *
+ * This should be in the format described for
+ * java.text.SimpleDateFormat
.
+ * property.
+ *
+ * setter for the recentDateFormatStr property. This property + * specifies a secondary date format that will be used by a parser + * configured by this configuration to parse file timestamps, typically + * those less than a year old. If this is not specified, such a parser + * will not attempt to parse using an alternate format. + *
+ * This is used primarily in unix-based systems. + * + * This should be in the format described for + *java.text.SimpleDateFormat
.
+ *
+ * @param recentDateFormatStr The recentDateFormatStr to set.
+ */
+ public void setRecentDateFormatStr(String recentDateFormatStr) {
+ this.recentDateFormatStr = recentDateFormatStr;
+ }
+
+ /**
+ * + * setter for the lenientFutureDates property. This boolean property + * (default: false) only has meaning when a + * {@link #setRecentDateFormatStr(String) recentDateFormatStr} property + * has been set. In that case, if this property is set true, then the + * parser, when it encounters a listing parseable with the recent date + * format, will only consider a date to belong to the previous year if + * it is more than one day in the future. This will allow all + * out-of-synch situations (whether based on "slop" - i.e. servers simply + * out of synch with one another or because of time zone differences - + * but in the latter case it is highly recommended to use the + * {@link #setServerTimeZoneId(String) serverTimeZoneId} property + * instead) to resolve correctly. + *
+ * This is used primarily in unix-based systems. + *
+ * @param lenientFutureDates set true to compensate for out-of-synch + * conditions. + */ + public void setLenientFutureDates(boolean lenientFutureDates) { + this.lenientFutureDates = lenientFutureDates; + } + /** + *+ * setter for the serverTimeZoneId property. This property + * allows a time zone to be specified corresponding to that known to be + * used by an FTP server in file listings. This might be particularly + * useful to clients such as Ant that try to use these timestamps for + * dependency checking. + *
+ * This should be one of the identifiers used by
+ * java.util.TimeZone
to refer to time zones, for example,
+ * America/Chicago
or Asia/Rangoon
.
+ *
+ * setter for the shortMonthNames property. + * This property allows the user to specify a set of month names + * used by the server that is different from those that may be + * specified using the {@link #setServerLanguageCode(String) serverLanguageCode} + * property. + *
+ * This should be a string containing twelve strings each composed of
+ * three characters, delimited by pipe (|) characters. Currently,
+ * only 8-bit ASCII characters are known to be supported. For example,
+ * a set of month names used by a hypothetical Icelandic FTP server might
+ * conceivably be specified as
+ * "jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des"
.
+ *
+ * setter for the serverLanguageCode property. This property allows + * user to specify a + * + * two-letter ISO-639 language code that will be used to + * configure the set of month names used by the file timestamp parser. + * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames} + * is specified, parsing will assume English month names, which may or + * may not be significant, depending on whether the date format(s) + * specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} + * and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} are using + * numeric or alphabetic month names. + *
+ *If the code supplied is not supported here, en_US
+ * month names will be used. We are supporting here those language
+ * codes which, when a java.util.Locale
is constucted
+ * using it, and a java.text.SimpleDateFormat
is
+ * constructed using that Locale, the array returned by the
+ * SimpleDateFormat's getShortMonths()
method consists
+ * solely of three 8-bit ASCII character strings. Additionally,
+ * languages which do not meet this requirement are included if a
+ * common alternative set of short month names is known to be used.
+ * This means that users who can tell us of additional such encodings
+ * may get them added to the list of supported languages by contacting
+ * the jakarta-commons-net team.
+ *
+ * Please note that this attribute will NOT be used to determine a
+ * locale-based date format for the language.
+ * Experience has shown that many if not most FTP servers outside the
+ * United States employ the standard en_US
date format
+ * orderings of MMM d yyyy
and MMM d HH:mm
+ * and attempting to deduce this automatically here would cause more
+ * problems than it would solve. The date format must be changed
+ * via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or
+ * {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters.
+ *
Locale.US
+ * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode}
+ * @return a DateFormatSymbols object configured with short month names
+ * corresponding to the supplied code, or with month names for
+ * Locale.US
if there is no corresponding entry in the internal
+ * table.
+ */
+ public static DateFormatSymbols lookupDateFormatSymbols(String languageCode)
+ {
+ Object lang = LANGUAGE_CODE_MAP.get(languageCode);
+ if (lang != null) {
+ if (lang instanceof Locale) {
+ return new DateFormatSymbols((Locale) lang);
+ } else if (lang instanceof String){
+ return getDateFormatSymbols((String) lang);
+ }
+ }
+ return new DateFormatSymbols(Locale.US);
+ }
+
+ /**
+ * Returns a DateFormatSymbols object configured with short month names
+ * as in the supplied string
+ * @param shortmonths This should be as described in
+ * {@link #setShortMonthNames(String) shortMonthNames}
+ * @return a DateFormatSymbols object configured with short month names
+ * as in the supplied string
+ */
+ public static DateFormatSymbols getDateFormatSymbols(String shortmonths)
+ {
+ String[] months = splitShortMonthString(shortmonths);
+ DateFormatSymbols dfs = new DateFormatSymbols(Locale.US);
+ dfs.setShortMonths(months);
+ return dfs;
+ }
+
+ private static String[] splitShortMonthString(String shortmonths) {
+ StringTokenizer st = new StringTokenizer(shortmonths, "|");
+ int monthcnt = st.countTokens();
+ if (12 != monthcnt) {
+ throw new IllegalArgumentException(
+ "expecting a pipe-delimited string containing 12 tokens");
+ }
+ String[] months = new String[13];
+ int pos = 0;
+ while(st.hasMoreTokens()) {
+ months[pos++] = st.nextToken();
+ }
+ months[pos]="";
+ return months;
+ }
+
+ /**
+ * Returns a Collection of all the language codes currently supported
+ * by this class. See {@link #setServerLanguageCode(String) serverLanguageCode}
+ * for a functional descrption of language codes within this system.
+ *
+ * @return a Collection of all the language codes currently supported
+ * by this class
+ */
+ public static Collection