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 file name. 034 * <p> 035 * This is a sample of VMS LIST output 036 * </p> 037 * <pre> 038 * "1-JUN.LIS;1 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", 039 * "1-JUN.LIS;2 9/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", 040 * "DATA.DIR;1 1/9 2-JUN-1998 07:32:04 [GROUP,OWNER] (RWED,RWED,RWED,RE)", 041 * </pre> 042 * 043 * @see org.apache.commons.net.ftp.FTPFileEntryParser FTPFileEntryParser (for usage instructions) 044 */ 045public class VMSVersioningFTPEntryParser extends VMSFTPEntryParser 046{ 047 048 private final Pattern preparsePattern; 049 private static final String PRE_PARSE_REGEX = 050 "(.*?);([0-9]+)\\s*.*"; 051 052 /** 053 * Constructor for a VMSFTPEntryParser object. 054 * 055 * @throws IllegalArgumentException 056 * Thrown if the regular expression is unparseable. Should not be seen 057 * under normal conditions. It it is seen, this is a sign that 058 * <code>REGEX</code> is not a valid regular expression. 059 */ 060 public VMSVersioningFTPEntryParser() 061 { 062 this(null); 063 } 064 065 /** 066 * This constructor allows the creation of a VMSVersioningFTPEntryParser 067 * object with something other than the default configuration. 068 * 069 * @param config The {@link FTPClientConfig configuration} object used to 070 * configure this parser. 071 * @throws IllegalArgumentException 072 * Thrown if the regular expression is unparseable. Should not be seen 073 * under normal conditions. It it is seen, this is a sign that 074 * <code>REGEX</code> is not a valid regular expression. 075 * @since 1.4 076 */ 077 public VMSVersioningFTPEntryParser(final FTPClientConfig config) 078 { 079 configure(config); 080 try 081 { 082 //_preparse_matcher_ = new Perl5Matcher(); 083 preparsePattern = Pattern.compile(PRE_PARSE_REGEX); 084 } 085 catch (final PatternSyntaxException pse) 086 { 087 throw new IllegalArgumentException ( 088 "Unparseable regex supplied: " + PRE_PARSE_REGEX); 089 } 090 091 } 092 093 /** 094 * Implement hook provided for those implementers (such as 095 * VMSVersioningFTPEntryParser, and possibly others) which return 096 * multiple files with the same name to remove the duplicates .. 097 * 098 * @param original Original list 099 * 100 * @return Original list purged of duplicates 101 */ 102 @Override 103 public List<String> preParse(final List<String> original) { 104 final HashMap<String, Integer> existingEntries = new HashMap<>(); 105 final ListIterator<String> iter = original.listIterator(); 106 while (iter.hasNext()) { 107 final String entry = iter.next().trim(); 108 MatchResult result = null; 109 final Matcher _preparse_matcher_ = preparsePattern.matcher(entry); 110 if (_preparse_matcher_.matches()) { 111 result = _preparse_matcher_.toMatchResult(); 112 final String name = result.group(1); 113 final String version = result.group(2); 114 final Integer nv = Integer.valueOf(version); 115 final Integer existing = existingEntries.get(name); 116 if (null != existing) { 117 if (nv.intValue() < existing.intValue()) { 118 iter.remove(); // removes older version from original list. 119 continue; 120 } 121 } 122 existingEntries.put(name, nv); 123 } 124 125 } 126 // we've now removed all entries less than with less than the largest 127 // version number for each name that were listed after the largest. 128 // we now must remove those with smaller than the largest version number 129 // for each name that were found before the largest 130 while (iter.hasPrevious()) { 131 final String entry = iter.previous().trim(); 132 MatchResult result = null; 133 final Matcher _preparse_matcher_ = preparsePattern.matcher(entry); 134 if (_preparse_matcher_.matches()) { 135 result = _preparse_matcher_.toMatchResult(); 136 final String name = result.group(1); 137 final String version = result.group(2); 138 final Integer nv = Integer.valueOf(version); 139 final Integer existing = existingEntries.get(name); 140 if (null != existing) { 141 if (nv.intValue() < existing.intValue()) { 142 iter.remove(); // removes older version from original list. 143 } 144 } 145 } 146 147 } 148 return original; 149 } 150 151 152 @Override 153 protected boolean isVersioning() { 154 return true; 155 } 156 157}