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.Iterator;
25  import java.util.LinkedList;
26  import java.util.List;
27  import java.util.ListIterator;
28  
29  
30  /**
31   * This class handles the entire process of parsing a listing of
32   * file entries from the server.
33   * <p>
34   * This object defines a two-part parsing mechanism.
35   * <p>
36   * The first part is comprised of reading the raw input into an internal
37   * list of strings.  Every item in this list corresponds to an actual
38   * file.  All extraneous matter emitted by the server will have been
39   * removed by the end of this phase.  This is accomplished in conjunction
40   * with the FTPFileEntryParser associated with this engine, by calling
41   * its methods <code>readNextEntry()</code> - which handles the issue of
42   * what delimits one entry from another, usually but not always a line
43   * feed and <code>preParse()</code> - which handles removal of
44   * extraneous matter such as the preliminary lines of a listing, removal
45   * of duplicates on versioning systems, etc.
46   * <p>
47   * The second part is composed of the actual parsing, again in conjunction
48   * with the particular parser used by this engine.  This is controlled
49   * by an iterator over the internal list of strings.  This may be done
50   * either in block mode, by calling the <code>getNext()</code> and
51   * <code>getPrevious()</code> methods to provide "paged" output of less
52   * than the whole list at one time, or by calling the
53   * <code>getFiles()</code> method to return the entire list.
54   * <p>
55   * Examples:
56   * <p>
57   * Paged access:
58   * <pre>
59   *    FTPClient f=FTPClient();
60   *    f.connect(server);
61   *    f.login(username, password);
62   *    FTPListParseEngine engine = f.initiateListParsing(directory);
63   *
64   *    while (engine.hasNext()) {
65   *       FTPFile[] files = engine.getNext(25);  // "page size" you want
66   *       //do whatever you want with these files, display them, etc.
67   *       //expensive FTPFile objects not created until needed.
68   *    }
69   * </pre>
70   * <p>
71   * For unpaged access, simply use FTPClient.listFiles().  That method
72   * uses this class transparently.
73   * @version $Id: FTPListParseEngine.java 658518 2008-05-21 01:04:30Z sebb $
74   */
75  public class FTPListParseEngine {
76      private List<String> entries = new LinkedList<String>();
77      private ListIterator<String> _internalIterator = entries.listIterator();
78  
79      FTPFileEntryParser parser = null;
80  
81      public FTPListParseEngine(FTPFileEntryParser parser) {
82          this.parser = parser;
83      }
84  
85      /**
86       * handle the iniitial reading and preparsing of the list returned by
87       * the server.  After this method has completed, this object will contain
88       * a list of unparsed entries (Strings) each referring to a unique file
89       * on the server.
90       *
91       * @param stream input stream provided by the server socket.
92       *
93       * @exception IOException
94       *                   thrown on any failure to read from the sever.
95       */
96      public void readServerList(InputStream stream, String encoding)
97      throws IOException
98      {
99          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 }