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.parser; 017 018 import java.text.DateFormatSymbols; 019 import java.text.ParseException; 020 import java.text.ParsePosition; 021 import java.text.SimpleDateFormat; 022 import java.util.Calendar; 023 import java.util.Date; 024 import java.util.TimeZone; 025 026 import org.apache.commons.net.ftp.Configurable; 027 import org.apache.commons.net.ftp.FTPClientConfig; 028 029 /** 030 * Default implementation of the {@link FTPTimestampParser FTPTimestampParser} 031 * interface also implements the {@link org.apache.commons.net.ftp.Configurable Configurable} 032 * interface to allow the parsing to be configured from the outside. 033 * 034 * @see ConfigurableFTPFileEntryParserImpl 035 * @since 1.4 036 */ 037 public class FTPTimestampParserImpl implements 038 FTPTimestampParser, Configurable 039 { 040 041 042 private SimpleDateFormat defaultDateFormat; 043 private SimpleDateFormat recentDateFormat; 044 045 046 /** 047 * The only constructor for this class. 048 */ 049 public FTPTimestampParserImpl() { 050 setDefaultDateFormat(DEFAULT_SDF); 051 setRecentDateFormat(DEFAULT_RECENT_SDF); 052 } 053 054 /** 055 * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method} 056 * in the {@link FTPTimestampParser FTPTimestampParser} interface 057 * according to this algorithm: 058 * 059 * If the recentDateFormat member has been defined, try to parse the 060 * supplied string with that. If that parse fails, or if the recentDateFormat 061 * member has not been defined, attempt to parse with the defaultDateFormat 062 * member. If that fails, throw a ParseException. 063 * 064 * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String) 065 */ 066 /* (non-Javadoc) 067 * 068 */ 069 public Calendar parseTimestamp(String timestampStr) throws ParseException { 070 Calendar now = Calendar.getInstance(); 071 now.setTimeZone(this.getServerTimeZone()); 072 073 Calendar working = Calendar.getInstance(); 074 working.setTimeZone(this.getServerTimeZone()); 075 ParsePosition pp = new ParsePosition(0); 076 077 Date parsed = null; 078 if (this.recentDateFormat != null) { 079 parsed = recentDateFormat.parse(timestampStr, pp); 080 } 081 if (parsed != null && pp.getIndex() == timestampStr.length()) 082 { 083 working.setTime(parsed); 084 working.set(Calendar.YEAR, now.get(Calendar.YEAR)); 085 if (working.after(now)) { 086 working.add(Calendar.YEAR, -1); 087 } 088 } else { 089 pp = new ParsePosition(0); 090 parsed = defaultDateFormat.parse(timestampStr, pp); 091 // note, length checks are mandatory for us since 092 // SimpleDateFormat methods will succeed if less than 093 // full string is matched. They will also accept, 094 // despite "leniency" setting, a two-digit number as 095 // a valid year (e.g. 22:04 will parse as 22 A.D.) 096 // so could mistakenly confuse an hour with a year, 097 // if we don't insist on full length parsing. 098 if (parsed != null && pp.getIndex() == timestampStr.length()) { 099 working.setTime(parsed); 100 } else { 101 throw new ParseException( 102 "Timestamp could not be parsed with older or recent DateFormat", 103 pp.getIndex()); 104 } 105 } 106 return working; 107 } 108 109 /** 110 * @return Returns the defaultDateFormat. 111 */ 112 public SimpleDateFormat getDefaultDateFormat() { 113 return defaultDateFormat; 114 } 115 /** 116 * @return Returns the defaultDateFormat pattern string. 117 */ 118 public String getDefaultDateFormatString() { 119 return defaultDateFormat.toPattern(); 120 } 121 /** 122 * @param defaultDateFormat The defaultDateFormat to be set. 123 */ 124 private void setDefaultDateFormat(String format) { 125 if (format != null) { 126 this.defaultDateFormat = new SimpleDateFormat(format); 127 this.defaultDateFormat.setLenient(false); 128 } 129 } 130 /** 131 * @return Returns the recentDateFormat. 132 */ 133 public SimpleDateFormat getRecentDateFormat() { 134 return recentDateFormat; 135 } 136 /** 137 * @return Returns the recentDateFormat. 138 */ 139 public String getRecentDateFormatString() { 140 return recentDateFormat.toPattern(); 141 } 142 /** 143 * @param recentDateFormat The recentDateFormat to set. 144 */ 145 private void setRecentDateFormat(String format) { 146 if (format != null) { 147 this.recentDateFormat = new SimpleDateFormat(format); 148 this.recentDateFormat.setLenient(false); 149 } 150 } 151 152 /** 153 * @return returns an array of 12 strings representing the short 154 * month names used by this parse. 155 */ 156 public String[] getShortMonths() { 157 return defaultDateFormat.getDateFormatSymbols().getShortMonths(); 158 } 159 160 161 /** 162 * @return Returns the serverTimeZone used by this parser. 163 */ 164 public TimeZone getServerTimeZone() { 165 return this.defaultDateFormat.getTimeZone(); 166 } 167 /** 168 * sets a TimeZone represented by the supplied ID string into all 169 * of the parsers used by this server. 170 * @param serverTimeZone Time Id java.util.TimeZone id used by 171 * the ftp server. If null the client's local time zone is assumed. 172 */ 173 private void setServerTimeZone(String serverTimeZoneId) { 174 TimeZone serverTimeZone = TimeZone.getDefault(); 175 if (serverTimeZoneId != null) { 176 serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId); 177 } 178 this.defaultDateFormat.setTimeZone(serverTimeZone); 179 if (this.recentDateFormat != null) { 180 this.recentDateFormat.setTimeZone(serverTimeZone); 181 } 182 } 183 184 /** 185 * Implementation of the {@link Configurable Configurable} 186 * interface. Configures this <code>FTPTimestampParser</code> according 187 * to the following logic: 188 * <p> 189 * Set up the {@link FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat} 190 * and optionally the {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat} 191 * to values supplied in the config based on month names configured as follows: 192 * </p><p><ul> 193 * <li>If a {@link FTPClientConfig#setShortMonthNames(String) shortMonthString} 194 * has been supplied in the <code>config</code>, use that to parse parse timestamps.</li> 195 * <li>Otherwise, if a {@link FTPClientConfig#setServerLanguageCode(String) serverLanguageCode} 196 * has been supplied in the <code>config</code>, use the month names represented 197 * by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language} 198 * to parse timestamps.</li> 199 * <li>otherwise use default English month names</li> 200 * </ul></p><p> 201 * Finally if a {@link org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId} 202 * has been supplied via the config, set that into all date formats that have 203 * been configured. 204 * </p> 205 */ 206 public void configure(FTPClientConfig config) { 207 DateFormatSymbols dfs = null; 208 209 String languageCode = config.getServerLanguageCode(); 210 String shortmonths = config.getShortMonthNames(); 211 if (shortmonths != null) { 212 dfs = FTPClientConfig.getDateFormatSymbols(shortmonths); 213 } else if (languageCode != null) { 214 dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode); 215 } else { 216 dfs = FTPClientConfig.lookupDateFormatSymbols("en"); 217 } 218 219 220 String recentFormatString = config.getRecentDateFormatStr(); 221 if (recentFormatString == null) { 222 this.recentDateFormat = null; 223 } else { 224 this.recentDateFormat = new SimpleDateFormat(recentFormatString, dfs); 225 this.recentDateFormat.setLenient(false); 226 } 227 228 String defaultFormatString = config.getDefaultDateFormatStr(); 229 if (defaultFormatString == null) { 230 throw new IllegalArgumentException("defaultFormatString cannot be null"); 231 } 232 this.defaultDateFormat = new SimpleDateFormat(defaultFormatString, dfs); 233 this.defaultDateFormat.setLenient(false); 234 235 setServerTimeZone(config.getServerTimeZoneId()); 236 } 237 }