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 }