001 /* 002 * Copyright 2004 The Apache Software Foundation 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package org.apache.commons.net.ftp; 017 018 import java.io.BufferedReader; 019 import java.io.IOException; 020 import java.io.InputStream; 021 import java.io.InputStreamReader; 022 import java.util.Iterator; 023 import java.util.LinkedList; 024 import java.util.List; 025 import java.util.ListIterator; 026 027 028 /** 029 * This class handles the entire process of parsing a listing of 030 * file entries from the server. 031 * <p> 032 * This object defines a two-part parsing mechanism. 033 * <p> 034 * The first part is comprised of reading the raw input into an internal 035 * list of strings. Every item in this list corresponds to an actual 036 * file. All extraneous matter emitted by the server will have been 037 * removed by the end of this phase. This is accomplished in conjunction 038 * with the FTPFileEntryParser associated with this engine, by calling 039 * its methods <code>readNextEntry()</code> - which handles the issue of 040 * what delimits one entry from another, usually but not always a line 041 * feed and <code>preParse()</code> - which handles removal of 042 * extraneous matter such as the preliminary lines of a listing, removal 043 * of duplicates on versioning systems, etc. 044 * <p> 045 * The second part is composed of the actual parsing, again in conjunction 046 * with the particular parser used by this engine. This is controlled 047 * by an iterator over the internal list of strings. This may be done 048 * either in block mode, by calling the <code>getNext()</code> and 049 * <code>getPrevious()</code> methods to provide "paged" output of less 050 * than the whole list at one time, or by calling the 051 * <code>getFiles()</code> method to return the entire list. 052 * <p> 053 * Examples: 054 * <p> 055 * Paged access: 056 * <pre> 057 * FTPClient f=FTPClient(); 058 * f.connect(server); 059 * f.login(username, password); 060 * FTPListParseEngine engine = f.initiateListParsing(directory); 061 * 062 * while (engine.hasNext()) { 063 * FTPFile[] files = engine.getNext(25); // "page size" you want 064 * //do whatever you want with these files, display them, etc. 065 * //expensive FTPFile objects not created until needed. 066 * } 067 * </pre> 068 * <p> 069 * For unpaged access, simply use FTPClient.listFiles(). That method 070 * uses this class transparently. 071 * @version $Id: FTPListParseEngine.java 155429 2005-02-26 13:13:04Z dirkv $ 072 */ 073 public class FTPListParseEngine { 074 private List entries = new LinkedList(); 075 private ListIterator _internalIterator = entries.listIterator(); 076 077 FTPFileEntryParser parser = null; 078 079 public FTPListParseEngine(FTPFileEntryParser parser) { 080 this.parser = parser; 081 } 082 083 /** 084 * handle the iniitial reading and preparsing of the list returned by 085 * the server. After this method has completed, this object will contain 086 * a list of unparsed entries (Strings) each referring to a unique file 087 * on the server. 088 * 089 * @param stream input stream provided by the server socket. 090 * 091 * @exception IOException 092 * thrown on any failure to read from the sever. 093 */ 094 public void readServerList(InputStream stream, String encoding) 095 throws IOException 096 { 097 this.entries = new LinkedList(); 098 readStream(stream, encoding); 099 this.parser.preParse(this.entries); 100 resetIterator(); 101 } 102 103 /** 104 * handle the iniitial reading and preparsing of the list returned by 105 * the server. After this method has completed, this object will contain 106 * a list of unparsed entries (Strings) each referring to a unique file 107 * on the server. 108 * 109 * @param stream input stream provided by the server socket. 110 * 111 * @exception IOException 112 * thrown on any failure to read from the sever. 113 * 114 * @deprecated The version of this method which takes an encoding should be used. 115 */ 116 public void readServerList(InputStream stream) 117 throws IOException 118 { 119 readServerList(stream, null); 120 } 121 122 123 124 /** 125 * Internal method for reading the input into the <code>entries</code> list. 126 * After this method has completed, <code>entries</code> will contain a 127 * collection of entries (as defined by 128 * <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain 129 * various non-entry preliminary lines from the server output, duplicates, 130 * and other data that will not be part of the final listing. 131 * 132 * @param stream The socket stream on which the input will be read. 133 * @param encoding The encoding to use. 134 * 135 * @exception IOException 136 * thrown on any failure to read the stream 137 */ 138 private void readStream(InputStream stream, String encoding) throws IOException 139 { 140 BufferedReader reader; 141 if (encoding == null) 142 { 143 reader = new BufferedReader(new InputStreamReader(stream)); 144 } 145 else 146 { 147 reader = new BufferedReader(new InputStreamReader(stream, encoding)); 148 } 149 150 String line = this.parser.readNextEntry(reader); 151 152 while (line != null) 153 { 154 this.entries.add(line); 155 line = this.parser.readNextEntry(reader); 156 } 157 reader.close(); 158 } 159 160 /** 161 * Returns an array of at most <code>quantityRequested</code> FTPFile 162 * objects starting at this object's internal iterator's current position. 163 * If fewer than <code>quantityRequested</code> such 164 * elements are available, the returned array will have a length equal 165 * to the number of entries at and after after the current position. 166 * If no such entries are found, this array will have a length of 0. 167 * 168 * After this method is called this object's internal iterator is advanced 169 * by a number of positions equal to the size of the array returned. 170 * 171 * @param quantityRequested 172 * the maximum number of entries we want to get. 173 * 174 * @return an array of at most <code>quantityRequested</code> FTPFile 175 * objects starting at the current position of this iterator within its 176 * list and at least the number of elements which exist in the list at 177 * and after its current position. 178 * <p><b> 179 * NOTE:</b> This array may contain null members if any of the 180 * individual file listings failed to parse. The caller should 181 * check each entry for null before referencing it. 182 */ 183 public FTPFile[] getNext(int quantityRequested) { 184 List tmpResults = new LinkedList(); 185 int count = quantityRequested; 186 while (count > 0 && this._internalIterator.hasNext()) { 187 String entry = (String) this._internalIterator.next(); 188 FTPFile temp = this.parser.parseFTPEntry(entry); 189 tmpResults.add(temp); 190 count--; 191 } 192 return (FTPFile[]) tmpResults.toArray(new FTPFile[0]); 193 194 } 195 196 /** 197 * Returns an array of at most <code>quantityRequested</code> FTPFile 198 * objects starting at this object's internal iterator's current position, 199 * and working back toward the beginning. 200 * 201 * If fewer than <code>quantityRequested</code> such 202 * elements are available, the returned array will have a length equal 203 * to the number of entries at and after after the current position. 204 * If no such entries are found, this array will have a length of 0. 205 * 206 * After this method is called this object's internal iterator is moved 207 * back by a number of positions equal to the size of the array returned. 208 * 209 * @param quantityRequested 210 * the maximum number of entries we want to get. 211 * 212 * @return an array of at most <code>quantityRequested</code> FTPFile 213 * objects starting at the current position of this iterator within its 214 * list and at least the number of elements which exist in the list at 215 * and after its current position. This array will be in the same order 216 * as the underlying list (not reversed). 217 * <p><b> 218 * NOTE:</b> This array may contain null members if any of the 219 * individual file listings failed to parse. The caller should 220 * check each entry for null before referencing it. 221 */ 222 public FTPFile[] getPrevious(int quantityRequested) { 223 List tmpResults = new LinkedList(); 224 int count = quantityRequested; 225 while (count > 0 && this._internalIterator.hasPrevious()) { 226 String entry = (String) this._internalIterator.previous(); 227 FTPFile temp = this.parser.parseFTPEntry(entry); 228 tmpResults.add(0,temp); 229 count--; 230 } 231 return (FTPFile[]) tmpResults.toArray(new FTPFile[0]); 232 } 233 234 /** 235 * Returns an array of FTPFile objects containing the whole list of 236 * files returned by the server as read by this object's parser. 237 * 238 * @return an array of FTPFile objects containing the whole list of 239 * files returned by the server as read by this object's parser. 240 * <p><b> 241 * NOTE:</b> This array may contain null members if any of the 242 * individual file listings failed to parse. The caller should 243 * check each entry for null before referencing it. 244 * @exception IOException 245 */ 246 public FTPFile[] getFiles() 247 throws IOException 248 { 249 List tmpResults = new LinkedList(); 250 Iterator iter = this.entries.iterator(); 251 while (iter.hasNext()) { 252 String entry = (String) iter.next(); 253 FTPFile temp = this.parser.parseFTPEntry(entry); 254 tmpResults.add(temp); 255 } 256 return (FTPFile[]) tmpResults.toArray(new FTPFile[0]); 257 258 } 259 260 /** 261 * convenience method to allow clients to know whether this object's 262 * internal iterator's current position is at the end of the list. 263 * 264 * @return true if internal iterator is not at end of list, false 265 * otherwise. 266 */ 267 public boolean hasNext() { 268 return _internalIterator.hasNext(); 269 } 270 271 /** 272 * convenience method to allow clients to know whether this object's 273 * internal iterator's current position is at the beginning of the list. 274 * 275 * @return true if internal iterator is not at beginning of list, false 276 * otherwise. 277 */ 278 public boolean hasPrevious() { 279 return _internalIterator.hasPrevious(); 280 } 281 282 /** 283 * resets this object's internal iterator to the beginning of the list. 284 */ 285 public void resetIterator() { 286 this._internalIterator = this.entries.listIterator(); 287 } 288 }