/*license*\ XBN-Java: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com) This software is dual-licensed under the: - Lesser General Public License (LGPL) version 3.0 or, at your option, any later version; - Apache Software License (ASL) version 2.0. Either license may be applied at your discretion. More information may be found at - http://en.wikipedia.org/wiki/Multi-licensing. The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at: - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt \*license*/ package com.github.xbn.regexutil; import java.util.regex.Matcher; import java.util.regex.Pattern; import com.github.xbn.lang.RuleType; import com.github.xbn.analyze.validate.NullnessValidator; import com.github.xbn.regexutil.z.RegexValidator_Fieldable; import com.github.xbn.text.padchop.VzblPadChop; import com.github.xbn.text.padchop.NewVzblPadChopFor; /** <p>Validate a string against a regular expression.</p> <A NAME="cfg"></a><h3>Builder Configuration: {@link com.github.xbn.regexutil.z.RegexValidator_Cfg RegexValidator_Cfg}</h3> <p><ul> <li><b>Find what:</b> <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#literal(String) literal}(s)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#pattern(Pattern) pattern}(p)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#regex(String) regex}(s)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#regex(String, int) regex}(s,i)</code></li> <li><b>Matcher uses:</b> <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#useFind() useFind}()</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#useLookingAt() useLookingAt}()</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#useMatches() useMatches}()</code></li> <li><b>Basics:</b> <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#unfiltered() unfiltered}()</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#filter(ValidResultFilter) filter}(vrf)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#invert(boolean) invert}(b)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#nullOk(boolean) nullOk}(b)</code></li> <li><b>Other:</b> <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#reset() reset}()</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#debugTo(Appendable) debugTo}(apbl)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#extraErrInfo(Object) extraErrInfo}(o)</code>, <code>{@link com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#chainID(boolean, Object) chainID}(b,o)</code></li> </ul></p> * @since 0.1.0 * @author Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <a href="http://xbnjava.aliteralmind.com">{@code http://xbnjava.aliteralmind.com}</a>, <a href="https://github.com/aliteralmind/xbnjava">{@code https://github.com/aliteralmind/xbnjava}</a> **/ public class RegexValidator extends NullnessValidator<String> implements PatternHaser { private final SimplePatternHaser ptrnHsr; private final Matcher mtchr ; private String validStr = null; //internal private static final VzblPadChop VPC_DBG = NewVzblPadChopFor.trimEscChopWithDDD(true, null, 30); /** <p>Create a new instance from a fieldable.</p> * <p>Equal to <br/>     <code>{@link #RegexValidator(RuleType, RegexValidator_Fieldable) this}({@link com.github.xbn.lang.RuleType RuleType}.{@link com.github.xbn.lang.RuleType#RESTRICTED RESTRICTED}, fieldable)</code></p> */ public RegexValidator(RegexValidator_Fieldable fieldable) { this(RuleType.RESTRICTED, fieldable); } /** <p>Create a new instance from a fieldable.</p> <p>This<ol> <li>Calls {@link com.github.xbn.analyze.validate.NullnessValidator#NullnessValidator(RuleType, ValueValidator_Fieldable) super}{@code (rule_type, fieldable)}</li> <li>Sets all internal fields from {@code fieldable}.</li> <li>Calls {@link #resetStateRV() resetStateRV}{@code ()}.</li> </ol></p> * @param fieldable May not be {@code null}, and its fields must conform to all restrictions as documented in the <a href="#cfg">builder's setter functions</a>. * @see #RegexValidator(RegexValidator_Fieldable) this(rv_f) * @see #RegexValidator(RegexValidator) this(rv) */ protected RegexValidator(RuleType rule_type, RegexValidator_Fieldable fieldable) { super(rule_type, fieldable); ptrnHsr = (new SimplePatternHaser()).pattern(fieldable.getPattern()). matcherUses(fieldable.getMatcherUses()); ptrnHsr.getMatcherUses().crashIfForbiddenValue(MatcherUses.CUSTOM, "fieldable.getMatcherUses()", null); //Unused search, so the matcher can be reset(s) at each call to isValid(o) mtchr = getPattern().matcher(""); resetStateRV(); } /** <p>Create a new instance as a duplicate of another.</p> <p>This<ol> <li>Calls {@link com.github.xbn.analyze.validate.NullnessValidator#NullnessValidator(ValueValidator) super}{@code (to_copy)}</li> </ol></p> * @param to_copy May not be {@code null}. * @see #RegexValidator(RuleType, RegexValidator_Fieldable) this(rti,rv_f) * @see #getObjectCopy() */ public RegexValidator(RegexValidator to_copy) { super(to_copy); ptrnHsr = new SimplePatternHaser(to_copy); //Unused search, so the matcher can be reset(s) at each call to isValid(o) mtchr = getPattern().matcher(""); validStr = to_copy.getValidString(); } /** <p>Reset state in preparation for the next validation.</p> <p>This calls<ol> <li><code>{@link com.github.xbn.analyze.validate.NullnessValidator NullnessValidator}.{@link com.github.xbn.analyze.validate.NullnessValidator#resetState() resetState}()</code></li> <li>{@link #resetStateRV() resetStateRV}{@code ()}</li> </ol></p> */ public void resetState() { super.resetState(); resetStateRV(); } /** <p>Reset state specific to this {@code RegexValidator}.</p> <p>This sets<ol> <li>{@link #getValidString() getValidString}{@code ()} to {@code null}</li> <li>{@link #wasJustMatched() wasJustMatched}{@code ()} to {@code false}.</li> </ol></p> * @see #resetState() */ public void resetStateRV() { validStr = null; ptrnHsr.declareNotMatched(); } /** <p>The most-recently analyzed string that <i>did match</i> the pattern.</p> * @return If {@link #wasJustMatched() wasJustMatched}{@code ()} is<ul> <li>{@code true}: The string from the most recent call to {@link #doesFollowRulesPreInvert(String) doesFollowRulesPreInvert}{@code (s)}.</li> <li>{@code false}: {@code null}</li> </ul> * @see #getMatchedIndex() */ public String getValidString() { return validStr; } //Composition implementation: null...START /** <p>The pattern a string must match, in order to be considered valid.</p> * @see com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#pattern(Pattern) RegexValidator_Cfg#pattern(p) */ public Pattern getPattern() { return ptrnHsr.getPattern(); } /** <p>The index in {@code getValidString()} at which the match was found.</p> * @return If {@link #wasJustMatched() wasJustMatched}{@code ()} is<ul> <li>{@code true}: A number zero through <code>({@link #getValidString() getValidString}().length() - 1)</code>.</li> <li>{@code false}: {@code -1}</li> </ul> */ public int getMatchedIndex() { return ptrnHsr.getMatchedIndex(); } public int getMatchCount() { return ptrnHsr.getMatchCount(); } /** <p>Did the most-recently analyzed string match the pattern?.</p> * @return The value as returned by the most recent call to {@link #doesFollowRulesPreInvert(String) doesFollowRulesPreInvert}{@code ()}, or {@code false} if {@link #resetState() resetState}{@code ()} was more recently called. This returns the same value as <code>{@link com.github.xbn.analyze.validate.AbstractValidator AbstractValidator}.{@link com.github.xbn.analyze.validate.AbstractValidator#isValid() wasValid}()</code>. */ public boolean wasJustMatched() { return ptrnHsr.wasJustMatched(); } /** <p>Should the string be matched as a whole?. Should <code><i>[{@link java.util.regex.Matcher}]</i>.{@link java.util.regex.Matcher#matches() matches}()</code> be used?.</p> * @return A non-{@code null} {@code MatcherUses} that is equal to something other than {@link com.github.xbn.regexutil.MatcherUses#CUSTOM CUSTOM} * @see com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#useMatches() Cfg.useMatches() * @see com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#useFind() Cfg.useFind() * @see com.github.xbn.regexutil.z.RegexValidator_CfgForNeeder#useLookingAt() Cfg.useLookingAt() */ public MatcherUses getMatcherUses() { return ptrnHsr.getMatcherUses(); } //Composition implementation: null...END /** <p>The matcher that searches each string. This is not affected by {@link #resetState() resetState}{@code ()}).</p> */ protected Matcher getMatchedMatcher() { return mtchr; } /** <p>Does the string match the pattern?.</p> * @return {@code true}: If<ul> <li>{@code to_validate} is {@code null} and that is {@link com.github.xbn.analyze.validate.NullnessValidator#isNullOkGivenInvert() okay}, or</li> <li>it is non-{@code null} and {@link #getMatchedMatcher() matches} the {@link #getPattern() pattern} in the {@link #getMatcherUses() expected way}.</li> </ul> * @see <code>NullnessValidator.<!-- GENERIC PARAMETERS FAIL IN @link --><a href="#doesFollowRulesPreInvert(O)">doesFollowRulesPreInvert</a>(O)</code></li> * @see #getValidString() * @see #getMatchedIndex() * @see #wasJustMatched() */ public boolean doesFollowRulesPreInvert(String to_validate) { if(isDebugOn()) { getDebugAptr().appentln("<RV> testing \"" + VPC_DBG.get(to_validate) + "\", " + this); } validStr = null; if(!super.doesFollowRulesPreInvert(to_validate)) { if(isDebugOn()) { getDebugAptr().appentln("<RV> super.doesFollowRulesPreInvert(s) is false."); } return false; } if(to_validate == null) { if(isDebugOn()) { getDebugAptr().appentln("<RV> to_validate is null (which is valid)."); } return true; } ptrnHsr.declareNotMatched(); //Assume matched (1/2) validStr = to_validate; mtchr.reset(validStr); if(getMatcherUses().isMatches() && mtchr.matches()) { ptrnHsr.matchedIndex(0); if(isDebugOn()) { getDebugAptr().appentln("<RV> Matched via matches."); } return true; } if(getMatcherUses().isFind() && mtchr.find()) { ptrnHsr.matchedIndex(mtchr.start()); if(isDebugOn()) { getDebugAptr().appentln("<RV> Found at index " + mtchr.start() + "."); } return true; } if(mtchr.lookingAt()) { ptrnHsr.matchedIndex(0); if(isDebugOn()) { getDebugAptr().appentln("<RV> Matched via lookingAt."); } return true; } //Not matched (2/2) validStr = null; if(isDebugOn()) { getDebugAptr().appentln("<RV> Not matched (invalid)."); } return false; } /* <p>Does not change the value of {@link #getMatchedIndex}{@code ()}</p> public void crashIfBadValue(String to_validate, String toVldt_name) { super.crashIfBadValue(to_validate, toVldt_name); if(getMatcherUses().isMatches()) { if(!mtchr.matches()) { throw new IllegalArgumentException(getXMsg("[getPattern().matcher(" + toVldt_name + ").matches() is false.", getExtraErrInfo())); } } else if(getMatcherUses().isFind()) { if(!mtchr.find()) { throw new IllegalArgumentException(getXMsg("(The first call to) [getPattern().matcher(" + toVldt_name + ").find() is false.", getExtraErrInfo())); } } else if(!mtchr.lookingAt()) { throw new IllegalArgumentException(getXMsg("[getPattern().matcher(" + toVldt_name + ").lookingAt() is false.", getExtraErrInfo())); } } */ public StringBuilder appendRules(StringBuilder to_appendTo) { if(NullnessValidator.isImpossibleConsideringNullInvert(this)) { return to_appendTo.append("impossible (null-ok, inverted)"); } to_appendTo.append("null-"). append(isNullOk() ? "ok" : "bad"); if(doInvertRules()) { to_appendTo.append(", inverted"); } return to_appendTo.append(", pattern=[\"").append(getPattern()). append("\", flags=" + getPattern().flags() + "], MatcherUses=").append(getMatcherUses()); } /** <p>Duplicate this <code>RegexValidator</code>.</p> * @return <code>(new {@link #RegexValidator(RegexValidator) RegexValidator}(this))</code> */ public RegexValidator getObjectCopy() { return (new RegexValidator(this)); } /* public RegexValidator extraErrInfo(Object info) { super.extraErrInfo(info); return this; } */ }