    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            }
    094            return createFileEntryParser(key, null);
    095        }
    096
    097        // Common method to process both key and config parameters.
    098        private FTPFileEntryParser createFileEntryParser(String key, FTPClientConfig config) {
    099            FTPFileEntryParser parser = null;
    100
    101            // Is the key a possible class name?
    102            if (JAVA_QUALIFIED_NAME_PATTERN.matcher(key).matches()) {
    103                try
    104                {
    105                    Class<?> parserClass = Class.forName(key);
    106                    try {
    107                        parser = (FTPFileEntryParser) parserClass.newInstance();
    108                    } catch (ClassCastException e) {
    109                        throw new ParserInitializationException(parserClass.getName()
    110                            + " does not implement the interface "
    111                            + "org.apache.commons.net.ftp.FTPFileEntryParser.", e);
    112                    } catch (Exception e) {
    113                        throw new ParserInitializationException("Error initializing parser", e);
    114                    } catch (ExceptionInInitializerError e) {
    115                        throw new ParserInitializationException("Error initializing parser", e);
    116                    }
    117                } catch (ClassNotFoundException e) {
    118                    // OK, assume it is an alias
    119                }
    120            }
    121
    122            if (parser == null) { // Now try for aliases
    123                String ukey = key.toUpperCase(java.util.Locale.ENGLISH);
    124                if (ukey.indexOf(FTPClientConfig.SYST_UNIX) >= 0)
    125                {
    126                    parser = new UnixFTPEntryParser(config);
    127                }
    128                else if (ukey.indexOf(FTPClientConfig.SYST_VMS) >= 0)
    129                {
    130                    parser = new VMSVersioningFTPEntryParser(config);
    131                }
    132                else if (ukey.indexOf(FTPClientConfig.SYST_NT) >= 0)
    133                {
    134                    parser = createNTFTPEntryParser(config);
    135                }
    136                else if (ukey.indexOf(FTPClientConfig.SYST_OS2) >= 0)
    137                {
    138                    parser = new OS2FTPEntryParser(config);
    139                }
    140                else if (ukey.indexOf(FTPClientConfig.SYST_OS400) >= 0 ||
    141                        ukey.indexOf(FTPClientConfig.SYST_AS400) >= 0)
    142                {
    143                    parser = createOS400FTPEntryParser(config);
    144                }
    145                else if (ukey.indexOf(FTPClientConfig.SYST_MVS) >= 0)
    146                {
    147                    parser = new MVSFTPEntryParser(); // Does not currently support config parameter
    148                }
    149                else if (ukey.indexOf(FTPClientConfig.SYST_NETWARE) >= 0)
    150                {
    151                    parser = new NetwareFTPEntryParser(config);
    152                }
    153                else if (ukey.indexOf(FTPClientConfig.SYST_MACOS_PETER) >= 0)
    154                {
    155                    parser = new MacOsPeterFTPEntryParser(config);
    156                }
    157                else if (ukey.indexOf(FTPClientConfig.SYST_L8) >= 0)
    158                {
    159                    // L8 normally means Unix, but move it to the end for some L8 systems that aren't.
    160                    // This check should be last!
    161                    parser = new UnixFTPEntryParser(config);
    162                }
    163                else
    164                {
    165                    throw new ParserInitializationException("Unknown parser type: " + key);
    166                }
    167            }
    168
    169            if (parser instanceof Configurable) {
    170                ((Configurable)parser).configure(config);
    171            }
    172            return parser;
    173        }
    174
    175        /**
    176         * <p>Implementation extracts a key from the supplied
    177         * {@link  FTPClientConfig FTPClientConfig}
    178         * parameter and creates an object implementing the
    179         * interface FTPFileEntryParser and uses the supplied configuration
    180         * to configure it.
    181         * </p><p>
    182         * Note that this method will generally not be called in scenarios
    183         * that call for autodetection of parser type but rather, for situations
    184         * where the user knows that the server uses a non-default configuration
    185         * and knows what that configuration is.
    186         * </p>
    187         * @param config  A {@link  FTPClientConfig FTPClientConfig}
    188         * used to configure the parser created
    189         *
    190         * @return the @link  FTPFileEntryParser FTPFileEntryParser} so created.
    191         * @exception ParserInitializationException
    192         *                   Thrown on any exception in instantiation
    193         * @throws NullPointerException if {@code config} is {@code null}
    194         * @since 1.4
    195         */
    196        public FTPFileEntryParser createFileEntryParser(FTPClientConfig config)
    197        throws ParserInitializationException
    198        {
    199            String key = config.getServerSystemKey();
    200            return createFileEntryParser(key, config);
    201        }
    202
    203
    204        public FTPFileEntryParser createUnixFTPEntryParser()
    205        {
    206            return new UnixFTPEntryParser();
    207        }
    208
    209        public FTPFileEntryParser createVMSVersioningFTPEntryParser()
    210        {
    211            return new VMSVersioningFTPEntryParser();
    212        }
    213
    214        public FTPFileEntryParser createNetwareFTPEntryParser() {
    215            return new NetwareFTPEntryParser();
    216        }
    217
    218        public FTPFileEntryParser createNTFTPEntryParser()
    219        {
    220            return createNTFTPEntryParser(null);
    221        }
    222
    223        /**
    224         * Creates an NT FTP parser: if the config exists, and the system key equals
    225         * {@link FTPClientConfig.SYST_NT} then a plain {@link NTFTPEntryParser} is used,
    226         * otherwise a composite of {@link NTFTPEntryParser} and {@link UnixFTPEntryParser} is used.
    227         * @param config the config to use, may be {@code null}
    228         * @return the parser
    229         */
    230        private FTPFileEntryParser createNTFTPEntryParser(FTPClientConfig config)
    231        {
    232            if (config != null && FTPClientConfig.SYST_NT.equals(
    233                    config.getServerSystemKey()))
    234            {
    235                return new NTFTPEntryParser(config);
    236            } else {
    237                return new CompositeFileEntryParser(new FTPFileEntryParser[]
    238                       {
    239                           new NTFTPEntryParser(config),
    240                           new UnixFTPEntryParser(config)
    241                       });
    242            }
    243        }
    244
    245         public FTPFileEntryParser createOS2FTPEntryParser()
    246        {
    247            return new OS2FTPEntryParser();
    248        }
    249
    250        public FTPFileEntryParser createOS400FTPEntryParser()
    251        {
    252            return createOS400FTPEntryParser(null);
    253        }
    254
    255        /**
    256         * Creates an OS400 FTP parser: if the config exists, and the system key equals
    257         * {@link FTPClientConfig.SYST_OS400} then a plain {@link OS400FTPEntryParser} is used,
    258         * otherwise a composite of {@link OS400FTPEntryParser} and {@link UnixFTPEntryParser} is used.
    259         * @param config the config to use, may be {@code null}
    260         * @return the parser
    261         */
    262        private FTPFileEntryParser createOS400FTPEntryParser(FTPClientConfig config)
    263            {
    264            if (config != null &&
    265                    FTPClientConfig.SYST_OS400.equals(config.getServerSystemKey()))
    266            {
    267                return new OS400FTPEntryParser(config);
    268            } else {
    269                return new CompositeFileEntryParser(new FTPFileEntryParser[]
    270                    {
    271                        new OS400FTPEntryParser(config),
    272                        new UnixFTPEntryParser(config)
    273                    });
    274            }
    275        }
    276
    277        public FTPFileEntryParser createMVSEntryParser()
    278        {
    279            return new MVSFTPEntryParser();
    280        }
    281
    282    }
    283