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 = null;
042
043    /**
044     * internal match result used by the parser
045     */
046    private MatchResult result = null;
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_ = null;
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     * @exception 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(String regex) {
070        super();
071        compileRegex(regex, 0);
072    }
073
074    /**
075     * The constructor for a RegexFTPFileEntryParserImpl object.
076     *
077     * @param regex  The regular expression with which this object is
078     * initialized.
079     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
080     *
081     * @exception IllegalArgumentException
082     * Thrown if the regular expression is unparseable.  Should not be seen in
083     * normal conditions.  It it is seen, this is a sign that a subclass has
084     * been created with a bad regular expression.   Since the parser must be
085     * created before use, this means that any bad parser subclasses created
086     * from this will bomb very quickly,  leading to easy detection.
087     * @since 3.4
088     */
089    public RegexFTPFileEntryParserImpl(String regex, final int flags) {
090        super();
091        compileRegex(regex, flags);
092    }
093
094    /**
095     * Convenience method delegates to the internal MatchResult's matches()
096     * method.
097     *
098     * @param s the String to be matched
099     * @return true if s matches this object's regular expression.
100     */
101
102    public boolean matches(String s) {
103        this.result = null;
104        _matcher_ = pattern.matcher(s);
105        if (_matcher_.matches()) {
106            this.result = _matcher_.toMatchResult();
107        }
108        return null != this.result;
109    }
110
111    /**
112     * Convenience method
113     *
114     * @return the number of groups() in the internal MatchResult.
115     */
116
117    public int getGroupCnt() {
118        if (this.result == null) {
119            return 0;
120        }
121        return this.result.groupCount();
122    }
123
124    /**
125     * Convenience method delegates to the internal MatchResult's group()
126     * method.
127     *
128     * @param matchnum match group number to be retrieved
129     *
130     * @return the content of the <code>matchnum'th</code> group of the internal
131     *         match or null if this method is called without a match having
132     *         been made.
133     */
134    public String group(int matchnum) {
135        if (this.result == null) {
136            return null;
137        }
138        return this.result.group(matchnum);
139    }
140
141    /**
142     * For debugging purposes - returns a string shows each match group by
143     * number.
144     *
145     * @return a string shows each match group by number.
146     */
147
148    public String getGroupsAsString() {
149        StringBuilder b = new StringBuilder();
150        for (int i = 1; i <= this.result.groupCount(); i++) {
151            b.append(i).append(") ").append(this.result.group(i)).append(
152                    System.getProperty("line.separator"));
153        }
154        return b.toString();
155    }
156
157    /**
158     * Alter the current regular expression being utilised for entry parsing
159     * and create a new {@link Pattern} instance.
160     * @param regex The new regular expression
161     * @return  true
162     * @since 2.0
163     * @throws IllegalArgumentException if the regex cannot be compiled
164     */
165    public boolean setRegex(final String regex) {
166        compileRegex(regex, 0);
167        return true;
168    }
169
170
171    /**
172     * Alter the current regular expression being utilised for entry parsing
173     * and create a new {@link Pattern} instance.
174     * @param regex The new regular expression
175     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
176     * @return  true
177     * @since 3.4
178     * @throws IllegalArgumentException if the regex cannot be compiled
179     */
180    public boolean setRegex(final String regex, final int flags) {
181        compileRegex(regex, flags);
182        return true;
183    }
184
185    /**
186     * Compile the regex and store the {@link Pattern}.
187     *
188     * This is an internal method to do the work so the constructor does not
189     * have to call an overrideable method.
190     *
191     * @param regex the expression to compile
192     * @param flags the flags to apply, see {@link Pattern#compile(String, int)}. Use 0 for none.
193     * @throws IllegalArgumentException if the regex cannot be compiled
194     */
195    private void compileRegex(final String regex, final int flags) {
196        try {
197            pattern = Pattern.compile(regex, flags);
198        } catch (PatternSyntaxException pse) {
199            throw new IllegalArgumentException("Unparseable regex supplied: " + regex);
200        }
201    }
202}