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