001    /*
002     * Copyright 2001-2005 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.parser;
017    import java.io.BufferedReader;
018    import java.io.IOException;
019    import java.io.InputStream;
020    import java.text.ParseException;
021    import java.util.StringTokenizer;
022    
023    import org.apache.commons.net.ftp.FTPClientConfig;
024    import org.apache.commons.net.ftp.FTPFile;
025    import org.apache.commons.net.ftp.FTPListParseEngine;
026    
027    /**
028     * Implementation FTPFileEntryParser and FTPFileListParser for VMS Systems.
029     * This is a sample of VMS LIST output
030     *
031     *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
032     *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
033     *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
034     * <P><B>
035     * Note: VMSFTPEntryParser can only be instantiated through the
036     * DefaultFTPParserFactory by classname.  It will not be chosen
037     * by the autodetection scheme.
038     * </B>
039     * <P>
040     *
041     * @author  <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
042     * @author <a href="mailto:scohen@apache.org">Steve Cohen</a>
043     * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
044     * @version $Id: VMSFTPEntryParser.java 155429 2005-02-26 13:13:04Z dirkv $
045     *
046     * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
047     * @see org.apache.commons.net.ftp.parser.DefaultFTPFileEntryParserFactory
048     */
049    public class VMSFTPEntryParser extends ConfigurableFTPFileEntryParserImpl
050    {
051    
052        private static final String DEFAULT_DATE_FORMAT 
053                    = "d-MMM-yyyy HH:mm:ss"; //9-NOV-2001 12:30:24
054    
055        /**
056         * this is the regular expression used by this parser.
057         */
058        private static final String REGEX =
059            "(.*;[0-9]+)\\s*"
060            + "(\\d+)/\\d+\\s*"
061            +"(\\S+)\\s+(\\S+)\\s+"
062            + "\\[(([0-9$A-Za-z_]+)|([0-9$A-Za-z_]+),([0-9$a-zA-Z_]+))\\]?\\s*"
063            + "\\([a-zA-Z]*,[a-zA-Z]*,[a-zA-Z]*,[a-zA-Z]*\\)";
064    
065    
066    
067        /**
068         * Constructor for a VMSFTPEntryParser object.
069         *
070         * @exception IllegalArgumentException
071         * Thrown if the regular expression is unparseable.  Should not be seen
072         * under normal conditions.  It it is seen, this is a sign that
073         * <code>REGEX</code> is  not a valid regular expression.
074         */
075        public VMSFTPEntryParser()
076        {
077            this(null);
078        }
079    
080        /**
081         * This constructor allows the creation of a VMSFTPEntryParser object with
082         * something other than the default configuration.
083         *
084         * @param config The {@link FTPClientConfig configuration} object used to 
085         * configure this parser.
086         * @exception IllegalArgumentException
087         * Thrown if the regular expression is unparseable.  Should not be seen
088         * under normal conditions.  It it is seen, this is a sign that
089         * <code>REGEX</code> is  not a valid regular expression.
090         * @since 1.4
091         */
092        public VMSFTPEntryParser(FTPClientConfig config)
093        {
094            super(REGEX);
095            configure(config);
096        }
097    
098    
099    
100        /***
101         * Parses an FTP server file listing and converts it into a usable format
102         * in the form of an array of <code> FTPFile </code> instances.  If the
103         * file list contains no files, <code> null </code> should be
104         * returned, otherwise an array of <code> FTPFile </code> instances
105         * representing the files in the directory is returned.
106         * <p>
107         * @param listStream The InputStream from which the file list should be
108         *        read.
109         * @return The list of file information contained in the given path.  null
110         *     if the list could not be obtained or if there are no files in
111         *     the directory.
112         * @exception IOException  If an I/O error occurs reading the listStream.
113         ***/
114        public FTPFile[] parseFileList(InputStream listStream) throws IOException {
115            FTPListParseEngine engine = new FTPListParseEngine(this);
116            engine.readServerList(listStream);
117            return engine.getFiles();
118        }
119    
120    
121    
122        /**
123         * Parses a line of a VMS FTP server file listing and converts it into a
124         * usable format in the form of an <code> FTPFile </code> instance.  If the
125         * file listing line doesn't describe a file, <code> null </code> is
126         * returned, otherwise a <code> FTPFile </code> instance representing the
127         * files in the directory is returned.
128         * <p>
129         * @param entry A line of text from the file listing
130         * @return An FTPFile instance corresponding to the supplied entry
131         */
132        public FTPFile parseFTPEntry(String entry)
133        {
134            //one block in VMS equals 512 bytes
135            long longBlock = 512;
136    
137            if (matches(entry))
138            {
139                FTPFile f = new FTPFile();
140                f.setRawListing(entry);
141                String name = group(1);
142                String size = group(2);
143                    String datestr = group(3)+" "+group(4);
144                String owner = group(5);
145                try
146                {
147                    f.setTimestamp(super.parseTimestamp(datestr));
148                }
149                catch (ParseException e)
150                {
151                    return null;  // this is a parsing failure too.
152                }
153    
154    
155                String grp;
156                String user;
157                StringTokenizer t = new StringTokenizer(owner, ",");
158                switch (t.countTokens()) {
159                    case 1:
160                        grp  = null;
161                        user = t.nextToken();
162                        break;
163                    case 2:
164                        grp  = t.nextToken();
165                        user = t.nextToken();
166                        break;
167                    default:
168                        grp  = null;
169                        user = null;
170                }
171    
172                if (name.lastIndexOf(".DIR") != -1)
173                {
174                    f.setType(FTPFile.DIRECTORY_TYPE);
175                }
176                else
177                {
178                    f.setType(FTPFile.FILE_TYPE);
179                }
180                //set FTPFile name
181                //Check also for versions to be returned or not
182                if (isVersioning())
183                {
184                    f.setName(name);
185                }
186                else
187                {
188                    name = name.substring(0, name.lastIndexOf(";"));
189                    f.setName(name);
190                }
191                //size is retreived in blocks and needs to be put in bytes
192                //for us humans and added to the FTPFile array
193                long sizeInBytes = Long.parseLong(size) * longBlock;
194                f.setSize(sizeInBytes);
195    
196                f.setGroup(grp);
197                f.setUser(user);
198                //set group and owner
199                //Since I don't need the persmissions on this file (RWED), I'll
200                //leave that for further development. 'Cause it will be a bit
201                //elaborate to do it right with VMSes World, Global and so forth.
202                return f;
203            }
204            return null;
205        }
206    
207    
208        /**
209         * Reads the next entry using the supplied BufferedReader object up to
210         * whatever delemits one entry from the next.   This parser cannot use
211         * the default implementation of simply calling BufferedReader.readLine(),
212         * because one entry may span multiple lines.
213         *
214         * @param reader The BufferedReader object from which entries are to be
215         * read.
216         *
217         * @return A string representing the next ftp entry or null if none found.
218         * @exception IOException thrown on any IO Error reading from the reader.
219         */
220        public String readNextEntry(BufferedReader reader) throws IOException
221        {
222            String line = reader.readLine();
223            StringBuffer entry = new StringBuffer();
224            while (line != null)
225            {
226                if (line.startsWith("Directory") || line.startsWith("Total")) {
227                    line = reader.readLine();
228                    continue;
229                }
230    
231                entry.append(line);
232                if (line.trim().endsWith(")"))
233                {
234                    break;
235                }
236                line = reader.readLine();
237            }
238            return (entry.length() == 0 ? null : entry.toString());
239        }
240    
241        protected boolean isVersioning() {
242            return false;
243        }
244        
245        /**
246         * Defines a default configuration to be used when this class is
247         * instantiated without a {@link  FTPClientConfig  FTPClientConfig}
248         * parameter being specified.
249         * @return the default configuration for this parser.
250         */
251       protected FTPClientConfig getDefaultConfiguration() {
252            return new FTPClientConfig(
253                    FTPClientConfig.SYST_VMS,
254                    DEFAULT_DATE_FORMAT,
255                    null, null, null, null);
256        }
257    
258    
259    }
260    
261    /* Emacs configuration
262     * Local variables:        **
263     * mode:             java  **
264     * c-basic-offset:   4     **
265     * indent-tabs-mode: nil   **
266     * End:                    **
267     */