/*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.experimental;
import static com.github.xbn.lang.CrashIfBase.*;
/**
<p>Defines the legal positioning within an ordered collection--where an element <i>could</i> be.</p>
<h3>Positions in an ordered collection (such as a List or sequence)</h3>
<p><ul>
<li><b>First-not-last:</b> <i>Could</i> be the first element in an ordered collection, but not the last.</li>
<li><b>Middle:</b> Could be neither the first nor the last.</li>
<li><b>Last-not-first:</b> Could be the last element, but not the first.</li>
<li><b>Only:</b> Could be the first-and-last element in the sequence.</li>
</ul></p>
<p>For example:</p>
<pre> new LegalFMLfl(true, false, true, true)</pre>
<p><ul>
<li><b>Parameter 1 (true):</b> This object could be the first-but not the last</li>
<li><b>Parameter 2 (false):</b> If it were somewhere in the middle (neither the first or last elemnt), that would be bad.</li>
<li><b>Parameter 3 (true):</b> It could be the last element.</li>
<li><b>Parameter 4 (true):</b> It could be the only element.</li>
</ul></p>
<h3>Different flavors of the same thing</h3>
<p>Internally, a {@code LegalFMLfl} stores four booleans: {@link #isFirstOnly() isFirstOnly}{@code ()}, {@link #isMiddle() isMiddle}{@code ()}, {@link #isLastOnly() isLastOnly}{@code ()} and {@link #isFirstLast() isFirstLast}{@code ()}.</p>
<p>A string of boolean-initials could be provided instead, as a much-shorter alternative.</p>
<pre> //true, false, true, true
LegalFMLfl lfmlfl = new LegalFMLfl("tftt");</pre>
<h4>Beginability and endability: {@code com.github.xbn.experimental.MustOptOrForb}</h4>
<p><code>An {@link com.github.xbn.experimental.MustOptOrForb MustOptOrForb} object</code> may also be used to define legal positinos in a more relaxed, zen-like state of <b>beginability</b> and <b>endability</b>. For example:</p>
<pre> LegalFMLfl lfmlfl = new LegalFMLfl(MustOptOrForb.OPTIONAL, MustOptOrForb.MUST)
System.out.println(lfmlfl.isFirstOnly());
System.out.println(lfmlfl.isMiddle());
System.out.println(lfmlfl.isLastOnly());
System.out.println(lfmlfl.isFirstLast());</pre>
<p>Output:</p>
<pre> true
false
true
true</pre>
<p><ul>
<li>If it is beginable.OPTIONAL, then it <i>could</i> be first-but-not-last.</li>
<li>Since it is endable-MUST, it could <i>not</i> be middle (neither first-nor-last).</li>
<li>It could be last-only--not beginable, but still endable.</li>
<li>It could be first-last, since beginable is OPTIONAL.</li>
</ul></p>
<h3>{@code com.github.xbn.txol.SubSnip}</h3>
<p>This class was originally created for use by {@code <a href="../txol/SubSnip.html">SubSnip</a>}.</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 LegalFMLfl {
private String sFMLflBoolInits = null;
private MustOptOrForb emofBegin = null;
private MustOptOrForb emofEnd = null;
private boolean bFirstOnly = false;
private boolean bMiddle = false;
private boolean bLastOnly = false;
private boolean bFirstLast = false;
/**
<p>Create a new LegalFMLfl.</p>
* <p>Equal to
<br/> {@code this(true, true, true, true)}</p>
*/
public LegalFMLfl() {
this(true, true, true, true);
}
/**
<p>Create a new LegalFMLfl.</p>
* @param is_1stOnlyOk Could it be first-but-not-last? Get with {@link #isFirstOnly() isFirstOnly}{@code ()}.
* @param is_middleOk Could it be middle (not first or last)? Get with {@link #isMiddle() isMiddle}{@code ()}.
* @param is_lastOnlyOk Could it be last-but-not-first? Get with {@link #isLastOnly() isLastOnly}{@code ()}.
* @param is_1stLastOkay Could it be both first and last? Get with {@link #isFirstLast() isFirstLast}{@code ()}.
*/
public LegalFMLfl(boolean is_1stOnlyOk, boolean is_middleOk, boolean is_lastOnlyOk, boolean is_1stLastOkay) {
bFirstOnly = is_1stOnlyOk;
bMiddle = is_middleOk;
bLastOnly = is_lastOnlyOk;
bFirstLast = is_1stLastOkay;
}
/**
<p>Create a new LegalFMLfl with a boolean-initial string.</p>
* @param initial_strLengthFourTtFf Four boolean initials, as described at the top of this class. Must be non-null, and contain exactly four characters, where each is either {@code '<b>t</b>'} or {@code 'T'}, or {@code 'f'} or {@code 'F'}. Get with {@link #getFMLflBoolInits() getFMLflBoolInits}{@code ()}.
*/
public LegalFMLfl(String initial_strLengthFourTtFf) {
if(initial_strLengthFourTtFf == null) {
throw new NullPointerException("initial_strLengthFourTtFf");
}
if(initial_strLengthFourTtFf.length() != 4) {
throwBSX(initial_strLengthFourTtFf);
}
sFMLflBoolInits = initial_strLengthFourTtFf;
bFirstOnly = getBoolean(0, initial_strLengthFourTtFf);
bMiddle = getBoolean(1, initial_strLengthFourTtFf);
bLastOnly = getBoolean(2, initial_strLengthFourTtFf);
bFirstLast = getBoolean(3, initial_strLengthFourTtFf);
}
/**
<p>Create a new LegalFMLfl with beginability. See the top of this class for details.</p>
* @param beginability Beginability. May not be {@code null}.
* @param endability Endability. May not be {@code null}.
*/
public LegalFMLfl(MustOptOrForb beginability, MustOptOrForb endability) {
if(beginability == null) {
throw new NullPointerException("beginability");
}
if(endability == null) {
throw new NullPointerException("endability");
}
bFirstOnly = !beginability.isForbidden();
bMiddle = (!beginability.isMust() && !endability.isMust());
bLastOnly = !endability.isForbidden();
bFirstLast = (bFirstOnly && bLastOnly);
}
/**
<p>Is first-but-not-last a legal position?.</p>
* @return <b>{@code is_1stOnlyOk}</b>, as provided to the constructor.
*/
public boolean isFirstOnly() {
return bFirstOnly;
}
/**
<p>Is neither-first-nor-last a legal position?.</p>
* @return <b>{@code is_middleOk}</b>, as provided to the constructor.
*/
public boolean isMiddle() {
return bMiddle;
}
/**
<p>Is last-but-not-first a legal position?.</p>
* @return <b>{@code is_lastOnlyOk}</b>, as provided to the constructor.
*/
public boolean isLastOnly() {
return bLastOnly;
}
/**
<p>Is first-and-last a legal position?.</p>
* @return <b>{@code is_1stLastOkay}</b>, as provided to the constructor.
*/
public boolean isFirstLast() {
return bFirstLast;
}
/**
<p>Does the actual position conform to the legal positions?.</p>
* @return {@code isValidActualLocation(is_first, is_last, true, null)}
*/
public boolean isValidActualLocation(boolean is_first, boolean is_last) {
return isValidActualLocation(is_first, is_last, true, null);
}
/**
<p>Does the actual position conform to the legal positions?.</p>
* @param is_actuallyFirst Is the element actually first in the sequence?
* @param is_actuallyEnd Is the element actually last in the sequence?
* @return <b>{@code false}</b> If <ul>
<li>{@code is_actuallyFirst} is true, but {@link #isBeginable() beginability} is not legal, or</li>
<li>{@code is_actuallyEnd} is true, but {@link #isEndable() endability} is not legal.</li>
</ul>
<br/><b>{@code true}</b> If otherwise.
*/
public boolean isValidActualLocation(boolean is_actuallyFirst, boolean is_actuallyEnd, boolean do_returnIfFalse, Object xtra_errInfo) {
if((is_actuallyFirst && !isBeginable()) ||
(is_actuallyEnd && !isEndable())) {
if(do_returnIfFalse) {
return false;
}
if((is_actuallyFirst && !isBeginable()) ||
(is_actuallyEnd && !isEndable())) {
String s = "is_actuallyFirst=" + is_actuallyFirst + ", isBeginable()=" + isBeginable() + ", is_actuallyEnd=" + is_actuallyEnd + ", isEndable()=" + isEndable();
throw new IllegalPositionException(getXMsg(s, xtra_errInfo));
}
}
return true;
}
/**
<p>Get an enum representing beginability.</p>
* @return {@code MustOptOrForb.MUST}, {@code MustOptOrForb.OPTIONAL} or {@code MustOptOrForb.FORBIDDEN}, as described at the top of this class.
* @see #getEndability()
* @see #isBeginable()
* @see #isBeginableMust()
* @see #isBeginableOptional()
* @see #isBeginableForbidden()
* @see #isBeginableMust()
*/
public final MustOptOrForb getBeginability() {
if(emofBegin == null) {
if(bFirstOnly || bFirstLast) {
emofBegin = (bMiddle
? MustOptOrForb.OPTIONAL
: MustOptOrForb.MUST);
} else {
emofBegin = MustOptOrForb.FORBIDDEN;
}
}
return emofBegin;
}
/**
<p>Get an enum representing endability.</p>
* @return {@code MustOptOrForb.MUST}, {@code MustOptOrForb.OPTIONAL} or {@code MustOptOrForb.FORBIDDEN}, as described at the top of this class.
* @see #getBeginability()
* @see #isBeginable()
* @see #isEndableMust()
* @see #isEndableOptional()
* @see #isEndableForbidden()
*/
public final MustOptOrForb getEndability() {
if(emofEnd == null) {
if(bLastOnly || bFirstLast) {
emofEnd = (bMiddle
? MustOptOrForb.OPTIONAL
: MustOptOrForb.MUST);
} else {
emofEnd = MustOptOrForb.FORBIDDEN;
}
}
return emofEnd;
}
/**
<p>Can this element be beginable?.</p>
* @return <code>(!{@link #isBeginableForbidden() isBeginableForbidden}())</code>
*/
public final boolean isBeginable() {
return (!isBeginableForbidden());
}
/**
<p>Is beginability set to MUST?.</p>
* @return <code>{@link #getBeginability}{@code ()}.{@link MustOptOrForb#isMust() isMust}()</code>
*/
public final boolean isBeginableMust() {
return getBeginability().isMust();
}
/**
<p>Is beginability set to OPTIONAL?.</p>
* @return <code>{@link #getBeginability}{@code ()}.{@link MustOptOrForb#isOptional() isOptional}()</code>
*/
public final boolean isBeginableOptional() {
return getBeginability().isOptional();
}
/**
<p>Is beginability set to FORBIDDEN?.</p>
* @return <code>{@link #getBeginability}{@code ()}.{@link MustOptOrForb#isForbidden() isForbidden}()</code>
*/
public final boolean isBeginableForbidden() {
return getBeginability().isForbidden();
}
/**
<p>Can this element be endable?.</p>
* @return <code>(!{@link #isEndableForbidden() isEndableForbidden}())</code>
*/
public final boolean isEndable() {
return (!isEndableForbidden());
}
/**
<p>Is endability set to MUST?.</p>
* @return <code>{@link #getEndability}{@code ()}.{@link MustOptOrForb#isMust() isMust}()</code>
*/
public final boolean isEndableMust() {
return getEndability().isMust();
}
/**
<p>Is endability set to OPTIONAL?.</p>
* @return <code>{@link #getEndability}{@code ()}.{@link MustOptOrForb#isOptional() isOptional}()</code>
*/
public final boolean isEndableOptional() {
return getEndability().isOptional();
}
/**
<p>Is endability set to FORBIDDEN?.</p>
* @return <code>{@link #getEndability}{@code ()}.{@link MustOptOrForb#isForbidden() isForbidden}()</code>
*/
public final boolean isEndableForbidden() {
return getEndability().isForbidden();
}
/**
<p>Is it legal for another element to go before this one?.</p>
* @return <code>(!}{@link #isBeginableMust() isBeginableMust}())</code>
*/
public final boolean isPrecedable() {
return (!isBeginableMust());
}
/**
<p>Is it legal for another element to be after this one?.</p>
* @return <code>(!}{@link #isEndableMust() isEndableMust}())</code>
*/
public final boolean isFollowable() {
return (!isEndableMust());
}
/**
<p>Get a long representation of this class. Each position, if-and-only if true, is fully spelled out. For example:</p>
<p>{@code FIRST-ONLY, FIRST-LAST}</p>
*/
public final String getDiagnostics() {
StringBuilder sb = new StringBuilder();
if(isFirstOnly()) {
sb.append("FIRST-ONLY");
}
if(isMiddle()) {
appCma(sb);
sb.append("MIDDLE");
}
if(isLastOnly()) {
appCma(sb);
sb.append("LAST-ONLY");
}
if(isFirstLast()) {
appCma(sb);
sb.append("FIRST-LAST");
}
return sb.toString();
}
private static void appCma(StringBuilder to_appendTo) {
if(to_appendTo.length() > 0) {
to_appendTo.append(", ");
}
}
/**
<p>Get the boolean-initial string for this {@code LegalFMLfl}.</p>
* @return A string in the format as described at the top of this class.
* @see #getFMLflBoolInits(boolean, boolean, boolean, boolean) getFMLflBoolInits(b,b,b,b)
* @see #getBoolean(int, String) getBoolean(i,s)
*/
public String getFMLflBoolInits() {
if(sFMLflBoolInits == null) {
sFMLflBoolInits = getFMLflBoolInits(isFirstOnly(), isMiddle(), isLastOnly(), isFirstLast());
}
return sFMLflBoolInits;
}
/**
<p>Utility function to create a boolean initail string, usable in the {@code LegalLFMfl} constructor.</p>
* @return A string in the format as described at the top of this class, representing these parameter-values, <i>not</i> the internally-held booleans.
* @see #getFMLflBoolInits()
* @see #getBoolean(int, String) getBoolean(i,s)
*/
public static String getFMLflBoolInits(boolean is_1stOnlyOk, boolean is_middleOk, boolean is_lastOnlyOk, boolean is_1stLastOkay) {
return (is_1stOnlyOk ? "T" : "F") +
(is_middleOk ? "T" : "F") +
(is_lastOnlyOk ? "T" : "F") +
(is_1stLastOkay ? "T" : "F");
}
/**
<p>Utility function to get a particular boolean value from a boolean-initial string.</p>
* @param index The char-index of the boolean you want, within {@code initial_strLengthFourTtFf}.
* @param initial_strLengthFourTtFf The boolean-initial string, as discussed at the top of this class.
*/
public static final boolean getBoolean(int index, String initial_strLengthFourTtFf) {
char c = Character.toUpperCase(initial_strLengthFourTtFf.charAt(index));
if(c == 'T') {
return true;
}
if(c == 'F') {
return false;
}
throwBSX(initial_strLengthFourTtFf);
return false;
}
private static String throwBSX(String initial_strLengthFourTtFf) {
throw new IllegalArgumentException("initial_strLengthFourTtFf (currently '" + initial_strLengthFourTtFf + "') must be exactly four characters, and each must be a t, T, f or F.");
}
/**
* @return <b>{@link #getFMLflBoolInits() getFMLflBoolInits}{@code ()}</b>
*/
public final String toString() {
return getFMLflBoolInits();
}
}