/*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.number;
import com.github.xbn.lang.CrashIfObject;
import com.github.xbn.lang.IllegalArgumentStateException;
import com.github.xbn.lang.Invert;
import com.github.xbn.lang.RuleType;
import com.github.xbn.lang.XInfoAccumulator;
import java.util.Objects;
import static com.github.xbn.lang.CrashIfBase.*;
/**
* <p>Determines if an int is within an {@code IntRange}.</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 IntInRange extends NumberInRange<Integer> implements IntRange {
/**
* <p>An {@code LengthInRange} with no bounds.</p>
*
* <p>Equal to
* <br/> <code>new {@link #IntInRange() IntInRange}()</code></p>
*/
public static final IntInRange UNRESTRICTED = new IntInRange();
/**
* <p>An {@code LengthInRange} with no members.</p>
*
* <p>Equal to
* <br/> <code>new {@link #IntInRange(Invert, IntBound, IntBound) IntInRange}({@link com.github.xbn.lang.Invert}.{@link com.github.xbn.lang.Invert#YES YES}, null, null</code></p>
*/
public static final IntInRange IMPOSSIBLE = new IntInRange(Invert.YES, null, null);
//constructors...START
/**
* <p>Create a new unrestricted instance.</p>
*
* <p>Equal to
* <br/> {@link NumberInRange#NumberInRange super}{@code ()}</p>
* @see #IntInRange(IntBound, IntBound) IntInRange(nb,nb)
*/
public IntInRange() {
super();
}
/**
* <p>Create a new instance with bounds.</p>
*
* <p>Equal to
* <br/> <code>{@link #IntInRange(int, int, String, String) this}(min, max, null, null)</code></p>
*/
public IntInRange(int min, int max) {
this(min, max, null, null);
}
/**
* <p>Create a new instance with bounds.</p>
*
* <p>Equal to
* <br/> <code>{@link #IntInRange(IntBound, IntBound) this}(new {@link IntBoundInclusive#IntBoundInclusive(Integer, String) IntBoundInclusive}(min, min_name), new IntBoundInclusive(max, max_name))</code></p>
*/
public IntInRange(int min, int max, String min_name, String max_name) {
this(new IntBoundInclusive(min, min_name), new IntBoundInclusive(max, max_name));
}
/**
* <p>Create a new instance with bounds.</p>
*
* <p>Equal to
* <br/> {@link NumberInRange#NumberInRange(NumberBound, NumberBound) super}{@code (min, max)}</p>
*/
public IntInRange(IntBound min, IntBound max) {
super(min, max);
}
/**
* <p>Create a new instance with bounds and invert setting.</p>
*
* <p>Equal to
* <br/> <code>{@link #IntInRange(Invert, int, int, String, String) this}(invert, min, max, null, null)</code></p>
*/
public IntInRange(Invert invert, int min, int max) {
this(invert, min, max, null, null);
}
/**
* <p>Create a new instance with bounds and invert setting.</p>
*
* <p>Equal to
* <br/> <code>{@link #IntInRange(Invert, IntBound, IntBound) this}(invert, new {@link IntBoundInclusive#IntBoundInclusive(Integer, String) IntBoundInclusive}(min, min_name), new IntBoundInclusive(max, max_name))</code></p>
*/
public IntInRange(Invert invert, int min, int max, String min_name, String max_name) {
this(invert, new IntBoundInclusive(min, min_name), new IntBoundInclusive(max, max_name));
}
/**
* <p>Create a new instance with bounds and invert setting.</p>
*
* <p>Equal to
* <br/> {@link NumberInRange#NumberInRange(Invert, NumberBound, NumberBound) super}{@code (invert, min, max)}</p>
* @see #IntInRange()
* @see #IntInRange(int, int) IntInRange(i,i)
* @see #IntInRange(Invert, int, int) IntInRange(inv,i,i)
* @see #IntInRange(int, int, String, String) IntInRange(i,i,s,s)
* @see #IntInRange(Invert, int, int, String, String) IntInRange(inv,i,i,s,s)
* @see #IntInRange(IntBound, IntBound) IntInRange(ib,ib)
*/
public IntInRange(Invert invert, IntBound min, IntBound max) {
super(invert, min, max);
}
//constructors...END
//setters...START
/*
public IntInRange extraErrInfo(Object info) {
setExtraErrInfo(info);
return this;
}
*/
/**
* <p>If the bounds of this {@code IntInRange} are invalid, crash.</p>
*
* <p>Equal to
* <br/> <code>IntInRange.{@link #crashIfBadBounds(IntBound, IntBound, String, String, Object) crashIfBadBounds}({@link com.github.xbn.number.NumberInRange#getMinBound() getMinBound}(), {@link com.github.xbn.number.NumberInRange#getMaxBound() getMaxBound}, "getMinBound()", "getMaxBound()",
* <br/> {@link com.github.xbn.lang.ExtraErrInfoable#getExtraErrInfo() getExtraErrInfo}())</code></p>
*/
public void crashIfBadBoundsForCnstr() {
IntInRange.crashIfBadBounds(getMinBound(), getMaxBound(),
"getMinBound()", "getMaxBound()", getExtraErrInfo());
}
//setters...END
//getters...START
//getters: atomic...START
public IntBound getMinBound() {
return (IntBound)super.getMinBound();
}
public IntBound getMaxBound() {
return (IntBound)super.getMaxBound();
}
/**
* <p>Get the minimum bound int.</p>
* @return <code><i>[{@link NumberInRange super}]</i>.{@link NumberInRange#getMinNumber() getMinNumber}().intValue()</code>
* @see #getMax()
*/
public int getMin() {
return getMinNumber().intValue();
}
/**
* <p>Get the maximum bound int.</p>
* @return <code><i>[{@link NumberInRange super}]</i>.{@link NumberInRange#getMaxNumber() getMaxNumber}().intValue()</code>
* @see #getMin()
*/
public int getMax() {
return getMaxNumber().intValue();
}
/**
* @return <blockquote><pre>new {@link IntInRange#IntInRange(com.github.xbn.lang.Invert, int, int) IntInRange}({@link Invert}.{@link Invert#getForBoolean(boolean) getForBoolean}(!{@link NumberInRange.html#isInverted() isInverted}()),
* {@link #getMinBound()}(), {@link #getMaxBound()}()</pre></blockquote>
*/
public IntInRange getInvertedCopy() {
return new IntInRange(Invert.getForBoolean(!isInverted()),
getMinBound(), getMaxBound());
}
public NumberInRange<Integer> getIntersection(NumberInRange<Integer> to_intersectWith) {
if(!doesOverlap(to_intersectWith)) {
return null;
}
IntBound highestMinBound = null;
try {
if(!to_intersectWith.hasMin()) {
highestMinBound = (!hasMin() ? null
: new IntBoundInclusive(getMinBound().get(), null));
} else if(!hasMin()) {
highestMinBound = (!to_intersectWith.hasMin() ? null
: new IntBoundInclusive(to_intersectWith.getMinBound().get(), null));
} else {
iasxIfDiffInclsivities("in", "intersect", getMinBound(),
to_intersectWith.getMinBound());
Inclusive incl = Inclusive.getForBoolean(getMinBound().isInclusive());
int tiwMinIncl = to_intersectWith.getMinGivenIncl();
int num = ((tiwMinIncl < getMinGivenIncl()) ? getMinGivenIncl() : tiwMinIncl);
if(incl.isNo()) {
//Both bounds exist, and they're both inclusive
num--;
}
highestMinBound = new IntBound(num, incl, null);
}
} catch(NullPointerException npx) {
throw new NullPointerException("to_intersectWith");
}
IntBound lowestMaxBound = null;
try {
if(!to_intersectWith.hasMax()) {
lowestMaxBound = (!hasMax() ? null
: new IntBoundInclusive(getMaxBound().get(), null));
} else if(!hasMax()) {
lowestMaxBound = (!to_intersectWith.hasMax() ? null
: new IntBoundInclusive(to_intersectWith.getMaxBound().get(), null));
} else {
iasxIfDiffInclsivities("ax", "intersect", getMaxBound(),
to_intersectWith.getMaxBound());
Inclusive incl = Inclusive.getForBoolean(getMaxBound().isInclusive());
int tiwMaxIncl = to_intersectWith.getMaxGivenIncl();
int num = ((getMaxGivenIncl() < tiwMaxIncl) ? getMaxGivenIncl() : tiwMaxIncl);
if(incl.isNo()) {
//Both bounds exist, and they're both inclusive
num++;
}
lowestMaxBound = new IntBound(num, incl, null);
}
} catch(NullPointerException npx) {
throw new NullPointerException("to_intersectWith");
}
return new IntInRange(highestMinBound, lowestMaxBound);
}
public NumberInRange<Integer> getMerged(NumberInRange<Integer> to_mergeWith,
OverlapRequired overlap_rqd) {
try {
if(overlap_rqd.isYes() && !doesOverlap(to_mergeWith)) {
throw new IllegalArgumentStateException("to_mergeWith < " +
to_mergeWith + " > does not overlap *this* < " + this + " >");
}
} catch(NullPointerException npx) {
CrashIfObject.nullOrReturnCause(overlap_rqd, "overlap_rqd", null, npx);
}
IntBound lowestMinBound = null;
try {
if(!to_mergeWith.hasMin() || !hasMin()) {
lowestMinBound = null;
} else {
iasxIfDiffInclsivities("in", "merge", getMinBound(),
to_mergeWith.getMinBound());
Inclusive incl = Inclusive.getForBoolean(getMinBound().isInclusive());
int tiwMinIncl = to_mergeWith.getMinGivenIncl();
int num = ((getMinGivenIncl() < tiwMinIncl) ? getMinGivenIncl() : tiwMinIncl);
if(incl.isNo()) {
//Both bounds exist, and they're both inclusive
num--;
}
lowestMinBound = new IntBound(num, incl, null);
}
} catch(NullPointerException npx) {
throw new NullPointerException("to_mergeWith");
}
IntBound highestMaxBound = null;
try {
if(!to_mergeWith.hasMax() || !hasMax()) {
highestMaxBound = null;
} else {
iasxIfDiffInclsivities("ax", "merge", getMaxBound(),
to_mergeWith.getMaxBound());
Inclusive incl = Inclusive.getForBoolean(getMaxBound().isInclusive());
int tiwMaxIncl = to_mergeWith.getMaxGivenIncl();
int num = ((tiwMaxIncl < getMaxGivenIncl()) ? getMaxGivenIncl() : tiwMaxIncl);
if(incl.isNo()) {
//Both bounds exist, and they're both inclusive
num++;
}
highestMaxBound = new IntBound(num, incl, null);
}
} catch(NullPointerException npx) {
throw new NullPointerException("to_mergeWith");
}
return new IntInRange(lowestMinBound, highestMaxBound);
}
private void iasxIfDiffInclsivities(String in_ax, String mrg_intr,
NumberBound<Integer> this_bound, NumberBound<Integer> that_bound) {
if(this_bound.isInclusive() != that_bound.isInclusive()) {
throw new IllegalArgumentStateException("getM" + in_ax +
"Bound().isInclusive() (" + this_bound.isInclusive() +
") is different than to_" + mrg_intr + "With.getM" + in_ax +
"Bound().isInclusive().");
}
}
//other...START
public boolean isValidInclusiveRange(Integer min, Integer max) {
try {
return ((min.compareTo(max) <= 0) && //min <= max
isIn(min) && isIn(max));
} catch(RuntimeException rx) {
Objects.requireNonNull(min, "min");
throw CrashIfObject.nullOrReturnCause(max, "max", null, rx);
}
}
public boolean isValidInclusiveRange(int min, int max) {
return isValidInclusiveRange((new Integer(min)), (new Integer(max)));
}
public boolean isGTOEMinGivenIncl(Integer num) {
return (getInclMinComparedTo(num) <= 0);
}
public boolean isLTOEMaxGivenIncl(Integer num) {
return (getInclMaxComparedTo(num) >= 0);
}
public Integer getInclMinComparedToOrIfNoMin(Integer I_toCompare, Integer I_ifNoMin) {
return (hasMin()
? getInclMinComparedTo(I_toCompare)
: I_ifNoMin);
}
public Integer getInclMaxComparedToOrIfNoMax(Integer I_toCompare, Integer I_ifNoMax) {
return (hasMax()
? getInclMaxComparedTo(I_toCompare)
: I_ifNoMax);
}
/**
* <p>If a range's min is greater than max, or either are outside of the
* bounds <i>as configured in this class</i>, crash.</p>
*
* <p>Equal to
* <br/> <code>{@link #crashIfBadRange(int, int, String, String) crashIfBadRange}(min, max, "min", "max")</code></p>
*/
public void crashIfBadRange(int min, int max) {
crashIfBadRange(min, max, "min", "max");
}
/**
* <p>If a range's min is greater than max, or either are outside of the
* bounds <i>as configured in this class</i>, crash.</p>
*
* <p>Equal to
* <br/> <code>IntInRange.{@link #crashIfBadRange(IntInRange, int, int, String, String, Object) crashIfBadRange}(this, min, max, min_name, max_name,
* <br/> {@link com.github.xbn.lang.AbstractExtraErrInfoable#getExtraErrInfo() getExtraErrInfo}()*)</code></p>
*/
public void crashIfBadRange(int min, int max, String min_name, String max_name) {
IntInRange.crashIfBadRange(this, min, max, min_name, max_name,
getExtraErrInfo());
}
public void crashIfBadValue(int num, String int_objName) {
crashIfBadIntObject(num, int_objName);
}
/**
* <p>If a standalone-int is invalid, crash.</p>
*
* <p>Equal to
* <br/> <code>IntInRange.{@link #crashIfBadIntObject(IntInRange, Integer, String) crashIfBadIntObject}(this, num, int_objName)</code></p>
*/
public void crashIfBadIntObject(Integer num, String int_objName) {
IntInRange.crashIfBadIntObject(this, num, int_objName);
}
/**
* <p>If an int (as contained by an object) <i>that is an element of a
* larger object</i> is invalid, crash.</p>
*
* <p>Equal to
* <br/> <code>{@link #crashIfBadIntElement(IntInRange, Integer, String, int) crashIfBadIntElement}(this, num, cntr_name, idx_inCntr)</code></p>
*/
public void crashIfBadIntElement(Integer num, String cntr_name, int idx_inCntr) {
IntInRange.crashIfBadIntElement(this, num, cntr_name, idx_inCntr);
}
protected RuleType getRuleTypeFromBoundsForLenIdx() {
return ((getMinBound().get() == 0 && getMaxBound() == null)
? (isInverted() ? RuleType.IMPOSSIBLE : RuleType.UNRESTRICTED)
: RuleType.RESTRICTED);
}
public boolean areFieldsEqual(NumberInRange<Integer> to_compareTo) {
try {
return (Objects.equals(to_compareTo.getMinBound(), getMinBound()) &&
Objects.equals(to_compareTo.getMaxBound(), getMaxBound()) &&
to_compareTo.isInverted() == isInverted());
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(to_compareTo, "to_compareTo", null, rx);
}
}
public boolean areFieldsEqual(IntInRange to_compareTo) {
return areFieldsEqual((NumberInRange<Integer>)to_compareTo);
}
//other...END
//static...START
/**
* <p>If the bounds are not valid, crash.</p>
*
* <p>If either {@code min} or {@code max} are {@code null}, this
* <i><b>exits</b></i>. Otherwise, if...<ul>
* <li>both <code>min.{@link NumberBound#isInclusive() isInclusive}()</code>
* and {@code max.isInclusive()} are {@code true}, then
* <code>min.{@link IntBoundInclusive#getInt() getInt}()</code> may
* not be greater than {@code max.getInt()}</li>
* <li>exactly one of {@code min.isInclusive()} or
* {@code max.isInclusive()} is {@code false}, then
* {@code min.getInt()} may not be greater than
* {@code (max.getInt() - 1)}</li>
* <li>both {@code min.isInclusive()} and {@code max.isInclusive()}
* are {@code false}, then {@code min.getInt()} may not be greater
* than {@code (max.getInt() - 2)}</li>
* <li>either (or both) {@code min} or {@code max} are {@code null},
* then (when non-{@code null}) there are no restrictions on
* {@code min.getInt()} (or {@code max.getInt()}).</li>
* </ul></p>
* @exception IllegalArgumentStateException If both bounds are non-{@code null}, and violated the above rules are violated.
* @see #crashIfBadBoundsForLength(IntBoundInclusive, IntBound, String, String, Object) crashIfBadBoundsForLength(ibi,nb,s,s,o)
* @see <code><!-- GENERIC PARAMETERS FAIL IN @link --><a href="NumberInRange.html#isIn(N)">isIn</a>(N)*</code>
*/
public static final void crashIfBadBounds(IntBound min, IntBound max,
String min_name, String max_name, Object xtra_errInfo) {
if(min == null || max == null) {
return;
}
int iAway = (min.isInclusive() ? 0 : 1) + (max.isInclusive() ? 0 : 1);
if(min.get() > (max.get() - iAway)) {
String sMid = ((min.isInclusive() && max.isInclusive()) ? "."
: " minus " + iAway + " (max.get()-" + iAway + "=" +
(max.get() - iAway) + ").");
String sMsg = "Both " + min_name + " and " + max_name +
" are non-null, but " + min_name + ".get() (" + min.get() +
") is greater than " + max_name + ".get() (" + max.get() + ")" +
sMid;
throw new IllegalArgumentStateException(getXMsg(sMsg,
XInfoAccumulator.getAddedOrNew(xtra_errInfo,
min_name + ".isInclusive()=" + min.isInclusive() + ", " + max_name +
".isInclusive()=" + max.isInclusive())));
}
}
/**
* <p>If the bounds needed by {@code LengthInRange} or {@code IndexInRange}
* are not valid, crash.</p>
*
* <p>In addition to the requirements of
* {@link #crashIfBadBounds(IntBound, IntBound, String, String, Object) crashIfBadBounds},
* the minimum bound must be non-{@code null} and
* greater-than-or-equal-to zero.</p>
*/
protected static final void crashIfBadBoundsForLength(IntBoundInclusive min,
IntBound max, String min_name, String max_name, Object xtra_errInfo) {
try {
if(min.getInt() < 0) {
throw new IllegalArgumentException("Min less than zero: " + min);
}
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(min, "min", null, rx);
}
if(!min.isInclusive()) {
throw new IllegalArgumentException("Min not inclusive: " + min);
}
crashIfBadBounds(min, max, min_name, max_name, xtra_errInfo);
}
/**
* <p>If a standalone int is invalid, crash. The only difference between
* this and {@link #crashIfBadIntElement(IntInRange, Integer, String, int) crashIfBadIntObject}{@code (iir,I,s,i)}
* is the format of the potential error message.</p>
* @param int_objName For example, "{@code aString.length}" or "{@code coll.size()}". <i>Should</i> not be {@code null} or empty.
* @exception IllegalArgumentException If
* <br/> <code>range.<!-- GENERIC PARAMETERS FAIL IN @link --><a href="NumberInRange.html#isIn(N)">isIn</a>(num)*</code>
* <br/>is {@code false}.
* @see #crashIfBadIntObject(Integer, String) crashIfBadIntObject(I,s)
*/
public static final void crashIfBadIntObject(IntInRange range, Integer num, String int_objName) {
boolean bValid = false;
try {
bValid = range.isIn(num);
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(range, "range", range.getExtraErrInfo(), rx);
}
if(!bValid) {
throw new IllegalArgumentException(
getXMsg(int_objName + " (" + num + ") is invalid according to the rules: " + range.getRules(), range.getExtraErrInfo()));
}
}
/**
* <p>If an int (as contained by an object) <i>that is an element of a
* larger object</i> is invalid, crash. The only difference between this
* and {@link #crashIfBadIntObject(IntInRange, Integer, String) crashIfBadIntObject}{@code (iir,I,s)}
* is the format of the potential error message.</p>
* @param cntr_name The name of the container (string, array,
* collection, etc.) in which the int is an element.
* @param idx_inCntr The element index at which the int resides.
* @exception IllegalArgumentException If
* <code>range.<!-- GENERIC PARAMETERS FAIL IN @link --><a href="NumberInRange.html#isIn(N)">isIn</a>(num)</code>
* is {@code false}.
* @see #crashIfBadIntElement(Integer, String, int) crashIfBadIntElement(I,s,i)
*/
public static final void crashIfBadIntElement(IntInRange range, Integer num, String cntr_name, int idx_inCntr) {
boolean bValid = false;
try {
bValid = range.isIn(num);
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(range, "range", range.getExtraErrInfo(), rx);
}
if(!bValid) {
throw new IllegalArgumentException(
getXMsg("Element " + idx_inCntr + " in " + cntr_name +
" (" + num + ") is invalid according to the rules: " + range.getRules(), range.getExtraErrInfo()));
}
}
/**
* <p>If a range's min is greater than max, or either are outside of the
* bounds <i>as configured in this class</i>, crash.</p>
* @param min Range-minimum to test.
* @param max Range-maximum to test.
* @param range The rules to test for. May not be {@code null}.
* @param min_name Descriptive name of {@code min}. <i>Should</i> not
* be {@code null} or empty.
* @param max_name Descriptive name of {@code max}
* @exception IllegalArgumentException If {@code min} is less than
* {@link #getMin() getMin}{@code ()}, or {@code max} is greater than
* {@link #getMax() getMax}{@code ()} (unless {@code getMax()} is
* {@code -1}, in which case there is no maximum restriction).
* @exception IllegalArgumentStateException If {@code min} is greater
* than {@code max}.
* @see #crashIfBadRange(int, int) crashIfBadRange(i,i)
* @see #crashIfBadRange(int, int, String, String) crashIfBadRange(i,i,s,s)
*/
public static final void crashIfBadRange(IntInRange range, int min, int max, String min_name, String max_name, Object xtra_errInfo) {
if(min < range.getMin()) {
throw new IllegalArgumentException(getXMsg(min_name + " (" + min + ") is less than range.getMin() (" + range.getMin() + ")", xtra_errInfo));
}
if(range.getMax() != -1 && max > range.getMax()) {
throw new IllegalArgumentException(getXMsg(max_name + " (" + max + ") is greater than range.getMax() (" + range.getMax() + ")", xtra_errInfo));
}
if(min > max) {
throw new IllegalArgumentStateException(getXMsg(min_name + " (" + min + ") is greater than " + max_name + " (" + max + ")", xtra_errInfo));
}
}
//static...END
}