diff --git a/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomInt.java b/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomInt.java index 10d11de0..8aca277f 100644 --- a/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomInt.java +++ b/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomInt.java @@ -42,102 +42,29 @@ import static com.helger.jcodemodel.util.JCHashCodeGenerator.getHashCode; -import java.util.function.IntFunction; - import org.jspecify.annotations.NonNull; import com.helger.base.equals.EqualsHelper; +import com.helger.jcodemodel.literals.AIntegerRepresented; +import com.helger.jcodemodel.literals.IntegerRepresentation; /** * A special atom for int values */ -public class JAtomInt implements IJExpression +public class JAtomInt extends AIntegerRepresented implements IJExpression { - /// @see https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.10.1 - public static enum Representation - { - BINARY ("0b", Integer::toBinaryString), - DECIMAL ("", Integer::toString), - HEX ("0x", Integer::toHexString), - OCTAL ("0", Integer::toOctalString); - - @NonNull - final IntFunction representer; - - @NonNull - final String prefix; - - Representation (String prefix, IntFunction representer) - { - this.prefix = prefix; - this.representer = representer; - } - - public String represent (int i, int every, int sepSize) - { - boolean neg = i < 0; - i = neg ? -i : i; - StringBuilder sb = new StringBuilder (); - if (neg) - sb.append ('-'); - sb.append (prefix); - addSep (representer.apply (i), every, sepSize, sb); - return sb.toString (); - } - - /// @param source unsigned non-prefixed representation , eg a5 for -0xa5 . - static void addSep(@NonNull String source, int every, int sepSize, StringBuilder sb) { - if (every < 1 || every >= source.length () || sepSize < 1) - { - sb.append (source); - return; - } - String sep = "_".repeat (sepSize); - for (int start = 0, end = source.length () % every; end <= source.length (); start = end, end += every) - { - if (start != 0) - sb.append (sep); - sb.append (source.substring (start, end)); - } - } - } - private final int m_nValue; - @NonNull - private Representation representation = Representation.DECIMAL; - protected JAtomInt (final int nWhat) { m_nValue = nWhat; } - public JAtomInt representation (Representation representation) - { - if (representation != null) - this.representation = representation; - return this; - } - - public JAtomInt binary () - { - return representation (Representation.BINARY); - } - - public JAtomInt decimal () - { - return representation (Representation.DECIMAL); - } - - public JAtomInt hex () + protected JAtomInt (final int nWhat, IntegerRepresentation representation) { - return representation (Representation.HEX); - } - - public JAtomInt octal () - { - return representation (Representation.OCTAL); + m_nValue = nWhat; + representation (representation); } public int what () @@ -145,37 +72,9 @@ public int what () return m_nValue; } - /// how many underscores per separation - private int separatorSize = 1; - - public int separatorSize () - { - return separatorSize; - } - - public JAtomInt separatorSize (int size) - { - this.separatorSize = size; - return this; - } - - /// how many underscores per separation - private int separateEvery = 0; - - public int separateEvery () - { - return separateEvery; - } - - public JAtomInt separateEvery (int every) - { - this.separateEvery = every; - return this; - } - public void generate (@NonNull final IJFormatter f) { - f.print (representation.represent (m_nValue, separateEvery, separatorSize)); + f.print (representation.format (m_nValue)); } @Override diff --git a/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomLong.java b/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomLong.java index e1f654fc..6b0e8978 100644 --- a/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomLong.java +++ b/jcodemodel/src/main/java/com/helger/jcodemodel/JAtomLong.java @@ -45,13 +45,14 @@ import org.jspecify.annotations.NonNull; import com.helger.base.equals.EqualsHelper; +import com.helger.jcodemodel.literals.AIntegerRepresented; +import com.helger.jcodemodel.literals.IntegerRepresentation; /** * A special atom for long values */ -public class JAtomLong implements IJExpression +public class JAtomLong extends AIntegerRepresented implements IJExpression { - public static final String SUFFIX_LONG = "L"; private final long m_nValue; @@ -60,6 +61,12 @@ protected JAtomLong (final long nWhat) m_nValue = nWhat; } + public JAtomLong (final long nWhat, IntegerRepresentation representation) + { + m_nValue = nWhat; + representation (representation); + } + public long what () { return m_nValue; @@ -67,7 +74,7 @@ public long what () public void generate (@NonNull final IJFormatter f) { - f.print (Long.toString (m_nValue) + SUFFIX_LONG); + f.print (representation.format (m_nValue)); } @Override diff --git a/jcodemodel/src/main/java/com/helger/jcodemodel/literals/AIntegerRepresented.java b/jcodemodel/src/main/java/com/helger/jcodemodel/literals/AIntegerRepresented.java new file mode 100644 index 00000000..8f0d5cb3 --- /dev/null +++ b/jcodemodel/src/main/java/com/helger/jcodemodel/literals/AIntegerRepresented.java @@ -0,0 +1,108 @@ +package com.helger.jcodemodel.literals; + +import org.jspecify.annotations.NonNull; + +/// Something that has an IntegerRepresentation to update, in practice only JAtomInt and JAtomLong +/// +/// Its abstract because it's just a tooling class to extend. +/// +/// @param T must be declaring class, eg `class A extends AIntegerRepresented` +public abstract class AIntegerRepresented > +{ + + @NonNull + protected IntegerRepresentation representation = IntegerRepresentation.DEFAULT; + + @SuppressWarnings ("unchecked") + protected T self () + { + return (T) this; + } + + @NonNull + public IntegerRepresentation representation () + { + return representation; + } + + /// change the internal representation to the provided one + /// + /// @return this + /// @param representation if null, nothing changes. + public @NonNull T representation (IntegerRepresentation representation) + { + if (representation != null) + this.representation = representation; + return self (); + } + + /// change the internal representation to show positive sign + /// + /// @return this + public @NonNull T positiveSign (boolean positiveSign) + { + return representation (representation.positiveSign (positiveSign)); + } + + /// change the internal representation to use binary base + /// + /// @return this + public @NonNull T binary () + { + return representation (representation.base (EIntegerBase.BINARY)); + } + + /// change the internal representation to use decimal base + /// + /// @return this + public @NonNull T decimal () + { + return representation (representation.base (EIntegerBase.DECIMAL)); + } + + /// change the internal representation to use hexadecimal base + /// + /// @return this + public @NonNull T hexadecimal () + { + return representation (representation.base (EIntegerBase.HEXADECIMAL)); + } + + /// change the internal representation to use octal base + /// + /// @return this + public @NonNull T octal () + { + return representation (representation.base (EIntegerBase.OCTAL)); + } + + /// change the internal representation to use a fixed separator size (the number of character + /// BETWEEN + /// each separated group), used only when **NO** separator format is provided + /// + /// @return this + public @NonNull T separatorSize (int size) + { + return representation (representation.separatorSize (size)); + } + + /// change the internal representation to use a fixed separator distance (the maximum number of + /// character IN + /// a separated group), used only when **NO** separator format is provided + /// + /// @return this + public @NonNull T separateEvery (int every) + { + return representation (representation.separateEvery (every)); + } + + /// change the internal representation to use a padding value. The padding is not used for decimal + /// base, since leading "0" makes an octal. + /// + /// @return this + public @NonNull T padding (int padding) + { + return representation (representation.padding (padding)); + } + +} diff --git a/jcodemodel/src/main/java/com/helger/jcodemodel/literals/EIntegerBase.java b/jcodemodel/src/main/java/com/helger/jcodemodel/literals/EIntegerBase.java new file mode 100644 index 00000000..666f1743 --- /dev/null +++ b/jcodemodel/src/main/java/com/helger/jcodemodel/literals/EIntegerBase.java @@ -0,0 +1,228 @@ +package com.helger.jcodemodel.literals; + +import java.util.Locale; +import java.util.function.IntFunction; +import java.util.function.LongFunction; + +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +/// base to represent an int/long in. +/// +/// Those also contain the way to apply parameters when representing, in the corresponding represent methods +/// +/// @see https://docs.oracle.com/javase/specs/jls/se17/html/jls-3.html#jls-3.10.1 +public enum EIntegerBase +{ + BINARY ("0b", Integer::toBinaryString, Long::toBinaryString, false, true, false), + DECIMAL ("", Integer::toString, Long::toString, false, false, false), + HEXADECIMAL ("0x", Integer::toHexString, Long::toHexString, true, true, false), + OCTAL ("0", Integer::toOctalString, Long::toOctalString, false, true, true); + + @NonNull + final IntFunction intFormat; + + @NonNull + final LongFunction longFormat; + + /// the lower-case base prefix. Only hex and bin have prefix case diff. + @NonNull + final String prefixLowerCased; + + /// the upper-case base prefix. Only hex and bin have prefix case diff. + @NonNull + final String prefixUpperCased; + + /// when true, the base allows padding. Only decimal does not allow padding, as a non-single + /// leading 0 digit means base octal. + final boolean enablePadding; + + /// when true (only hex), this base can produce different uppercase and lowercase body. + final boolean enableBodyUpper; + + /// when true, the separator format can produce leading separators in the body ; when false, the + /// body will always start with a base char. + final boolean allowBodyLeadingSep; + + EIntegerBase (String prefix, + IntFunction intFormat, + LongFunction longFormat, + boolean enableBodyUpper, + boolean enablePadding, + boolean allowBodyLeadingSep) + { + this.prefixLowerCased = prefix.toLowerCase (Locale.ROOT); + this.prefixUpperCased = prefix.toUpperCase (Locale.ROOT); + this.intFormat = intFormat; + this.longFormat = longFormat; + this.enableBodyUpper = enableBodyUpper; + this.enablePadding = enablePadding; + this.allowBodyLeadingSep = allowBodyLeadingSep; + } + + /// @return sb + public StringBuilder represent (int i, + StringBuilder sb, + IntegerRepresentation f) + { + boolean neg = i < 0; + int posI = neg ? -i : i; + if (neg) + sb.append ('-'); + else + if (f.positiveSign ()) + sb.append ('+'); + sb.append (f.prefixUpper () ? prefixUpperCased : prefixLowerCased); + addSep (padBody (caseBody (intFormat.apply (posI), f.bodyUpper ()), f.padding ()), + f.separateFormat (), + allowBodyLeadingSep, + f.separateEvery (), + f.separatorSize (), + sb); + return sb; + } + + /// @return sb + public StringBuilder represent (long l, + StringBuilder sb, + IntegerRepresentation f) + { + boolean neg = l < 0; + long posL = neg ? -l : l; + if (neg) + sb.append ('-'); + else + if (f.positiveSign ()) + sb.append ('+'); + sb.append (f.prefixUpper () ? prefixUpperCased : prefixLowerCased); + addSep (padBody (caseBody (longFormat.apply (posL), f.bodyUpper ()), f.padding ()), + f.separateFormat (), + allowBodyLeadingSep, + f.separateEvery (), + f.separatorSize (), + sb); + sb.append (f.suffixUpper () ? 'L' : 'l'); + return sb; + } + + /// if the base differentiates upper and lower body, put it in the corresponding case. + protected @NonNull String caseBody (@NonNull String body, boolean upper) + { + if (enableBodyUpper) + return upper ? body.toUpperCase (Locale.ROOT) : body.toLowerCase (Locale.ROOT); + return body; + } + + private static final char PAD_CHAR = '0'; + private static final String PAD_STRING = String.valueOf (PAD_CHAR); + + /// if the base is padding enabled (so not decimal), prefix the body to match given length + protected @NonNull String padBody (@NonNull String body, int qtty) + { + if (!enablePadding || qtty <= body.length ()) + return body; + return PAD_STRING.repeat (qtty - body.length ()) + body; + } + + private static final char SEP_CHAR = '_'; + private static final String SEP_STRING = String.valueOf (SEP_CHAR); + + /// Append a source with inserted separators into a stringbuilder. + /// + /// + /// ## Separator format + /// + /// The separator format is applied when non null ; otherwise, the sepEvery and sepSize are used. + /// + /// Each '_' in it specifies before which char (from the end) and in which quantity to insert + /// separator ( '_' ) ; other chars mean to copy from source. Last char is always assumed to be + /// non-separator, as terminating sep is not allowed in the body. + /// + /// For example, a format "c__l" means to insert 2 underscore before the last char, denoted with + /// 'l'. In that example, the 'c' character is useless so this is functionally the same as "__l", + /// or "cc__a". + /// + /// The format "__" means to insert a single sep before the last char. This is because the last + /// format char is always assumed to be non-sep ; so this is functionally the same as "_X" + /// + /// ## Separate every, size + /// + /// When the format is null and both sepEvery and sepSize are >0 , series of *sepSize* separators + /// are inserted every *sepEvery* character of source, starting from the end. + /// + /// @param source unsigned non-prefixed body representation , eg a5 for -0xa5L . If empty, nothing + /// is done (should never be called) + /// @param sepFormat separator format. + /// @param allowLeadingSep when true, allow to insert separator before the first source char. When + /// false, the first added char should be the one in source. + static void addSep (@NonNull String source, + @Nullable String sepFormat, + boolean allowLeadingSep, + int sepEvery, + int sepSize, + @NonNull StringBuilder sb) + { + if (source.isEmpty ()) + return; + if (sepFormat != null) + { + if (sepFormat.indexOf (SEP_CHAR) == -1) + { + sb.append (source); + return; + } + else + { + // since we use separators, we start from the last chars, so we reverse the separator format + // and build the reversed formatted representation. + StringBuilder reversed = new StringBuilder (); + // body must always end with non-sep, so here assume last format is non-sep + reversed.append (source.charAt (source.length () - 1)); + for (int formatIndex = sepFormat.length () - 2, sourceIndex = source.length () - 2; formatIndex >= 0 || + sourceIndex >= 0; formatIndex--) + { + if (formatIndex < 0) + { + for (int i = sourceIndex; i >= 0; i--) + reversed.append (source.charAt (i)); + break; + } + else + { + if (sepFormat.charAt (formatIndex) == SEP_CHAR) + if (allowLeadingSep || sourceIndex >= 0) + reversed.append (SEP_CHAR); + else + break; + else + if (sourceIndex < 0) + { + break; + } + else + { + reversed.append (source.charAt (sourceIndex)); + sourceIndex--; + } + } + } + sb.append (reversed.reverse ()); + } + } + else + { + if (sepEvery < 1 || sepEvery >= source.length () || sepSize < 1) + { + sb.append (source); + return; + } + String sep = SEP_STRING.repeat (sepSize); + for (int start = 0, end = source.length () % sepEvery; end <= source.length (); start = end, end += sepEvery) + { + if (start != 0) + sb.append (sep); + sb.append (source.substring (start, end)); + } + } + } +} diff --git a/jcodemodel/src/main/java/com/helger/jcodemodel/literals/IntegerRepresentation.java b/jcodemodel/src/main/java/com/helger/jcodemodel/literals/IntegerRepresentation.java new file mode 100644 index 00000000..d56680c7 --- /dev/null +++ b/jcodemodel/src/main/java/com/helger/jcodemodel/literals/IntegerRepresentation.java @@ -0,0 +1,216 @@ +package com.helger.jcodemodel.literals; + +import java.util.Objects; +import java.util.function.Consumer; + +import org.jspecify.annotations.NonNull; + +/// record containing the params to represent an int/long. This is mostly a data carrier, the actual behavior is in the base. +/// +/// A representation is made using a base, of a prefix, a body, and a suffix, with : +/// +/// - The prefix is absent for decimal, but discriminant for other bases. +/// - The prefix can be set upper or lower (as prefix "0x" is same as "0X" ) +/// - The body is never empty, the actual representation depends on the base +/// - once build from the base, the body *may* be padded, if requested, depending on the base (dec base does not allow padding) +/// - separator are then applied to the (padded) body, using "_" +/// - suffix is only present for long type. They can be upper or lower cased. +public record IntegerRepresentation ( + /// should we print '+' if the number is positive ? + boolean positiveSign, + /// if the base needs a prefix, should we uppercase it ? + boolean prefixUpper, + /// the base in which we want to represent the number, eg + /// octal, binary + @NonNull EIntegerBase base, + // if using hex, should we uppercase the body ? + boolean bodyUpper, + /// if possible (not dec base), append 0 to make the + /// representation's body at least + int padding, + /// when non null, specifies where to put underscores in the + /// body. + /// For example, " _ __ " requires to add 2 underscores before + /// the last char and 1 before the one before. Note that + /// leading underscores may be discarded, depending on the + /// base. + String separateFormat, + /// how many characters to skip in the body before adding a + /// separator String. + int separateEvery, + /// number of "_" characters a separator string is made of. + int separatorSize, + /// if representing long, should we uppercase the terminal "l" + /// ? + boolean suffixUpper) +{ + + // validation + + public IntegerRepresentation + { + Objects.requireNonNull (base); + } + + // copier + + /// Intermediate mutable class for mutate a record. Waiting for java withers … + public static class Copier + { + public boolean positiveSign; + public boolean prefixUpper; + @NonNull + public EIntegerBase base; + public boolean bodyUpper; + public int padding; + public String separateFormat; + public int separateEvery; + public int separatorSize; + public boolean suffixUpper; + + Copier(IntegerRepresentation ir){ + positiveSign=ir.positiveSign; + prefixUpper = ir.prefixUpper; + base=ir.base; + bodyUpper=ir.bodyUpper; + padding=ir.padding; + separateFormat=ir.separateFormat; + separateEvery=ir.separateEvery; + separatorSize=ir.separatorSize; + suffixUpper = ir.suffixUpper; + } + + IntegerRepresentation toRecord () + { + return new IntegerRepresentation (positiveSign, + prefixUpper, + base, + bodyUpper, + padding, + separateFormat, + separateEvery, + separatorSize, + suffixUpper); + } + + public Copier apply (Consumer c) + { + c.accept (this); + return this; + } + + } + + public Copier copier () + { + return new Copier (this); + } + + public IntegerRepresentation with (Consumer change) + { + return copier ().apply (change).toRecord (); + } + + // default values + + /// Default options are : + /// + /// - use decimal base. + /// - lowercase the prefix, so "0x" instead of "0X" + /// - no padding of the body + /// - no separator + /// - if adding separators, use size 1 + /// - for long type, uppercase the terminal "L" + /// + /// from spec : + /// > The suffix L is preferred, because the letter l (ell) is often hard to distinguish from the + /// > digit 1 (one). + /// + public static final IntegerRepresentation DEFAULT = new IntegerRepresentation (false, + false, + EIntegerBase.DECIMAL, + true, + 0, + null, + 0, + 1, + true); + + /// print binary with 8-chars separations, eg "1_00000000" or "1_00000000_00000000L" + public static final IntegerRepresentation BIN = DEFAULT.with (ir -> { + ir.base = EIntegerBase.BINARY; + ir.separateEvery = 8; + }); + + /// bits with padding to 8 chars + public static final IntegerRepresentation BIN8 = BIN.padding (8); + + /// print decimals with 3-chars separations, eg "1_000" or "1_234_567L" + public static final IntegerRepresentation DEC = DEFAULT.separateEvery (3); + + // print hexa with 2-chars separations, eg "0xAA_BB" + public static final IntegerRepresentation HEX = DEFAULT.with (ir -> { + ir.base = EIntegerBase.HEXADECIMAL; + ir.separateEvery = 2; + }); + + // print octal with 4-chars separations, eg "01_0000" + public static final IntegerRepresentation OCT = DEFAULT.with (ir -> { + ir.base = EIntegerBase.OCTAL; + ir.separateEvery = 4; + }); + + // + // mutators + // + + public IntegerRepresentation positiveSign (boolean positiveSign) + { + return positiveSign == this.positiveSign ? this : with (ir -> { ir.positiveSign = positiveSign; }); + } + + public IntegerRepresentation prefixUpper (boolean prefixUpper) + { + return prefixUpper == this.prefixUpper ? this : with (ir -> { ir.prefixUpper = prefixUpper; }); + } + + public IntegerRepresentation base (@NonNull EIntegerBase base) + { + return base == this.base ? this : with (ir -> { ir.base = base; }); + } + + public IntegerRepresentation padding (int padding) + { + return padding == this.padding ? this : with (ir -> { ir.padding = padding; }); + } + + public IntegerRepresentation separateEvery (int separateEvery) + { + return separateEvery == this.separateEvery ? this : with (ir -> { ir.separateEvery = separateEvery; }); + } + + public IntegerRepresentation separatorSize (int separatorSize) + { + return separatorSize == this.separatorSize ? this : with (ir -> { ir.separatorSize = separatorSize; }); + } + + public IntegerRepresentation sufixUpper (boolean suffixUpper) + { + return suffixUpper == this.suffixUpper ? this : with (ir -> { ir.suffixUpper = suffixUpper; }); + } + + // + // actual formatting is delegated to the base + // + + public String format (int i) + { + return base.represent (i, new StringBuilder (), this).toString (); + } + + public String format (long l) + { + return base.represent (l, new StringBuilder (), this).toString (); + } + +} diff --git a/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomIntTest.java b/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomIntTest.java index f7921f8c..6df57750 100644 --- a/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomIntTest.java +++ b/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomIntTest.java @@ -3,57 +3,87 @@ import org.junit.Assert; import org.junit.Test; +import com.helger.jcodemodel.literals.EIntegerBase; +import com.helger.jcodemodel.literals.IntegerRepresentation; import com.helger.jcodemodel.util.CodeModelTestsHelper; public class JAtomIntTest { @Test - public void testRepresentation () + public void testRepresentationDefault () { // basic representation { - JAtomInt i42 = new JAtomInt (42).separateEvery (0); + JAtomInt i42 = new JAtomInt (42); Assert.assertEquals ("0b101010", CodeModelTestsHelper.toString (i42.binary ())); Assert.assertEquals ("42", CodeModelTestsHelper.toString (i42.decimal ())); - Assert.assertEquals ("0x2a", CodeModelTestsHelper.toString (i42.hex ())); + Assert.assertEquals ("0x2A", CodeModelTestsHelper.toString (i42.hexadecimal ())); Assert.assertEquals ("052", CodeModelTestsHelper.toString (i42.octal ())); } { - JAtomInt i0 = new JAtomInt (0).separateEvery (0); + JAtomInt i0 = new JAtomInt (0); Assert.assertEquals ("0b0", CodeModelTestsHelper.toString (i0.binary ())); Assert.assertEquals ("0", CodeModelTestsHelper.toString (i0.decimal ())); - Assert.assertEquals ("0x0", CodeModelTestsHelper.toString (i0.hex ())); + Assert.assertEquals ("0x0", CodeModelTestsHelper.toString (i0.hexadecimal ())); Assert.assertEquals ("00", CodeModelTestsHelper.toString (i0.octal ())); } { - JAtomInt iNeg2 = new JAtomInt (-2).separateEvery (0); + JAtomInt iNeg2 = new JAtomInt (-2); Assert.assertEquals ("-0b10", CodeModelTestsHelper.toString (iNeg2.binary ())); Assert.assertEquals ("-2", CodeModelTestsHelper.toString (iNeg2.decimal ())); - Assert.assertEquals ("-0x2", CodeModelTestsHelper.toString (iNeg2.hex ())); + Assert.assertEquals ("-0x2", CodeModelTestsHelper.toString (iNeg2.hexadecimal ())); Assert.assertEquals ("-02", CodeModelTestsHelper.toString (iNeg2.octal ())); } + } - // separators - { - JAtomInt ia = new JAtomInt (-1234567).separatorSize (2).separateEvery (2).decimal (); - Assert.assertEquals ("-1__23__45__67", CodeModelTestsHelper.toString (ia)); - } - { - JAtomInt ia = new JAtomInt (-12345678).separatorSize (2).separateEvery (2).decimal (); - Assert.assertEquals ("-12__34__56__78", CodeModelTestsHelper.toString (ia)); - } - { - JAtomInt ia = new JAtomInt (-10).separatorSize (1).separateEvery (3).binary (); - Assert.assertEquals ("-0b1_010", CodeModelTestsHelper.toString (ia)); - } + @Test + public void testRepresentationSeparator () + { { - JAtomInt ia = new JAtomInt (4).separatorSize (1).separateEvery (3).binary (); - Assert.assertEquals ("0b100", CodeModelTestsHelper.toString (ia)); + IntegerRepresentation r = IntegerRepresentation.DEFAULT.separatorSize (2) + .separateEvery (2) + .base (EIntegerBase.DECIMAL); + Assert.assertEquals ("-1__23__45__67", CodeModelTestsHelper.toString (new JAtomInt (-1234567, r))); + Assert.assertEquals ("-12__34__56__78", CodeModelTestsHelper.toString (new JAtomInt (-12345678, r))); } { - JAtomInt ia = new JAtomInt (8).separatorSize (1).separateEvery (3).binary (); - Assert.assertEquals ("0b1_000", CodeModelTestsHelper.toString (ia)); + IntegerRepresentation r = IntegerRepresentation.DEFAULT.separatorSize (1) + .separateEvery (3) + .base (EIntegerBase.BINARY); + Assert.assertEquals ("-0b1_010", CodeModelTestsHelper.toString (new JAtomInt (-10, r))); + Assert.assertEquals ("0b100", CodeModelTestsHelper.toString (new JAtomInt (4, r))); + Assert.assertEquals ("0b1_000", CodeModelTestsHelper.toString (new JAtomInt (8, r))); } } + @Test + public void testRepresentationPadding () + { + JAtomInt ia = new JAtomInt (42).separateEvery (0).padding (5); + Assert.assertEquals ("0b101010", CodeModelTestsHelper.toString (ia.binary ())); + Assert.assertEquals ("42", CodeModelTestsHelper.toString (ia.decimal ())); + Assert.assertEquals ("0x0002A", CodeModelTestsHelper.toString (ia.hexadecimal ())); + Assert.assertEquals ("000052", CodeModelTestsHelper.toString (ia.octal ())); + } + + @Test + public void testRepresentationSign () + { + JAtomInt ja = new JAtomInt (42); + for (boolean positiveSign : new boolean [] { true, false }) + for (boolean prefixUpper : new boolean [] { true, false }) + { + for (boolean suffixUpper : new boolean [] { true, false }) + { + ja.representation (ja.representation () + .with (ir->{ + ir.positiveSign=positiveSign; + ir.prefixUpper = prefixUpper; + ir.suffixUpper = suffixUpper; + })); + Assert.assertEquals (positiveSign ? "+42" : "42", CodeModelTestsHelper.toString (ja)); + } + } + } + } diff --git a/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomLongTest.java b/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomLongTest.java new file mode 100644 index 00000000..83be410e --- /dev/null +++ b/jcodemodel/src/test/java/com/helger/jcodemodel/JAtomLongTest.java @@ -0,0 +1,23 @@ +package com.helger.jcodemodel; + +import org.junit.Assert; +import org.junit.Test; + +import com.helger.jcodemodel.literals.IntegerRepresentation; +import com.helger.jcodemodel.util.CodeModelTestsHelper; + +public class JAtomLongTest +{ + + @Test + public void testRepresentationBasic () + { + Assert.assertEquals ("42L", CodeModelTestsHelper.toString (new JAtomLong (42, IntegerRepresentation.DEFAULT))); + Assert.assertEquals ("0b1_00000001L", + CodeModelTestsHelper.toString (new JAtomLong (257, IntegerRepresentation.BIN))); + Assert.assertEquals ("1_024L", CodeModelTestsHelper.toString (new JAtomLong (1024, IntegerRepresentation.DEC))); + Assert.assertEquals ("0x2AL", CodeModelTestsHelper.toString (new JAtomLong (42, IntegerRepresentation.HEX))); + Assert.assertEquals ("-052L", CodeModelTestsHelper.toString (new JAtomLong (-42, IntegerRepresentation.OCT))); + } + +} diff --git a/jcodemodel/src/test/java/com/helger/jcodemodel/literals/EIntegerBaseTest.java b/jcodemodel/src/test/java/com/helger/jcodemodel/literals/EIntegerBaseTest.java new file mode 100644 index 00000000..393828b2 --- /dev/null +++ b/jcodemodel/src/test/java/com/helger/jcodemodel/literals/EIntegerBaseTest.java @@ -0,0 +1,37 @@ +package com.helger.jcodemodel.literals; + +import org.junit.Assert; +import org.junit.Test; + +public class EIntegerBaseTest +{ + + static void checkFormat (String expected, String source, String format, boolean allowLeadingSep) + { + StringBuilder sb = new StringBuilder (); + EIntegerBase.addSep (source, format, allowLeadingSep, 0, 0, sb); + Assert.assertEquals (expected, sb.toString ()); + } + + /// some tests on adding separators to an int representation using [EIntegerBase#addSep] + @Test + public void testAddSep () + { + { + checkFormat ("_0", "0", "_ ", true); + checkFormat ("__0", "0", "__ ", true); + checkFormat ("0", "0", "__ ", false); + checkFormat ("012__3", "0123", "__ ", true); + + checkFormat ("_0_12_3", "0123", "_c_cc_c", true); + checkFormat ("0_12_3", "0123", "_c_cc_c", false); + checkFormat ("___0_12_3", "0123", "___c_cc_c", true); + checkFormat ("0_12_3", "0123", "___c_cc_c", false); + + checkFormat ("01__2_3", "0123", "__d_d", true); + checkFormat ("01__2_3", "0123", "__d__", true); + checkFormat ("01234_56_789", "0123456789", "_dd_ddd", true); + } + } + +} diff --git a/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery0Size1.java b/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery0Size1.java index 5d4d41d9..9fb83c62 100644 --- a/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery0Size1.java +++ b/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery0Size1.java @@ -30,4 +30,8 @@ public class FormatIntWithSeparatorEvery0Size1 { public static final int iNeg1kd = -1024; public static final int iNeg1kh = -0x400; public static final int iNeg1ko = -02000; + public static final int i42p5b = 0b101010; + public static final int i42p5d = 42; + public static final int i42p5h = 0x0002A; + public static final int i42p5o = 000052; } diff --git a/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery2Size2.java b/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery2Size2.java index 5ac193fb..b22d17fa 100644 --- a/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery2Size2.java +++ b/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery2Size2.java @@ -30,4 +30,8 @@ public class FormatIntWithSeparatorEvery2Size2 { public static final int iNeg1kd = -10__24; public static final int iNeg1kh = -0x4__00; public static final int iNeg1ko = -020__00; + public static final int i42p5b = 0b10__10__10; + public static final int i42p5d = 42; + public static final int i42p5h = 0x0__00__2A; + public static final int i42p5o = 00__00__52; } diff --git a/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery3Size1.java b/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery3Size1.java index 4eb9673d..a140e4d4 100644 --- a/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery3Size1.java +++ b/jcodemodeltests/src/generated/javatest/com/helger/jcodemodel/tests/intformat/FormatIntWithSeparatorEvery3Size1.java @@ -30,4 +30,8 @@ public class FormatIntWithSeparatorEvery3Size1 { public static final int iNeg1kd = -1_024; public static final int iNeg1kh = -0x400; public static final int iNeg1ko = -02_000; + public static final int i42p5b = 0b101_010; + public static final int i42p5d = 42; + public static final int i42p5h = 0x00_02A; + public static final int i42p5o = 000_052; } diff --git a/jcodemodeltests/src/main/java/com/helger/jcodemodel/tests/intformat/IntFormatTestGen.java b/jcodemodeltests/src/main/java/com/helger/jcodemodel/tests/intformat/IntFormatTestGen.java index 77bf7dd9..b8159d3e 100644 --- a/jcodemodeltests/src/main/java/com/helger/jcodemodel/tests/intformat/IntFormatTestGen.java +++ b/jcodemodeltests/src/main/java/com/helger/jcodemodel/tests/intformat/IntFormatTestGen.java @@ -16,7 +16,7 @@ static void addFields(JDefinedClass clazz, int separateEvery, int sepSize) { clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i32d", JExpr.lit(32).decimal().separateEvery(separateEvery).separatorSize(sepSize)); clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i32h", - JExpr.lit(32).hex().separateEvery(separateEvery).separatorSize(sepSize)); + JExpr.lit(32).hexadecimal().separateEvery(separateEvery).separatorSize(sepSize)); clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i32o", JExpr.lit(32).octal().separateEvery(separateEvery).separatorSize(sepSize)); @@ -25,7 +25,7 @@ static void addFields(JDefinedClass clazz, int separateEvery, int sepSize) { clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i1Md", JExpr.lit(1024 * 1024).decimal().separateEvery(separateEvery).separatorSize(sepSize)); clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i1Mh", - JExpr.lit(1024 * 1024).hex().separateEvery(separateEvery).separatorSize(sepSize)); + JExpr.lit(1024 * 1024).hexadecimal().separateEvery(separateEvery).separatorSize(sepSize)); clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i1Mo", JExpr.lit(1024 * 1024).octal().separateEvery(separateEvery).separatorSize(sepSize)); @@ -34,9 +34,18 @@ static void addFields(JDefinedClass clazz, int separateEvery, int sepSize) { clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "iNeg1kd", JExpr.lit(-1024).decimal().separateEvery(separateEvery).separatorSize(sepSize)); clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "iNeg1kh", - JExpr.lit(-1024).hex().separateEvery(separateEvery).separatorSize(sepSize)); + JExpr.lit(-1024).hexadecimal().separateEvery(separateEvery).separatorSize(sepSize)); clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "iNeg1ko", JExpr.lit(-1024).octal().separateEvery(separateEvery).separatorSize(sepSize)); + + clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i42p5b", + JExpr.lit(42).binary().separateEvery(separateEvery).separatorSize(sepSize).padding(5)); + clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i42p5d", + JExpr.lit(42).decimal().separateEvery(separateEvery).separatorSize(sepSize).padding(5)); + clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i42p5h", + JExpr.lit(42).hexadecimal().separateEvery(separateEvery).separatorSize(sepSize).padding(5)); + clazz.field(JMod.PUBLIC_STATIC_FINAL, clazz.owner().INT, "i42p5o", + JExpr.lit(42).octal().separateEvery(separateEvery).separatorSize(sepSize).padding(5)); } static void generate(JPackage root, int separateEvery, int sepSize) throws JCodeModelException {