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