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 classname (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 identirier 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 classname known to the system, this method will then attempt to see whether it <b>contains</b> 072 * a string identifying one of the known parsers. This comparison is <b>case-insensitive</b>. The intent here is where possible, to select as keys strings 073 * which are returned by the SYST command on the systems which the corresponding parser successfully parses. This enables this factory to be used in the 074 * auto-detection system. 075 * 076 * @param key should be a fully qualified classname corresponding to a class implementing the FTPFileEntryParser interface<br> 077 * OR<br> 078 * a string containing (case-insensitively) one of the following keywords: 079 * <ul> 080 * <li>{@link FTPClientConfig#SYST_UNIX UNIX}</li> 081 * <li>{@link FTPClientConfig#SYST_NT WINDOWS}</li> 082 * <li>{@link FTPClientConfig#SYST_OS2 OS/2}</li> 083 * <li>{@link FTPClientConfig#SYST_OS400 OS/400}</li> 084 * <li>{@link FTPClientConfig#SYST_AS400 AS/400}</li> 085 * <li>{@link FTPClientConfig#SYST_VMS VMS}</li> 086 * <li>{@link FTPClientConfig#SYST_MVS MVS}</li> 087 * <li>{@link FTPClientConfig#SYST_NETWARE NETWARE}</li> 088 * <li>{@link FTPClientConfig#SYST_L8 TYPE:L8}</li> 089 * </ul> 090 * @return the FTPFileEntryParser corresponding to the supplied key. 091 * @throws ParserInitializationException thrown if for any reason the factory cannot resolve the supplied key into an FTPFileEntryParser. 092 * @see FTPFileEntryParser 093 */ 094 @Override 095 public FTPFileEntryParser createFileEntryParser(final String key) { 096 if (key == null) { 097 throw new ParserInitializationException("Parser key cannot be null"); 098 } 099 return createFileEntryParser(key, null); 100 } 101 102 // Common method to process both key and config parameters. 103 private FTPFileEntryParser createFileEntryParser(final String key, final FTPClientConfig config) { 104 FTPFileEntryParser parser = null; 105 106 // Is the key a possible class name? 107 if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) { 108 try { 109 final Class<?> parserClass = Class.forName(key); 110 try { 111 parser = (FTPFileEntryParser) parserClass.newInstance(); 112 } catch (final ClassCastException e) { 113 throw new ParserInitializationException( 114 parserClass.getName() + " does not implement the interface " + "org.apache.commons.net.ftp.FTPFileEntryParser.", e); 115 } catch (final Exception | ExceptionInInitializerError e) { 116 throw new ParserInitializationException("Error initializing parser", e); 117 } 118 } catch (final ClassNotFoundException e) { 119 // OK, assume it is an alias 120 } 121 } 122 123 if (parser == null) { // Now try for aliases 124 final String ukey = key.toUpperCase(java.util.Locale.ENGLISH); 125 if (ukey.contains(FTPClientConfig.SYST_UNIX_TRIM_LEADING)) { 126 parser = new UnixFTPEntryParser(config, true); 127 } 128 // must check this after SYST_UNIX_TRIM_LEADING as it is a substring of it 129 else if (ukey.contains(FTPClientConfig.SYST_UNIX)) { 130 parser = new UnixFTPEntryParser(config, false); 131 } else if (ukey.contains(FTPClientConfig.SYST_VMS)) { 132 parser = new VMSVersioningFTPEntryParser(config); 133 } else if (ukey.contains(FTPClientConfig.SYST_NT)) { 134 parser = createNTFTPEntryParser(config); 135 } else if (ukey.contains(FTPClientConfig.SYST_OS2)) { 136 parser = new OS2FTPEntryParser(config); 137 } else if (ukey.contains(FTPClientConfig.SYST_OS400) || ukey.contains(FTPClientConfig.SYST_AS400)) { 138 parser = createOS400FTPEntryParser(config); 139 } else if (ukey.contains(FTPClientConfig.SYST_MVS)) { 140 parser = new MVSFTPEntryParser(); // Does not currently support config parameter 141 } else if (ukey.contains(FTPClientConfig.SYST_NETWARE)) { 142 parser = new NetwareFTPEntryParser(config); 143 } else if (ukey.contains(FTPClientConfig.SYST_MACOS_PETER)) { 144 parser = new MacOsPeterFTPEntryParser(config); 145 } else if (ukey.contains(FTPClientConfig.SYST_L8)) { 146 // L8 normally means Unix, but move it to the end for some L8 systems that aren't. 147 // This check should be last! 148 parser = new UnixFTPEntryParser(config); 149 } else { 150 throw new ParserInitializationException("Unknown parser type: " + key); 151 } 152 } 153 154 if (parser instanceof Configurable) { 155 ((Configurable) parser).configure(config); 156 } 157 return parser; 158 } 159 160 public FTPFileEntryParser createMVSEntryParser() { 161 return new MVSFTPEntryParser(); 162 } 163 164 public FTPFileEntryParser createNetwareFTPEntryParser() { 165 return new NetwareFTPEntryParser(); 166 } 167 168 public FTPFileEntryParser createNTFTPEntryParser() { 169 return createNTFTPEntryParser(null); 170 } 171 172 /** 173 * 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, 174 * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used. 175 * 176 * @param config the config to use, may be {@code null} 177 * @return the parser 178 */ 179 private FTPFileEntryParser createNTFTPEntryParser(final FTPClientConfig config) { 180 if (config != null && FTPClientConfig.SYST_NT.equals(config.getServerSystemKey())) { 181 return new NTFTPEntryParser(config); 182 } 183 // clone the config as it may be changed by the parsers (NET-602) 184 final FTPClientConfig config2 = config != null ? new FTPClientConfig(config) : null; 185 return new CompositeFileEntryParser(new FTPFileEntryParser[] { new NTFTPEntryParser(config), 186 new UnixFTPEntryParser(config2, config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) }); 187 } 188 189 public FTPFileEntryParser createOS2FTPEntryParser() { 190 return new OS2FTPEntryParser(); 191 } 192 193 public FTPFileEntryParser createOS400FTPEntryParser() { 194 return createOS400FTPEntryParser(null); 195 } 196 197 /** 198 * Creates an OS400 FTP parser: if the config exists, and the system key equals {@link FTPClientConfig#SYST_OS400} then a plain {@link OS400FTPEntryParser} 199 * is used, otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used. 200 * 201 * @param config the config to use, may be {@code null} 202 * @return the parser 203 */ 204 private FTPFileEntryParser createOS400FTPEntryParser(final FTPClientConfig config) { 205 if (config != null && FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey())) { 206 return new OS400FTPEntryParser(config); 207 } 208 // clone the config as it may be changed by the parsers (NET-602) 209 final FTPClientConfig config2 = config != null ? new FTPClientConfig(config) : null; 210 return new CompositeFileEntryParser(new FTPFileEntryParser[] { new OS400FTPEntryParser(config), 211 new UnixFTPEntryParser(config2, config2 != null && FTPClientConfig.SYST_UNIX_TRIM_LEADING.equals(config2.getServerSystemKey())) }); 212 } 213 214 public FTPFileEntryParser createUnixFTPEntryParser() { 215 return new UnixFTPEntryParser(); 216 } 217 218 public FTPFileEntryParser createVMSVersioningFTPEntryParser() { 219 return new VMSVersioningFTPEntryParser(); 220 } 221 222}