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}