/*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 java.util.Objects;
import com.github.xbn.analyze.AnalyzerComposer;
import com.github.xbn.analyze.validate.z.Validator_Fieldable;
import com.github.xbn.io.TextAppenter;
import com.github.xbn.lang.CrashIfObject;
import com.github.xbn.lang.RuleType;
import com.github.xbn.lang.RuleableComposer;
import com.github.xbn.lang.XInfoAccumulator;
import com.github.xbn.testdev.CompositionConstructor;
import com.github.xbn.testdev.CompositionFunction;
import com.github.xbn.util.tuple.ThreeTSObjects;
import static com.github.xbn.lang.CrashIfBase.*;
/**
<p>For classes that need to implement {@code Validator}, that cannot extend {@code AbstractValidator}. See {@link com.github.xbn.analyze.AbstractAnalyzer AbstractValidator}.</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 ValidatorComposer extends AnalyzerComposer {
//state
private boolean wasValid;
private ResultReturnedBy validResultSource;
//counts
private int validCount = -1;
//config
private final boolean doInvert ;
private final RuleableComposer ruleCmpsr;
private final ValidResultFilter vrFilter ;
//constructors...START
/**
<p>Create a new instance.</p>
<p>Field-documentation:<ul>
<li><code>fieldable.{@link com.github.xbn.analyze.validate.z.Validator_Fieldable#getFilter() getFilter}()</code>: If non-{@code null}, this suppresses or alters valid results. It may not be {@link com.github.xbn.lang.Expirable#isExpired() expired}. If {@code null}, then {@link #unfiltered() no filtering} occurs.</li>
<li><code>fieldable.{@link com.github.xbn.analyze.validate.z.Validator_Fieldable#getDebugApbl() getDebugApbl}():</code> If non-{@code null}, then debugging is {@link com.github.xbn.io.Debuggable#setDebugOn(boolean) turned on}, and printed by this.</li>
</ul></p>
* @see #ValidatorComposer(boolean, ValidatorComposer) this(b,vc)
* @see #ValidatorComposer(Validator) this(v)
*/
public ValidatorComposer(RuleType rule_type, Validator_Fieldable fieldable) {
super();
ruleCmpsr = new RuleableComposer(rule_type, "rule_type");
doInvert = fieldable.doInvertRules();
vrFilter = ((fieldable.getFilter() != null) ? fieldable.getFilter()
: ValidatorComposer.unfiltered());
if(vrFilter.doesExpire()) {
declareExpirable_4prot();
}
onIfNonNull(fieldable.getDebugApbl());
//Never ever call interface functions, directly or indirectly, in a constructor.
zresetCountsVB();
}
/**
<p>Create a new {@code ValidatorComposer} as a duplicate of another.</p>
<p>This<ol>
<li>Calls <code>{@link com.github.xbn.analyze.AnalyzerComposer#AnalyzerComposer(boolean, AnalyzerComposer) super}(ignored, to_copy)</code></li>
<li>Sets {@link #getValidCount() getValidCount}{@code ()} to {@code to_copy.getValidCount()}.</li>
</ol></p>
* @param to_copy May not be {@code null}.
* @see #getObjectCopy()
* @see #ValidatorComposer(RuleType, Validator_Fieldable) this(rti,vrFilter)
*/
public ValidatorComposer(boolean ignored, ValidatorComposer to_copy) {
super(ignored, to_copy);
validCount = to_copy.getValidCount();
doInvert = to_copy.doInvertRules();
ruleCmpsr = new RuleableComposer(to_copy.getRuleType());
vrFilter = to_copy.getFilter();
}
/**
<p>Create a new {@code ValidatorComposer} from a {@code Validator}.</p>
<p>This<ol>
<li>Calls {@link com.github.xbn.analyze.AnalyzerComposer#AnalyzerComposer(Analyzer) super}{@code (validator)}</li>
<li>Sets {@link #getValidCount() getValidCount}{@code ()} to {@code validator.getValidCount()}</li>
</ol></p>
* @see #ValidatorComposer(RuleType, Validator_Fieldable) this(rti,vrFilter)
*/
@CompositionConstructor
public ValidatorComposer(Validator validator) {
super(validator);
validCount = validator.getValidCount();
doInvert = validator.doInvertRules();
wasValid = false;
ruleCmpsr = new RuleableComposer(validator.getRuleType());
vrFilter = validator.getFilter();
}
//constructors...END
//setters...START
@CompositionFunction
public void resetState() {
super.resetState();
zresetStateVB();
}
protected final void zresetStateVB() {
wasValid = false;
validResultSource = null;
}
/**
<p>Revert all counts to zero.</p>
<p><i>There is no {@code resetState()} function, because there is no state unique to this {@code ValidatorComposer}.</i></p>
<p>This calls<ol>
<li>{@link com.github.xbn.analyze.AnalyzerComposer super}.{@link com.github.xbn.analyze.AnalyzerComposer#resetCounts() resetCounts}{@code ()}</li>
<li>{@link #zresetCountsVB() zresetCountsVB}{@code ()}</li>
</ol></p>
*/
@CompositionFunction
public void resetCounts() {
super.resetCounts();
zresetCountsVB();
}
/**
<p>Reset counts specific to this {@code ValidatorComposer}.</p>
<p>This sets {@link #getValidCount() getValidCount}{@code ()} to zero.</p>
* @see #resetCounts()
*/
protected final void zresetCountsVB() {
validCount = 0;
}
public ValidResultFilter getFilter() {
return vrFilter;
}
public boolean declareGetValidFromPreFilterNoInvert_4prot(boolean is_validAccordingToPreFilter) {
return declareValidSetSourceInvertIfRules_4prot(is_validAccordingToPreFilter, ResultReturnedBy.PRE_FILTER);
}
public boolean declareValidForRulesGetInverted_4prot(boolean are_preInvertRulesFollowed) {
return declareValidSetSourceInvertIfRules_4prot(are_preInvertRulesFollowed, ResultReturnedBy.RULES);
}
public void declarePostFilterReturnValue_4prot(FilterAfterValue fp_v, boolean is_validAccordingToPostFilter) {
declareValidSetSourceInvertIfRules_4prot(is_validAccordingToPostFilter, ResultReturnedBy.POST_FILTER);
}
private boolean declareValidSetSourceInvertIfRules_4prot(boolean is_valid, ResultReturnedBy source) {
declareAnalyzed_4prot();
try {
if(source.isRules()) {
wasValid = (doInvertRules() ? !is_valid : is_valid);
}
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(source, "source", null, rx);
}
validResultSource = source;
if(wasValid) {
validCount++;
}
return wasValid;
}
//setters...END
//getters...START
/**
* @return The number of times {@link #declareValidSetSourceInvertIfRules_4prot(boolean, ResultReturnedBy) declarePreInvertGetValidSetSource_4prot}{@code (true, ...)} was called, since the last call to {@link #zresetCountsVB() zresetCountsVB}{@code ()}.
*/
@CompositionFunction
public int getValidCount() {
return validCount;
}
@CompositionFunction
public ResultReturnedBy getValidResultSource() {
return validResultSource;
}
/**
* @return {@code true} If {@link #declareValidSetSourceInvertIfRules_4prot(boolean, ResultReturnedBy) declarePreInvertGetValidSetSource_4prot}{@code (true, ...)} was called more recently than {@link #resetState() resetState}{@code ()}.
*/
@CompositionFunction
public boolean isValid() {
return wasValid;
}
@CompositionFunction
public boolean doInvertRules() {
return doInvert;
}
@CompositionFunction
public final RuleType getRuleType() {
return ruleCmpsr.getRuleType();
}
@CompositionFunction
public void setERuleType_4prot(RuleType type) {
}
/*
@CompositionFunction
public void setERuleType_4prot(RuleType type, String type_name) {
ruleCmpsr.setERuleType_4prot(type, type_name);
}
*/
//getters...END
//other...START
@CompositionFunction
public String toString() {
return appendToString(new StringBuilder()).toString();
}
public StringBuilder appendToString(StringBuilder to_appendTo) {
super.appendToString(to_appendTo);
if(doInvertRules()) {
to_appendTo.append(", doInvertRules()=true");
}
if(wasAnalyzed()) {
to_appendTo.append(", isValid()=").append(isValid());
}
to_appendTo.append(", getValidCount()=").append(getValidCount()).append(", ");
ruleCmpsr.appendToString(to_appendTo);
if(!getFilter().doesNothing()) {
to_appendTo.append(", ").append("filter:<");
getFilter().appendToString(to_appendTo).append(">");
}
return to_appendTo;
}
public static final boolean areValidatorFieldsEqual(Validator vldtr_a, Validator vldtr_b) {
try {
return vldtr_a.doInvertRules() == vldtr_b.doInvertRules() &&
vldtr_a.getRuleType().equals(vldtr_b.getRuleType());
} catch(RuntimeException rx) {
Objects.requireNonNull(vldtr_a, "vldtr_a");
throw CrashIfObject.nullOrReturnCause(vldtr_b, "vldtr_b", null, rx);
}
}
//other...END
//static...START
/**
<p>If a <code>Validator</code>-s rule-type is not as required, crash.</p>
* <p>Equal to
<br/> <code>validator.getRuleType().{@link com.github.xbn.lang.RuleType#crashIfNotRequiredValue(RuleType, String, Object) ciNotRequiredRuleType}(validator.{@link #getRuleType() getRuleType}(), rqd_value, vldtr_name)</code></p>
*/
public static final void ciNotRequiredRuleType(Validator validator, RuleType rqd_value, String vldtr_name, Object xtra_errInfo) {
try {
validator.getRuleType().crashIfNotRequiredValue(rqd_value, sVNM_GERT,
XInfoAccumulator.getAddedOrNew(xtra_errInfo, toVNM_EQLS.set2(vldtr_name)));
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(validator, "validator", null, rx);
}
}
/**
<p>If a <code>Validator</code>-s rule-type is a forbidden value, crash.</p>
* <p>Equal to
<br/> <code>validator.getRuleType().{@link com.github.xbn.lang.RuleType#crashIfForbiddenValue(RuleType, String, Object)}(validator.{@link #getRuleType() getRuleType}(), rqd_value, vldtr_name)</code></p>
*/
public static final void ciForbiddenRuleType(Validator validator, RuleType rqd_value, String vldtr_name, Object xtra_errInfo) {
try {
validator.getRuleType().crashIfForbiddenValue(rqd_value, sVNM_GERT,
XInfoAccumulator.getAddedOrNew(xtra_errInfo, toVNM_EQLS.set2(vldtr_name)));
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(validator, vldtr_name, null, rx);
}
}
private static final String sVNM_GERT = "[Validator-name].getRuleType()";
private static final ThreeTSObjects toVNM_EQLS = new ThreeTSObjects(
"[Validator-name]=\"", null, "\"");
public static final <O> void ciNullOk(ValueValidator<O> validator, String vldtr_name, Object xtra_errInfo) {
ciNullFlagEqualTo(true, validator, vldtr_name, xtra_errInfo);
}
public static final <O> void ciNullBad(ValueValidator<O> validator, String vldtr_name, Object xtra_errInfo) {
ciNullFlagEqualTo(false, validator, vldtr_name, xtra_errInfo);
}
private static final <O> void ciNullFlagEqualTo(boolean flag_value, ValueValidator<O> validator, String vldtr_name, Object xtra_errInfo) {
boolean bNk = false;
try {
bNk = (validator.isNullOk() && !validator.doInvertRules());
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(validator, "validator", null, rx);
}
if(bNk == flag_value) {
throw new IllegalArgumentException(getXMsg(vldtr_name + ".isNullOk() is " + validator.isNullOk() + ", and validator.doInvertRules() is " + validator.doInvertRules() + ". This means null is " +
(bNk ? "okay" : "bad")
+ ", which is unacceptable." + ".", xtra_errInfo));
}
}
public static final boolean doesFilterExpire(ValidResultFilterable vr_fbl, String sreplacerfblName, Object xtra_errInfo) {
try {
return vr_fbl.getFilter().doesExpire();
} catch(RuntimeException rx) {
CrashIfObject.nnull(vr_fbl, "vr_fbl", xtra_errInfo);
throw CrashIfObject.nullOrReturnCause(vr_fbl.getFilter(), sreplacerfblName + ".getFilter()", xtra_errInfo, rx);
}
}
//static...END
/**
<p>A valid-result filter that does nothing--it always passes the follows-the-rules value unchanged. This is a singleton that never {@link com.github.xbn.lang.Expirable#isExpired() expires}, whose {@link ValidResultFilter#getPreAction() pre-action} is always {@link FilterPreAction#PROCEED PROCEED}, {@link ValidResultFilter#getAfterValueFromInvertedRules(boolean) post-value} is always {@link FilterAfterValue#UNCHANGED UNCHANGED}.</p>
*/
public static final ValidResultFilter unfiltered() {
return VRFDoesNothing.INSTANCE;
}
}
enum VRFDoesNothing implements ValidResultFilter {
INSTANCE;
public final FilterPreAction getPreAction() {
return FilterPreAction.PROCEED;
}
public final FilterAfterValue getAfterValueFromInvertedRules(boolean ignored) {
return FilterAfterValue.UNCHANGED;
}
public final VRFDoesNothing getObjectCopy() {
return INSTANCE;
}
public final String toString() {
return appendToString(new StringBuilder()).toString();
}
public final StringBuilder appendToString(StringBuilder to_appendTo) {
try {
return to_appendTo.append(this.getClass().getName());
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
}
}
public final boolean doesExpire() {
return false;
}
public final boolean isExpired() {
return false;
}
public final boolean doesNothing() {
return true;
}
public final void setDebug(Appendable destination, boolean is_on) {
}
public final void setDebugOn(boolean is_on) {
}
public final boolean isDebugOn() {
return false;
}
public final TextAppenter getDebugAptr() {
return null;
}
public final TextAppenter debug(Object ignored) {
return null;
}
public final void debugln(Object ignored) {
}
public final Appendable getDebugApbl() {
return null;
}
}