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 }