/*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.text.padchop;
import com.github.xbn.io.SimpleDebuggable;
import com.github.xbn.io.Debuggable;
import java.io.IOException;
import org.apache.commons.lang3.StringEscapeUtils;
import static com.github.xbn.lang.XbnConstants.*;
import com.github.xbn.io.IOUtil;
import com.github.xbn.lang.ToStringAppendable;
import com.github.xbn.lang.CrashIfObject;
import com.github.xbn.lang.Copyable;
import com.github.xbn.lang.IllegalArgumentStateException;
import com.github.xbn.text.StringUtil;
import com.github.xbn.text.padchop.z.VzblPadChop_Fieldable;
/**
<p>Highly configurable trimming, unescaping, padding, and chopping of a string.</p>
{@.codelet.and.out com.github.xbn.examples.text.padchop.VzblPadChopXmpl%eliminateCommentBlocksAndPackageDecl()}
<A NAME="cfg"></a><h3>Builder Configuration: {@link com.github.xbn.text.padchop.z.VzblPadChop_Cfg VzblPadChop_Cfg}</h3>
<p><ul>
<li><b>Used by:</b> <code>xbn.list.lister.<a href="../../list/lister/LLCfgOverall.html#cfg">LLConfigOverall</a></code> and <code><a href="../../list/lister/LLCfgElement.html#cfg">LLCfgElement</a></code></li>
</ul></li>
<li><b>Sub configs:</b><ul>
<li><b><code><a href="ChopString.html#cfg">ChopString</a></code>:</b> {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgChop() cfgChop}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgChop(boolean) cfgChop}{@code (b)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgChop(int) cfgChop}{@code (b)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgChop(boolean, int) cfgChop}{@code (b,i)}<ul>
<li>{@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#chop(boolean) chop}{@code (b)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#chopFirst() chopFirst}{@code ()}</li>
</ul></li>
<li><b><code><a href="PadString.html#cfg">PadString</a></code>:</b> {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgPad() cfgPad}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgPad(boolean) cfgPad}{@code (b)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgPad(int) cfgPad}{@code (i)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgPad(boolean, int) cfgPad}{@code (b,i)}<ul>
<li>{@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#pad(boolean) pad}{@code (b)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#padFirst() padFirst}{@code ()}</li>
</ul></li>
</ul>
<li><b>Pad-chop goal length:</b> {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#goalLen(int) goalLen}{@code (i)}</li>
<li><b>EscapeAction:</b> {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#escape() escape}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#noEscape() noEscape}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#unescape() unescape}{@code ()}</li>
<li><b>Trim:</b> {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#trim() trim}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#trim(boolean, boolean) trim}{@code (b,b)}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#trimLeft() trimLeft}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#trimRight() trimRight}{@code ()}</li>
<li><b>Defaults:</b> {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#reset() reset}{@code ()}, {@link com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#reset(int) reset}{@code (i)}</li>
</ul></p>
**/
public class VzblPadChop extends SimpleDebuggable implements ToStringAppendable, Copyable, Debuggable {
private boolean bTrimR = false;
private boolean bTrimL = false;
private EscapeAction eesc = null;
private boolean bPad = false;
private boolean bChop = false;
private PadString ps = null;
private ChopString cs = null;
private boolean bPad1st = false;
//constructors...START
/**
<p>Create a new {@code VzblPadChop}.</p>
* @see #VzblPadChop(VzblPadChop) this(padchop)
*/
public VzblPadChop(DifferentGoalLengths gl_da, VzblPadChop_Fieldable fieldable) {
super(fieldable);
bTrimR = fieldable.doTrimRight();
bTrimL = fieldable.doTrimLeft();
eesc = fieldable.getEscapeAction();
bPad = fieldable.doPad();
ps = fieldable.getPad();
bChop = fieldable.doChop();
cs = fieldable.getChop();
bPad1st = fieldable.doPadFirst();
try {
if(eesc == null) {
throw new NullPointerException("fieldable.getEscapeAction()");
}
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(fieldable, "fieldable", null, rx);
}
/*
try {
if(bChop && cs.getGoalLen() == -1) {
throw new IllegalArgumentStateException("fieldable.doChop() is true, but fieldable.getChop().getGoalLen() is -1.");
}
} catch(RuntimeException rx) {
Objects.requireNonNull(cs, "fieldable.getChop()");
CrashIfObject.nullOrReturnCause(ps, "fieldable.getPad()", null, rx);
}
*/
int iNewPadGl = ps.getGoalLen(); //Will be new after the next if-block
int iNewChopGl = cs.getGoalLen();
if(iNewPadGl != iNewChopGl) {
if(gl_da.doCrash()) {
throw new IllegalArgumentStateException("fieldable.getPad().getGoalLen() (" + iNewPadGl + ") and fieldable.getChop().getGoalLen() (" + cs.getGoalLen() + ") are different, and gl_da.doCrash() is true.");
}
if(gl_da.doUsePad()) {
if(iNewPadGl == -1) {
throw new IllegalArgumentStateException("fieldable.getPad().getGoalLen() is -1, fieldable.getChop().getGoalLen() is " + cs.getGoalLen() + ", and gl_da.doUsePad() is true");
}
iNewChopGl = iNewPadGl;
}
if(gl_da.doUseChop()) {
if(ps.getGoalLen() == -1) {
throw new IllegalArgumentStateException("fieldable.getChop().getGoalLen() is -1, fieldable.getPad().getGoalLen() is " + ps.getGoalLen() + ", and gl_da.doUseChop() is true");
}
iNewPadGl = iNewChopGl;
}
if(gl_da.doUseNonNeg1()) {
if(iNewPadGl != -1) {
iNewChopGl = iNewPadGl;
} else {
iNewPadGl = iNewChopGl;
}
}
}
if(cs.getGoalLen() != iNewChopGl) {
cs = cs.getCopyNewGoalLen(iNewChopGl);
}
if(ps.getGoalLen() != iNewPadGl) {
ps = ps.getCopyNewGoalLen(iNewPadGl);
}
setDebug(getDebugApbl(), isDebugOn()); //See setDebugOn(apbl,b)
}
/**
<p>Create a new {@code VzblPadChop} as a duplicate of another.</p>
<p>This<ol>
</ol></p>
* @param to_copy May not be {@code null}.
* @see #getObjectCopy()
* @see #VzblPadChop(DifferentGoalLengths, VzblPadChop_Fieldable) this(idgl,vpc_cfn)
*/
public VzblPadChop(VzblPadChop to_copy) {
super(to_copy);
bTrimR = to_copy.doTrimRight();
bTrimL = to_copy.doTrimLeft();
eesc = to_copy.getEscapeAction();
bPad = to_copy.doPad();
ps = to_copy.getPad();
bChop = to_copy.doChop();
cs = to_copy.getChop();
bPad1st = to_copy.doPadFirst();
}
//constructors...END
//getters...START
/**
<p>Is the string trimmed on the left side?.</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#trim(boolean, boolean) VzblPadChop_Cfg#trim(b,b)
* @see #doTrimLeftRight()
* @see #doTrimRight()
* @see #getEscapeAction()
*/
public boolean doTrimLeft() {
return bTrimL;
}
/**
<p>Is the string trimmed on the right side?.</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#trim(boolean, boolean) VzblPadChop_Cfg#trim(b,b)
* @see #doTrimLeft()
*/
public boolean doTrimRight() {
return bTrimR;
}
/**
<p>Will the string be escaped, unescaped, or left alone?.</p>
* @see #doTrimLeft()
*/
public EscapeAction getEscapeAction() {
return eesc;
}
/**
<p>YYY</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgPad(boolean, int) VzblPadChop_Cfg#cfgPad(b,i)
* @see #getPad()
*/
public boolean doPad() {
return bPad;
}
/**
<p>YYY</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgChop(boolean, int) VzblPadChop_Cfg#cfgChop(b,i)
* @see #getPad()
*/
public boolean doChop() {
return bChop;
}
/**
<p>YYY</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgPad(boolean, int) VzblPadPad_Cfg#cfgPad(b,i)
* @see #doChop()
* @see #doPad()
* @see #doPadChop()
* @see #doPadFirst()
* @see #getChop()
*/
public PadString getPad() {
return ps;
}
/**
<p>YYY</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#cfgChop(boolean, int) VzblPadChop_Cfg#cfgChop(b,i)
* @see #getPad()
*/
public ChopString getChop() {
return cs;
}
/**
<p>When both padding and chopping are active, which occurs first?.</p>
* @see com.github.xbn.text.padchop.z.VzblPadChop_CfgForNeeder#padFirst() VzblPadChop_Cfg#padFirst()
* @see #getPad()
*/
public boolean doPadFirst() {
return bPad1st;
}
/**
<p>Is the string trimmed on both sides?.</p>
* @return <code>({@link #doTrimLeft() doTrimLeft}() && {@link #doTrimRight() doTrimRight}())</code>
* @see #getPad()
*/
public boolean doTrimLeftRight() {
return (doTrimLeft() && doTrimRight());
}
/**
<p>Is the string both padded and chopped?.</p>
* @return <code>({@link #doPad() doPad}() && {@link #doChop() doChop}())</code>
* @see #getPad()
*/
public boolean doPadChop() {
return (doPad() && doChop());
}
public boolean doesNothing() {
int iStps = ((doTrimLeft() || doTrimRight()) ? 1 : 0) +
((!getEscapeAction().isNothing()) ? 1 : 0) +
(doPad() ? 1 : 0) +
(doChop() ? 1 : 0);
return (iStps == 0);
}
public int getGoalLenFromPadChop() {
if(doPadChop()) {
return (doPadFirst()
? getPad().getGoalLen()
: getChop().getGoalLen());
}
if(doPad()) {
return getPad().getGoalLen();
}
if(doChop()) {
return getChop().getGoalLen();
}
return -1;
}
//getters...END
//other...START
public void setDebug(Appendable destination, boolean is_on) {
super.setDebug(destination, is_on);
if(getChop() != null) { //null during construction! (see bottom of constructor)
getChop().setDebug(destination, is_on);
}
}
public void setDebugOn(boolean is_on) {
super.setDebugOn(is_on);
if(getChop() != null) { //null during construction!
getChop().setDebugOn(is_on);
}
}
/**
<p>Alter the string as configured.</p>
* @return <code>{@link #get(int, Object) get}({@link #getGoalLenFromPadChop() getGoalLenFromPadChop}(), text)</code>
*/
public String get(Object text) {
return get(getGoalLenFromPadChop(), text);
}
/**
<p>Alter the string as configured, with a specific goal-length.</p>
* <p>Equal to
<br/> {@link #appendX(Appendable, int, Object) appendX}{@code ((new StringBuilder()), goal_len, text)}</p>
* @param text May not be {@code null}.
* @return {@code append((new StringBuilder()), goal_len, text).toString()}
*/
public String get(int goal_len, Object text) {
return append((new StringBuilder()), goal_len, text).toString();
}
public Appendable append(Appendable to_appendTo, Object text) {
try {
return appendX(to_appendTo, text);
} catch(IOException iox) {
throw new RuntimeException("append", iox);
}
}
/**
<p>YYY</p>
*/
public final Appendable append(Appendable to_appendTo, int goal_len, Object text) {
try {
return appendX(to_appendTo, goal_len, text);
} catch(IOException iox) {
throw new RuntimeException("append", iox);
}
}
/*
public Appendable appendln(Appendable to_appendTo, String prefix, Object text, String postfix) {
return appendlns(1, to_appendTo, prefix, text, postfix);
}
*/
public Appendable appendlns(int new_lineCount, Appendable to_appendTo, String prefix, Object text, String postfix) {
try {
to_appendTo.append(prefix);
appendX(to_appendTo, text).append(postfix);
IOUtil.appendNewLinesX(new_lineCount, to_appendTo);
return to_appendTo;
} catch(IOException iox) {
throw new RuntimeException("appendln(i,apbl,s,O,s)", iox);
}
}
public Appendable appendln(Appendable to_appendTo, int goal_len, String prefix, Object text, String postfix) {
return appendlns(1, to_appendTo, goal_len, prefix, text, postfix);
}
public Appendable appendlns(int new_lineCount, Appendable to_appendTo, int goal_len, String prefix, Object text, String postfix) {
try {
to_appendTo.append(prefix);
appendX(to_appendTo, goal_len, text).append(LINE_SEP);
IOUtil.appendNewLinesX(new_lineCount, to_appendTo);
return to_appendTo.append(postfix);
} catch(IOException iox) {
throw new RuntimeException("appendln(i,apbl,i,s,O,s)", iox);
}
}
/**
<p>Alter the string as configured.</p>
* @return <code>{@link #appendX(Appendable, int, Object) appendX}(to_appendTo, text, {@link #getGoalLenFromPadChop() getGoalLenFromPadChop}())</code>
*/
public Appendable appendX(Appendable to_appendTo, Object text) throws IOException {
return appendX(to_appendTo, getGoalLenFromPadChop(), text);
}
public final Appendable appendX(Appendable to_appendTo, int goal_len, Object text) throws IOException {
return VzblPadChop.appendX(this, to_appendTo, goal_len, text);
}
/**
<p>Format a string as configured.</p>
<p>This:<ol>
<li>If {@code text} is {@code null}, this returns {@code null}.</li>
<li>Otherwise, the string is<ol>
<li>Trimmed <code>({@link #doTrimLeftRight() doTrimLeftRight}())</code></li>
<li>Unescaped <code>(@code getConfig().{@link #getEscapeAction() getEscapeAction}())</code></li>
<li>Padded/chopped <code>(getConfig().{@link #doPad() doPad}(), {@link #doChop() doChop}())</code></li>
</ol></li>
</ol></p>
* @param vpc May not be {@code null}.
* @param to_appendTo May not be {@code null}.
* @param text The string to chop.
* @param goal_len The goal length to chop to. May not be less than one.
*/
public static final Appendable appendX(VzblPadChop vpc, Appendable to_appendTo, int goal_len, Object text) throws IOException {
if(text == null) {
text = "null";
}
String s = text.toString();
try {
if(vpc.isDebugOn()) {
vpc.getDebugAptr().appentln("<VPC> goal_len="+ goal_len + ", text:" + s.length());
}
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(vpc, "vpc", null, rx);
}
//System.out.println("vpc1 text=" + text + "");
if(vpc.doTrimLeftRight()) {
//System.out.println("vpc2");
s = s.trim();
} else if(vpc.doTrimLeft()) {
//System.out.println("vpc3");
s = StringUtil.ltrim(s);
} else if(vpc.doTrimRight()) {
//System.out.println("vpc4");
s = StringUtil.rtrim(s);
}
//System.out.println("vpc5");
if(vpc.getEscapeAction().isEscape()) {
s = StringEscapeUtils.escapeJava(s);
} else if(vpc.getEscapeAction().isUnescape()) {
s = StringEscapeUtils.unescapeJava(s);
}
if(vpc.doPadChop()) {
if(vpc.isDebugOn()) {
appendPadChoppedXWDbg(vpc, to_appendTo, goal_len, s);
} else {
if(vpc.doPadFirst()) {
vpc.getChop().appendChoppedX(to_appendTo, vpc.getPad().getPadded(goal_len, s));
} else {
vpc.getPad().appendPaddedX(to_appendTo, vpc.getChop().getChopped(goal_len, s));
}
}
return to_appendTo;
}
if(vpc.doPad()) {
vpc.getPad().appendPaddedX(to_appendTo, goal_len, s);
} else if(vpc.doChop()) {
vpc.getChop().appendChoppedX(to_appendTo, goal_len, s);
}
return to_appendTo;
}
private static final void appendPadChoppedXWDbg(VzblPadChop vpc, Appendable to_appendTo, int goal_len, String so_far) throws IOException {
if(vpc.doPadFirst()) {
so_far = vpc.getPad().getPadded(goal_len, so_far);
vpc.getDebugAptr().appentln("<VPC> pad-first: Padded: [" + so_far + "]");
so_far = vpc.getChop().getChopped(goal_len, so_far);
vpc.getDebugAptr().appentln("<VPC> pad-first: Chopped: [" + so_far + "]");
} else {
so_far = vpc.getChop().getChopped(goal_len, so_far);
vpc.getDebugAptr().appentln("<VPC> chop-first: Chopped: [" + so_far + "]");
so_far = vpc.getPad().getPadded(goal_len, so_far);
vpc.getDebugAptr().appentln("<VPC> chop-first: Padded: [" + so_far + "]");
}
to_appendTo.append(so_far);
}
public String toString() {
return appendToString(new StringBuilder()).toString();
}
public StringBuilder appendToString(StringBuilder to_appendTo) {
try {
to_appendTo.append(this.getClass().getName()).append(": ");
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(to_appendTo, "to_appendTo", null, rx);
}
if(doesNothing()) {
return to_appendTo.append("does nothing");
}
int iStp = 0;
if(doTrimLeft()) {
if(doTrimRight()) {
to_appendTo.append(++iStp).append("=trim");
} else {
to_appendTo.append(++iStp).append("=trim-left");
}
} else if(doTrimRight()) {
to_appendTo.append(++iStp).append("=trim-right");
}
boolean b1StepSoFar = (iStp > 0);
if(!getEscapeAction().isNothing()) {
StringUtil.appendIfTrueFalse(to_appendTo, ", ", b1StepSoFar, null);
to_appendTo.append(++iStp).append("=").append(getEscapeAction().name().toLowerCase());
b1StepSoFar = true;
}
if(doPad()) {
StringUtil.appendIfTrueFalse(to_appendTo, ", ", b1StepSoFar, null);
if(doChop()) {
if(doPadFirst()) {
appendPadChopTS(to_appendTo, ++iStp, getPad()).append(", ");
appendPadChopTS(to_appendTo, ++iStp, getChop());
} else {
appendPadChopTS(to_appendTo, ++iStp, getChop()).append(", ");
appendPadChopTS(to_appendTo, ++iStp, getPad());
}
}
} else if(doChop()) {
StringUtil.appendIfTrueFalse(to_appendTo, ", ", b1StepSoFar, null);
appendPadChopTS(to_appendTo, ++iStp, getChop());
}
return to_appendTo;
}
private static final StringBuilder appendPadChopTS(StringBuilder to_appendTo, int step_num, PadChopBase pc_base) {
return to_appendTo.append(step_num).append("=<").append(pc_base).append(">");
}
/**
<p>Duplicate this {@code VzblPadChop}</p>
* @return <code>(new {@link #VzblPadChop(VzblPadChop) VzblPadChop}(this))</code>
*/
public VzblPadChop getObjectCopy() {
return (new VzblPadChop(this));
}
//other...END
}