| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.commons.net.ftp.parser; |
| |
| import java.text.DateFormatSymbols; |
| import java.text.ParseException; |
| import java.text.ParsePosition; |
| import java.text.SimpleDateFormat; |
| import java.util.Calendar; |
| import java.util.Date; |
| import java.util.TimeZone; |
| |
| import org.apache.commons.net.ftp.Configurable; |
| import org.apache.commons.net.ftp.FTPClientConfig; |
| |
| /** |
| * Default implementation of the {@link FTPTimestampParser FTPTimestampParser} |
| * interface also implements the {@link org.apache.commons.net.ftp.Configurable Configurable} |
| * interface to allow the parsing to be configured from the outside. |
| * |
| * @see ConfigurableFTPFileEntryParserImpl |
| * @since 1.4 |
| */ |
| public class FTPTimestampParserImpl implements |
| FTPTimestampParser, Configurable |
| { |
| |
| |
| private SimpleDateFormat defaultDateFormat; |
| private SimpleDateFormat recentDateFormat; |
| private boolean lenientFutureDates = false; |
| |
| |
| /** |
| * The only constructor for this class. |
| */ |
| public FTPTimestampParserImpl() { |
| setDefaultDateFormat(DEFAULT_SDF); |
| setRecentDateFormat(DEFAULT_RECENT_SDF); |
| } |
| |
| /** |
| * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method} |
| * in the {@link FTPTimestampParser FTPTimestampParser} interface |
| * according to this algorithm: |
| * |
| * If the recentDateFormat member has been defined, try to parse the |
| * supplied string with that. If that parse fails, or if the recentDateFormat |
| * member has not been defined, attempt to parse with the defaultDateFormat |
| * member. If that fails, throw a ParseException. |
| * |
| * This method allows a {@link Calendar} instance to be passed in which represents the |
| * current (system) time. |
| * |
| * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String) |
| * |
| * @param timestampStr The timestamp to be parsed |
| */ |
| public Calendar parseTimestamp(String timestampStr) throws ParseException { |
| Calendar now = Calendar.getInstance(); |
| return parseTimestamp(timestampStr, now); |
| } |
| |
| /** |
| * Implements the one {@link FTPTimestampParser#parseTimestamp(String) method} |
| * in the {@link FTPTimestampParser FTPTimestampParser} interface |
| * according to this algorithm: |
| * |
| * If the recentDateFormat member has been defined, try to parse the |
| * supplied string with that. If that parse fails, or if the recentDateFormat |
| * member has not been defined, attempt to parse with the defaultDateFormat |
| * member. If that fails, throw a ParseException. |
| * |
| * @see org.apache.commons.net.ftp.parser.FTPTimestampParser#parseTimestamp(java.lang.String) |
| * @param timestampStr The timestamp to be parsed |
| * @param serverTime The current time for the server |
| * @since 1.5 |
| */ |
| public Calendar parseTimestamp(String timestampStr, Calendar serverTime) throws ParseException { |
| Calendar now = (Calendar) serverTime.clone();// Copy this, because we may change it |
| now.setTimeZone(this.getServerTimeZone()); |
| Calendar working = (Calendar) now.clone(); |
| working.setTimeZone(getServerTimeZone()); |
| ParsePosition pp = new ParsePosition(0); |
| |
| Date parsed = null; |
| String timeStampStrPlusYear=""; |
| if (recentDateFormat != null) { |
| if (lenientFutureDates) { |
| // add a day to "now" so that "slop" doesn't cause a date |
| // slightly in the future to roll back a full year. (Bug 35181) |
| now.add(Calendar.DATE, 1); |
| } |
| // Temporarily add the current year to the short date time |
| // to cope with short-date leap year strings. |
| // e.g. Java's DateFormatter will assume that "Feb 29 12:00" refers to |
| // Feb 29 1970 (an invalid date) rather than a potentially valid leap year date. |
| // This is pretty bad hack to work around the deficiencies of the JDK date/time classes. |
| int year = now.get(Calendar.YEAR); |
| timeStampStrPlusYear = timestampStr + " " + year; |
| SimpleDateFormat hackFormatter = new SimpleDateFormat(recentDateFormat.toPattern() + " yyyy", |
| recentDateFormat.getDateFormatSymbols()); |
| hackFormatter.setLenient(false); |
| hackFormatter.setTimeZone(recentDateFormat.getTimeZone()); |
| parsed = hackFormatter.parse(timeStampStrPlusYear, pp); |
| } |
| if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) |
| { |
| working.setTime(parsed); |
| |
| if (working.after(now)) { |
| working.add(Calendar.YEAR, -1); |
| } |
| } else { |
| pp = new ParsePosition(0); |
| parsed = defaultDateFormat.parse(timestampStr, pp); |
| // note, length checks are mandatory for us since |
| // SimpleDateFormat methods will succeed if less than |
| // full string is matched. They will also accept, |
| // despite "leniency" setting, a two-digit number as |
| // a valid year (e.g. 22:04 will parse as 22 A.D.) |
| // so could mistakenly confuse an hour with a year, |
| // if we don't insist on full length parsing. |
| if (parsed != null && pp.getIndex() == timestampStr.length()) { |
| working.setTime(parsed); |
| } else { |
| throw new ParseException( |
| "Timestamp could not be parsed with older or recent DateFormat", |
| pp.getIndex()); |
| } |
| } |
| return working; |
| } |
| |
| /** |
| * @return Returns the defaultDateFormat. |
| */ |
| public SimpleDateFormat getDefaultDateFormat() { |
| return defaultDateFormat; |
| } |
| /** |
| * @return Returns the defaultDateFormat pattern string. |
| */ |
| public String getDefaultDateFormatString() { |
| return defaultDateFormat.toPattern(); |
| } |
| /** |
| * @param defaultDateFormat The defaultDateFormat to be set. |
| */ |
| private void setDefaultDateFormat(String format) { |
| if (format != null) { |
| this.defaultDateFormat = new SimpleDateFormat(format); |
| this.defaultDateFormat.setLenient(false); |
| } |
| } |
| /** |
| * @return Returns the recentDateFormat. |
| */ |
| public SimpleDateFormat getRecentDateFormat() { |
| return recentDateFormat; |
| } |
| /** |
| * @return Returns the recentDateFormat. |
| */ |
| public String getRecentDateFormatString() { |
| return recentDateFormat.toPattern(); |
| } |
| /** |
| * @param recentDateFormat The recentDateFormat to set. |
| */ |
| private void setRecentDateFormat(String format) { |
| if (format != null) { |
| this.recentDateFormat = new SimpleDateFormat(format); |
| this.recentDateFormat.setLenient(false); |
| } |
| } |
| |
| /** |
| * @return returns an array of 12 strings representing the short |
| * month names used by this parse. |
| */ |
| public String[] getShortMonths() { |
| return defaultDateFormat.getDateFormatSymbols().getShortMonths(); |
| } |
| |
| |
| /** |
| * @return Returns the serverTimeZone used by this parser. |
| */ |
| public TimeZone getServerTimeZone() { |
| return this.defaultDateFormat.getTimeZone(); |
| } |
| /** |
| * sets a TimeZone represented by the supplied ID string into all |
| * of the parsers used by this server. |
| * @param serverTimeZone Time Id java.util.TimeZone id used by |
| * the ftp server. If null the client's local time zone is assumed. |
| */ |
| private void setServerTimeZone(String serverTimeZoneId) { |
| TimeZone serverTimeZone = TimeZone.getDefault(); |
| if (serverTimeZoneId != null) { |
| serverTimeZone = TimeZone.getTimeZone(serverTimeZoneId); |
| } |
| this.defaultDateFormat.setTimeZone(serverTimeZone); |
| if (this.recentDateFormat != null) { |
| this.recentDateFormat.setTimeZone(serverTimeZone); |
| } |
| } |
| |
| /** |
| * Implementation of the {@link Configurable Configurable} |
| * interface. Configures this <code>FTPTimestampParser</code> according |
| * to the following logic: |
| * <p> |
| * Set up the {@link FTPClientConfig#setDefaultDateFormatStr(java.lang.String) defaultDateFormat} |
| * and optionally the {@link FTPClientConfig#setRecentDateFormatStr(String) recentDateFormat} |
| * to values supplied in the config based on month names configured as follows: |
| * </p><p><ul> |
| * <li>If a {@link FTPClientConfig#setShortMonthNames(String) shortMonthString} |
| * has been supplied in the <code>config</code>, use that to parse parse timestamps.</li> |
| * <li>Otherwise, if a {@link FTPClientConfig#setServerLanguageCode(String) serverLanguageCode} |
| * has been supplied in the <code>config</code>, use the month names represented |
| * by that {@link FTPClientConfig#lookupDateFormatSymbols(String) language} |
| * to parse timestamps.</li> |
| * <li>otherwise use default English month names</li> |
| * </ul></p><p> |
| * Finally if a {@link org.apache.commons.net.ftp.FTPClientConfig#setServerTimeZoneId(String) serverTimeZoneId} |
| * has been supplied via the config, set that into all date formats that have |
| * been configured. |
| * </p> |
| */ |
| public void configure(FTPClientConfig config) { |
| DateFormatSymbols dfs = null; |
| |
| String languageCode = config.getServerLanguageCode(); |
| String shortmonths = config.getShortMonthNames(); |
| if (shortmonths != null) { |
| dfs = FTPClientConfig.getDateFormatSymbols(shortmonths); |
| } else if (languageCode != null) { |
| dfs = FTPClientConfig.lookupDateFormatSymbols(languageCode); |
| } else { |
| dfs = FTPClientConfig.lookupDateFormatSymbols("en"); |
| } |
| |
| |
| String recentFormatString = config.getRecentDateFormatStr(); |
| if (recentFormatString == null) { |
| this.recentDateFormat = null; |
| } else { |
| this.recentDateFormat = new SimpleDateFormat(recentFormatString, dfs); |
| this.recentDateFormat.setLenient(false); |
| } |
| |
| String defaultFormatString = config.getDefaultDateFormatStr(); |
| if (defaultFormatString == null) { |
| throw new IllegalArgumentException("defaultFormatString cannot be null"); |
| } |
| this.defaultDateFormat = new SimpleDateFormat(defaultFormatString, dfs); |
| this.defaultDateFormat.setLenient(false); |
| |
| setServerTimeZone(config.getServerTimeZoneId()); |
| |
| this.lenientFutureDates = config.isLenientFutureDates(); |
| } |
| /** |
| * @return Returns the lenientFutureDates. |
| */ |
| boolean isLenientFutureDates() { |
| return lenientFutureDates; |
| } |
| /** |
| * @param lenientFutureDates The lenientFutureDates to set. |
| */ |
| void setLenientFutureDates(boolean lenientFutureDates) { |
| this.lenientFutureDates = lenientFutureDates; |
| } |
| } |
| |