    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 *      https://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.MatchResult;
    021import java.util.regex.Matcher;
    022import java.util.regex.Pattern;
    023import java.util.regex.PatternSyntaxException;
    024
    025import org.apache.commons.net.ftp.FTPFileEntryParserImpl;
    026
    027/**
    028 * This abstract class implements both the older FTPFileListParser and newer FTPFileEntryParser interfaces with default functionality. All the classes in the
    029 * parser subpackage inherit from this.
    030 * <p>
    031 * This is the base class for all regular expression based FTPFileEntryParser classes
    032 * </p>
    033 */
    034public abstract class RegexFTPFileEntryParserImpl extends FTPFileEntryParserImpl {
    035
    036    /**
    037     * Internal pattern the matcher tries to match, representing a file entry
    038     */
    039    private Pattern pattern;
    040
    041    /**
    042     * Internal match result used by the parser
    043     */
    044    private MatchResult result;
    045
    046    /**
    047     * Internal PatternMatcher object used by the parser. It has protected scope in case subclasses want to make use of it for their own purposes.
    048     */
    049    protected Matcher _matcher_;
    050
    051    /**
    052     * The constructor for a RegexFTPFileEntryParserImpl object. The expression is compiled with flags = 0.
    053     *
    054     * @param regex The regular expression with which this object is initialized.
    055     * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen in normal conditions. If it is seen, this is a sign
    056     *                                  that a subclass has been created with a bad regular expression. Since the parser must be created before use, this means
    057     *                                  that any bad parser subclasses created from this will bomb very quickly, leading to easy detection.
    058     */
    059
    060    public RegexFTPFileEntryParserImpl(final String regex) {
    061        compileRegex(regex, 0);
    062    }
    063
    064    /**
    065     * The constructor for a RegexFTPFileEntryParserImpl object.
    066     *
    067     * @param regex The regular expression with which this object is initialized.
    068     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
    069     * @throws IllegalArgumentException Thrown if the regular expression is unparseable. Should not be seen in normal conditions. If it is seen, this is a sign
    070     *                                  that a subclass has been created with a bad regular expression. Since the parser must be created before use, this means
    071     *                                  that any bad parser subclasses created from this will bomb very quickly, leading to easy detection.
    072     * @since 3.4
    073     */
    074    public RegexFTPFileEntryParserImpl(final String regex, final int flags) {
    075        compileRegex(regex, flags);
    076    }
    077
    078    /**
    079     * Compile the regex and store the {@link Pattern}.
    080     *
    081     * This is an internal method to do the work so the constructor does not have to call an overrideable method.
    082     *
    083     * @param regex the expression to compile
    084     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
    085     * @throws IllegalArgumentException if the regex cannot be compiled
    086     */
    087    private void compileRegex(final String regex, final int flags) {
    088        try {
    089            pattern = Pattern.compile(regex, flags);
    090        } catch (final PatternSyntaxException pse) {
    091            throw new IllegalArgumentException("Unparseable regex supplied: " + regex);
    092        }
    093    }
    094
    095    /**
    096     * Convenience method
    097     *
    098     * @return the number of groups() in the internal MatchResult.
    099     */
    100    public int getGroupCnt() {
    101        if (result == null) {
    102            return 0;
    103        }
    104        return result.groupCount();
    105    }
    106
    107    /**
    108     * Gets a string shows each match group by number.
    109     * <p>
    110     * For debugging purposes.
    111     * </p>
    112     *
    113     * @return a string shows each match group by number.
    114     */
    115    public String getGroupsAsString() {
    116        final StringBuilder b = new StringBuilder();
    117        for (int i = 1; i <= result.groupCount(); i++) {
    118            b.append(i).append(") ").append(result.group(i)).append(System.lineSeparator());
    119        }
    120        return b.toString();
    121    }
    122
    123    /**
    124     * Convenience method delegates to the internal MatchResult's group() method.
    125     *
    126     * @param matchNum match group number to be retrieved
    127     * @return the content of the {@code matchnum'th} group of the internal match or null if this method is called without a match having been made.
    128     */
    129    public String group(final int matchNum) {
    130        if (result == null) {
    131            return null;
    132        }
    133        return result.group(matchNum);
    134    }
    135
    136    /**
    137     * Convenience method delegates to the internal MatchResult's matches() method.
    138     *
    139     * @param s the String to be matched
    140     * @return true if s matches this object's regular expression.
    141     */
    142    public boolean matches(final String s) {
    143        result = null;
    144        _matcher_ = pattern.matcher(s);
    145        if (_matcher_.matches()) {
    146            result = _matcher_.toMatchResult();
    147        }
    148        return null != result;
    149    }
    150
    151    /**
    152     * Sets the regular expression for entry parsing and create a new {@link Pattern} instance.
    153     *
    154     * @param regex The new regular expression
    155     * @return true
    156     * @throws IllegalArgumentException if the regex cannot be compiled
    157     * @since 2.0
    158     */
    159    public boolean setRegex(final String regex) {
    160        compileRegex(regex, 0);
    161        return true;
    162    }
    163
    164    /**
    165     * Sets the regular expression for entry parsing and create a new {@link Pattern} instance.
    166     *
    167     * @param regex The new regular expression
    168     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
    169     * @return true
    170     * @throws IllegalArgumentException if the regex cannot be compiled
    171     * @since 3.4
    172     */
    173    public boolean setRegex(final String regex, final int flags) {
    174        compileRegex(regex, flags);
    175        return true;
    176    }
    177}