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