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.parser;
19  
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.ListIterator;
23  import java.util.regex.MatchResult;
24  import java.util.regex.Matcher;
25  import java.util.regex.Pattern;
26  import java.util.regex.PatternSyntaxException;
27  
28  import org.apache.commons.net.ftp.FTPClientConfig;
29  
30  /**
31   * Special implementation VMSFTPEntryParser with versioning turned on. This parser removes all duplicates and only leaves the version with the highest version
32   * number for each file name.
33   * <p>
34   * This is a sample of VMS LIST output
35   * </p>
36   *
37   * <pre>
38   *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
39   *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
40   *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
41   * </pre>
42   *
43   * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
44   */
45  public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser {
46  
47      private static final String PRE_PARSE_REGEX = "(.*?);([0-9]+)\\s*.*";
48      private final Pattern preparsePattern;
49  
50      /**
51       * Constructor for a VMSFTPEntryParser object.
52       *
53       * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen,
54       *                                  this is a sign that <code>REGEX</code> is not a valid regular expression.
55       */
56      public VMSVersioningFTPEntryParser() {
57          this(null);
58      }
59  
60      /**
61       * This constructor allows the creation of a VMSVersioningFTPEntryParser object with something other than the default configuration.
62       *
63       * @param config The {@link FTPClientConfig configuration} object used to configure this parser.
64       * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen,
65       *                                  this is a sign that <code>REGEX</code> is not a valid regular expression.
66       * @since 1.4
67       */
68      public VMSVersioningFTPEntryParser(final FTPClientConfig config) {
69          configure(config);
70          try {
71              // _preparse_matcher_ = new Perl5Matcher();
72              preparsePattern = Pattern.compile(PRE_PARSE_REGEX);
73          } catch (final PatternSyntaxException pse) {
74              throw new IllegalArgumentException("Unparseable regex supplied:  " + PRE_PARSE_REGEX);
75          }
76  
77      }
78  
79      @Override
80      protected boolean isVersioning() {
81          return true;
82      }
83  
84      /**
85       * Implement hook provided for those implementers (such as VMSVersioningFTPEntryParser, and possibly others) which return multiple files with the same name
86       * to remove the duplicates ..
87       *
88       * @param original Original list
89       *
90       * @return Original list purged of duplicates
91       */
92      @Override
93      public List<String> preParse(final List<String> original) {
94          final HashMap<String, Integer> existingEntries = new HashMap<>();
95          final ListIterator<String> iter = original.listIterator();
96          while (iter.hasNext()) {
97              final String entry = iter.next().trim();
98              MatchResult result;
99              final Matcher _preparse_matcher_ = preparsePattern.matcher(entry);
100             if (_preparse_matcher_.matches()) {
101                 result = _preparse_matcher_.toMatchResult();
102                 final String name = result.group(1);
103                 final String version = result.group(2);
104                 final Integer nv = Integer.valueOf(version);
105                 final Integer existing = existingEntries.get(name);
106                 if (null != existing && nv.intValue() < existing.intValue()) {
107                     iter.remove(); // removes older version from original list.
108                     continue;
109                 }
110                 existingEntries.put(name, nv);
111             }
112 
113         }
114         // we've now removed all entries less than with less than the largest
115         // version number for each name that were listed after the largest.
116         // we now must remove those with smaller than the largest version number
117         // for each name that were found before the largest
118         while (iter.hasPrevious()) {
119             final String entry = iter.previous().trim();
120             MatchResult result = null;
121             final Matcher _preparse_matcher_ = preparsePattern.matcher(entry);
122             if (_preparse_matcher_.matches()) {
123                 result = _preparse_matcher_.toMatchResult();
124                 final String name = result.group(1);
125                 final String version = result.group(2);
126                 final int nv = Integer.parseInt(version);
127                 final Integer existing = existingEntries.get(name);
128                 if (null != existing && nv < existing.intValue()) {
129                     iter.remove(); // removes older version from original list.
130                 }
131             }
132 
133         }
134         return original;
135     }
136 
137 }