View Javadoc

1   package com.sharkysoft.printf.engine;
2   
3   import java.math.BigDecimal;
4   
5   /***
6    * Formats numbers.
7    *
8    * <p>A <code>NumberFormatter</code> formats numbers in a field by adjusting
9    * their precision and padding them as necessary, according to criteria selected
10   * by the client.  Properties control various formatting constraints, such as
11   * field width, justification style, padding characters, and so on.</p>
12   *
13   * <p>Because applications that format numeric values frequently format multiple
14   * values with the same criteria, this class is designed to "remember" selected
15   * formatting properties so that the settings they can be reused with each
16   * subsequent formatting request.  This approach minimizes the number of
17   * parameters that must be passed with each formatting call and results in more
18   * efficient execution for large formatting jobs.</p>
19   *
20   * <p><code>NumberFormatter</code> extends {@link StringFormatter} with abstract
21   * number formatting constraints that are common to {@link IntegerFormatter} and
22   * {@link RealFormatter}.  This class does not actually implement any of the
23   * specific numeric formatting rules, but leaves that work to the
24   * subclasses.</p>
25   *
26   * @see IntegerFormatter
27   * @see RealFormatter
28   *
29   * @author Sharky
30   */
31  public abstract class NumberFormatter extends StringFormatter
32  {
33  
34    /////////////
35    //         //
36    //  Radix  //
37    //         //
38    /////////////
39  
40    /***
41     * Radix.
42     *
43     * <p><b>Details:</b> Property <code>Radix</code> is the output radix (numeric
44     * base) for stringified numbers.  For example, a value of 16 indicates that
45     * the value will be represented in hexadecimal.  This value may not be set
46     * to values less than 2 or greater than 36.  <b>Default value:</b> 10.</p>
47     *
48     * @see #getRadix()
49     * @see #setRadix(int)
50     */
51    protected int mnRadix = 10;
52  
53    /***
54     * Retrieves Radix property.
55     *
56     * @return current value
57     *
58     * @see #mnRadix
59     */
60    public int getRadix() {return mnRadix;}
61  
62    /***
63     * Updates Radix property.
64     *
65     * @param inRadix new value
66     *
67     * @see #mnRadix
68     */
69    public void setRadix(final int inRadix)
70    {
71      if (inRadix < 2 || inRadix > 36)
72        throw new IllegalArgumentException("inRadix=" + inRadix);
73      mnRadix = inRadix;
74    }
75  
76    /////////////////
77    //             //
78    //  UpperCase  //
79    //             //
80    /////////////////
81  
82    /***
83     * Upper case/lower case.
84     *
85     * <p><b>Details:</b> Property <code>UpperCase</code> determines whether upper
86     * or lower case letters will be used when property <code>Radix</code> is
87     * greater than 10.  If this property is <code>true</code>, upper case letters
88     * will be used.  If this property is <code>false</code>, lower case letters
89     * will be used.  The default value for this property is
90     * <code>false</code>.</p>
91     *
92     * @see #getUpperCase()
93     * @see #setUpperCase(boolean)
94     */
95    protected boolean mzUpperCase = false;
96  
97    /***
98     * Retrieves UpperCase property.
99     *
100    * @return current value
101    *
102    * @see #mzUpperCase
103    */
104   public boolean getUpperCase() {return mzUpperCase;}
105 
106   /***
107    * Updates UpperCase property.
108    *
109    * @param izUpperCase new value
110    *
111    * @see #mzUpperCase
112    */
113   public void setUpperCase(final boolean izUpperCase)
114   {
115       mzUpperCase = izUpperCase;
116   }
117 
118   /////////////////
119   //             //
120   //  SigDigits  //
121   //             //
122   /////////////////
123 
124   /***
125    * Significant digits.
126    *
127    * <p><b>Details:</b> Property <code>SigDigits</code> constrains the number of
128    * significant digits to express in the output, so that the value will be
129    * rounded if necessary.  Setting this value to 0 indicates that all digits
130    * are considered significant.  This constraint may conflict with the
131    * MaxRightDigits property in {@link RealFormatter}.  Default value: 0.</p>
132    *
133    * @see #getSigDigits()
134    * @see #setSigDigits(int)
135    */
136   protected int mnSigDigits = 0;
137 
138   /***
139    * Retrieves SigDigits property.
140    *
141    * @return current value
142    *
143    * @see #mnSigDigits
144    */
145   public int getSigDigits() {return mnSigDigits;}
146 
147   /***
148    * Updates SigDigits property.
149    *
150    * @param inSigDigits new value
151    *
152    * @see #mnSigDigits
153    */
154   public void setSigDigits(final int inSigDigits)
155   {
156     if (inSigDigits < 0)
157       throw new IllegalArgumentException("inSigDigits=" + inSigDigits);
158     mnSigDigits = inSigDigits;
159   }
160 
161   /////////////////
162   //             //
163   //  PosPrefix  //
164   //             //
165   /////////////////
166 
167   /***
168    * Positive prefix.
169    *
170    * <p><b>Details:</b> Property <code>PosPrefix</code> is the string that
171    * precedes positive values.  This property may not be <code>null</code>.
172    * Default value: "".</p>
173    *
174    * @see #getPosPrefix()
175    * @see #setPosPrefix(String)
176    */
177   protected String msPosPrefix = "";
178 
179   /***
180    * Retrieves PosPrefix property.
181    *
182    * @return current value
183    *
184    * @see #msPosPrefix
185    */
186   public String getPosPrefix() {return msPosPrefix;}
187 
188   /***
189    * Updates PosPrefix property.
190    *
191    * @param isPosPrefix new value
192    *
193    * @see #msPosPrefix
194    */
195   public void setPosPrefix(final String isPosPrefix)
196   {
197     if (isPosPrefix == null)
198       throw new NullPointerException("isPosPrefix");
199     msPosPrefix = isPosPrefix;
200   }
201 
202   /////////////////
203   //             //
204   //  PosSuffix  //
205   //             //
206   /////////////////
207 
208   /***
209    * Positive suffix.
210    *
211    * <p><b>Details:</b> Property <code>PosSuffix</code> is the string that
212    * follows positive values.  This property may not be <code>null</code>.
213    * Default value: "".</p>
214    *
215    * @see #getPosSuffix()
216    * @see #setPosSuffix(String)
217    */
218   protected String msPosSuffix = "";
219 
220   /***
221    * Retrieves PosSuffix property.
222    *
223    * @return current value
224    *
225    * @see #msPosSuffix
226    */
227   public String getPosSuffix() {return msPosSuffix;}
228 
229   /***
230    * Updates PosSuffix property.
231    *
232    * @param isPosSuffix new value
233    *
234    * @see #msPosSuffix
235    */
236   public void setPosSuffix(final String isPosSuffix)
237   {
238     if (isPosSuffix == null)
239       throw new NullPointerException("isPosSuffix");
240     msPosSuffix = isPosSuffix;
241   }
242 
243   //////////////////
244   //              //
245   //  ZeroPrefix  //
246   //              //
247   //////////////////
248 
249   /***
250    * Zero prefix.
251    *
252    * <p><b>Details:</b> Property <code>ZeroPrefix</code> is the string that
253    * precedes zero values.  This property may not be <code>null</code>.  Default
254    * value: "".</p>
255    *
256    * @see #getZeroPrefix()
257    * @see #setZeroPrefix(String)
258    */
259   protected String msZeroPrefix = "";
260 
261   /***
262    * Retrieves ZeroPrefix property.
263    *
264    * @return current value
265    *
266    * @see #msZeroPrefix
267    */
268   public String getZeroPrefix() {return msZeroPrefix;}
269 
270   /***
271    * Updates ZeroPrefix property.
272    *
273    * @param isZeroPrefix new value
274    *
275    * @see #msZeroPrefix
276    */
277   public void setZeroPrefix(final String isZeroPrefix)
278   {
279     if (isZeroPrefix == null)
280       throw new NullPointerException("isZeroPrefix");
281     msZeroPrefix = isZeroPrefix;
282   }
283 
284   //////////////////
285   //              //
286   //  ZeroSuffix  //
287   //              //
288   //////////////////
289 
290   /***
291    * Zero suffix.
292    *
293    * <p><b>Details:</b> Property <code>ZeroSuffix</code> is the string that
294    * follows zero values.  This property may not be <code>null</code>.  Default
295    * value: "".</p>
296    *
297    * @see #getZeroSuffix()
298    * @see #setZeroSuffix(String)
299    */
300   protected String msZeroSuffix = "";
301 
302   /***
303    * Retrieves ZeroSuffix property.
304    *
305    * @return current value
306    *
307    * @see #msZeroSuffix
308    */
309   public String getZeroSuffix() {return msZeroSuffix;}
310 
311   /***
312    * Updates ZeroSuffix property.
313    *
314    * @param isZeroSuffix new value
315    *
316    * @see #msZeroSuffix
317    */
318   public void setZeroSuffix(final String isZeroSuffix)
319   {
320     if (isZeroSuffix == null)
321       throw new NullPointerException("isZeroSuffix");
322     msZeroSuffix = isZeroSuffix;
323   }
324 
325   /////////////////
326   //             //
327   //  NegPrefix  //
328   //             //
329   /////////////////
330 
331   /***
332    * Negative prefix.
333    *
334    * <p><b>Details:</b> Property <code>NegPrefix</code> is the string that
335    * precedes negative values.  This property may not be <code>null</code>.
336    * Default value: "-".</p>
337    *
338    * @see #getNegPrefix()
339    * @see #setNegPrefix(String)
340    */
341   protected String msNegPrefix = "-";
342 
343   /***
344    * Retrieves NegPrefix property.
345    *
346    * @return current value
347    *
348    * @see #msNegPrefix
349    */
350   public String getNegPrefix() {return msNegPrefix;}
351 
352   /***
353    * Updates NegPrefix property.
354    *
355    * @param isNegPrefix new value
356    *
357    * @see #msNegPrefix
358    */
359   public void setNegPrefix(final String isNegPrefix)
360   {
361     if (isNegPrefix == null)
362       throw new NullPointerException("isNegPrefix");
363     msNegPrefix = isNegPrefix;
364   }
365 
366   /////////////////
367   //             //
368   //  NegSuffix  //
369   //             //
370   /////////////////
371 
372   /***
373    * Negative suffix.
374    *
375    * <p><b>Details:</b> Property <code>NegSuffix</code> is the string that
376    * follows negative values.  This property may not be <code>null</code>.
377    * Default value: "".</p>
378    *
379    * @see #getNegSuffix()
380    * @see #setNegSuffix(String)
381    */
382   protected String msNegSuffix = "";
383 
384   /***
385    * Retrieves NegSuffix property.
386    *
387    * @return current value
388    *
389    * @see #msNegSuffix
390    */
391   public String getNegSuffix() {return msNegSuffix;}
392 
393   /***
394    * Updates NegSuffix property.
395    *
396    * @param isNegSuffix new value
397    *
398    * @see #msNegSuffix
399    */
400   public void setNegSuffix(final String isNegSuffix)
401   {
402     if (isNegSuffix == null)
403       throw new NullPointerException("isNegSuffix");
404     msNegSuffix = isNegSuffix;
405   }
406 
407   /***
408    * Default constructor.
409    */
410   protected NumberFormatter() {}
411 
412   /***
413    * Adjusts significant digits.
414    *
415    * <p><b>Details:</b> This method adjusts the number of significant digits in
416    * the given <code>BigDecimal</code> and returns the adjusted value.</p>
417    *
418    * <p>This method is a temporary hack to overcome a limitation in
419    * <code>BigDecimal</code>.  A feature request has been submitted to the Java
420    * Bug Parade and it has been address.  This method should be reimplemented
421    * using the latest methods available in <code>BigDecimal</code>.</p>
422    *
423    * @param ipValue value to adjust
424    * @param inSigDigits number of significant digits
425    * @return adjusted value
426    */
427   static final BigDecimal setSignificantDigits(BigDecimal ipValue, final int inSigDigits)
428   {
429     if (inSigDigits <= 0)
430       throw new IllegalArgumentException("inSigDigits=" + inSigDigits);
431     final int vnScale = ipValue.scale();
432     ipValue = ipValue.movePointRight(vnScale);
433     final String vsString = ipValue.toString();
434     int vnSize = vsString.length();
435     if (vsString.charAt(vnSize - 1) == '.')
436       -- vnSize;
437     if (vsString.charAt(0) == '-')
438       -- vnSize;
439     ipValue = ipValue.movePointLeft(vnSize);
440     ipValue = ipValue.setScale(inSigDigits, BigDecimal.ROUND_HALF_UP);
441     return ipValue.movePointRight(vnSize - vnScale);
442   }
443 
444 }
445