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
018package org.apache.commons.net.ftp.parser;
019
020import java.util.HashMap;
021import java.util.List;
022import java.util.ListIterator;
023import java.util.regex.MatchResult;
024import java.util.regex.Matcher;
025import java.util.regex.Pattern;
026import java.util.regex.PatternSyntaxException;
027
028import org.apache.commons.net.ftp.FTPClientConfig;
029
030/**
031 * Special implementation VMSFTPEntryParser with versioning turned on.
032 * This parser removes all duplicates and only leaves the version with the highest
033 * version number for each filename.
034 *
035 * This is a sample of VMS LIST output
036 *
037 *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
038 *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
039 *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
040 * <P>
041 *
042 * @author  <a href="Winston.Ojeda@qg.com">Winston Ojeda</a>
043 * @author <a href="sestegra@free.fr">Stephane ESTE-GRACIAS</a>
044 * @version $Id: VMSVersioningFTPEntryParser.java 1084380 2011-03-22 22:23:35Z sebb $
045 *
046 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
047 */
048public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser
049{
050
051    private final Pattern _preparse_pattern_;
052    private static final String PRE_PARSE_REGEX =
053        "(.*);([0-9]+)\\s*.*";
054
055    /**
056     * Constructor for a VMSFTPEntryParser object.
057     *
058     * @exception IllegalArgumentException
059     * Thrown if the regular expression is unparseable.  Should not be seen
060     * under normal conditions.  It it is seen, this is a sign that
061     * <code>REGEX</code> is  not a valid regular expression.
062     */
063    public VMSVersioningFTPEntryParser()
064    {
065        this(null);
066    }
067
068    /**
069     * This constructor allows the creation of a VMSVersioningFTPEntryParser
070     * object with something other than the default configuration.
071     *
072     * @param config The {@link FTPClientConfig configuration} object used to
073     * configure this parser.
074     * @exception IllegalArgumentException
075     * Thrown if the regular expression is unparseable.  Should not be seen
076     * under normal conditions.  It it is seen, this is a sign that
077     * <code>REGEX</code> is  not a valid regular expression.
078     * @since 1.4
079     */
080    public VMSVersioningFTPEntryParser(FTPClientConfig config)
081    {
082        super();
083        configure(config);
084        try
085        {
086            //_preparse_matcher_ = new Perl5Matcher();
087            _preparse_pattern_ = Pattern.compile(PRE_PARSE_REGEX);
088        }
089        catch (PatternSyntaxException pse)
090        {
091            throw new IllegalArgumentException (
092                "Unparseable regex supplied:  " + PRE_PARSE_REGEX);
093        }
094
095   }
096
097    /**
098     * Implement hook provided for those implementers (such as
099     * VMSVersioningFTPEntryParser, and possibly others) which return
100     * multiple files with the same name to remove the duplicates ..
101     *
102     * @param original Original list
103     *
104     * @return Original list purged of duplicates
105     */
106    @Override
107    public List<String> preParse(List<String> original) {
108        HashMap<String, Integer> existingEntries = new HashMap<String, Integer>();
109        ListIterator<String> iter = original.listIterator();
110        while (iter.hasNext()) {
111            String entry = iter.next().trim();
112            MatchResult result = null;
113            Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry);
114            if (_preparse_matcher_.matches()) {
115                result = _preparse_matcher_.toMatchResult();
116                String name = result.group(1);
117                String version = result.group(2);
118                Integer nv = Integer.valueOf(version);
119                Integer existing = existingEntries.get(name);
120                if (null != existing) {
121                    if (nv.intValue() < existing.intValue()) {
122                        iter.remove();  // removes older version from original list.
123                        continue;
124                    }
125                }
126                existingEntries.put(name, nv);
127            }
128
129        }
130        // we've now removed all entries less than with less than the largest
131        // version number for each name that were listed after the largest.
132        // we now must remove those with smaller than the largest version number
133        // for each name that were found before the largest
134        while (iter.hasPrevious()) {
135            String entry = iter.previous().trim();
136            MatchResult result = null;
137            Matcher _preparse_matcher_ = _preparse_pattern_.matcher(entry);
138            if (_preparse_matcher_.matches()) {
139                result = _preparse_matcher_.toMatchResult();
140                String name = result.group(1);
141                String version = result.group(2);
142                Integer nv = Integer.valueOf(version);
143                Integer existing = existingEntries.get(name);
144                if (null != existing) {
145                    if (nv.intValue() < existing.intValue()) {
146                        iter.remove(); // removes older version from original list.
147                    }
148                }
149            }
150
151        }
152        return original;
153    }
154
155
156    @Override
157    protected boolean isVersioning() {
158        return true;
159    }
160
161}
162
163/* Emacs configuration
164 * Local variables:        **
165 * mode:             java  **
166 * c-basic-offset:   4     **
167 * indent-tabs-mode: nil   **
168 * End:                    **
169 */