You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by se...@apache.org on 2008/05/21 01:48:51 UTC

svn commit: r658491 [2/3] - in /commons/proper/net/trunk/src: java/examples/nntp/ java/examples/ntp/ java/org/apache/commons/net/ftp/ java/org/apache/commons/net/ftp/parser/ java/org/apache/commons/net/nntp/ java/org/apache/commons/net/ntp/ java/org/ap...

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/FTPTimestampParserImpl.java Tue May 20 16:48:50 2008
@@ -37,70 +37,70 @@
  * @since 1.4
  */
 public class FTPTimestampParserImpl implements
-		FTPTimestampParser, Configurable 
+        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);
+    
+    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) {
+        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)
@@ -118,165 +118,165 @@
             hackFormatter.setLenient(false);
             hackFormatter.setTimeZone(recentDateFormat.getTimeZone());
             parsed = hackFormatter.parse(timeStampStrPlusYear, pp);
-		}
-		if (parsed != null && pp.getIndex() == timeStampStrPlusYear.length()) 
-		{
-			working.setTime(parsed);
+        }
+        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;
-	}
+            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 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.
      */

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/MVSFTPEntryParser.java Tue May 20 16:48:50 2008
@@ -32,8 +32,8 @@
     /**
      * This is the regular expression used by this parser.
      */
-	private static final String REGEX = "(.*)\\s+([^\\s]+)\\s*";
-	
+    private static final String REGEX = "(.*)\\s+([^\\s]+)\\s*";
+    
     /**
      * Although this parser is now ignoring dates, someone may someday
      * figure out a way to accomodate this and this appears to be the 
@@ -41,32 +41,32 @@
      * SMC 2005/04/08
      */
     static final String DEFAULT_DATE_FORMAT 
