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