001 /* 002 * Copyright 2005 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.apache.commons.net.ftp; 017 018 import java.text.DateFormatSymbols; 019 import java.util.Collection; 020 import java.util.Locale; 021 import java.util.Map; 022 import java.util.StringTokenizer; 023 import java.util.TreeMap; 024 025 /** 026 * <p> 027 * This class implements an alternate means of configuring the 028 * {@link org.apache.commons.net.ftp.FTPClient FTPClient} object and 029 * also subordinate objects which it uses. Any class implementing the 030 * {@link org.apache.commons.net.ftp.Configurable Configurable } 031 * interface can be configured by this object. 032 * </p><p> 033 * In particular this class was designed primarily to support configuration 034 * of FTP servers which express file timestamps in formats and languages 035 * other than those for the US locale, which although it is the most common 036 * is not universal. Unfortunately, nothing in the FTP spec allows this to 037 * be determined in an automated way, so manual configuration such as this 038 * is necessary. 039 * </p><p> 040 * This functionality was designed to allow existing clients to work exactly 041 * as before without requiring use of this component. This component should 042 * only need to be explicitly invoked by the user of this package for problem 043 * cases that previous implementations could not solve. 044 * </p> 045 * <h3>Examples of use of FTPClientConfig</h3> 046 * Use cases: 047 * You are trying to access a server that 048 * <ul> 049 * <li>lists files with timestamps that use month names in languages other 050 * than English</li> 051 * <li>lists files with timestamps that use date formats other 052 * than the American English "standard" <code>MM dd yyyy</code></li> 053 * <li>is in different timezone and you need accurate timestamps for 054 * dependency checking as in Ant</li> 055 * </ul> 056 * <p> 057 * Unpaged (whole list) access on a UNIX server that uses French month names 058 * but uses the "standard" <code>MMM d yyyy</code> date formatting 059 * <pre> 060 * FTPClient f=FTPClient(); 061 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); 062 * conf.setServerLanguageCode("fr"); 063 * f.configure(conf); 064 * f.connect(server); 065 * f.login(username, password); 066 * FTPFile[] files = listFiles(directory); 067 * </pre> 068 * </p> 069 * <p> 070 * Paged access on a UNIX server that uses Danish month names 071 * and "European" date formatting in Denmark's time zone, when you 072 * are in some other time zone. 073 * <pre> 074 * FTPClient f=FTPClient(); 075 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); 076 * conf.setServerLanguageCode("da"); 077 * conf.setDefaultDateFormat("d MMM yyyy"); 078 * conf.setRecentDateFormat("d MMM HH:mm"); 079 * conf.setTimeZoneId("Europe/Copenhagen"); 080 * f.configure(conf); 081 * f.connect(server); 082 * f.login(username, password); 083 * FTPListParseEngine engine = 084 * f.initiateListParsing("com.whatever.YourOwnParser", directory); 085 * 086 * while (engine.hasNext()) { 087 * FTPFile[] files = engine.getNext(25); // "page size" you want 088 * //do whatever you want with these files, display them, etc. 089 * //expensive FTPFile objects not created until needed. 090 * } 091 * </pre> 092 * </p> 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 * <p> 109 * Unpaged (whole list) access on a Windows-NT server in a different time zone. 110 * (Note, since the NT Format uses numeric date formatting, language issues 111 * are irrelevant here). 112 * <pre> 113 * FTPClient f=FTPClient(); 114 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_NT); 115 * conf.setTimeZoneId("America/Denver"); 116 * f.configure(conf); 117 * f.connect(server); 118 * f.login(username, password); 119 * FTPFile[] files = listFiles(directory); 120 * </pre> 121 * </p> 122 * Unpaged (whole list) access on a Windows-NT server in a different time zone 123 * but which has been configured to use a unix-style listing format. 124 * <pre> 125 * FTPClient f=FTPClient(); 126 * FTPClientConfig conf = new FTPClientConfig(FTPClientConfig.SYST_UNIX); 127 * conf.setTimeZoneId("America/Denver"); 128 * f.configure(conf); 129 * f.connect(server); 130 * f.login(username, password); 131 * FTPFile[] files = listFiles(directory); 132 * </pre> 133 * </p> 134 * @since 1.4 135 * @see org.apache.commons.net.ftp.Configurable 136 * @see org.apache.commons.net.ftp.FTPClient 137 * @see org.apache.commons.net.ftp.parser.FTPTimestampParserImpl#configure(FTPClientConfig) 138 * @see org.apache.commons.net.ftp.parser.ConfigurableFTPFileEntryParserImpl 139 */ 140 public class FTPClientConfig 141 { 142 143 /** 144 * Identifier by which a unix-based ftp server is known throughout 145 * the commons-net ftp system. 146 */ 147 public static final String SYST_UNIX = "UNIX"; 148 149 /** 150 * Identifier by which a vms-based ftp server is known throughout 151 * the commons-net ftp system. 152 */ 153 public static final String SYST_VMS = "VMS"; 154 155 /** 156 * Identifier by which a WindowsNT-based ftp server is known throughout 157 * the commons-net ftp system. 158 */ 159 public static final String SYST_NT = "WINDOWS"; 160 161 /** 162 * Identifier by which an OS/2-based ftp server is known throughout 163 * the commons-net ftp system. 164 */ 165 public static final String SYST_OS2 = "OS/2"; 166 167 /** 168 * Identifier by which an OS/400-based ftp server is known throughout 169 * the commons-net ftp system. 170 */ 171 public static final String SYST_OS400 = "OS/400"; 172 173 /** 174 * Identifier by which an MVS-based ftp server is known throughout 175 * the commons-net ftp system. 176 */ 177 public static final String SYST_MVS = "MVS"; 178 179 private final String serverSystemKey; 180 private String defaultDateFormatStr = null; 181 private String recentDateFormatStr = null; 182 private String serverLanguageCode = null; 183 private String shortMonthNames = null; 184 private String serverTimeZoneId = null; 185 186 187 /** 188 * The main constructor for an FTPClientConfig object 189 * @param systemKey key representing system type of the server being 190 * connected to. See {@link #getServerSystemKey() serverSystemKey} 191 */ 192 public FTPClientConfig(String systemKey) { 193 this.serverSystemKey = systemKey; 194 } 195 196 /** 197 * Convenience constructor mainly for use in testing. 198 * Constructs a UNIX configuration. 199 */ 200 public FTPClientConfig() { 201 this(SYST_UNIX); 202 } 203 204 /** 205 * Constructor which allows setting of all member fields 206 * @param systemKey key representing system type of the server being 207 * connected to. See 208 * {@link #getServerSystemKey() serverSystemKey} 209 * @param defaultDateFormatStr See 210 * {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} 211 * @param recentDateFormatStr See 212 * {@link #setRecentDateFormatStr(String) recentDateFormatStr} 213 * @param serverLanguageCode See 214 * {@link #setServerLanguageCode(String) serverLanguageCode} 215 * @param shortMonthNames See 216 * {@link #setShortMonthNames(String) shortMonthNames} 217 * @param serverTimeZoneId See 218 * {@link #setServerTimeZoneId(String) serverTimeZoneId} 219 */ 220 public FTPClientConfig(String systemKey, 221 String defaultDateFormatStr, 222 String recentDateFormatStr, 223 String serverLanguageCode, 224 String shortMonthNames, 225 String serverTimeZoneId) 226 { 227 this(systemKey); 228 this.defaultDateFormatStr = defaultDateFormatStr; 229 this.recentDateFormatStr = recentDateFormatStr; 230 this.serverLanguageCode = serverLanguageCode; 231 this.shortMonthNames = shortMonthNames; 232 this.serverTimeZoneId = serverTimeZoneId; 233 } 234 235 private static Map LANGUAGE_CODE_MAP = new TreeMap(); 236 static { 237 238 // if there are other commonly used month name encodings which 239 // correspond to particular locales, please add them here. 240 241 242 243 // many locales code short names for months as all three letters 244 // these we handle simply. 245 LANGUAGE_CODE_MAP.put("en", Locale.ENGLISH); 246 LANGUAGE_CODE_MAP.put("de",Locale.GERMAN); 247 LANGUAGE_CODE_MAP.put("it",Locale.ITALIAN); 248 LANGUAGE_CODE_MAP.put("es", new Locale("es", "", "")); // spanish 249 LANGUAGE_CODE_MAP.put("pt", new Locale("pt", "", "")); // portuguese 250 LANGUAGE_CODE_MAP.put("da", new Locale("da", "", "")); // danish 251 LANGUAGE_CODE_MAP.put("sv", new Locale("sv", "", "")); // swedish 252 LANGUAGE_CODE_MAP.put("no", new Locale("no", "", "")); // norwegian 253 LANGUAGE_CODE_MAP.put("nl", new Locale("nl", "", "")); // dutch 254 LANGUAGE_CODE_MAP.put("ro", new Locale("ro", "", "")); // romanian 255 LANGUAGE_CODE_MAP.put("sq", new Locale("sq", "", "")); // albanian 256 LANGUAGE_CODE_MAP.put("sh", new Locale("sh", "", "")); // serbo-croatian 257 LANGUAGE_CODE_MAP.put("sk", new Locale("sk", "", "")); // slovak 258 LANGUAGE_CODE_MAP.put("sl", new Locale("sl", "", "")); // slovenian 259 260 261 // some don't 262 LANGUAGE_CODE_MAP.put("fr", 263 "jan|f\u00e9v|mar|avr|mai|jun|jui|ao\u00fb|sep|oct|nov|d\u00e9c"); //french 264 265 } 266 267 /** 268 * Getter for the serverSystemKey property. This property 269 * specifies the general type of server to which the client connects. 270 * Should be either one of the <code>FTPClientConfig.SYST_*</code> codes 271 * or else the fully qualified class name of a parser implementing both 272 * the <code>FTPFileEntryParser</code> and <code>Configurable</code> 273 * interfaces. 274 * @return Returns the serverSystemKey property. 275 */ 276 public String getServerSystemKey() { 277 return serverSystemKey; 278 } 279 280 /** 281 * getter for the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} 282 * property. 283 * @return Returns the defaultDateFormatStr property. 284 */ 285 public String getDefaultDateFormatStr() { 286 return defaultDateFormatStr; 287 } 288 289 /** 290 * getter for the {@link #setRecentDateFormatStr(String) recentDateFormatStr} property. 291 * @return Returns the recentDateFormatStr property. 292 */ 293 294 public String getRecentDateFormatStr() { 295 return recentDateFormatStr; 296 } 297 298 /** 299 * getter for the {@link #setServerTimeZoneId(String) serverTimeZoneId} property. 300 * @return Returns the serverTimeZoneId property. 301 */ 302 public String getServerTimeZoneId() { 303 return serverTimeZoneId; 304 } 305 306 /** 307 * <p> 308 * getter for the {@link #setShortMonthNames(String) shortMonthNames} 309 * property. 310 * </p> 311 * @return Returns the shortMonthNames. 312 */ 313 public String getShortMonthNames() { 314 return shortMonthNames; 315 } 316 317 /** 318 * <p> 319 * getter for the {@link #setServerLanguageCode(String) serverLanguageCode} property. 320 * </p> 321 * * @return Returns the serverLanguageCode property. 322 */ 323 public String getServerLanguageCode() { 324 return serverLanguageCode; 325 } 326 327 /** 328 * <p> 329 * setter for the defaultDateFormatStr property. This property 330 * specifies the main date format that will be used by a parser configured 331 * by this configuration to parse file timestamps. If this is not 332 * specified, such a parser will use as a default value, the most commonly 333 * used format which will be in as used in <code>en_US</code> locales. 334 * </p><p> 335 * This should be in the format described for 336 * <code>java.text.SimpleDateFormat</code>. 337 * property. 338 * </p> 339 * @param defaultDateFormatStr The defaultDateFormatStr to set. 340 */ 341 public void setDefaultDateFormatStr(String defaultDateFormatStr) { 342 this.defaultDateFormatStr = defaultDateFormatStr; 343 } 344 345 /** 346 * <p> 347 * setter for the recentDateFormatStr property. This property 348 * specifies a secondary date format that will be used by a parser 349 * configured by this configuration to parse file timestamps, typically 350 * those less than a year old. If this is not specified, such a parser 351 * will not attempt to parse using an alternate format. 352 * </p> 353 * This is used primarily in unix-based systems. 354 * </p> 355 * This should be in the format described for 356 * <code>java.text.SimpleDateFormat</code>. 357 * </p> 358 * @param recentDateFormatStr The recentDateFormatStr to set. 359 */ 360 public void setRecentDateFormatStr(String recentDateFormatStr) { 361 this.recentDateFormatStr = recentDateFormatStr; 362 } 363 364 /** 365 * <p> 366 * setter for the serverTimeZoneId property. This property 367 * allows a time zone to be specified corresponding to that known to be 368 * used by an FTP server in file listings. This might be particularly 369 * useful to clients such as Ant that try to use these timestamps for 370 * dependency checking. 371 * </p><p> 372 * This should be one of the identifiers used by 373 * <code>java.util.TimeZone</code> to refer to time zones, for example, 374 * <code>America/Chicago</code> or <code>Asia/Rangoon</code>. 375 * </p> 376 * @param serverTimeZoneId The serverTimeZoneId to set. 377 */ 378 public void setServerTimeZoneId(String serverTimeZoneId) { 379 this.serverTimeZoneId = serverTimeZoneId; 380 } 381 382 /** 383 * <p> 384 * setter for the shortMonthNames property. 385 * This property allows the user to specify a set of month names 386 * used by the server that is different from those that may be 387 * specified using the {@link #setServerLanguageCode(String) serverLanguageCode} 388 * property. 389 * </p><p> 390 * This should be a string containing twelve strings each composed of 391 * three characters, delimited by pipe (|) characters. Currently, 392 * only 8-bit ASCII characters are known to be supported. For example, 393 * a set of month names used by a hypothetical Icelandic FTP server might 394 * conceivably be specified as 395 * <code>"jan|feb|mar|apr|maí|jún|júl|ágú|sep|okt|nóv|des"</code>. 396 * </p> 397 * @param shortMonthNames The value to set to the shortMonthNames property. 398 */ 399 public void setShortMonthNames(String shortMonthNames) { 400 this.shortMonthNames = shortMonthNames; 401 } 402 403 /** 404 * <p> 405 * setter for the serverLanguageCode property. This property allows 406 * user to specify a 407 * <a href="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt"> 408 * two-letter ISO-639 language code</a> that will be used to 409 * configure the set of month names used by the file timestamp parser. 410 * If neither this nor the {@link #setShortMonthNames(String) shortMonthNames} 411 * is specified, parsing will assume English month names, which may or 412 * may not be significant, depending on whether the date format(s) 413 * specified via {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} 414 * and/or {@link #setRecentDateFormatStr(String) recentDateFormatStr} are using 415 * numeric or alphabetic month names. 416 * </p> 417 * <p>If the code supplied is not supported here, <code>en_US</code> 418 * month names will be used. We are supporting here those language 419 * codes which, when a <code> java.util.Locale</code> is constucted 420 * using it, and a <code>java.text.SimpleDateFormat</code> is 421 * constructed using that Locale, the array returned by the 422 * SimpleDateFormat's <code>getShortMonths()</code> method consists 423 * solely of three 8-bit ASCII character strings. Additionally, 424 * languages which do not meet this requirement are included if a 425 * common alternative set of short month names is known to be used. 426 * This means that users who can tell us of additional such encodings 427 * may get them added to the list of supported languages by contacting 428 * the jakarta-commons-net team. 429 * </p> 430 * <p><strong> 431 * Please note that this attribute will NOT be used to determine a 432 * locale-based date format for the language. </strong> 433 * Experience has shown that many if not most FTP servers outside the 434 * United States employ the standard <code>en_US</code> date format 435 * orderings of <code>MMM d yyyy</code> and <code>MMM d HH:mm</code> 436 * and attempting to deduce this automatically here would cause more 437 * problems than it would solve. The date format must be changed 438 * via the {@link #setDefaultDateFormatStr(String) defaultDateFormatStr} and/or 439 * {@link #setRecentDateFormatStr(String) recentDateFormatStr} parameters. 440 * </p> 441 * @param serverLanguageCode The value to set to the serverLanguageCode property. 442 */ 443 public void setServerLanguageCode(String serverLanguageCode) { 444 this.serverLanguageCode = serverLanguageCode; 445 } 446 447 /** 448 * Looks up the supplied language code in the internally maintained table of 449 * language codes. Returns a DateFormatSymbols object configured with 450 * short month names corresponding to the code. If there is no corresponding 451 * entry in the table, the object returned will be that for 452 * <code>Locale.US</code> 453 * @param languageCode See {@link #setServerLanguageCode(String) serverLanguageCode} 454 * @return a DateFormatSymbols object configured with short month names 455 * corresponding to the supplied code, or with month names for 456 * <code>Locale.US</code> if there is no corresponding entry in the internal 457 * table. 458 */ 459 public static DateFormatSymbols lookupDateFormatSymbols(String languageCode) 460 { 461 Object lang = LANGUAGE_CODE_MAP.get(languageCode); 462 if (lang != null) { 463 if (lang instanceof Locale) { 464 return new DateFormatSymbols((Locale) lang); 465 } else if (lang instanceof String){ 466 return getDateFormatSymbols((String) lang); 467 } 468 } 469 return new DateFormatSymbols(Locale.US); 470 } 471 472 /** 473 * Returns a DateFormatSymbols object configured with short month names 474 * as in the supplied string 475 * @param shortmonths This should be as described in 476 * {@link #setShortMonthNames(String) shortMonthNames} 477 * @return a DateFormatSymbols object configured with short month names 478 * as in the supplied string 479 */ 480 public static DateFormatSymbols getDateFormatSymbols(String shortmonths) 481 { 482 String[] months = splitShortMonthString(shortmonths); 483 DateFormatSymbols dfs = new DateFormatSymbols(Locale.US); 484 dfs.setShortMonths(months); 485 return dfs; 486 } 487 488 private static String[] splitShortMonthString(String shortmonths) { 489 StringTokenizer st = new StringTokenizer(shortmonths, "|"); 490 int monthcnt = st.countTokens(); 491 if (12 != monthcnt) { 492 throw new IllegalArgumentException( 493 "expecting a pipe-delimited string containing 12 tokens"); 494 } 495 String[] months = new String[13]; 496 int pos = 0; 497 while(st.hasMoreTokens()) { 498 months[pos++] = st.nextToken(); 499 } 500 months[pos]=""; 501 return months; 502 } 503 504 /** 505 * Returns a Collection of all the language codes currently supported 506 * by this class. See {@link #setServerLanguageCode(String) serverLanguageCode} 507 * for a functional descrption of language codes within this system. 508 * 509 * @return a Collection of all the language codes currently supported 510 * by this class 511 */ 512 public static Collection getSupportedLanguageCodes() { 513 return LANGUAGE_CODE_MAP.keySet(); 514 } 515 516 517 }