View Javadoc

1   package com.sharkysoft.printf.engine;
2   
3   import java.math.BigDecimal;
4   import java.math.BigInteger;
5   
6   import com.sharkysoft.string.StringToolbox;
7   
8   /***
9    * Formats integers.
10   *
11   * <p>An <code>IntegerFormatter</code> formats integers.  See
12   * {@link NumberFormatter} for a generalized discussion relating to this
13   * class.</p>
14   *
15   * <blockquote class="example">
16   *
17   *   <p><b>Example:</b> Output the contents of integer array <var>vals</var>,
18   *   one integer per line, right-<wbr>justified in a 25-<wbr>character-<wbr>wide
19   *   field.  Round each integer to 3 significant digits.</p>
20   *
21      *<blockquote><pre>
22        *int[] vals = {2000, 34, 32768};
23        *IntegerFormatter formatter = new IntegerFormatter();
24        *formatter.setFieldWidth(25);
25        *formatter.setPadChar(':'); // non-space chosen for emphasis
26        *formatter.setAlignment(AlignmentMode.gpRight);
27        *formatter.setSigDigits(3);
28        *for (int i = 0; i &lt; vals.length; ++ i)
29        *  System.out.println(formatter.format(vals[i]));
30      *</pre></blockquote>
31   *
32   *   <p>This produces the following output:</p>
33   *
34      *<blockquote><pre>
35        *:::::::::::::::::::::2000
36        *:::::::::::::::::::::::34
37        *::::::::::::::::::::32800
38      *</pre></blockquote>
39   *
40   * </blockquote>
41   *
42   * <p>The formatting properties for a given instance of this class can be
43   * updated at any time, and doing so changes the way that subsequent data is
44   * formatted.  For a detailed description of all formatting properties, review
45   * the documentation below and in the superclass.</p>
46   *
47   * @author Sharky
48   */
49  public class IntegerFormatter extends NumberFormatter
50  {
51  
52    ////////////////
53    //            //
54    //  PadDigit  //
55    //            //
56    ////////////////
57  
58    /***
59     * Pad digit.
60     *
61     * <p>Details:</b> Property <code>PadDigit</code> is the pad digit to use when
62     * extra digits are required because of the <code>MinDigits</code> property.
63     * <b>Default value:</b> '0'.</p>
64     *
65     * @see #getPadDigit()
66     * @see #setPadDigit(char)
67     */
68    protected char mcPadDigit = '0';
69  
70    /***
71     * Retrieves PadDigit property.
72     *
73     * @return current value
74     *
75     * @see #mcPadDigit
76     */
77    public char getPadDigit()
78    {
79      return mcPadDigit;
80    }
81  
82    /***
83     * Updates PadDigit property.
84     *
85     * @param icPadDigit new value
86     *
87     * @see #mcPadDigit
88     */
89    public void setPadDigit(final char icPadDigit)
90    {
91      mcPadDigit = icPadDigit;
92    }
93  
94    /////////////////
95    //             //
96    //  MinDigits  //
97    //             //
98    /////////////////
99  
100   /***
101    * Minimum digits.
102    *
103    * <p><b>Details:</b> Property <code>MinDigits</code> is the minimum number of
104    * digits to display in the formatted output.  If the number isn't large
105    * enough to fill all the digits, the padding digit will be used to fill out
106    * the rest.  If this property is 0, digits will only be displayed if the
107    * value being rendered is non-zero.  <b>Default value:</b> 1.</p>
108    *
109    * @see #getMinDigits()
110    * @see #setMinDigits(int)
111    */
112   protected int mnMinDigits  = 1;
113 
114   /***
115    * Retrieves MinDigits property.
116    *
117    * @return current value
118    *
119    * @see #mnMinDigits
120    */
121   public int getMinDigits()
122   {
123     return mnMinDigits;
124   }
125 
126   /***
127    * Updates MinDigits property.
128    *
129    * @param inMinDigits new value
130    *
131    * @see #mnMinDigits
132    */
133   public void setMinDigits(final int inMinDigits)
134   {
135     if (inMinDigits < 0)
136       throw new IllegalArgumentException("inMinDigits=" + inMinDigits);
137     mnMinDigits = inMinDigits;
138   }
139 
140   /***
141    * Default constructor.
142    */
143   public IntegerFormatter() {}
144 
145   /***
146    * Renders value.
147    *
148    * <p><b>Details:</b> <code>format</code> renders the given integer value into
149    * a numeric string, formatted according to the criteria set forth in the
150    * properties.</p>
151    *
152    * @param ipValue value to render
153    * @return rendered value
154    */
155   public String format(BigInteger ipValue)
156   {
157     if (mnSigDigits > 0)
158       ipValue = setSignificantDigits(new BigDecimal(ipValue), mnSigDigits).toBigInteger();
159     final String vsPrefix;
160     final String vsSuffix;
161     String vsDigits;
162     switch (ipValue.signum())
163     {
164     case +1:
165       vsPrefix = msPosPrefix;
166       vsSuffix = msPosSuffix;
167       vsDigits = ipValue.toString(mnRadix);
168       break;
169     case 0:
170       vsPrefix = msZeroPrefix;
171       vsSuffix = msZeroSuffix;
172       vsDigits = mnMinDigits > 0 ? "0" : "";
173       break;
174     case -1:
175       vsPrefix = msNegPrefix;
176       vsSuffix = msNegSuffix;
177       vsDigits = ipValue.negate().toString(mnRadix);
178       break;
179     default:
180       throw new RuntimeException ("unreachable code");
181     }
182     if (mzUpperCase)
183       vsDigits = vsDigits.toUpperCase();
184     // Make extender:
185     final String vsExtender;
186     final int vsExtra = mnMinDigits - vsDigits.length();
187     if (vsExtra > 0)
188     {
189       if (mcPadDigit != 0)
190         vsExtender = StringToolbox.repeat(mcPadDigit, vsExtra);
191       else
192         vsExtender = StringToolbox.repeat(' ', vsExtra);
193     }
194     else
195       vsExtender = "";
196     StringBuffer vsLeftSide = new StringBuffer();
197     StringBuffer vsRightSide = new StringBuffer();
198     // Build leader:
199     if (mnAlignment == AlignmentMode.FULL)
200     {
201       vsLeftSide.append(vsPrefix);
202       vsRightSide.append(vsExtender);
203     }
204     else
205     {
206       if (mcPadDigit != 0)
207       {
208         vsRightSide.append(vsPrefix);
209         vsRightSide.append(vsExtender);
210       }
211       else
212       {
213         vsRightSide.append(vsExtender);
214         vsRightSide.append(vsPrefix);
215       }
216     }
217     // Append actual value:
218     vsRightSide.append(vsDigits);
219     // Concatenate the two sides together and finish formatting:
220     return format(vsLeftSide.toString(), vsRightSide.toString());
221   }
222 
223   /***
224    * Renders value.
225    *
226    * <p><b>Details:</b> <code>format</code> renders the given integer value into
227    * a numeric string, formatted according to the criteria set forth in the
228    * properties.</p>
229    *
230    * @param ilValue value to render
231    * @return rendered value
232    */
233   public final String format(final long ilValue)
234   {
235     return format(BigInteger.valueOf(ilValue));
236   }
237 
238   /***
239    * Renders value.
240    *
241    * <p><b>Details:</b> <code>format</code> renders the given integer value into
242    * a numeric string, formatted according to the criteria set forth in the
243    * properties.</p>
244    *
245    * @param inValue value to render
246    * @return rendered value
247    */
248   public final String format(final int inValue)
249   {
250     return format(BigInteger.valueOf(inValue));
251   }
252 
253   //////////////////////////
254   //                      //
255   //  unsigned rendering  //
256   //                      //
257   //////////////////////////
258 
259   /***
260    * Long MSB manipulation constant.
261    *
262    * <p><b>Details:</b> This constant is used by <code>formatUnsigned</code> to
263    * manipulate the sign bit of a long value into the value's most significant
264    * bit.  Refer to the source for <code>formatUnsigned(long)</code> for more
265    * details.</p>
266    */
267   private static final BigInteger LONG_NEG_ADJUSTMENT = new BigInteger("ffffffffffffffff", 16).not();
268 
269   /***
270    * Renders unsigned value.
271    *
272    * <p><b>Details:</b> <code>formatUnsigned</code> renders the given integer
273    * value as a numeric string, formatted according to the criteria set forth in
274    * the properties.  The integer value is interpreted as an unsigned value.</p>
275    *
276    * @param ilValue value to render
277    * @return rendered value
278    */
279   public final String formatUnsigned(final long ilValue)
280   {
281     BigInteger vpBig = BigInteger.valueOf(ilValue);
282     if (ilValue < 0)
283       vpBig = vpBig.xor(LONG_NEG_ADJUSTMENT);
284     return format(vpBig);
285   }
286 
287   /***
288    * Renders unsigned value.
289    *
290    * <p><b>Details:</b> <code>formatUnsigned</code> renders the given integer
291    * value as a numeric string, formatted according to the criteria set forth in
292    * the properties.  The integer value is interpreted as an unsigned value.</p>
293    *
294    * @param inValue value to render
295    * @return rendered value
296    */
297   public final String formatUnsigned(final int inValue)
298   {
299     return format(BigInteger.valueOf(inValue & 0xffffffffL));
300   }
301 
302   /***
303    * Renders unsigned value.
304    *
305    * <p><b>Details:</b> <code>formatUnsigned</code> renders the given integer
306    * value as a numeric string, formatted according to the criteria set forth in
307    * the properties.  The integer value is interpreted as an unsigned value.</p>
308    *
309    * @param iwValue value to render
310    * @return rendered value
311    */
312   public final String formatUnsigned(final short iwValue)
313   {
314     return format(BigInteger.valueOf(iwValue & 0xffffL));
315   }
316 
317   /***
318    * Renders unsigned value.
319    *
320    * <p><b>Details:</b> <code>formatUnsigned</code> renders the given integer
321    * value as a numeric string, formatted according to the criteria set forth in
322    * the properties.  The integer value is interpreted as an unsigned value.</p>
323    *
324    * @param ibValue value to render
325    * @return rendered value
326    */
327   public final String formatUnsigned(byte ibValue)
328   {
329     return format(BigInteger.valueOf(ibValue & 0xffL));
330   }
331 
332 }
333