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