/*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.analyze.validate;
import static com.github.xbn.lang.CrashIfBase.*;
import com.github.xbn.lang.CrashIfObject;
import com.github.xbn.lang.RuleType;
import com.github.xbn.analyze.validate.z.ValueValidator_Fieldable;
import com.github.xbn.text.Trim;
import com.github.xbn.text.StringUtilBase;
/**
<p>A {@code ValueValidator} that only checks for {@code null}-ness. This is useful in and of itself, but is mostly intended as a base-class for custom validators.</p>
<h3>Implementation</h3>
<p>At a minimum, the following functions must be overridden:<ul>
<li>{@link #appendRules(StringBuilder) appendRules}{@code (sd)}</li>
<li><code><!-- GENERIC PARAMETERS FAIL IN @link --><a href="#doesFollowRulesPreInvert(O)">doesFollowRulesPreInvert</a>(O)</code></li>
<li>{@link #getRuleTypeFromFieldsVVN(ValueValidator_Fieldable) getRuleTypeFromFieldsVVN}{@code (fieldable)}</li>
</ul>Commonly, the super-version of these functions (in <i>this</i> class, {@code NullnessValidator}) will be called by sub-versions.</p>
<p>Depending on the needs of sub-classes, it may also be necessary to override {@link #adjustForPostFilterReturnValue(boolean) adjustForPostFilterReturnValue}{@code (b)}.</p>
<A NAME="cfg"></a><h3>Builder configuration: {@link com.github.xbn.analyze.validate.z.ValueValidator_Cfg}</h3>
<p><ul>
<li><b>Convenience builders:</b> {@link com.github.xbn.analyze.validate.NewValueValidatorFor}</li>
<li><b>Basic:</b> <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#invert(boolean) invert}(b)</code>, <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#nullOk(boolean) nullOk}(b)</code>, <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#reset() reset}()</code></li>
<li><b>Filter:</b> <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#unfiltered() unfiltered}()</code>, <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#filter(ValidResultFilter) filter}(vrf)</code></li>
<li><b>Other:</b> <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#chainID(boolean, Object) chainID}(b,o)</code>, <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#debugTo(Appendable) debugTo}(apbl)</code>, <code>{@link com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#extraErrInfo(Object) extraErrInfo}(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 NullnessValidator<O> extends AbstractValidator implements ValueValidator<O> {
private final boolean isNullOk;
/**
<p>Create a new instance from its fieldable.</p>
* <p>Equal to
<br/> <code>this(NullnessValidator.{@link #getRuleTypeFromFieldsVVN(ValueValidator_Fieldable) getRuleTypeFromFieldsVVN}(fieldable), fieldable)</code></p>
*/
public NullnessValidator(ValueValidator_Fieldable fieldable) {
this(NullnessValidator.getRuleTypeFromFieldsVVN(fieldable), fieldable);
}
/**
<p>Create a new instance from its rule-type and fieldable.</p>
* @param rule_type Defines the restrictiveness of this instance. May not be {@code null}. Get with {@link com.github.xbn.analyze.validate.AbstractValidator#getRuleType() getRuleType}{@code ()}*.
* @param fieldable May not be {@code null}, and its fields must conform to the restrictions as documented in the <a href="#cfg">buider's setter functions</a>.
* @see #NullnessValidator(ValueValidator_Fieldable)
* @see #NullnessValidator(ValueValidator)
*/
protected NullnessValidator(RuleType rule_type, ValueValidator_Fieldable fieldable) {
super(rule_type, fieldable);
isNullOk = fieldable.isNullOk();
}
/**
<p>Create a new instance from a <code>ValueValidator</code>.</p>
* @see #NullnessValidator(RuleType, ValueValidator_Fieldable)
*/
public NullnessValidator(ValueValidator<O> validator) {
super(validator);
isNullOk = validator.isNullOk();
}
/**
<p>Is the value acceptable?.</p>
<p>Steps taken:<ol>
<li>If the {@link com.github.xbn.analyze.validate.ValidResultFilter#getPreAction() pre-filter} {@link com.github.xbn.analyze.validate.FilterPreAction#isReturn() returns a value}: This <i><b>returns</b></i> {@link FilterPreAction#getReturnValue() that value} (values returned by the filter are never {@link com.github.xbn.analyze.validate.Validator#doInvertRules() inverted}).</li>
<li>Otherwise, {@code to_validate} is <!-- GENERIC PARAMETERS FAIL IN @link --><a href="#doesFollowRulesPreInvert(O)">validated</a> against the <i>un-inverted</i> rules, and that result is inverted if necessary.</li>
<li>If the {@link com.github.xbn.analyze.validate.ValidResultFilter#getAfterValueFromInvertedRules(boolean) post-filter} leaves that value alone (because it is either {@link com.github.xbn.analyze.validate.FilterAfterValue#UNCHANGED UNCHANGED}, or equal to the follows-the-rules finding), this <i><b>returns</b></i> it.</li>
<li>Otherwise this {@link #adjustForPostFilterReturnValue(boolean) makes adjustments}, and then <i><b>returns</b></i> it (un-inverted).</li>
</ol></p>
* @see com.github.xbn.analyze.Analyzer#getAnalyzedCount() Analyzer#getAnalyzedCount()
* @see com.github.xbn.analyze.validate.Validator#getValidCount() Validator#getValidCount()
* @see com.github.xbn.analyze.validate.Validator#getValidResultSource() Validator#getValidResultSource()
* @see com.github.xbn.analyze.validate.Validator#isValid() Validator#isValid()
*/
public final boolean isValid(O to_validate) {
if(isDebugOn()) { getDebugAptr().appentln("<NV> isValid(to_validate): to_validate=[" + StringUtilBase.getChopped(Trim.YES, to_validate, 30, "...") + "], " + this); }
ValidatorComposer.autoResetStateOrCINeedTo(this);
FilterPreAction vfpa = getFilter().getPreAction();
if(vfpa.isReturn()) {
if(isDebugOn()) { getDebugAptr().appentln("<NV> Returning declarePreInvertGetValidSetSource(" + vfpa.getReturnValue() + ", " + ResultReturnedBy.PRE_FILTER + ")"); }
return declareGetValidFromPreFilterNoInvert(vfpa.getReturnValue());
// return declarePreInvertGetValidSetSource(vfpa.getReturnValue(), ResultReturnedBy.PRE_FILTER);
}
//vfpa.isProceed() is true
boolean bPreInvFollowsRules = false;
try {
bPreInvFollowsRules = doesFollowRulesPreInvert(to_validate);
} catch(RuntimeException rx) {
throw new IllegalStateException("Attempting doesFollowRulesPreInvert(to_validate)", rx);
}
if(isDebugOn()) { getDebugAptr().appentln("<NV> doesFollowRulesPreInvert(to_validate)=" + bPreInvFollowsRules); }
boolean bFollowsRules = declareValidForRulesGetInverted(bPreInvFollowsRules);
FilterAfterValue fpv = getFilter().getAfterValueFromInvertedRules(bFollowsRules);
if(getFilter().isExpired()) {
if(isDebugOn()) { getDebugAptr().appentln("<NV> getFilter().isExpired()=true. Calling declareExpired()"); }
declareExpired();
}
boolean bPostValid = ValidResultFilterUtil.getPostReturnValueFromInvertedRules(fpv, bFollowsRules);
if(isDebugOn()) {
getDebugAptr().appentln("<NV> follows-rules-post-invert=" + bFollowsRules);
getDebugAptr().appentln("<NV> getFilter().getAfterValueFromInvertedRules(bFollowsRules)=FilterAfterValue." + fpv);
getDebugAptr().appentln("<NV> ValidResultFilterUtil.getPostReturnValueFromInvertedRules(FilterAfterValue." + fpv + ", bFollowsRules)=" + bPostValid);
}
if(bPostValid != bFollowsRules) {
if(isDebugOn()) { getDebugAptr().appentln("<NV> Post-filter return value different than rules: Calling adjustForPostFilterReturnValue(" + bPostValid + ")"); }
adjustForPostFilterReturnValue(bPostValid);
declarePostFilterReturnValue(fpv, bPostValid);
}
if(isDebugOn()) { getDebugAptr().appentln("<NV> Returning " + bPostValid + " (getValidResultSource()=" + getValidResultSource() + ")"); }
return bPostValid;
}
/**
<p>Adjusts internal state when the (post-inverted) <i>does-follow-the-rules</i> finding is overridden with a <i>different</i> post-filter value. This implementation of {@code adjustForPostFilterReturnValue(b)} does nothing. It must be overriden in sub-classes.</p>
<h3>Example</h3>
<pre> bPreInvFollowsRules = <!-- GENERIC PARAMETERS FAIL IN @link --><a href="#doesFollowRulesPreInvert(O)">doesFollowRulesPreInvert</a>(value);
boolean bFollowsRules = ({@link com.github.xbn.analyze.validate.Validator#doInvertRules() doInvertRules}() ? !bFollowsRules : bFollowsRules);
{@link com.github.xbn.analyze.validate.FilterAfterValue} fpv = {@link com.github.xbn.analyze.validate.Validator#getFilter() getFilter}().{@link com.github.xbn.analyze.validate.ValidResultFilter#getAfterValueFromInvertedRules(boolean) getAfterValueFromInvertedRules}(bFollowsRules);
boolean bPostValid = {@link com.github.xbn.analyze.validate.ValidResultFilterUtil}.{@link com.github.xbn.analyze.validate.ValidResultFilterUtil#getPostReturnValueFromInvertedRules(FilterAfterValue, boolean) getPostReturnValueFromInvertedRules}(fpv, bFollowsRules);
if(bPostValid != bFollowsRules) {
adjustForPostFilterReturnValue(bPostValid);
}</pre>
* @param isValid_fromPostFilter The value from the post filter, which is <i>different</i> than that just-returned by {@code doesFollowRulesPreInvert(O)} (post-inversion).
*/
public void adjustForPostFilterReturnValue(boolean isValid_fromPostFilter) {
}
@Override
public void crashIfBadValue(O to_validate, String to_vldtName) {
if(!isValid(to_validate)) {
throw new IllegalArgumentException(getXMsg(to_vldtName + " (rules: " + getRules() + "). " + to_vldtName + "=[" + StringUtilBase.getChopped(Trim.YES, to_validate, 30, "...") + "]", getExtraErrInfo()));
}
}
/**
<p>Given the current invert-setting, is {@code null} an acceptable value?.</p>
* @return <code>NullnessValidator.{@link #isNullOkGivenInvert(ValueValidator) isNullOkGivenInvert}(this)</code>
*/
public boolean isNullOkGivenInvert() {
return NullnessValidator.isNullOkGivenInvert(this);
}
/**
<p>Given the current invert-setting, is {@code null} an acceptable value?.</p>
* @param validator May not be {@code null}.
* @return <code>(validator.{@link com.github.xbn.analyze.validate.Validator#doInvertRules() doInvertRules}()* ? !validator.{@link com.github.xbn.analyze.validate.ValueValidator#isNullOk() isNullOk}()* : validator.isNullOk())</code>
* @see #isNullOkGivenInvert()
* @see #isImpossibleConsideringNullInvert(ValueValidator) isImpossibleConsideringNullInvert(vv)
*/
public static final <O> boolean isNullOkGivenInvert(ValueValidator<O> validator) {
try {
return (validator.doInvertRules() ? !validator.isNullOk() : validator.isNullOk());
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(validator, "validator", null, rx);
}
}
/**
<p>Given the current invert and {@code null} settings, is impossible for a value to be valid?.</p>
* @param validator May not be {@code null}.
* @return <code>(validator.{@link com.github.xbn.analyze.validate.ValueValidator#isNullOk() isNullOk}() && validator.{@link #doInvertRules() doInvertRules}())</code>
*/
public static final <O> boolean isImpossibleConsideringNullInvert(ValueValidator<O> validator) {
try {
return (validator.isNullOk() && validator.doInvertRules());
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(validator, "validator", null, rx);
}
}
public StringBuilder appendRules(StringBuilder to_appendTo) {
try {
return to_appendTo.append(isNullOk()
? ((doInvertRules() ? "null-ok, inverted (impossible)" : "null-ok (unrestricted)"))
: ((doInvertRules() ? "only null acceptable" : "null bad") + " (restricted)"));
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
}
}
/**
<p>Are the rules followed?.</p>
* @param to_validate The value to validate.
* @return {@code true} If {@link com.github.xbn.analyze.validate.ValueValidator#isNullOk() null is okay}, or the value is not {@code null}.
* @see <code><!-- GENERIC PARAMETERS FAIL IN @link --><a href="#isValid(O)">isValid</a>(O)</code>
*/
public boolean doesFollowRulesPreInvert(O to_validate) {
if(isNullOk()) {
if(isDebugOn()) { getDebugAptr().appentln("<NV> doesFollowRulesPreInvert(to_validate): isNullOk() is true. Returning true."); }
return true;
}
if(isDebugOn()) { getDebugAptr().appentln("<NV> doesFollowRulesPreInvert(to_validate): isNullOk() is false and to_validate is " + ((to_validate != null)?"non-":"") + "null. Returning " + to_validate != null); }
return (to_validate != null);
}
/**
* @see com.github.xbn.analyze.validate.z.ValueValidator_CfgForNeeder#nullOk(boolean) ValueValidator_Cfg#nullOk(b)
*/
public boolean isNullOk() {
return isNullOk;
}
/**
@return <code>true</code> If <code>to_compareTo</code> is non-<code>null</code>, a <code>NullnessValidator</code>, and <code><a href="#areFieldsEqual(xbn.analyze.validate.NullnessValidator)">areFieldsEqual</a>((NullnessValidator)to_compareTo)</code> is <code>true</code>. <i>This is implemented as suggested by Joshua Bloch in "Effective Java" (2nd ed, item 8, page 46).</i>
*/
@Override
public boolean equals(Object to_compareTo) {
//Check for object equality first, since it's faster than instanceof.
if(this == to_compareTo) {
return true;
}
if(!(to_compareTo instanceof NullnessValidator)) {
//to_compareTo is either null or not an NullnessValidator.
//java.lang.Object.object(o):
// "For any non-null reference value x, x.equals(null) should return false."
//See the bottom of this class for a counter-argument (which I'm not going with).
return false;
}
//Safe to cast
NullnessValidator o = (NullnessValidator)to_compareTo;
//Finish with field-by-field comparison.
return areFieldsEqual(o);
}
public boolean areFieldsEqual(NullnessValidator to_compareTo) {
return super.areFieldsEqual(to_compareTo) &&
isNullOk() == to_compareTo.isNullOk();
}
//other...END
//static...START
/**
<p>Get the rule-type from the fieldable instance. This must be overridden by sub-classes, which will likely call this function ({@code super.getRuleTypeFromFieldsVVN(fieldable)}) as their first line.</p>
@return
<pre>
is_nullOk do_invert Returns Description
---------------- ------------ --------------
true true IMPOSSIBLE
true false UNRESTRICTED
false true RESTRICTED Non-null bad, null okay
false false RESTRICTED Non-null okay, null bad</pre>
@see #NullnessValidator(ValueValidator_Fieldable) this(vv_f)
*/
public static final RuleType getRuleTypeFromFieldsVVN(ValueValidator_Fieldable fieldable) {
return (fieldable.isNullOk()
? (fieldable.doInvertRules() ? RuleType.IMPOSSIBLE : RuleType.UNRESTRICTED)
: RuleType.RESTRICTED);
}
/**
<p>Duplicate this <code>NullnessValidator</code>.</p>
* @return <code>(new {@link #NullnessValidator(NullnessValidator) NullnessValidator}<O>(this))</code>
*/
public NullnessValidator<O> getObjectCopy() {
return (new NullnessValidator<O>(this));
}
public static final <O> boolean isValidDefensive(ValueValidator<O> validator, O to_validate, String validator_name, String to_validateName) {
try {
return validator.isValid(to_validate);
} catch(RuntimeException rx) {
CrashIfObject.nnull(validator, validator_name, null);
throw new RuntimeException("Attempting " + validator_name + ".isValid(" + to_validateName + ")", rx);
}
}
}