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.regex.Pattern; 021 022import org.apache.commons.net.ftp.Configurable; 023import org.apache.commons.net.ftp.FTPClientConfig; 024import org.apache.commons.net.ftp.FTPFileEntryParser; 025 026/** 027 * This is the default implementation of the FTPFileEntryParserFactory interface. This is the implementation that will be used by 028 * org.apache.commons.net.ftp.FTPClient.listFiles() if no other implementation has been specified. 029 * 030 * @see org.apache.commons.net.ftp.FTPClient#listFiles 031 * @see org.apache.commons.net.ftp.FTPClient#setParserFactory 032 */ 033public class DefaultFTPFileEntryParserFactory implements FTPFileEntryParserFactory { 034 035 // Match a plain Java Identifier 036 private static final String JAVA_IDENTIFIER = "\\p{javaJavaIdentifierStart}(\\p{javaJavaIdentifierPart})*"; 037 // Match a qualified name, e.g. a.b.c.Name - but don't allow the default package as that would allow "VMS"/"UNIX" etc. 038 private static final String JAVA_QUALIFIED_NAME = "(" + JAVA_IDENTIFIER + "\\.)+" + JAVA_IDENTIFIER; 039 // Create the pattern, as it will be reused many times 040 private static final Pattern JAVA_QUALIFIED_NAME_PATTERN = Pattern.compile(JAVA_QUALIFIED_NAME); 041 042 /** 043 * <p> 044 * Implementation extracts a key from the supplied {@link FTPClientConfig FTPClientConfig} parameter and creates an object implementing the interface 045 * FTPFileEntryParser and uses the supplied configuration to configure it. 046 * </p> 047 * <p> 048 * Note that this method will generally not be called in scenarios that call for autodetection of parser type but rather, for situations where the user 049 * knows that the server uses a non-default configuration and knows what that configuration is. 050 * </p> 051 * 052 * @param config A {@link FTPClientConfig FTPClientConfig} used to configure the parser created 053 * 054 * @return the {@link FTPFileEntryParser} so created. 055 * @throws ParserInitializationException Thrown on any exception in instantiation 056 * @throws NullPointerException if {@code config} is {@code null} 057 * @since 1.4 058 */ 059 @Override 060 public FTPFileEntryParser createFileEntryParser(final FTPClientConfig config) throws ParserInitializationException { 061 final String key = config.getServerSystemKey(); 062 return createFileEntryParser(key, config); 063 } 064 065 /** 066 * This default implementation of the FTPFileEntryParserFactory interface works according to the following logic: First it attempts to interpret the 067 * supplied key as a fully qualified class name (default package is not allowed) of a class implementing the FTPFileEntryParser interface. If that succeeds, 068 * a parser object of this class is instantiated and is returned; otherwise it attempts to interpret the key as an identifier commonly used by the FTP SYST 069 * command to identify systems. 070 * <p> 071 * If <code>key</code> is not recognized as a fully qualified class name known to the system, this method will then attempt to see whether it 072 * <b>contains</b> a string identifying one of the known parsers. This comparison is <b>case-insensitive</b>. The intent here is where possible, to select 073 * as keys strings which are returned by the SYST command on the systems which the corresponding parser successfully parses. This enables this factory to be 074 * used in the auto-detection system. 075 * </p> 076 * 077 * @param key should be a fully qualified class name corresponding to a class implementing the FTPFileEntryParser interface<br> 078 * OR<br> 079 * a string containing (case-insensitively) one of the following keywords: 080 * <ul> 081 * <li>{@link FTPClientConfig#SYST_UNIX UNIX}</li> 082 * <li>{@link FTPClientConfig#SYST_NT WINDOWS}</li> 083 * <li>{@link FTPClientConfig#SYST_OS2 OS/2}</li> 084 * <li>{@link FTPClientConfig#SYST_OS400 OS/400}</li> 085 * <li>{@link FTPClientConfig#SYST_AS400 AS/400}</li> 086 * <li>{@link FTPClientConfig#SYST_VMS VMS}</li> 087 * <li>{@link FTPClientConfig#SYST_MVS MVS}</li> 088 * <li>{@link FTPClientConfig#SYST_NETWARE NETWARE}</li> 089 * <li>{@link FTPClientConfig#SYST_L8 TYPE:L8}</li> 090 * </ul> 091 * @return the FTPFileEntryParser corresponding to the supplied key. 092 * @throws ParserInitializationException thrown if for any reason the factory cannot resolve the supplied key into an FTPFileEntryParser. 093 * @see FTPFileEntryParser 094 */ 095 @Override 096 public FTPFileEntryParser createFileEntryParser(final String key) { 097 if (key == null) { 098 throw new ParserInitializationException("Parser key cannot be null"); 099 } 100 return createFileEntryParser(key, null); 101 } 102 103 // Common method to process both key and config parameters. 104 private FTPFileEntryParser createFileEntryParser(final String key, final FTPClientConfig config) { 105 FTPFileEntryParser parser = null; 106 107 // Is the key a possible class name? 108 if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) { 109 try { 110 final Class<?> parserClass = Class.forName(key); 111 try { 112 parser = (FTPFileEntryParser) parserClass.getConstructor().newInstance(); 113 } catch (final ClassCastException e) { 114 throw new ParserInitializationException( 115 parserClass.getName() + " does not implement the interface " + "org.apache.commons.net.ftp.FTPFileEntryParser.", e); 116 } catch (final Exception | ExceptionInInitializerError e) { 117 throw new ParserInitializationException("Error initializing parser", e); 118 } 119 } catch (final ClassNotFoundException e) { 120 // OK, assume it is an alias 121 } 122 } 123 124 if (parser == null) { // Now try for aliases 125 final String ukey = key.toUpperCase(java.util.Locale.ENGLISH); 126 if (ukey.contains(FTPClientConfig.SYST_UNIX_TRIM_LEADING)) { 127 parser = new UnixFTPEntryParser(config, true); 128 } 129 // must check this after SYST_UNIX_TRIM_LEADING as it is a substring of it 130 else if (ukey.contains(FTPClientConfig.SYST_UNIX)) { 131 parser = new UnixFTPEntryParser(config, false); 132 } else if (ukey.contains(FTPClientConfig.SYST_VMS)) { 133 parser = new VMSVersioningFTPEntryParser(config); 134 } else if (ukey.contains(FTPClientConfig.SYST_NT)) { 135 parser = createNTFTPEntryParser(config); 136 } else if (ukey.contains(FTPClientConfig.SYST_OS2)) { 137 parser = new OS2FTPEntryParser(config); 138 } else if (ukey.contains(FTPClientConfig.SYST_OS400) || ukey.contains(FTPClientConfig.SYST_AS400)) { 139 parser = createOS400FTPEntryParser(config); 140 } else if (ukey.contains(FTPClientConfig.SYST_MVS)) { 141 parser = new MVSFTPEntryParser(); // Does not currently support config parameter 142 } else if (ukey.contains(FTPClientConfig.SYST_NETWARE)) { 143 parser = new NetwareFTPEntryParser(config); 144 } else if (ukey.contains(FTPClientConfig.SYST_MACOS_PETER)) { 145 parser = new MacOsPeterFTPEntryParser(config); 146 } else if (ukey.contains(FTPClientConfig.SYST_L8)) { 147 // L8 normally means Unix, but move it to the end for some L8 systems that aren't. 148 // This check should be last! 149 parser = new UnixFTPEntryParser(config); 150 } else { 151 throw new ParserInitializationException("Unknown parser type: " + key); 152 } 153 } 154 155 if (parser instanceof Configurable) { 156 ((Configurable) parser).configure(config); 157 } 158 return parser; 159 } 160 161 public FTPFileEntryParser createMVSEntryParser() { 162 return new MVSFTPEntryParser(); 163 } 164 165 public FTPFileEntryParser createNetwareFTPEntryParser() { 166 return new NetwareFTPEntryParser(); 167 } 168 169 public FTPFileEntryParser createNTFTPEntryParser() { 170 return createNTFTPEntryParser(null); 171 } 172 173 /** 174 * Creates an NT FTP parser: if the config exists, and the system key equals {@link FTPClientConfig#SYST_NT} then a plain {@link NTFTPEntryParser} is used, 175 * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used. 176 * 177 * @param config the config to use, may be {@code null} 178 * @return the parser 179 */ 180 private FTPFileEntryParser createNTFTPEntryParser(final FTPClientConfig config) { 181 if (config != null && FTPClientConfig.SYST_NT.equals(config.getServerSystemKey())) { 182 return new NTFTPEntryParser(config); 183 } 184 // clone the config as it may be changed by the parsers (NET-602) 185 final FTPClientConfig config2 = config != null ? new FTPClientConfig(config) : null; 186 return new CompositeFileEntryParser(new FTPFileEntryParser[] { new NTFTPEntryParser(config), 187 new UnixFTPEntryParser(config2, config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) }); 188 } 189 190 public FTPFileEntryParser createOS2FTPEntryParser() { 191 return new OS2FTPEntryParser(); 192 } 193 194 public FTPFileEntryParser createOS400FTPEntryParser() { 195 return createOS400FTPEntryParser(null); 196 } 197 198 /** 199 * Creates an OS400 FTP parser: if the config exists, and the system key equals {@link FTPClientConfig#SYST_OS400} then a plain {@link OS400FTPEntryParser} 200 * is used, otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used. 201 * 202 * @param config the config to use, may be {@code null} 203 * @return the parser 204 */ 205 private FTPFileEntryParser createOS400FTPEntryParser(final FTPClientConfig config) { 206 if (config != null && FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) { 207 return new OS400FTPEntryParser(config); 208 } 209 // clone the config as it may be changed by the parsers (NET-602) 210 final FTPClientConfig config2 = config != null ? new FTPClientConfig(config) : null; 211 return new CompositeFileEntryParser(new FTPFileEntryParser[] { new OS400FTPEntryParser(config), 212 new UnixFTPEntryParser(config2, config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) }); 213 } 214 215 public FTPFileEntryParser createUnixFTPEntryParser() { 216 return new UnixFTPEntryParser(); 217 } 218 219 public FTPFileEntryParser createVMSVersioningFTPEntryParser() { 220 return new VMSVersioningFTPEntryParser(); 221 } 222 223}