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