View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.net.ftp;
19  
20  import java.io.BufferedReader;
21  import java.io.IOException;
22  import java.io.InputStream;
23  import java.io.InputStreamReader;
24  import java.util.ArrayList;
25  import java.util.Iterator;
26  import java.util.LinkedList;
27  import java.util.List;
28  import java.util.ListIterator;
29  
30  import org.apache.commons.net.util.Charsets;
31  
32  
33  /**
34   * This class handles the entire process of parsing a listing of
35   * file entries from the server.
36   * <p>
37   * This object defines a two-part parsing mechanism.
38   * <p>
39   * The first part is comprised of reading the raw input into an internal
40   * list of strings.  Every item in this list corresponds to an actual
41   * file.  All extraneous matter emitted by the server will have been
42   * removed by the end of this phase.  This is accomplished in conjunction
43   * with the FTPFileEntryParser associated with this engine, by calling
44   * its methods <code>readNextEntry()</code> - which handles the issue of
45   * what delimits one entry from another, usually but not always a line
46   * feed and <code>preParse()</code> - which handles removal of
47   * extraneous matter such as the preliminary lines of a listing, removal
48   * of duplicates on versioning systems, etc.
49   * <p>
50   * The second part is composed of the actual parsing, again in conjunction
51   * with the particular parser used by this engine.  This is controlled
52   * by an iterator over the internal list of strings.  This may be done
53   * either in block mode, by calling the <code>getNext()</code> and
54   * <code>getPrevious()</code> methods to provide "paged" output of less
55   * than the whole list at one time, or by calling the
56   * <code>getFiles()</code> method to return the entire list.
57   * <p>
58   * Examples:
59   * <p>
60   * Paged access:
61   * <pre>
62   *    FTPClient f=FTPClient();
63   *    f.connect(server);
64   *    f.login(username, password);
65   *    FTPListParseEngine engine = f.initiateListParsing(directory);
66   *
67   *    while (engine.hasNext()) {
68   *       FTPFile[] files = engine.getNext(25);  // "page size" you want
69   *       //do whatever you want with these files, display them, etc.
70   *       //expensive FTPFile objects not created until needed.
71   *    }
72   * </pre>
73   * <p>
74   * For unpaged access, simply use FTPClient.listFiles().  That method
75   * uses this class transparently.
76   * @version $Id: FTPListParseEngine.java 1414510 2012-11-28 02:40:39Z ggregory $
77   */
78  public class FTPListParseEngine {
79      private List<String> entries = new LinkedList<String>();
80      private ListIterator<String> _internalIterator = entries.listIterator();
81  
82      private final FTPFileEntryParser parser;
83  
84      public FTPListParseEngine(FTPFileEntryParser parser) {
85          this.parser = parser;
86      }
87  
88      /**
89       * handle the initial reading and preparsing of the list returned by
90       * the server.  After this method has completed, this object will contain
91       * a list of unparsed entries (Strings) each referring to a unique file
92       * on the server.
93       *
94       * @param stream input stream provided by the server socket.
95       * @param encoding the encoding to be used for reading the stream
96       *
97       * @exception IOException
98       *                   thrown on any failure to read from the sever.
99       */
100     public void readServerList(InputStream stream, String encoding)
101     throws IOException
102     {
103         this.entries = new LinkedList<String>();
104         readStream(stream, encoding);
105         this.parser.preParse(this.entries);
106         resetIterator();
107     }
108 
109     /**
110      * Internal method for reading the input into the <code>entries</code> list.
111      * After this method has completed, <code>entries</code> will contain a
112      * collection of entries (as defined by
113      * <code>FTPFileEntryParser.readNextEntry()</code>), but this may contain
114      * various non-entry preliminary lines from the server output, duplicates,
115      * and other data that will not be part of the final listing.
116      *
117      * @param stream The socket stream on which the input will be read.
118      * @param encoding The encoding to use.
119      *
120      * @exception IOException
121      *                   thrown on any failure to read the stream
122      */
123     private void readStream(InputStream stream, String encoding) throws IOException
124     {
125         BufferedReader reader = new BufferedReader(
126                 new InputStreamReader(stream, Charsets.toCharset(encoding)));
127 
128         String line = this.parser.readNextEntry(reader);
129 
130         while (line != null)
131         {
132             this.entries.add(line);
133             line = this.parser.readNextEntry(reader);
134         }
135         reader.close();
136     }
137 
138     /**
139      * Returns an array of at most <code>quantityRequested</code> FTPFile
140      * objects starting at this object's internal iterator's current position.
141      * If fewer than <code>quantityRequested</code> such
142      * elements are available, the returned array will have a length equal
143      * to the number of entries at and after after the current position.
144      * If no such entries are found, this array will have a length of 0.
145      *
146      * After this method is called this object's internal iterator is advanced
147      * by a number of positions equal to the size of the array returned.
148      *
149      * @param quantityRequested
150      * the maximum number of entries we want to get.
151      *
152      * @return an array of at most <code>quantityRequested</code> FTPFile
153      * objects starting at the current position of this iterator within its
154      * list and at least the number of elements which  exist in the list at
155      * and after its current position.
156      * <p><b>
157      * NOTE:</b> This array may contain null members if any of the
158      * individual file listings failed to parse.  The caller should
159      * check each entry for null before referencing it.
160      */
161     public FTPFile[] getNext(int quantityRequested) {
162         List<FTPFile> tmpResults = new LinkedList<FTPFile>();
163         int count = quantityRequested;
164         while (count > 0 && this._internalIterator.hasNext()) {
165             String entry = this._internalIterator.next();
166             FTPFile temp = this.parser.parseFTPEntry(entry);
167             tmpResults.add(temp);
168             count--;
169         }
170         return tmpResults.toArray(new FTPFile[tmpResults.size()]);
171 
172     }
173 
174     /**
175      * Returns an array of at most <code>quantityRequested</code> FTPFile
176      * objects starting at this object's internal iterator's current position,
177      * and working back toward the beginning.
178      *
179      * If fewer than <code>quantityRequested</code> such
180      * elements are available, the returned array will have a length equal
181      * to the number of entries at and after after the current position.
182      * If no such entries are found, this array will have a length of 0.
183      *
184      * After this method is called this object's internal iterator is moved
185      * back by a number of positions equal to the size of the array returned.
186      *
187      * @param quantityRequested
188      * the maximum number of entries we want to get.
189      *
190      * @return an array of at most <code>quantityRequested</code> FTPFile
191      * objects starting at the current position of this iterator within its
192      * list and at least the number of elements which  exist in the list at
193      * and after its current position.  This array will be in the same order
194      * as the underlying list (not reversed).
195      * <p><b>
196      * NOTE:</b> This array may contain null members if any of the
197      * individual file listings failed to parse.  The caller should
198      * check each entry for null before referencing it.
199      */
200     public FTPFile[] getPrevious(int quantityRequested) {
201         List<FTPFile> tmpResults = new LinkedList<FTPFile>();
202         int count = quantityRequested;
203         while (count > 0 && this._internalIterator.hasPrevious()) {
204             String entry = this._internalIterator.previous();
205             FTPFile temp = this.parser.parseFTPEntry(entry);
206             tmpResults.add(0,temp);
207             count--;
208         }
209         return tmpResults.toArray(new FTPFile[tmpResults.size()]);
210     }
211 
212     /**
213      * Returns an array of FTPFile objects containing the whole list of
214      * files returned by the server as read by this object's parser.
215      *
216      * @return an array of FTPFile objects containing the whole list of
217      *         files returned by the server as read by this object's parser.
218      * None of the entries will be null
219      * @exception IOException - not ever thrown, may be removed in a later release
220      */
221     public FTPFile[] getFiles()
222     throws IOException // TODO remove; not actually thrown
223     {
224         return getFiles(FTPFileFilters.NON_NULL);
225     }
226 
227     /**
228      * Returns an array of FTPFile objects containing the whole list of
229      * files returned by the server as read by this object's parser.
230      * The files are filtered before being added to the array.
231      *
232      * @param filter FTPFileFilter, must not be <code>null</code>.
233      *
234      * @return an array of FTPFile objects containing the whole list of
235      *         files returned by the server as read by this object's parser.
236      * <p><b>
237      * NOTE:</b> This array may contain null members if any of the
238      * individual file listings failed to parse.  The caller should
239      * check each entry for null before referencing it, or use the
240      * a filter such as {@link FTPFileFilters#NON_NULL} which does not
241      * allow null entries.
242      * @since 2.2
243      * @exception IOException - not ever thrown, may be removed in a later release
244      */
245     public FTPFile[] getFiles(FTPFileFilter filter)
246     throws IOException // TODO remove; not actually thrown
247     {
248         List<FTPFile> tmpResults = new ArrayList<FTPFile>();
249         Iterator<String> iter = this.entries.iterator();
250         while (iter.hasNext()) {
251             String entry = iter.next();
252             FTPFile temp = this.parser.parseFTPEntry(entry);
253             if (filter.accept(temp)){
254                 tmpResults.add(temp);
255             }
256         }
257         return tmpResults.toArray(new FTPFile[tmpResults.size()]);
258 
259     }
260 
261     /**
262      * convenience method to allow clients to know whether this object's
263      * internal iterator's current position is at the end of the list.
264      *
265      * @return true if internal iterator is not at end of list, false
266      * otherwise.
267      */
268     public boolean hasNext() {
269         return _internalIterator.hasNext();
270     }
271 
272     /**
273      * convenience method to allow clients to know whether this object's
274      * internal iterator's current position is at the beginning of the list.
275      *
276      * @return true if internal iterator is not at beginning of list, false
277      * otherwise.
278      */
279     public boolean hasPrevious() {
280         return _internalIterator.hasPrevious();
281     }
282 
283     /**
284      * resets this object's internal iterator to the beginning of the list.
285      */
286     public void resetIterator() {
287         this._internalIterator = this.entries.listIterator();
288     }
289 
290     // DEPRECATED METHODS - for API compatibility only - DO NOT USE
291 
292     /**
293      * Do not use.
294      * @deprecated use {@link #readServerList(InputStream, String)} instead
295     */
296     @Deprecated
297     public void readServerList(InputStream stream)
298     throws IOException
299     {
300         readServerList(stream, null);
301     }
302 
303 }