-		= "yyyy/MM/dd"; // 2001/11/09
+        = "yyyy/MM/dd"; // 2001/11/09
 
         
- 	// This is not at all the tightest possible regexp for MVS LIST
-	// output, but I'm not a mainframe guru so I have little idea what the
-	// range of valid values are.  I just needed to get the filename (Dsname);
-	// note that no other FTPFile fields can be filled in with the results of
-	// a LIST on MVS.  The 'Referred' date seems to be 'last accessed date'
-	// and not 'last modified date' so I didn't bother parsing it.
-	//
-	// Of course it works perfectly as-is and it distinguishes header lines from
-	// file results so that's the important thing.  
-	//
-	// This parser should be used when SYST returns:
-	// 'MVS is the operating system of this server. FTP Server is running on z/OS.'
-	//
-	// Also note that there is no concept of directories in MVS, just datasets,
-	// which have names composed of four dot separated names of up to 8 chars.
-	// As a result, FTPFile.FILE_TYPE is always used. -JN 6/2004 jnadler<at>srcginc<dotcom>
-
-	// Sample LIST results from MVS:
-	//
-	//Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
-	//FPFS42 3390   2004/06/23  1    1  FB     128  6144  PS  INCOMING.RPTBM023.D061704
-	//FPFS41 3390   2004/06/23  1    1  FB     128  6144  PS  INCOMING.RPTBM056.D061704
-	//FPFS25 3390   2004/06/23  1    1  FB     128  6144  PS  INCOMING.WTM204.D061704
+    // This is not at all the tightest possible regexp for MVS LIST
+    // output, but I'm not a mainframe guru so I have little idea what the
+    // range of valid values are.  I just needed to get the filename (Dsname);
+    // note that no other FTPFile fields can be filled in with the results of
+    // a LIST on MVS.  The 'Referred' date seems to be 'last accessed date'
+    // and not 'last modified date' so I didn't bother parsing it.
+    //
+    // Of course it works perfectly as-is and it distinguishes header lines from
+    // file results so that's the important thing.  
+    //
+    // This parser should be used when SYST returns:
+    // 'MVS is the operating system of this server. FTP Server is running on z/OS.'
+    //
+    // Also note that there is no concept of directories in MVS, just datasets,
+    // which have names composed of four dot separated names of up to 8 chars.
+    // As a result, FTPFile.FILE_TYPE is always used. -JN 6/2004 jnadler<at>srcginc<dotcom>
+
+    // Sample LIST results from MVS:
+    //
+    //Volume Unit    Referred Ext Used Recfm Lrecl BlkSz Dsorg Dsname
+    //FPFS42 3390   2004/06/23  1    1  FB     128  6144  PS  INCOMING.RPTBM023.D061704
+    //FPFS41 3390   2004/06/23  1    1  FB     128  6144  PS  INCOMING.RPTBM056.D061704
+    //FPFS25 3390   2004/06/23  1    1  FB     128  6144  PS  INCOMING.WTM204.D061704
 
     /**
      * The sole constructor for a MVSFTPEntryParser object.
@@ -99,9 +99,9 @@
             f = new FTPFile();
             String dataSetName = group(2);
             f.setType(FTPFile.FILE_TYPE);
-			f.setName(dataSetName);
+            f.setName(dataSetName);
 
-			return (f);
+            return (f);
         }
         return null;
     }

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NTFTPEntryParser.java Tue May 20 16:48:50 2008
@@ -30,9 +30,9 @@
  */
 public class NTFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
 {
-	
+    
     private static final String DEFAULT_DATE_FORMAT 
-		= "MM-dd-yy hh:mma"; //11-09-01 12:30PM
+        = "MM-dd-yy hh:mma"; //11-09-01 12:30PM
 
 
     /**
@@ -91,7 +91,7 @@
 
         if (matches(entry))
         {
-        	String datestr = group(1)+" "+group(2);
+            String datestr = group(1)+" "+group(2);
             String dirString = group(3);
             String size = group(4);
             String name = group(5);

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/NetwareFTPEntryParser.java Tue May 20 16:48:50 2008
@@ -34,141 +34,141 @@
  */
 public class NetwareFTPEntryParser extends ConfigurableFTPFileEntryParserImpl {
 
-	/**
-	 * Default date format is e.g. Feb 22 2006
-	 */
-	private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy";
-
-	/**
-	 * Default recent date format is e.g. Feb 22 17:32
-	 */
-	private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm";
-
-	/**
-	 * this is the regular expression used by this parser.
-	 * Example: d [-W---F--] SCION_VOL2                        512 Apr 13 23:12 VOL2
-	 */
-	private static final String REGEX = "(d|-){1}\\s+"	// Directory/file flag 
-			+ "\\[(.*)\\]\\s+"			// Attributes
-			+ "(\\S+)\\s+" + "(\\d+)\\s+"		// Owner and size
-			+ "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))" // Long/short date format
-			+ "\\s+(.*)";				// Filename (incl. spaces)
-
-	/**
-	 * The default constructor for a NetwareFTPEntryParser object.
-	 *
-	 * @exception IllegalArgumentException
-	 * Thrown if the regular expression is unparseable.  Should not be seen
-	 * under normal conditions.  It it is seen, this is a sign that
-	 * <code>REGEX</code> is  not a valid regular expression.
-	 */
-	public NetwareFTPEntryParser() {
-		this(null);
-	}
-
-	/**
-	 * This constructor allows the creation of an NetwareFTPEntryParser object 
-	 * with something other than the default configuration.
-	 *
-	 * @param config The {@link FTPClientConfig configuration} object used to 
-	 * configure this parser.
-	 * @exception IllegalArgumentException
-	 * Thrown if the regular expression is unparseable.  Should not be seen
-	 * under normal conditions.  It it is seen, this is a sign that
-	 * <code>REGEX</code> is  not a valid regular expression.
-	 * @since 1.4
-	 */
-	public NetwareFTPEntryParser(FTPClientConfig config) {
-		super(REGEX);
-		configure(config);
-	}
-
-	/**
-	 * Parses a line of an NetwareFTP server file listing and converts it into a
-	 * usable format in the form of an <code> FTPFile </code> instance.  If the
-	 * file listing line doesn't describe a file, <code> null </code> is
-	 * returned, otherwise a <code> FTPFile </code> instance representing the
-	 * files in the directory is returned.
-	 * <p>
-	 * <p>
-	 * Netware file permissions are in the following format:  RWCEAFMS, and are explained as follows:
-	 * <ul>
-	 * <li><b>S</b> - Supervisor; All rights.
-	 * <li><b>R</b> - Read; Right to open and read or execute.
-	 * <li><b>W</b> - Write; Right to open and modify.
-	 * <li><b>C</b> - Create; Right to create; when assigned to a file, allows a deleted file to be recovered.
-	 * <li><b>E</b> - Erase; Right to delete.
-	 * <li><b>M</b> - Modify; Right to rename a file and to change attributes.
-	 * <li><b>F</b> - File Scan; Right to see directory or file listings.
-	 * <li><b>A</b> - Access Control; Right to modify trustee assignments and the Inherited Rights Mask.
-	 * </ul>
-	 * 
-	 * See <a href="http://www.novell.com/documentation/nfap10/index.html?page=/documentation/nfap10/nfaubook/data/abxraws.html">here</a> 
-	 * for more details
-	 * 
-	 * @param entry A line of text from the file listing
-	 * @return An FTPFile instance corresponding to the supplied entry
-	 */
-	public FTPFile parseFTPEntry(String entry) {
-
-		FTPFile f = new FTPFile();
-		if (matches(entry)) {
-			String dirString = group(1);
-			String attrib = group(2);
-			String user = group(3);
-			String size = group(4);
-			String datestr = group(5);
-			String name = group(9);
-
-			try {
-				f.setTimestamp(super.parseTimestamp(datestr));
-			} catch (ParseException e) {
-			     // intentionally do nothing
-			}
-
-			//is it a DIR or a file
-			if (dirString.trim().equals("d")) {
-				f.setType(FTPFile.DIRECTORY_TYPE);
-			} else // Should be "-"
-			{
-				f.setType(FTPFile.FILE_TYPE);
-			}
-
-			f.setUser(user);
-
-			//set the name
-			f.setName(name.trim());
-
-			//set the size
-			f.setSize(Long.parseLong(size.trim()));
-
-			// Now set the permissions (or at least a subset thereof - full permissions would probably require 
-			// subclassing FTPFile and adding extra metainformation there)
-			if (attrib.indexOf("R") != -1) {
-				f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION,
-						true);
-			}
-			if (attrib.indexOf("W") != -1) {
-				f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION,
-						true);
-			}
-
-			return (f);
-		}
-		return null;
-
-	}
-
-	/**
-	 * Defines a default configuration to be used when this class is
-	 * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
-	 * parameter being specified.
-	 * @return the default configuration for this parser.
-	 */
-	protected FTPClientConfig getDefaultConfiguration() {
-		return new FTPClientConfig(FTPClientConfig.SYST_NETWARE,
-				DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT, null, null,
-				null);
-	}
+    /**
+     * Default date format is e.g. Feb 22 2006
+     */
+    private static final String DEFAULT_DATE_FORMAT = "MMM dd yyyy";
+
+    /**
+     * Default recent date format is e.g. Feb 22 17:32
+     */
+    private static final String DEFAULT_RECENT_DATE_FORMAT = "MMM dd HH:mm";
+
+    /**
+     * this is the regular expression used by this parser.
+     * Example: d [-W---F--] SCION_VOL2                        512 Apr 13 23:12 VOL2
+     */
+    private static final String REGEX = "(d|-){1}\\s+"      // Directory/file flag 
+            + "\\[(.*)\\]\\s+"                              // Attributes
+            + "(\\S+)\\s+" + "(\\d+)\\s+"                   // Owner and size
+            + "(\\S+\\s+\\S+\\s+((\\d+:\\d+)|(\\d{4})))"    // Long/short date format
+            + "\\s+(.*)";                                   // Filename (incl. spaces)
+
+    /**
+     * The default constructor for a NetwareFTPEntryParser object.
+     *
+     * @exception IllegalArgumentException
+     * Thrown if the regular expression is unparseable.  Should not be seen
+     * under normal conditions.  It it is seen, this is a sign that
+     * <code>REGEX</code> is  not a valid regular expression.
+     */
+    public NetwareFTPEntryParser() {
+        this(null);
+    }
+
+    /**
+     * This constructor allows the creation of an NetwareFTPEntryParser object 
+     * with something other than the default configuration.
+     *
+     * @param config The {@link FTPClientConfig configuration} object used to 
+     * configure this parser.
+     * @exception IllegalArgumentException
+     * Thrown if the regular expression is unparseable.  Should not be seen
+     * under normal conditions.  It it is seen, this is a sign that
+     * <code>REGEX</code> is  not a valid regular expression.
+     * @since 1.4
+     */
+    public NetwareFTPEntryParser(FTPClientConfig config) {
+        super(REGEX);
+        configure(config);
+    }
+
+    /**
+     * Parses a line of an NetwareFTP server file listing and converts it into a
+     * usable format in the form of an <code> FTPFile </code> instance.  If the
+     * file listing line doesn't describe a file, <code> null </code> is
+     * returned, otherwise a <code> FTPFile </code> instance representing the
+     * files in the directory is returned.
+     * <p>
+     * <p>
+     * Netware file permissions are in the following format:  RWCEAFMS, and are explained as follows:
+     * <ul>
+     * <li><b>S</b> - Supervisor; All rights.
+     * <li><b>R</b> - Read; Right to open and read or execute.
+     * <li><b>W</b> - Write; Right to open and modify.
+     * <li><b>C</b> - Create; Right to create; when assigned to a file, allows a deleted file to be recovered.
+     * <li><b>E</b> - Erase; Right to delete.
+     * <li><b>M</b> - Modify; Right to rename a file and to change attributes.
+     * <li><b>F</b> - File Scan; Right to see directory or file listings.
+     * <li><b>A</b> - Access Control; Right to modify trustee assignments and the Inherited Rights Mask.
+     * </ul>
+     * 
+     * See <a href="http://www.novell.com/documentation/nfap10/index.html?page=/documentation/nfap10/nfaubook/data/abxraws.html">here</a> 
+     * for more details
+     * 
+     * @param entry A line of text from the file listing
+     * @return An FTPFile instance corresponding to the supplied entry
+     */
+    public FTPFile parseFTPEntry(String entry) {
+
+        FTPFile f = new FTPFile();
+        if (matches(entry)) {
+            String dirString = group(1);
+            String attrib = group(2);
+            String user = group(3);
+            String size = group(4);
+            String datestr = group(5);
+            String name = group(9);
+
+            try {
+                f.setTimestamp(super.parseTimestamp(datestr));
+            } catch (ParseException e) {
+                 // intentionally do nothing
+            }
+
+            //is it a DIR or a file
+            if (dirString.trim().equals("d")) {
+                f.setType(FTPFile.DIRECTORY_TYPE);
+            } else // Should be "-"
+            {
+                f.setType(FTPFile.FILE_TYPE);
+            }
+
+            f.setUser(user);
+
+            //set the name
+            f.setName(name.trim());
+
+            //set the size
+            f.setSize(Long.parseLong(size.trim()));
+
+            // Now set the permissions (or at least a subset thereof - full permissions would probably require 
+            // subclassing FTPFile and adding extra metainformation there)
+            if (attrib.indexOf("R") != -1) {
+                f.setPermission(FTPFile.USER_ACCESS, FTPFile.READ_PERMISSION,
+                        true);
+            }
+            if (attrib.indexOf("W") != -1) {
+                f.setPermission(FTPFile.USER_ACCESS, FTPFile.WRITE_PERMISSION,
+                        true);
+            }
+
+            return (f);
+        }
+        return null;
+
+    }
+
+    /**
+     * Defines a default configuration to be used when this class is
+     * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
+     * parameter being specified.
+     * @return the default configuration for this parser.
+     */
+    protected FTPClientConfig getDefaultConfiguration() {
+        return new FTPClientConfig(FTPClientConfig.SYST_NETWARE,
+                DEFAULT_DATE_FORMAT, DEFAULT_RECENT_DATE_FORMAT, null, null,
+                null);
+    }
 
 }

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS2FTPEntryParser.java Tue May 20 16:48:50 2008
@@ -31,9 +31,9 @@
 public class OS2FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
 
 {
-	
+    
     private static final String DEFAULT_DATE_FORMAT 
-		= "MM-dd-yy HH:mm"; //11-09-01 12:30
+        = "MM-dd-yy HH:mm"; //11-09-01 12:30
     /**
      * this is the regular expression used by this parser.
      */
@@ -94,7 +94,7 @@
             String size = group(1);
             String attrib = group(2);
             String dirString = group(3);
-        	String datestr = group(4)+" "+group(5);
+            String datestr = group(4)+" "+group(5);
             String name = group(6);
             try
             {

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/OS400FTPEntryParser.java Tue May 20 16:48:50 2008
@@ -28,11 +28,11 @@
 public class OS400FTPEntryParser extends ConfigurableFTPFileEntryParserImpl
 {
     private static final String DEFAULT_DATE_FORMAT 
-		= "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24
+        = "yy/MM/dd HH:mm:ss"; //01/11/09 12:30:24
     
 
 
-	private static final String REGEX =
+    private static final String REGEX =
         "(\\S+)\\s+"                // user
         + "(\\d+)\\s+"              // size
         + "(\\S+)\\s+(\\S+)\\s+"    // date stuff 
@@ -83,7 +83,7 @@
         {
             String usr = group(1);
             String filesize = group(2);
-        	String datestr = group(3)+" "+group(4);
+            String datestr = group(3)+" "+group(4);
             String typeStr = group(5);
             String name = group(6);
             
@@ -93,7 +93,7 @@
             }
             catch (ParseException e)
             {
-            	// intentionally do nothing
+                // intentionally do nothing
             }
 
 

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/UnixFTPEntryParser.java Tue May 20 16:48:50 2008
@@ -40,13 +40,13 @@
         "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)";
     
     static final String DEFAULT_DATE_FORMAT 
-		= "MMM d yyyy"; //Nov 9 2001
+        = "MMM d yyyy"; //Nov 9 2001
     
     static final String DEFAULT_RECENT_DATE_FORMAT 
-		= "MMM d HH:mm"; //Nov 9 20:06
+        = "MMM d HH:mm"; //Nov 9 20:06
 
     static final String NUMERIC_DATE_FORMAT 
-		= "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
+        = "yyyy-MM-dd HH:mm"; //2001-11-09 20:06
 
     /**
      * Some Linux distributions are now shipping an FTP server which formats
@@ -97,14 +97,14 @@
           numeric or standard format date
         */
         + "((?:\\d+[-/]\\d+[-/]\\d+)|(?:\\S+\\s+\\S+))\\s+"
-		
+        
         /* 
            year (for non-recent standard format) 
-		   or time (for numeric or recent standard format  
-		*/
-		+ "(\\d+(?::\\d+)?)\\s+"
+           or time (for numeric or recent standard format  
+        */
+        + "(\\d+(?::\\d+)?)\\s+"
         
-		+ "(\\S*)(\\s*.*)";
+        + "(\\S*)(\\s*.*)";
 
 
     /**
@@ -149,7 +149,7 @@
      * @param entry A line of text from the file listing
      * @return An FTPFile instance corresponding to the supplied entry
      */
-	public FTPFile parseFTPEntry(String entry) {
+    public FTPFile parseFTPEntry(String entry) {
         FTPFile file = new FTPFile();
         file.setRawListing(entry);
         int type;
@@ -191,8 +191,8 @@
                 // break; - fall through
             case 'f':
             case '-':
-            	type = FTPFile.FILE_TYPE;
-            	break;
+                type = FTPFile.FILE_TYPE;
+                break;
             default:
                 type = FTPFile.UNKNOWN_TYPE;
             }
@@ -276,7 +276,7 @@
             return file;
         }
         return null;
-	}
+    }
 
     /**
      * Defines a default configuration to be used when this class is

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ftp/parser/VMSFTPEntryParser.java Tue May 20 16:48:50 2008
@@ -51,7 +51,7 @@
 {
 
     private static final String DEFAULT_DATE_FORMAT 
-		= "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24
+        = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24
 
     /**
      * this is the regular expression used by this parser.
@@ -142,10 +142,10 @@
             f.setRawListing(entry);
             String name = group(1);
             String size = group(2);
-        	String datestr = group(3)+" "+group(4);
+            String datestr = group(3)+" "+group(4);
             String owner = group(5);
-        	String permissions[] = new String[3];
-        	permissions[0]= group(9);
+            String permissions[] = new String[3];
+            permissions[0]= group(9);
             permissions[1]= group(10);
             permissions[2]= group(11);
             try

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Article.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Article.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Article.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Article.java Tue May 20 16:48:50 2008
@@ -27,227 +27,227 @@
  *
  */
 public class Article implements Threadable {
-	private int articleNumber;
-	private String subject;
-	private String date;
-	private String articleId;
-	private String simplifiedSubject;
-	private String from;
-	private StringBuffer header;
-	private StringBuffer references;
-	private boolean isReply = false;
-	
-	public Article kid, next;
-
-	public Article() {
-		header = new StringBuffer();
-	}
-
-	/**
-	 * Adds an arbitrary header key and value to this message's header.
-	 * @param name the header name
-	 * @param val the header value
-	 */
-	public void addHeaderField(String name, String val) {
-		header.append(name);
-		header.append(": ");
-		header.append(val);
-		header.append('\n');
-	}
-	
-	/**
-	 * Adds a message-id to the list of messages that this message references (i.e. replies to)
-	 * @param msgId
-	 */
-	public void addReference(String msgId) {
-		if (references == null) {
-			references = new StringBuffer();
-			references.append("References: ");
-		}
-		references.append(msgId);
-		references.append("\t");
-	}
-
-	/**
-	 * Returns the MessageId references as an array of Strings
-	 * @return an array of message-ids
-	 */
-	public String[] getReferences() {
-		if (references == null)
-			return new String[0];
-		ArrayList list = new ArrayList();
-		int terminator = references.toString().indexOf(':');
-		StringTokenizer st =
-			new StringTokenizer(references.substring(terminator), "\t");
-		while (st.hasMoreTokens()) {
-			list.add(st.nextToken());
-		}
-		return (String[]) list.toArray(new String[list.size()]);
-	}
-	
-	/**
-	 * Attempts to parse the subject line for some typical reply signatures, and strip them out
-	 *
-	 */
-	private void simplifySubject() {
-			int start = 0;
-			String subject = getSubject();
-			int len = subject.length();
-
-			boolean done = false;
-
-			while (!done) {
-				done = true;
-
-				// skip whitespace
-				// "Re: " breaks this
-				while (start < len && subject.charAt(start) == ' ') {
-					start++;
-				}
-
-				if (start < (len - 2)
-					&& (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
-					&& (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {
-
-					if (subject.charAt(start + 2) == ':') {
-						start += 3; // Skip "Re:"
-						isReply = true;
-						done = false;
-					} else if (
-						start < (len - 2) 
-						&& 
-						(subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) {
-                    	
-						int i = start + 3;
-
-						while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9')
-							i++;
-
-						if (i < (len - 1)
-							&& (subject.charAt(i) == ']' || subject.charAt(i) == ')')
-							&& subject.charAt(i + 1) == ':') {
-							start = i + 2;
-							isReply = true;
-							done = false;
-						}
-					}
-				}
-
-				if (simplifiedSubject == "(no subject)")
-					simplifiedSubject = "";
-
-				int end = len;
-
-				while (end > start && subject.charAt(end - 1) < ' ')
-					end--;
-
-				if (start == 0 && end == len)
-					simplifiedSubject = subject;
-				else
-					simplifiedSubject = subject.substring(start, end);
-			}
-		}
-		
-	/**
-	 * Recursive method that traverses a pre-threaded graph (or tree) 
-	 * of connected Article objects and prints them out.  
-	 * @param article the root of the article 'tree'
-	 * @param depth the current tree depth
-	 */
-	public static void printThread(Article article, int depth) {
-			for (int i = 0; i < depth; ++i)
-				System.out.print("==>");
-			System.out.println(article.getSubject() + "\t" + article.getFrom());
-			if (article.kid != null)
-				printThread(article.kid, depth + 1);
-			if (article.next != null)
-				printThread(article.next, depth);
-	}
-
-	public String getArticleId() {
-		return articleId;
-	}
-
-	public int getArticleNumber() {
-		return articleNumber;
-	}
-
-	public String getDate() {
-		return date;
-	}
-
-	public String getFrom() {
-		return from;
-	}
-
-	public String getSubject() {
-		return subject;
-	}
-
-	public void setArticleId(String string) {
-		articleId = string;
-	}
-
-	public void setArticleNumber(int i) {
-		articleNumber = i;
-	}
-
-	public void setDate(String string) {
-		date = string;
-	}
-
-	public void setFrom(String string) {
-		from = string;
-	}
-
-	public void setSubject(String string) {
-		subject = string;
-	}
-
-	
-	public boolean isDummy() {
-		return (getSubject() == null);
-	}
-
-	public String messageThreadId() {
-		return articleId;
-	}
-	
-	public String[] messageThreadReferences() {
-		return getReferences();
-	}
-	
-	public String simplifiedSubject() {
-		if(simplifiedSubject == null)
-			simplifySubject();
-		return simplifiedSubject;
-	}
-
-	
-	public boolean subjectIsReply() {
-		if(simplifiedSubject == null)
-			simplifySubject();
-		return isReply;
-	}
-
-	
-	public void setChild(Threadable child) {
-		this.kid = (Article) child;
-		flushSubjectCache();
-	}
-
-	private void flushSubjectCache() {
-		simplifiedSubject = null;
-	}
-
-	
-	public void setNext(Threadable next) {
-		this.next = (Article)next;
-		flushSubjectCache();
-	}
-
-	
-	public Threadable makeDummy() {
-		return new Article();
-	}
+    private int articleNumber;
+    private String subject;
+    private String date;
+    private String articleId;
+    private String simplifiedSubject;
+    private String from;
+    private StringBuffer header;
+    private StringBuffer references;
+    private boolean isReply = false;
+    
+    public Article kid, next;
+
+    public Article() {
+        header = new StringBuffer();
+    }
+
+    /**
+     * Adds an arbitrary header key and value to this message's header.
+     * @param name the header name
+     * @param val the header value
+     */
+    public void addHeaderField(String name, String val) {
+        header.append(name);
+        header.append(": ");
+        header.append(val);
+        header.append('\n');
+    }
+    
+    /**
+     * Adds a message-id to the list of messages that this message references (i.e. replies to)
+     * @param msgId
+     */
+    public void addReference(String msgId) {
+        if (references == null) {
+            references = new StringBuffer();
+            references.append("References: ");
+        }
+        references.append(msgId);
+        references.append("\t");
+    }
+
+    /**
+     * Returns the MessageId references as an array of Strings
+     * @return an array of message-ids
+     */
+    public String[] getReferences() {
+        if (references == null)
+            return new String[0];
+        ArrayList list = new ArrayList();
+        int terminator = references.toString().indexOf(':');
+        StringTokenizer st =
+            new StringTokenizer(references.substring(terminator), "\t");
+        while (st.hasMoreTokens()) {
+            list.add(st.nextToken());
+        }
+        return (String[]) list.toArray(new String[list.size()]);
+    }
+    
+    /**
+     * Attempts to parse the subject line for some typical reply signatures, and strip them out
+     *
+     */
+    private void simplifySubject() {
+            int start = 0;
+            String subject = getSubject();
+            int len = subject.length();
+
+            boolean done = false;
+
+            while (!done) {
+                done = true;
+
+                // skip whitespace
+                // "Re: " breaks this
+                while (start < len && subject.charAt(start) == ' ') {
+                    start++;
+                }
+
+                if (start < (len - 2)
+                    && (subject.charAt(start) == 'r' || subject.charAt(start) == 'R')
+                    && (subject.charAt(start + 1) == 'e' || subject.charAt(start + 1) == 'E')) {
+
+                    if (subject.charAt(start + 2) == ':') {
+                        start += 3; // Skip "Re:"
+                        isReply = true;
+                        done = false;
+                    } else if (
+                        start < (len - 2) 
+                        && 
+                        (subject.charAt(start + 2) == '[' || subject.charAt(start + 2) == '(')) {
+
+                        int i = start + 3;
+
+                        while (i < len && subject.charAt(i) >= '0' && subject.charAt(i) <= '9')
+                            i++;
+
+                        if (i < (len - 1)
+                            && (subject.charAt(i) == ']' || subject.charAt(i) == ')')
+                            && subject.charAt(i + 1) == ':') {
+                            start = i + 2;
+                            isReply = true;
+                            done = false;
+                        }
+                    }
+                }
+
+                if (simplifiedSubject == "(no subject)")
+                    simplifiedSubject = "";
+
+                int end = len;
+
+                while (end > start && subject.charAt(end - 1) < ' ')
+                    end--;
+
+                if (start == 0 && end == len)
+                    simplifiedSubject = subject;
+                else
+                    simplifiedSubject = subject.substring(start, end);
+            }
+        }
+        
+    /**
+     * Recursive method that traverses a pre-threaded graph (or tree) 
+     * of connected Article objects and prints them out.  
+     * @param article the root of the article 'tree'
+     * @param depth the current tree depth
+     */
+    public static void printThread(Article article, int depth) {
+            for (int i = 0; i < depth; ++i)
+                System.out.print("==>");
+            System.out.println(article.getSubject() + "\t" + article.getFrom());
+            if (article.kid != null)
+                printThread(article.kid, depth + 1);
+            if (article.next != null)
+                printThread(article.next, depth);
+    }
+
+    public String getArticleId() {
+        return articleId;
+    }
+
+    public int getArticleNumber() {
+        return articleNumber;
+    }
+
+    public String getDate() {
+        return date;
+    }
+
+    public String getFrom() {
+        return from;
+    }
+
+    public String getSubject() {
+        return subject;
+    }
+
+    public void setArticleId(String string) {
+        articleId = string;
+    }
+
+    public void setArticleNumber(int i) {
+        articleNumber = i;
+    }
+
+    public void setDate(String string) {
+        date = string;
+    }
+
+    public void setFrom(String string) {
+        from = string;
+    }
+
+    public void setSubject(String string) {
+        subject = string;
+    }
+
+    
+    public boolean isDummy() {
+        return (getSubject() == null);
+    }
+
+    public String messageThreadId() {
+        return articleId;
+    }
+    
+    public String[] messageThreadReferences() {
+        return getReferences();
+    }
+    
+    public String simplifiedSubject() {
+        if(simplifiedSubject == null)
+            simplifySubject();
+        return simplifiedSubject;
+    }
+
+    
+    public boolean subjectIsReply() {
+        if(simplifiedSubject == null)
+            simplifySubject();
+        return isReply;
+    }
+
+    
+    public void setChild(Threadable child) {
+        this.kid = (Article) child;
+        flushSubjectCache();
+    }
+
+    private void flushSubjectCache() {
+        simplifiedSubject = null;
+    }
+
+    
+    public void setNext(Threadable next) {
+        this.next = (Article)next;
+        flushSubjectCache();
+    }
+
+    
+    public Threadable makeDummy() {
+        return new Article();
+    }
 }

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/NNTPClient.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/NNTPClient.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/NNTPClient.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/NNTPClient.java Tue May 20 16:48:50 2008
@@ -195,10 +195,10 @@
             result._setFirstArticle(firstNum);
             result._setLastArticle(lastNum);
 
-	    if((firstNum == 0) && (lastNum == 0))
-		    result._setArticleCount(0);
-	    else
-		    result._setArticleCount(lastNum - firstNum + 1);
+        if((firstNum == 0) && (lastNum == 0))
+            result._setArticleCount(0);
+        else
+            result._setArticleCount(lastNum - firstNum + 1);
         }
         catch (NumberFormatException e)
         {

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threadable.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threadable.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threadable.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threadable.java Tue May 20 16:48:50 2008
@@ -22,12 +22,12 @@
  *
  */
 public interface Threadable {
-	public boolean isDummy();
-	public String messageThreadId();
-	public String[] messageThreadReferences();
-	public String simplifiedSubject();
-	public boolean subjectIsReply();
-	public void setChild(Threadable child); 
-	public void setNext(Threadable next);
-	public Threadable makeDummy();
+    public boolean isDummy();
+    public String messageThreadId();
+    public String[] messageThreadReferences();
+    public String simplifiedSubject();
+    public boolean subjectIsReply();
+    public void setChild(Threadable child); 
+    public void setNext(Threadable next);
+    public Threadable makeDummy();
 }
\ No newline at end of file

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threader.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threader.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threader.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/nntp/Threader.java Tue May 20 16:48:50 2008
@@ -30,374 +30,374 @@
 import java.util.Iterator;
 
 public class Threader {
-	private ThreadContainer root;
-	private HashMap idTable;
-	private int bogusIdCount = 0;
-
-	/**
-	 * The main threader entry point - The client passes in an array of Threadable objects, and 
-	 * the Threader constructs a connected 'graph' of messages
-	 * @param messages
-	 * @return
-	 */
-	public Threadable thread(Threadable[] messages) {
-		if (messages == null)
-			return null;
-
-		idTable = new HashMap();
-
-		// walk through each Threadable element
-		for (int i = 0; i < messages.length; ++i) {
-			if (!messages[i].isDummy())
-				buildContainer(messages[i]);
-		}
-
-		root = findRootSet();
-		idTable.clear();
-		idTable = null;
-
-		pruneEmptyContainers(root);
-
-		root.reverseChildren();
-		gatherSubjects();
-
-		if (root.next != null)
-			throw new RuntimeException("root node has a next:" + root);
-
-		for (ThreadContainer r = root.child; r != null; r = r.next) {
-			if (r.threadable == null)
-				r.threadable = r.child.threadable.makeDummy();
-		}
-
-		Threadable result = (root.child == null ? null : root.child.threadable);
-		root.flush();
-		root = null;
-
-		return result;
-	}
-
-	/**
-	 * 
-	 * @param threadable
-	 */
-	private void buildContainer(Threadable threadable) {
-		String id = threadable.messageThreadId();
-		ThreadContainer container = (ThreadContainer) idTable.get(id);
-
-		// A ThreadContainer exists for this id already. This should be a forward reference, but may 
-		// be a duplicate id, in which case we will need to generate a bogus placeholder id
-		if (container != null) {
-			if (container.threadable != null) { // oops! duplicate ids...
-				id = "<Bogus-id:" + (bogusIdCount++) + ">";
-				container = null;
-			} else {
-				// The container just contained a forward reference to this message, so let's
-				// fill in the threadable field of the container with this message
-				container.threadable = threadable;
-			}
-		}
-
-		// No container exists for that message Id. Create one and insert it into the hash table.
-		if (container == null) {
-			container = new ThreadContainer();
-			container.threadable = threadable;
-			idTable.put(id, container);
-		}
-
-		// Iterate through all of the references and create ThreadContainers for any references that
-		// don't have them.
-		ThreadContainer parentRef = null;
-		{
-			String[] references = threadable.messageThreadReferences();
-			for (int i = 0; i < references.length; ++i) {
-				String refString = references[i];
-				ThreadContainer ref = (ThreadContainer) idTable.get(refString);
-
-				// if this id doesnt have a container, create one
-				if (ref == null) {
-					ref = new ThreadContainer();
-					idTable.put(refString, ref);
-				}
-
-				// Link references together in the order they appear in the References: header,
-				// IF they dont have a have a parent already &&
-				// IF it will not cause a circular reference
-				if ((parentRef != null)
-					&& (ref.parent == null)
-					&& (parentRef != ref)
-					&& !(parentRef.findChild(ref))) {
-					// Link ref into the parent's child list
-					ref.parent = parentRef;
-					ref.next = parentRef.child;
-					parentRef.child = ref;
-				}
-				parentRef = ref;
-			}
-		}
-
-		// parentRef is now set to the container of the last element in the references field. make that 
-		// be the parent of this container, unless doing so causes a circular reference
-		if (parentRef != null
-			&& (parentRef == container || container.findChild(parentRef)))
-			parentRef = null;
-
-		// if it has a parent already, its because we saw this message in a References: field, and presumed
-		// a parent based on the other entries in that field. Now that we have the actual message, we can
-		// throw away the old parent and use this new one
-		if (container.parent != null) {
-			ThreadContainer rest, prev;
-
-			for (prev = null, rest = container.parent.child;
-				rest != null;
-				prev = rest, rest = rest.next) {
-				if (rest == container)
-					break;
-			}
-
-			if (rest == null) {
-				throw new RuntimeException(
-					"Didnt find "
-						+ container
-						+ " in parent"
-						+ container.parent);
-			}
-
-			// Unlink this container from the parent's child list
-			if (prev == null)
-				container.parent.child = container.next;
-			else
-				prev.next = container.next;
-
-			container.next = null;
-			container.parent = null;
-		}
-
-		// If we have a parent, link container into the parents child list
-		if (parentRef != null) {
-			container.parent = parentRef;
-			container.next = parentRef.child;
-			parentRef.child = container;
-		}
-	}
-
-	/**
-	 * Find the root set of all existing ThreadContainers
-	 * @return root the ThreadContainer representing the root node
-	 */
-	private ThreadContainer findRootSet() {
-		ThreadContainer root = new ThreadContainer();
-		Iterator iter = idTable.keySet().iterator();
-
-		while (iter.hasNext()) {
-			Object key = iter.next();
-			ThreadContainer c = (ThreadContainer) idTable.get(key);
-			if (c.parent == null) {
-				if (c.next != null)
-					throw new RuntimeException(
-						"c.next is " + c.next.toString());
-				c.next = root.child;
-				root.child = c;
-			}
-		}
-		return root;
-	}
-
-	/**
-	 * Delete any empty or dummy ThreadContainers
-	 * @param parent
-	 */
-	private void pruneEmptyContainers(ThreadContainer parent) {
-		ThreadContainer container, prev, next;
-		for (prev = null, container = parent.child, next = container.next;
-			container != null;
-			prev = container,
-				container = next,
-				next = (container == null ? null : container.next)) {
-
-			// Is it empty and without any children? If so,delete it 
-			if (container.threadable == null && container.child == null) {
-				if (prev == null)
-					parent.child = container.next;
-				else
-					prev.next = container.next;
-
-				// Set container to prev so that prev keeps its same value the next time through the loop	
-				container = prev;
-			}
-
-			// Else if empty, with kids, and (not at root or only one kid)
-			else if (
-				container.threadable == null
-					&& container.child != null
-					&& (container.parent != null
-						|| container.child.next == null)) {
-				// We have an invalid/expired message with kids. Promote the kids to this level. 
-				ThreadContainer tail;
-				ThreadContainer kids = container.child;
-
-				// Remove this container and replace with 'kids'.
-				if (prev == null)
-					parent.child = kids;
-				else
-					prev.next = kids;
-
-				// Make each child's parent be this level's parent -> i.e. promote the children. Make the last child's next point to this container's next
-				// i.e. splice kids into the list in place of container
-				for (tail = kids; tail.next != null; tail = tail.next)
-					tail.parent = container.parent;
-
-				tail.parent = container.parent;
-				tail.next = container.next;
-
-				// next currently points to the item after the inserted items in the chain - reset that so we process the newly
-				// promoted items next time round
-				next = kids;
-
-				// Set container to prev so that prev keeps its same value the next time through the loop
-				container = prev;
-			} else if (container.child != null) {
-				// A real message , with kids
-				// Iterate over the children
-				pruneEmptyContainers(container);
-			}
-		}
-	}
-
-	/**
-	 *  If any two members of the root set have the same subject, merge them. This is to attempt to accomodate messages without References: headers. 
-	 */
-	private void gatherSubjects() {
-
-		int count = 0;
-
-		for (ThreadContainer c = root.child; c != null; c = c.next)
-			count++;
-
-		// TODO verify this will avoid rehashing
-		HashMap subjectTable = new HashMap((int) (count * 1.2), (float) 0.9);
-		count = 0;
-
-		for (ThreadContainer c = root.child; c != null; c = c.next) {
-			Threadable threadable = c.threadable;
-
-			// No threadable? If so, it is a dummy node in the root set.
-			// Only root set members may be dummies, and they alway have at least 2 kids
-			// Take the first kid as representative of the subject
-			if (threadable == null)
-				threadable = c.child.threadable;
-
-			String subj = threadable.simplifiedSubject();
-
-			if (subj == null || subj == "")
-				continue;
-
-			ThreadContainer old = (ThreadContainer) subjectTable.get(subj);
-
-			// Add this container to the table iff:
-			// - There exists no container with this subject
-			// - or this is a dummy container and the old one is not - the dummy one is
-			// more interesting as a root, so put it in the table instead
-			// - The container in the table has a "Re:" version of this subject, and 
-			// this container has a non-"Re:" version of this subject. The non-"Re:" version
-			// is the more interesting of the two.
-			if (old == null
-				|| (c.threadable == null && old.threadable != null)
-				|| (old.threadable != null
-					&& old.threadable.subjectIsReply()
-					&& c.threadable != null
-					&& !c.threadable.subjectIsReply())) {
-				subjectTable.put(subj, c);
-				count++;
-			}
-		}
-
-		// If the table is empty, we're done
-		if (count == 0)
-			return;
-
-		// subjectTable is now populated with one entry for each subject which occurs in the 
-		// root set. Iterate over the root set, and gather together the difference.
-		ThreadContainer prev, c, rest;
-		for (prev = null, c = root.child, rest = c.next;
-			c != null;
-			prev = c, c = rest, rest = (rest == null ? null : rest.next)) {
-			Threadable threadable = c.threadable;
-
-			// is it a dummy node?
-			if (threadable == null)
-				threadable = c.child.threadable;
-
-			String subj = threadable.simplifiedSubject();
-
-			// Dont thread together all subjectless messages
-			if (subj == null || subj == "")
-				continue;
-
-			ThreadContainer old = (ThreadContainer) subjectTable.get(subj);
-
-			if (old == c) // That's us
-				continue;
-
-			// We have now found another container in the root set with the same subject
-			// Remove the "second" message from the root set
-			if (prev == null)
-				root.child = c.next;
-			else
-				prev.next = c.next;
-			c.next = null;
-
-			if (old.threadable == null && c.threadable == null) {
-				// both dummies - merge them
-				ThreadContainer tail;
-				for (tail = old.child;
-					tail != null && tail.next != null;
-					tail = tail.next);
-
-				tail.next = c.child;
-
-				for (tail = c.child; tail != null; tail = tail.next)
-					tail.parent = old;
-
-				c.child = null;
-			} else if (
-				old.threadable == null
-					|| (c.threadable != null
-						&& c.threadable.subjectIsReply()
-						&& !old.threadable.subjectIsReply())) {
-				// Else if old is empty, or c has "Re:" and old does not  ==> make this message a child of old
-				c.parent = old;
-				c.next = old.child;
-				old.child = c;
-			} else {
-				//	else make the old and new messages be children of a new dummy container.
-				// We create a new container object for old.msg and empty the old container
-				ThreadContainer newc = new ThreadContainer();
-				newc.threadable = old.threadable;
-				newc.child = old.child;
-
-				for (ThreadContainer tail = newc.child;
-					tail != null;
-					tail = tail.next)
-					tail.parent = newc;
-
-				old.threadable = null;
-				old.child = null;
-
-				c.parent = old;
-				newc.parent = old;
-
-				// Old is now a dummy- give it 2 kids , c and newc
-				old.child = c;
-				c.next = newc;
-			}
-			// We've done a merge, so keep the same prev
-			c = prev;
-		}
+    private ThreadContainer root;
+    private HashMap idTable;
+    private int bogusIdCount = 0;
+
+    /**
+     * The main threader entry point - The client passes in an array of Threadable objects, and 
+     * the Threader constructs a connected 'graph' of messages
+     * @param messages
+     * @return
+     */
+    public Threadable thread(Threadable[] messages) {
+        if (messages == null)
+            return null;
+
+        idTable = new HashMap();
+
+        // walk through each Threadable element
+        for (int i = 0; i < messages.length; ++i) {
+            if (!messages[i].isDummy())
+                buildContainer(messages[i]);
+        }
+
+        root = findRootSet();
+        idTable.clear();
+        idTable = null;
+
+        pruneEmptyContainers(root);
+
+        root.reverseChildren();
+        gatherSubjects();
+
+        if (root.next != null)
+            throw new RuntimeException("root node has a next:" + root);
+
+        for (ThreadContainer r = root.child; r != null; r = r.next) {
+            if (r.threadable == null)
+                r.threadable = r.child.threadable.makeDummy();
+        }
+
+        Threadable result = (root.child == null ? null : root.child.threadable);
+        root.flush();
+        root = null;
+
+        return result;
+    }
+
+    /**
+     * 
+     * @param threadable
+     */
+    private void buildContainer(Threadable threadable) {
+        String id = threadable.messageThreadId();
+        ThreadContainer container = (ThreadContainer) idTable.get(id);
+
+        // A ThreadContainer exists for this id already. This should be a forward reference, but may 
+        // be a duplicate id, in which case we will need to generate a bogus placeholder id
+        if (container != null) {
+            if (container.threadable != null) { // oops! duplicate ids...
+                id = "<Bogus-id:" + (bogusIdCount++) + ">";
+                container = null;
+            } else {
+                // The container just contained a forward reference to this message, so let's
+                // fill in the threadable field of the container with this message
+                container.threadable = threadable;
+            }
+        }
+
+        // No container exists for that message Id. Create one and insert it into the hash table.
+        if (container == null) {
+            container = new ThreadContainer();
+            container.threadable = threadable;
+            idTable.put(id, container);
+        }
+
+        // Iterate through all of the references and create ThreadContainers for any references that
+        // don't have them.
+        ThreadContainer parentRef = null;
+        {
+            String[] references = threadable.messageThreadReferences();
+            for (int i = 0; i < references.length; ++i) {
+                String refString = references[i];
+                ThreadContainer ref = (ThreadContainer) idTable.get(refString);
+
+                // if this id doesnt have a container, create one
+                if (ref == null) {
+                    ref = new ThreadContainer();
+                    idTable.put(refString, ref);
+                }
+
+                // Link references together in the order they appear in the References: header,
+                // IF they dont have a have a parent already &&
+                // IF it will not cause a circular reference
+                if ((parentRef != null)
+                    && (ref.parent == null)
+                    && (parentRef != ref)
+                    && !(parentRef.findChild(ref))) {
+                    // Link ref into the parent's child list
+                    ref.parent = parentRef;
+                    ref.next = parentRef.child;
+                    parentRef.child = ref;
+                }
+                parentRef = ref;
+            }
+        }
+
+        // parentRef is now set to the container of the last element in the references field. make that 
+        // be the parent of this container, unless doing so causes a circular reference
+        if (parentRef != null
+            && (parentRef == container || container.findChild(parentRef)))
+            parentRef = null;
+
+        // if it has a parent already, its because we saw this message in a References: field, and presumed
+        // a parent based on the other entries in that field. Now that we have the actual message, we can
+        // throw away the old parent and use this new one
+        if (container.parent != null) {
+            ThreadContainer rest, prev;
+
+            for (prev = null, rest = container.parent.child;
+                rest != null;
+                prev = rest, rest = rest.next) {
+                if (rest == container)
+                    break;
+            }
+
+            if (rest == null) {
+                throw new RuntimeException(
+                    "Didnt find "
+                        + container
+                        + " in parent"
+                        + container.parent);
+            }
+
+            // Unlink this container from the parent's child list
+            if (prev == null)
+                container.parent.child = container.next;
+            else
+                prev.next = container.next;
+
+            container.next = null;
+            container.parent = null;
+        }
+
+        // If we have a parent, link container into the parents child list
+        if (parentRef != null) {
+            container.parent = parentRef;
+            container.next = parentRef.child;
+            parentRef.child = container;
+        }
+    }
+
+    /**
+     * Find the root set of all existing ThreadContainers
+     * @return root the ThreadContainer representing the root node
+     */
+    private ThreadContainer findRootSet() {
+        ThreadContainer root = new ThreadContainer();
+        Iterator iter = idTable.keySet().iterator();
+
+        while (iter.hasNext()) {
+            Object key = iter.next();
+            ThreadContainer c = (ThreadContainer) idTable.get(key);
+            if (c.parent == null) {
+                if (c.next != null)
+                    throw new RuntimeException(
+                        "c.next is " + c.next.toString());
+                c.next = root.child;
+                root.child = c;
+            }
+        }
+        return root;
+    }
+
+    /**
+     * Delete any empty or dummy ThreadContainers
+     * @param parent
+     */
+    private void pruneEmptyContainers(ThreadContainer parent) {
+        ThreadContainer container, prev, next;
+        for (prev = null, container = parent.child, next = container.next;
+            container != null;
+            prev = container,
+                container = next,
+                next = (container == null ? null : container.next)) {
+
+            // Is it empty and without any children? If so,delete it 
+            if (container.threadable == null && container.child == null) {
+                if (prev == null)
+                    parent.child = container.next;
+                else
+                    prev.next = container.next;
+
+                // Set container to prev so that prev keeps its same value the next time through the loop
+                container = prev;
+            }
+
+            // Else if empty, with kids, and (not at root or only one kid)
+            else if (
+                container.threadable == null
+                    && container.child != null
+                    && (container.parent != null
+                        || container.child.next == null)) {
+                // We have an invalid/expired message with kids. Promote the kids to this level. 
+                ThreadContainer tail;
+                ThreadContainer kids = container.child;
+
+                // Remove this container and replace with 'kids'.
+                if (prev == null)
+                    parent.child = kids;
+                else
+                    prev.next = kids;
+
+                // Make each child's parent be this level's parent -> i.e. promote the children. Make the last child's next point to this container's next
+                // i.e. splice kids into the list in place of container
+                for (tail = kids; tail.next != null; tail = tail.next)
+                    tail.parent = container.parent;
+
+                tail.parent = container.parent;
+                tail.next = container.next;
+
+                // next currently points to the item after the inserted items in the chain - reset that so we process the newly
+                // promoted items next time round
+                next = kids;
+
+                // Set container to prev so that prev keeps its same value the next time through the loop
+                container = prev;
+            } else if (container.child != null) {
+                // A real message , with kids
+                // Iterate over the children
+                pruneEmptyContainers(container);
+            }
+        }
+    }
+
+    /**
+     *  If any two members of the root set have the same subject, merge them. This is to attempt to accomodate messages without References: headers. 
+     */
+    private void gatherSubjects() {
+
+        int count = 0;
+
+        for (ThreadContainer c = root.child; c != null; c = c.next)
+            count++;
+
+        // TODO verify this will avoid rehashing
+        HashMap subjectTable = new HashMap((int) (count * 1.2), (float) 0.9);
+        count = 0;
+
+        for (ThreadContainer c = root.child; c != null; c = c.next) {
+            Threadable threadable = c.threadable;
+
+            // No threadable? If so, it is a dummy node in the root set.
+            // Only root set members may be dummies, and they alway have at least 2 kids
+            // Take the first kid as representative of the subject
+            if (threadable == null)
+                threadable = c.child.threadable;
+
+            String subj = threadable.simplifiedSubject();
+
+            if (subj == null || subj == "")
+                continue;
+
+            ThreadContainer old = (ThreadContainer) subjectTable.get(subj);
+
+            // Add this container to the table iff:
+            // - There exists no container with this subject
+            // - or this is a dummy container and the old one is not - the dummy one is
+            // more interesting as a root, so put it in the table instead
+            // - The container in the table has a "Re:" version of this subject, and 
+            // this container has a non-"Re:" version of this subject. The non-"Re:" version
+            // is the more interesting of the two.
+            if (old == null
+                || (c.threadable == null && old.threadable != null)
+                || (old.threadable != null
+                    && old.threadable.subjectIsReply()
+                    && c.threadable != null
+                    && !c.threadable.subjectIsReply())) {
+                subjectTable.put(subj, c);
+                count++;
+            }
+        }
+
+        // If the table is empty, we're done
+        if (count == 0)
+            return;
+
+        // subjectTable is now populated with one entry for each subject which occurs in the 
+        // root set. Iterate over the root set, and gather together the difference.
+        ThreadContainer prev, c, rest;
+        for (prev = null, c = root.child, rest = c.next;
+            c != null;
+            prev = c, c = rest, rest = (rest == null ? null : rest.next)) {
+            Threadable threadable = c.threadable;
+
+            // is it a dummy node?
+            if (threadable == null)
+                threadable = c.child.threadable;
+
+            String subj = threadable.simplifiedSubject();
+
+            // Dont thread together all subjectless messages
+            if (subj == null || subj == "")
+                continue;
+
+            ThreadContainer old = (ThreadContainer) subjectTable.get(subj);
+
+            if (old == c) // That's us
+                continue;
+
+            // We have now found another container in the root set with the same subject
+            // Remove the "second" message from the root set
+            if (prev == null)
+                root.child = c.next;
+            else
+                prev.next = c.next;
+            c.next = null;
+
+            if (old.threadable == null && c.threadable == null) {
+                // both dummies - merge them
+                ThreadContainer tail;
+                for (tail = old.child;
+                    tail != null && tail.next != null;
+                    tail = tail.next);
+
+                tail.next = c.child;
+
+                for (tail = c.child; tail != null; tail = tail.next)
+                    tail.parent = old;
+
+                c.child = null;
+            } else if (
+                old.threadable == null
+                    || (c.threadable != null
+                        && c.threadable.subjectIsReply()
+                        && !old.threadable.subjectIsReply())) {
+                // Else if old is empty, or c has "Re:" and old does not  ==> make this message a child of old
+                c.parent = old;
+                c.next = old.child;
+                old.child = c;
+            } else {
+                // else make the old and new messages be children of a new dummy container.
+                // We create a new container object for old.msg and empty the old container
+                ThreadContainer newc = new ThreadContainer();
+                newc.threadable = old.threadable;
+                newc.child = old.child;
+
+                for (ThreadContainer tail = newc.child;
+                    tail != null;
+                    tail = tail.next)
+                    tail.parent = newc;
+
+                old.threadable = null;
+                old.child = null;
+
+                c.parent = old;
+                newc.parent = old;
+
+                // Old is now a dummy- give it 2 kids , c and newc
+                old.child = c;
+                c.next = newc;
+            }
+            // We've done a merge, so keep the same prev
+            c = prev;
+        }
 
-		subjectTable.clear();
-		subjectTable = null;
+        subjectTable.clear();
+        subjectTable = null;
 
-	}
+    }
 }
 
 /**
@@ -408,73 +408,73 @@
  * @author Rory Winston <rw...@checkfree.com>
  */
 class ThreadContainer {
-	Threadable threadable;
-	ThreadContainer parent;
-	ThreadContainer prev;
-	ThreadContainer next;
-	ThreadContainer child;
-
-	/**
-	 * 
-	 * @param container
-	 * @return true if child is under self's tree. Detects circular references
-	 */
-	boolean findChild(ThreadContainer target) {
-		if (child == null)
-			return false;
-
-		else if (child == target)
-			return true;
-		else
-			return child.findChild(target);
-	}
-
-	// Copy the ThreadContainer tree structure down into the underlying Threadable objects
-	// (Make the Threadable tree look like the ThreadContainer tree)
-	void flush() {
-		if (parent != null && threadable == null)
-			throw new RuntimeException("no threadable in " + this.toString());
-
-		parent = null;
-
-		if (threadable != null)
-			threadable.setChild(child == null ? null : child.threadable);
-
-		if (child != null) {
-			child.flush();
-			child = null;
-		}
-
-		if (threadable != null)
-			threadable.setNext(next == null ? null : next.threadable);
-
-		if (next != null) {
-			next.flush();
-			next = null;
-		}
-
-		threadable = null;
-	}
-
-	/**
-	 * Reverse the entire set of children
-	 *
-	 */
-	void reverseChildren() {
-		if (child != null) {
-			ThreadContainer kid, prev, rest;
-			for (prev = null, kid = child, rest = kid.next;
-				kid != null;
-				prev = kid,
-					kid = rest,
-					rest = (rest == null ? null : rest.next))
-				kid.next = prev;
-
-			child = prev;
-
-			// Do it for the kids 
-			for (kid = child; kid != null; kid = kid.next)
-				kid.reverseChildren();
-		}
-	}
+    Threadable threadable;
+    ThreadContainer parent;
+    ThreadContainer prev;
+    ThreadContainer next;
+    ThreadContainer child;
+
+    /**
+     * 
+     * @param container
+     * @return true if child is under self's tree. Detects circular references
+     */
+    boolean findChild(ThreadContainer target) {
+        if (child == null)
+            return false;
+
+        else if (child == target)
+            return true;
+        else
+            return child.findChild(target);
+    }
+
+    // Copy the ThreadContainer tree structure down into the underlying Threadable objects
+    // (Make the Threadable tree look like the ThreadContainer tree)
+    void flush() {
+        if (parent != null && threadable == null)
+            throw new RuntimeException("no threadable in " + this.toString());
+
+        parent = null;
+
+        if (threadable != null)
+            threadable.setChild(child == null ? null : child.threadable);
+
+        if (child != null) {
+            child.flush();
+            child = null;
+        }
+
+        if (threadable != null)
+            threadable.setNext(next == null ? null : next.threadable);
+
+        if (next != null) {
+            next.flush();
+            next = null;
+        }
+
+        threadable = null;
+    }
+
+    /**
+     * Reverse the entire set of children
+     *
+     */
+    void reverseChildren() {
+        if (child != null) {
+            ThreadContainer kid, prev, rest;
+            for (prev = null, kid = child, rest = kid.next;
+                kid != null;
+                prev = kid,
+                    kid = rest,
+                    rest = (rest == null ? null : rest.next))
+                kid.next = prev;
+
+            child = prev;
+
+            // Do it for the kids 
+            for (kid = child; kid != null; kid = kid.next)
+                kid.reverseChildren();
+        }
+    }
 }

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/NtpV3Impl.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/NtpV3Impl.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/NtpV3Impl.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/NtpV3Impl.java Tue May 20 16:48:50 2008
@@ -95,10 +95,10 @@
 
     /***
      * Returns leap indicator as defined in RFC-1305 which is a two-bit code:
-     *	0=no warning
-     *	1=last minute has 61 seconds
-     *	2=last minute has 59 seconds
-     *	3=alarm condition (clock not synchronized)
+     *  0=no warning
+     *  1=last minute has 61 seconds
+     *  2=last minute has 59 seconds
+     *  3=alarm condition (clock not synchronized)
      *
      * @return leap indicator as defined in RFC-1305.
      */

Modified: commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/TimeInfo.java
URL: http://svn.apache.org/viewvc/commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/TimeInfo.java?rev=658491&r1=658490&r2=658491&view=diff
==============================================================================
--- commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/TimeInfo.java (original)
+++ commons/proper/net/trunk/src/java/org/apache/commons/net/ntp/TimeInfo.java Tue May 20 16:48:50 2008
@@ -153,7 +153,7 @@
          * according to this standard NTP equation:
          *
          * LocalClockOffset = ((ReceiveTimestamp - OriginateTimestamp) +
-         *		        (TransmitTimestamp - DestinationTimestamp)) / 2
+         *                     (TransmitTimestamp - DestinationTimestamp)) / 2
          *
          * equations from RFC-1305 (NTPv3)
          *      roundtrip delay = (t4 - t1) - (t3 - t2)