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