    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.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 *
    031 * This is the base class for all regular expression based FTPFileEntryParser classes
    032 */
    033public abstract class RegexFTPFileEntryParserImpl extends FTPFileEntryParserImpl {
    034    /**
    035     * internal pattern the matcher tries to match, representing a file entry
    036     */
    037    private Pattern pattern;
    038
    039    /**
    040     * internal match result used by the parser
    041     */
    042    private MatchResult result;
    043
    044    /**
    045     * Internal PatternMatcher object used by the parser. It has protected scope in case subclasses want to make use of it for their own purposes.
    046     */
    047    protected Matcher _matcher_;
    048
    049    /**
    050     * The constructor for a RegexFTPFileEntryParserImpl object. The expression is compiled with flags = 0.
    051     *
    052     * @param regex The regular expression with which this object is initialized.
    053     *
    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     *
    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
    101    public int getGroupCnt() {
    102        if (this.result == null) {
    103            return 0;
    104        }
    105        return this.result.groupCount();
    106    }
    107
    108    /**
    109     * For debugging purposes - returns a string shows each match group by number.
    110     *
    111     * @return a string shows each match group by number.
    112     */
    113
    114    public String getGroupsAsString() {
    115        final StringBuilder b = new StringBuilder();
    116        for (int i = 1; i <= this.result.groupCount(); i++) {
    117            b.append(i).append(") ").append(this.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     *
    127     * @return the content of the <code>matchnum'th</code> 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 (this.result == null) {
    131            return null;
    132        }
    133        return this.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
    143    public boolean matches(final String s) {
    144        this.result = null;
    145        _matcher_ = pattern.matcher(s);
    146        if (_matcher_.matches()) {
    147            this.result = _matcher_.toMatchResult();
    148        }
    149        return null != this.result;
    150    }
    151
    152    /**
    153     * Alter the current regular expression being utilised for entry parsing and create a new {@link Pattern} instance.
    154     *
    155     * @param regex The new regular expression
    156     * @return true
    157     * @since 2.0
    158     * @throws IllegalArgumentException if the regex cannot be compiled
    159     */
    160    public boolean setRegex(final String regex) {
    161        compileRegex(regex, 0);
    162        return true;
    163    }
    164
    165    /**
    166     * Alter the current regular expression being utilised for entry parsing and create a new {@link Pattern} instance.
    167     *
    168     * @param regex The new regular expression
    169     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
    170     * @return true
    171     * @since 3.4
    172     * @throws IllegalArgumentException if the regex cannot be compiled
    173     */
    174    public boolean setRegex(final String regex, final int flags) {
    175        compileRegex(regex, flags);
    176        return true;
    177    }
    178}