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}