VMSVersioningFTPEntryParser.java

  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. package org.apache.commons.net.ftp.parser;

  18. import java.util.HashMap;
  19. import java.util.List;
  20. import java.util.ListIterator;
  21. import java.util.regex.MatchResult;
  22. import java.util.regex.Matcher;
  23. import java.util.regex.Pattern;

  24. import org.apache.commons.net.ftp.FTPClientConfig;

  25. /**
  26.  * Special implementation VMSFTPEntryParser with versioning turned on. This parser removes all duplicates and only leaves the version with the highest version
  27.  * number for each file name.
  28.  * <p>
  29.  * This is a sample of VMS LIST output
  30.  * </p>
  31.  *
  32.  * <pre>
  33.  *  "1-JUN.LIS;1              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
  34.  *  "1-JUN.LIS;2              9/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
  35.  *  "DATA.DIR;1               1/9           2-JUN-1998 07:32:04  [GROUP,OWNER]    (RWED,RWED,RWED,RE)",
  36.  * </pre>
  37.  *
  38.  * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions)
  39.  */
  40. public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser {

  41.     /**
  42.      * Guard against polynomial regular expression used on uncontrolled data.
  43.      * Don't look for more than 20 digits for the version.
  44.      * Don't look for more than 80 spaces after the version.
  45.      * Don't look for more than 80 characters after the spaces.
  46.      */
  47.     private static final String REGEX = "(.*?);([0-9]{1,20})\\s{0,80}.{0,80}";
  48.     private static final Pattern PATTERN = Pattern.compile(REGEX);

  49.     /**
  50.      * Constructor for a VMSFTPEntryParser object.
  51.      *
  52.      * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen,
  53.      *                                  this is a sign that <code>REGEX</code> is not a valid regular expression.
  54.      */
  55.     public VMSVersioningFTPEntryParser() {
  56.         this(null);
  57.     }

  58.     /**
  59.      * This constructor allows the creation of a VMSVersioningFTPEntryParser object with something other than the default configuration.
  60.      *
  61.      * @param config The {@link FTPClientConfig configuration} object used to configure this parser.
  62.      * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen under normal conditions. If the exception is seen,
  63.      *                                  this is a sign that <code>REGEX</code> is not a valid regular expression.
  64.      * @since 1.4
  65.      */
  66.     public VMSVersioningFTPEntryParser(final FTPClientConfig config) {
  67.         configure(config);
  68.     }

  69.     @Override
  70.     protected boolean isVersioning() {
  71.         return true;
  72.     }

  73.     /**
  74.      * Implement hook provided for those implementers (such as VMSVersioningFTPEntryParser, and possibly others) which return multiple files with the same name
  75.      * to remove the duplicates ..
  76.      *
  77.      * @param original Original list
  78.      *
  79.      * @return Original list purged of duplicates
  80.      */
  81.     @Override
  82.     public List<String> preParse(final List<String> original) {
  83.         final HashMap<String, Integer> existingEntries = new HashMap<>();
  84.         final ListIterator<String> iter = original.listIterator();
  85.         while (iter.hasNext()) {
  86.             final String entry = iter.next().trim();
  87.             MatchResult result;
  88.             final Matcher matcher = PATTERN.matcher(entry);
  89.             if (matcher.matches()) {
  90.                 result = matcher.toMatchResult();
  91.                 final String name = result.group(1);
  92.                 final String version = result.group(2);
  93.                 final Integer nv = Integer.valueOf(version);
  94.                 final Integer existing = existingEntries.get(name);
  95.                 if (null != existing && nv.intValue() < existing.intValue()) {
  96.                     iter.remove(); // removes older version from original list.
  97.                     continue;
  98.                 }
  99.                 existingEntries.put(name, nv);
  100.             }

  101.         }
  102.         // we've now removed all entries less than with less than the largest
  103.         // version number for each name that were listed after the largest.
  104.         // we now must remove those with smaller than the largest version number
  105.         // for each name that were found before the largest
  106.         while (iter.hasPrevious()) {
  107.             final String entry = iter.previous().trim();
  108.             MatchResult result = null;
  109.             final Matcher matcher = PATTERN.matcher(entry);
  110.             if (matcher.matches()) {
  111.                 result = matcher.toMatchResult();
  112.                 final String name = result.group(1);
  113.                 final String version = result.group(2);
  114.                 final int nv = Integer.parseInt(version);
  115.                 final Integer existing = existingEntries.get(name);
  116.                 if (null != existing && nv < existing.intValue()) {
  117.                     iter.remove(); // removes older version from original list.
  118.                 }
  119.             }

  120.         }
  121.         return original;
  122.     }

  123. }