/*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.alter;
import java.util.Objects;
import com.github.xbn.text.Trim;
import static com.github.xbn.lang.XbnConstants.*;
import com.github.xbn.lang.CrashIfObject;
import com.github.xbn.lang.ObjectOrCrashIfNull;
import com.github.xbn.lang.RuleType;
import com.github.xbn.lang.XInfoAccumulator;
import com.github.xbn.analyze.validate.ValueValidator;
import com.github.xbn.analyze.validate.ValidatorComposer;
import com.github.xbn.analyze.validate.NewValueValidatorFor;
import com.github.xbn.text.StringUtilBase;
/**
<p>A {@code ValueAlterer} that does one of two things: One action when a condition is met, and another when it is not. Internally this is two {@code ValueAlterer}-s and an <code>xbn.analyze.validate.{@link com.github.xbn.analyze.validate.ValueValidator ValueValidator}</code>, the latter of which is the "alter condition". If
<br/> {@link #getCondition() getCondition}{@code ().isValid(V)}
<br/>is {@code true}, then {@code A} is altered by the {@link #getAlterValid() valid alterer}. Otherwise, it is altered by the {@link #getAlterInvalid() invalid alterer}.</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 AlterValueWhen<V,A> extends AbstractValueAlterer<V,A> {
//config
private final ValueValidator<V> vv ;
private final ValueAlterer<V,A> avValid ;
private final ValueAlterer<V,A> avInvalid ;
//internal
//cofig
// private boolean bNoCndValid = false;
//internal
//constructors...START
/**
<p>Create a new {@code AlterValueWhen} that makes no alterations.</p>
* <p>Equal to
<br/> <code>{@link #AlterValueWhen(ValueValidator, ValueAlterer, ValueAlterer) this}(
<br/> (ValueValidator<V>)NewValueValidatorFor.unrestricted(null, null),
<br/> (new ReturnValueUnchanged<V,A>()), (new ReturnValueUnchanged<V,A>()))</code></p>
*/
public AlterValueWhen() {
this(
NewValueValidatorFor.<V>unrestricted(null, null),
(new ReturnValueUnchanged<V,A>()), (new ReturnValueUnchanged<V,A>()));
}
/**
<p>Create a new {@code AlterValueWhen}.</p>
* <p>Equal to
<br/> <code>{@link #AlterValueWhen(ValueValidator, ValueAlterer, ValueAlterer) this}(
<br/> (ValueValidator<V>)new NewValueValidatorFor.unrestricted(null, null),
<br/> alter_forValid, (new {@link com.github.xbn.analyze.alter.ReturnValueUnchanged#ReturnValueUnchanged() ReturnValueUnchanged}<A>()))</code></p>
*/
public AlterValueWhen(ValueAlterer<V,A> alter_forValid) {
this(
NewValueValidatorFor.<V>unrestricted(null, null),
alter_forValid, (new ReturnValueUnchanged<V,A>()));
}
/**
<p>Create a new {@code AlterValueWhen} that does nothing.</p>
* <p>Equal to
<br/> <code>{@link #AlterValueWhen(ValueValidator, ValueAlterer, ValueAlterer) this}(condition, alter_forValid, (new ReturnValueUnchanged<V,A>()))</code></p>
*/
public AlterValueWhen(ValueValidator<V> condition, ValueAlterer<V,A> alter_forValid) {
this(condition, alter_forValid, (new ReturnValueUnchanged<V,A>()));
}
/**
<p>Create a new {@code AlterValueWhen}.</p>
<p>This calls {@link com.github.xbn.analyze.alter.AbstractValueAlterer#AbstractValueAlterer() super}{@code ()}, and resets {@link #zresetStateAVW() state} and {@link #zresetCountsAVW() counts}</li>
</ol></p>
<p><i>All parameters must be non-{@code null}. They are also all duplicated internally (defensively copied).</i></p>
* @param condition Get with {@link #getCondition() getCondition}{@code ()}.
* @param alter_forValid Get with {@link #getAlterValid() getAlterValid}{@code ()}.
* @param alter_forInvalid Get with {@link #getAlterInvalid() getAlterInvalid}{@code ()}. When attempting to <!-- GENERIC PARAMETERS FAIL IN @link --><a href="#getAltered(V, A)">make the alteration</a> and the condition is not met, {@code alter_forInvalid} makes the actual alteration, and therefore determines the value of <i>{@code this}</i> classes {@link com.github.xbn.analyze.alter.Alterer#wasAltered() wasAltered}{@code ()} flag. Therefore, when an alteration is not wanted when the condition is not met, <code>alter_forInvalid.{@link com.github.xbn.lang.Expirable#isExpired() isExpired}()</code> should be {@code true} (in most cases, expired alterers will return the original value unchanged).
* @see #AlterValueWhen() this()
* @see #AlterValueWhen(ValueValidator, ValueAlterer) this(vv,av)
* @see #AlterValueWhen(AlterValueWhen) this(avw)
*/
public AlterValueWhen(ValueValidator<V> condition, ValueAlterer<V,A> alter_forValid, ValueAlterer<V,A> alter_forInvalid) {
super();
try {
vv = condition.getObjectCopy();
avValid = alter_forValid.getObjectCopy();
avInvalid = alter_forInvalid.getObjectCopy();
} catch(RuntimeException rx) {
Objects.requireNonNull(condition, "condition");
Objects.requireNonNull(alter_forValid, "alter_forValid");
throw CrashIfObject.nullOrReturnCause(alter_forInvalid, "alter_forInvalid", null, rx);
}
//Never ever call interface functions, directly or indirectly, in a constructor.
zresetStateAVW();
zresetCountsAVW();
if(getAlterValid().isExpired() && getAlterInvalid().isExpired()) {
declareExpirable();
declareExpired();
}
if(!isExpired()) {
declareMayDelete(getAlterValid().mayDelete() && getAlterInvalid().mayDelete());
}
}
/**
<p>Create a new {@code AlterValueWhen} as a duplicate of another.</p>
<p>This calls<ol>
<li>{@link com.github.xbn.analyze.alter.AbstractValueAlterer#AbstractValueAlterer(ValueAlterer) super}{@code (to_copy)}</li>
<li>{@link #zresetStateAVW() zresetStateAVW}{@code ()}</li>
</ol></p>
* @see #getObjectCopy()
* @see #AlterValueWhen(ValueValidator, ValueAlterer, ValueAlterer) this(vv,av,av,b)
*/
//to_copy is an AlterValueWhen, and therefore DOES have
//the types as expected, for all three objects.
public AlterValueWhen(AlterValueWhen<V,A> to_copy) {
super(to_copy);
@SuppressWarnings("unchecked")
ValueValidator<V> vv2 = (ValueValidator<V>)ObjectOrCrashIfNull.
<ValueValidator>getCopy(to_copy.getCondition(), ValueValidator.class, "to_copy.getCondition()");
vv = vv2;
avValid = getAVCopy(to_copy.getAlterValid(), "to_copy.getAlterValid()");
avInvalid = getAVCopy(to_copy.getAlterInvalid(), "to_copy.getAlterInvalid()");
//Never ever call interface functions, directly or indirectly, in a constructor.
zresetStateAVW();
}
private static final <V,A> ValueAlterer<V,A> getAVCopy(ValueAlterer<V,A> alterer, String alter_name) {
@SuppressWarnings("unchecked")
ValueAlterer<V,A> avw2 = (ValueAlterer<V,A>)ObjectOrCrashIfNull.
<ValueAlterer>getCopy(alterer, ValueAlterer.class, alter_name);
return avw2;
}
//constructors...END
//setters...START
/**
<p>Reset non-count state.</p>
* <p>Equal to
<br/> {@link #zresetStateAVW() zresetStateAVW}{@code ()}</p>
* @see #resetCounts()
*/
public void resetState() {
super.resetState();
zresetStateAVW();
}
/**
<p>Reset non-count state specific to this {@code AlterValueWhen}.</p>
<p>This calls<ol>
<li>{@link #getCondition() getCondition}{@code ().}{@link #resetState() resetState}{@code ()}</li>
<li>{@link #getAlterValid() getAlterValid}{@code ().resetState()}</li>
<li>{@link #getAlterInvalid() getAlterInvalid}{@code ().resetState()}</li>
</ol></p>
*/
protected final void zresetStateAVW() {
getCondition().resetState();
getAlterValid().resetState();
getAlterInvalid().resetState();
}
/**
<p>Reset counters to zero.</p>
<p>This calls<ol>
<li>{@link #zresetCountsAVW() zresetCountsAVW}{@code ()}</li>
</ol></p>
* @see #resetState()
*/
public void resetCounts() {
super.resetCounts();
zresetCountsAVW();
}
/**
<p>Reset counters to zero specific to this {@code AlterValueWhen}.</p>
<p>This calls<ol>
<li>{@link #getCondition() getCondition}{@code ().}{@link #resetCounts() resetCounts}{@code ()}</li>
<li>{@link #getAlterValid() getAlterValid}{@code ().resetCounts()}</li>
<li>{@link #getAlterInvalid() getAlterInvalid}{@code ().resetCounts()}</li>
</ol></p>
* @see #resetCounts()
*/
protected final void zresetCountsAVW() {
getCondition().resetCounts();
getAlterValid().resetCounts();
getAlterValid().resetCounts();
}
/**
<p>Updates internal state after a manual deletion.</p>
<p>This calls<ol>
<li>{@link #getCondition() getCondition}{@code ().}{@link com.github.xbn.analyze.validate.ValidatorComposer#resetState() resetState}{@code ()}</li>
<li>{@link #getAlterValid() getAlterValid}{@code ().resetForDeletion()}</li>
<li>{@link #getAlterInvalid() getAlterInvalid}{@code ().resetForDeletion()}</li>
</ol></p>
*/
public void resetForDeletion() {
getCondition().resetState();
getAlterValid().resetForDeletion();
getAlterInvalid().resetForDeletion();
}
//getters...START
/**
<p>The alterer used when the condition is met.</p>
*/
public ValueAlterer<V,A> getAlterValid() {
return avValid;
}
/**
<p>The alterer used when the condition is not met.</p>
*/
public ValueAlterer<V,A> getAlterInvalid() {
return avInvalid;
}
/**
<p>The alter-condition.</p>
*/
public ValueValidator<V> getCondition() {
return vv;
}
public boolean doesExpire() {
return getCondition().doesExpire();
}
public boolean isExpired() {
return getCondition().isExpired();
}
public boolean doAutoResetState() {
return getCondition().doAutoResetState();
}
public RuleType getRuleType() {
return getCondition().getRuleType();
}
/**
* @return <code>{@link #appendRules(StringBuilder) appendRules}(new StringBuilder()).toString()</code>
*/
public String getRules() {
return appendRules(new StringBuilder()).toString();
}
/**
* @param to_appendTo May not be {@code null}.
* @see #getRules()
*/
public StringBuilder appendRules(StringBuilder to_appendTo) {
try {
getCondition().appendRules(to_appendTo);
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
}
return to_appendTo;
}
//getters...END
/**
<p>Get the altered value, based on the configuration throughout this class.</p>
* @param to_validate The object to validate.
* @param to_alter The object to alter.
*/
public A getAlteredPostResetCheck(V to_validate, A to_alter) {
if(isDebugOn()) { getDebugAptr().appentln("<AVW> to_validate=[" + StringUtilBase.getChopped(Trim.YES, to_validate, 30, "...") + "], to_alter=[" + StringUtilBase.getChopped(Trim.YES, to_alter, 30, "...") + "], [" + this + "]"); }
if(isExpired()) {
if(isDebugOn()) { getDebugAptr().appentln("<AVW> isExpired()=true (returning to_alter)"); }
return to_alter;
}
ValueAlterer<V,A> alterer = (getCondition().isValid(to_validate)
? getAlterValid()
: getAlterInvalid());
to_alter = alterer.getAltered(to_validate, to_alter);
boolean bWsLtrd = getAlterValid().wasAltered();
boolean bNtbDel = getAlterValid().needsToBeDeleted();
declareAltered(Altered.getForBoolean(bWsLtrd), NeedsToBeDeleted.getForBoolean(bNtbDel));
if(isDebugOn()) { getDebugAptr().appentln("<AVW> wasAltered()=" + wasAltered() + ", getCondition().isValid()=" + getCondition().isValid() + ". Returning [" + StringUtilBase.getChopped(Trim.YES, to_alter, 30, "...") + "], needsToBeDeleted()=" + needsToBeDeleted()); }
return to_alter;
}
//debugging...START
/**
<p>Turn debugging on or off.</p>
<p>This calls<ol>
<li>{@link #getCondition() getCondition}{@code ().setDebugOn(is_on)}</li>
<li>{@link #getAlterValid() getAlterValid}{@code ().setDebugOn(is_on)}</li>
<li>{@link #getAlterInvalid() getAlterInvalid}{@code ().setDebugOn(is_on)}</li>
</ol></p>
*/
public void setDebugOn(boolean is_on) {
super.setDebugOn(is_on);
getCondition().setDebugOn(is_on);
getAlterValid().setDebugOn(is_on);
getAlterInvalid().setDebugOn(is_on);
}
/**
<p>Set the debug-writer.</p>
<p>This calls<ol>
<li>{@link #getCondition() getCondition}{@code ().setDebug(destination)}</li>
<li>{@link #getAlterValid() getAlterValid}{@code ().setDebug(destination)}</li>
<li>{@link #getAlterInvalid() getAlterInvalid}{@code ().setDebug(destination)}</li>
</ol></p>
* @see #setDebugOn(boolean) setDebugOn(b)
*/
public void setDebug(Appendable destination, boolean is_on) {
super.setDebug(destination, is_on);
getCondition().setDebug(destination, is_on);
getAlterValid().setDebug(destination, is_on);
getAlterInvalid().setDebug(destination, is_on);
}
//debugging...END
//other...START
public StringBuilder appendToString(StringBuilder to_appendTo) {
try {
super.appendToString(to_appendTo);
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
}
return to_appendTo.append(": ").append(LINE_SEP).append(" - getCondition()=").append(getCondition()).append(LINE_SEP).
append(" - getAlterValid()=[").append(getAlterValid()).append("]").append(LINE_SEP).
append(" - getAlterInvalid()=[").append(getAlterInvalid());
}
public Object getExtraErrInfo() {
return getCondition().getExtraErrInfo();
}
public void setExtraErrInfo(Object info) {
super.setExtraErrInfo(info);
getCondition().setExtraErrInfo(info);
getAlterValid().setExtraErrInfo(info);
getAlterInvalid().setExtraErrInfo(info);
}
/**
* <p>Get a duplicate of this object.</p>
* @return <code>(new {@link #AlterValueWhen(AlterValueWhen) AlterValueWhen}<V,A>(this))</code>
*/
public AlterValueWhen<V,A> getObjectCopy() {
return (new AlterValueWhen<V,A>(this));
}
/*
public boolean wasAltered() {
return (getAlterValid().wasAltered() || getAlterInvalid().wasAltered());
}
public int getAlteredCount() {
return (getAlterValid().getAlteredCount() + getAlterInvalid().getAlteredCount());
}
public int getDeletedCount() {
return (getAlterValid().getDeletedCount() + getAlterInvalid().getDeletedCount());
}
public boolean needsToBeDeleted() {
return (getAlterValid().needsToBeDeleted() || getAlterInvalid().needsToBeDeleted());
}
public int getAnalyzedCount() {
return getCondition().getAnalyzedCount();
}
public boolean wasAnalyzed() {
return getCondition().wasAnalyzed();
}
public boolean isDebugOn() {
return getCondition().isDebugOn();
}
*/
//other...END
private static final String sERR_FOR_CND = "Error is for [AlterValueWhen].getCondition()";
public static final void ciConditionNotRqdRuleType(AlterValueWhen<?,?> alterer, RuleType rqd_value, String alter_name, Object xtra_errInfo) {
XInfoAccumulator xia = XInfoAccumulator.getAddedOrNew(xtra_errInfo, sERR_FOR_CND);
try {
ValidatorComposer.ciNotRequiredRuleType(alterer.getCondition(), rqd_value, alter_name, xia);
} catch(NullPointerException npx) {
throw CrashIfObject.nullOrReturnCause(alterer, alter_name, xia, npx);
}
}
public static final void ciConditionForbiddenRuleType(AlterValueWhen<?,?> alterer, RuleType rqd_value, String alter_name, Object xtra_errInfo) {
XInfoAccumulator xia = XInfoAccumulator.getAddedOrNew(xtra_errInfo, sERR_FOR_CND);
try {
ValidatorComposer.ciForbiddenRuleType(alterer.getCondition(), rqd_value, alter_name, xia);
} catch(NullPointerException npx) {
throw CrashIfObject.nullOrReturnCause(alterer, alter_name, xia, npx);
}
}
}