1    package com.instantbank.component.lettertemplate.util;
2    
3    import java.util.Hashtable;
4    import java.text.NumberFormat;
5    import java.text.DecimalFormat;
6    import java.text.ParseException;
7    import java.text.DateFormat;
8    import java.text.SimpleDateFormat;
9    import java.util.Locale;
10   import java.util.Date;
11   import com.instantbank.common.utilcomponents.Debug;
12   import com.instantbank.common.utilcomponents.LetterTemplateGlobals;
13   
14   /**
15    *  Formatter for variables values.
16    *
17    * @author InstantBank (Rodrigo Lopez)
18    * @created September 2002
19    */
20   public class VariablesFormat {
21     /**
22      *  Formats follow general conventions for the US.
23      */
24     Locale uslocale = Locale.US;
25   
26     // Constant names for number formats.
27   
28     /**
29      *  Code for format 9999
30      */
31     public static final int N9999 = 2;
32     /**
33      *  Code for format ZZZ9
34      */
35     public static final int NZZZ9 = 10;
36     /**
37      *  Code for format Z,ZZ9
38      */
39     public static final int NZcZZ9 = 11;
40     /**
41      *  Code for format ZZZ9-
42      */
43     public static final int NZZZ9_ = 12;
44     /**
45      *  Code for format Z,ZZ9-
46      */
47     public static final int NZcZZ9_ = 13;
48     /**
49      *  Code for format Z,ZZ9.99-
50      */
51     public static final int NZcZZ9p99_ = 14;
52     /**
53      *  Code for format ZZ9.999%
54      */
55     public static final int NZZ9p999pc = 15;
56     /**
57      *  Code for format ZZ9.9999%
58      */
59     public static final int NZZ9p9999pc = 16;
60     /**
61      *  Code for format 9999.99
62      */
63     public static final int N9999p99 = 17;
64     /**
65      *  Code for format ZZZ9.99
66      */
67     public static final int NZZZ9p99 = 18;
68     /**
69      *  Code for format 9,999
70      */
71     public static final int N9c999 = 19;
72     /**
73      *  Code for format 9,999.99
74      */
75     public static final int N9c999p99 = 20;
76     /**
77      *  Code for format Z,ZZ9.99
78      */
79     public static final int NZcZZ9p99 = 21;
80     /**
81      *  Code for format $9,999.99
82      */
83     public static final int Nd9c999p99 = 22;
84     /**
85      *  Code for format $Z,ZZ9.99
86      */
87     public static final int NdZcZZ9p99 = 23;
88     /**
89      *  Code for format SSN
90      */
91     public static final int NSSN = 24;
92     /**
93      *  Code for format Phone
94      */
95     public static final int NPhone = 25;
96   
97     /**
98      *  Table of actual codes for Number Formats
99      */
100  
101    public static final int[] numberFormatCode = {
102      N9999, NZZZ9, NZcZZ9, NZZZ9_, NZcZZ9_, NZcZZ9p99_,
103      NZZ9p999pc, NZZ9p9999pc,
104      N9999p99, NZZZ9p99, N9c999, N9c999p99, NZcZZ9p99,
105      Nd9c999p99, NdZcZZ9p99, NSSN, NPhone
106      };
107  
108    // Codes for date formats.
109  
110    /**
111     *  Code for format MM/DD/YY
112     */
113    public static final int MMsDDsYY = 3;
114    /**
115     *  Code for format MM/DD/YYYY
116     */
117    public static final int MMsDDsYYYY = 4;
118    /**
119     *  Code for format YYYY/MM/DD
120     */
121    public static final int YYYYsMMsDD = 5;
122    /**
123     *  Code for format DD-MMM-YYYY
124     */
125    public static final int DD_MMM_YYYY = 6;
126    /**
127     *  Code for format MMM DD, YY
128     */
129    public static final int MMMwDDcwYY = 7;
130    /**
131     *  Code for format MMM DD, YYYY
132     */
133    public static final int MMMwDDcwYYYY = 8;
134    /**
135     *  Code for format MMM, YYYY
136     */
137    public static final int MMMcwYYYY = 9;
138  
139    /**
140     *  dateFormatCode
141     */
142    public static final int[] dateFormatCode = {
143      MMsDDsYY, MMsDDsYYYY, YYYYsMMsDD, DD_MMM_YYYY,
144      MMMwDDcwYY, MMMwDDcwYYYY, MMMcwYYYY
145      };
146  
147    /**
148     *  Strings describing date formats.
149     */
150    public static final String[] dateFormatPattern = {
151      "MM/dd/yy", "MM/dd/yyyy", "yyyy/MM,dd", "dd-MMM-yyyy",
152      "MMM dd, yy", "MMM dd, yyyy", "MMM, yyyy"
153      };
154  
155    /**
156     *  Actual date formatters are stored here, next to their corresponding format
157     *  codes.
158     */
159    Hashtable dateFormatter = new Hashtable(6);
160    /**
161     *  Actual number formatters are stored here, next to their corresponding
162     *  format codes.
163     */
164    Hashtable numberFormatter = new Hashtable(15);
165  
166    /**
167     *  The overall number parser. It is used just before formatting numbers.
168     */
169    NumberFormat numberParser;
170    /**
171     *  The overall date parser. It is used just before formatting a date.
172     */
173    SimpleDateFormat dateParser;
174  
175    Debug debug;
176  
177  
178    /**
179     *  VariablesFormat constructor. Initiates {@link #dateParser} and {@link
180     *  #numberParser}. It also creates the formatters corresponding to all
181     *  formats.
182     */
183    public VariablesFormat() {
184  
185      debug = new Debug();
186      debug.setDebugginOn(true);
187      debug.setPreMessage("** VariablesFormat: ");
188  
189      numberParser = NumberFormat.getInstance(uslocale);
190      //Creates number formatters
191      for(int i = 0; i < numberFormatCode.length; i++) {
192        int formatCode = numberFormatCode[i];
193        numberFormatter.put(
194          new Long(formatCode), buildNumberFormatter(formatCode));
195      }
196  
197      dateParser =
198        new SimpleDateFormat(LetterTemplateGlobals.DATE_PARSING_PATTERN, uslocale);
199  
200      //Creates date formatters.
201      for(int i = 0; i < dateFormatCode.length; i++) {
202        long formatCode = dateFormatCode[i];
203        dateFormatter.put(
204          new Long(formatCode),
205          new SimpleDateFormat(dateFormatPattern[i]));
206      }
207  
208    }
209  
210  
211    /**
212     *  Every number formattes is constructed over an object like this one.
213     *
214     * @param formatNumber Format code.
215     * @param pattern Format pattern
216     * @return DecimalFormat
217     */
218    private DecimalFormat baseFormatter(int formatNumber, String pattern) {
219      NumberFormat nf = NumberFormat.getInstance(uslocale);
220      DecimalFormat df;
221      df = (DecimalFormat)nf;
222      df.setMinimumFractionDigits(5);
223      df.setMaximumFractionDigits(5);
224      df.setDecimalSeparatorAlwaysShown(true);
225      df.applyPattern(pattern);
226      return df;
227    }
228  
229  
230    /**
231     *  Delivers the number formatters. It is implemented as a large "switch" on
232     *  formatNumber.
233     *
234     * @param formatNumber Format code
235     * @return The constructed formatter for the formatNumber code.
236     */
237    private IbNumberFormat buildNumberFormatter(int formatNumber) {
238      // Z=#, 9=0
239      NumberFormat nf;
240      DecimalFormat df;
241      String pattern;
242      IbNumberFormat ib;
243      switch (formatNumber) {
244  
245        case N9999:
246          // 9999
247  
248          df = baseFormatter(formatNumber, "0000.00000");
249          if(df == null) {
250            return null;
251          }
252          return new IbNumberFormat(df, new Rounder(), "", "", true);
253        case NZZZ9:
254          // ZZZ9
255  
256          df = baseFormatter(formatNumber, "###0.00000");
257          if(df == null) {
258            return null;
259          }
260          return new IbNumberFormat(df, new Rounder(), "", "", true);
261        case NZcZZ9:
262          // Z,ZZ9
263  
264          df = baseFormatter(formatNumber, "#,##0.00000");
265          if(df == null) {
266            return null;
267          }
268          return new IbNumberFormat(df, new Rounder(), "", "", true);
269        case NZZZ9_:
270          // ZZZ9-
271  
272          df = baseFormatter(formatNumber, "###0.00000");
273          if(df == null) {
274            return null;
275          }
276          df.setNegativePrefix("");
277          df.setNegativeSuffix("-");
278          return new IbNumberFormat(df, new Rounder(), "-", "", true);
279        case NZcZZ9_:
280          // Z,ZZ9-
281  
282          df = baseFormatter(formatNumber, "#,##0.00000");
283          if(df == null) {
284            return null;
285          }
286          return new IbNumberFormat(df, new Rounder(), "-", "", true);
287        case NZcZZ9p99_:
288          // Z,ZZ9.99-
289  
290          df = baseFormatter(formatNumber, "#,##0.00000");
291          if(df == null) {
292            return null;
293          }
294          return new IbNumberFormat(df, new Rounder(2), "-", "", false);
295        case NZZ9p999pc:
296          // ZZ9.999%
297  
298          df = baseFormatter(formatNumber, "##0.00000");
299          if(df == null) {
300            return null;
301          }
302          ib = new IbNumberFormat(df, new Rounder(3, 3), "%", "", false);
303          ib.setRightPositive("%");
304          return ib;
305        case NZZ9p9999pc:
306          // ZZ9.9999%
307  
308          df = baseFormatter(formatNumber, "##0.00000");
309          if(df == null) {
310            return null;
311          }
312  
313          ib = new IbNumberFormat(df, new Rounder(3, 4), "%", "", false);
314          ib.setRightPositive("%");
315          return ib;
316        case N9999p99:
317          // 9999.99 - Pattern deletes decimal figures.
318  
319          df = baseFormatter(formatNumber, "0000.00000");
320          if(df == null) {
321            return null;
322          }
323          return new IbNumberFormat(df, new Rounder(2), "", "", false);
324        case NZZZ9p99:
325          // ZZZ9.99 - Pattern deletes decimal figures.
326  
327          df = baseFormatter(formatNumber, "###0.00000");
328          if(df == null) {
329            return null;
330          }
331          return new IbNumberFormat(df, new Rounder(2), "", "", false);
332        case N9c999:
333          // 9,999 - Pattern deletes decimal figures.
334  
335          df = baseFormatter(formatNumber, "0,000.00000");
336          if(df == null) {
337            return null;
338          }
339          return new IbNumberFormat(df, new Rounder(), "", "", true);
340        case N9c999p99:
341          // 9,999.99
342  
343          df = baseFormatter(formatNumber, "0,000.00000");
344          if(df == null) {
345            return null;
346          }
347          return new IbNumberFormat(df, new Rounder(2), "", "", false);
348        case NZcZZ9p99:
349          // Z,ZZ9.99
350  
351          df = baseFormatter(formatNumber, "#,##0.00000");
352          if(df == null) {
353            return null;
354          }
355          return new IbNumberFormat(df, new Rounder(2), "", "", false);
356        case Nd9c999p99:
357          // $9,999.99
358          df = baseFormatter(formatNumber, "0,000.00000");
359          if(df == null) {
360            return null;
361          }
362          return new IbNumberFormat(df, new Rounder(2), "", "$", false);
363        case NdZcZZ9p99:
364          // $Z,ZZ9.99
365          df = baseFormatter(formatNumber, "#,##0.00000");
366          if(df == null) {
367            return null;
368          }
369          return new IbNumberFormat(df, new Rounder(2), "", "$", false);
370        case NSSN:
371          //SSN
372          df = baseFormatter(formatNumber, "000000000");
373          return new IbNumberFormat(df, new SSN_MakeUp(), "", "", true);
374        case NPhone:
375          //Phone
376          df = baseFormatter(formatNumber, "0000000000");
377          return new IbNumberFormat(df, new Phone_MakeUp(), "", "", true);
378        default:
379          return null;
380      }
381  
382    }
383  
384  
385    /**
386     *  Formats a string of chars according to a format.
387     *
388     * @param varValue The string to be formated.
389     * @param fmtCode The format code.
390     * @return The formatted string or "-----/--/--" if a date formatting
391     *      fails or "#########" if a number formatting fails. The string to
392     * be formated is returned if it only contains whitespaces.
393     */
394    public String format(String varValue, Long fmtCode) {
395  
396      if(varValue.trim().equals("")) {
397        return varValue;
398      }
399  
400      DateFormat df = (DateFormat)dateFormatter.get(fmtCode);
401  
402      if(df != null) {
403        //Is a date format
404        try {
405          Date d = dateParser.parse(varValue);
406          return df.format(d);
407        }
408        catch(ParseException ex) {
409          debug.println(ex.toString());
410          return "----/--/--";
411        }
412      }
413  
414      IbNumberFormat ibf = (IbNumberFormat)numberFormatter.get(fmtCode);
415      if(ibf != null) {
416        //Is a number format
417        try {
418          Number num = numberParser.parse(varValue);
419          if(num.getClass() == Long.class) {
420            return ibf.format(num.longValue());
421          }
422          else if(num.getClass() == Double.class) {
423            return ibf.format(num.doubleValue());
424          }
425          else {
426            return "#########";
427          }
428        }
429        catch(ParseException ex) {
430          debug.println(ex.toString());
431          return "##########";
432        }
433      }
434  
435      return varValue;
436    }
437  
438  
439    /**
440     *  Auxiliary class representing all number formatters.
441     */
442    class IbNumberFormat {
443      /**
444       *  Flavor of the {@link #baseFormatter(int,String) baseFormatter} used by
445       *  this formatter.
446       */
447      NumberFormat formatter;
448      /**
449       *  Auxiliary object that trims figures to the right/left of the basic
450       *  formatted string.
451       */
452      MakeUpFormat rounder;
453      boolean trimPeriod = false;
454  
455      /**
456       *  Suffix to be added if number is negative
457       */
458      String rightNegative = "";
459      /**
460       *  Prefix to be added if number is positive
461       */
462      String leftPositive = "";
463      /**
464       *  Suffix to be added if number is positive
465       */
466      String rightPositive = "";
467      /**
468       *  Prefix to be added if number is negative
469       */
470      String leftNegative = "";
471  
472  
473      /**
474       *  Constructor.
475       *
476       * @param numFormat Basic number format.
477       * @param rounder Auxiliary Rounder.
478       */
479      public IbNumberFormat(NumberFormat numFormat, MakeUpFormat rounder) {
480        formatter = numFormat;
481        rounder = rounder;
482      }
483  
484  
485      /**
486       *  Constructor.
487       *
488       * @param numFormat Basic number format.
489       * @param rounder Auxiliary Rounder.
490       * @param rNeg Suffix if negative number.
491       * @param lPos Prefix if positive number.
492       * @param trimPeriod Decimal period should be deleted?
493       */
494      public IbNumberFormat(NumberFormat numFormat, MakeUpFormat rounder, String rNeg,
495                            String lPos, boolean trimPeriod) {
496        this.formatter = numFormat;
497        this.rounder = rounder;
498        this.rightNegative = rNeg;
499        this.leftPositive = lPos;
500        this.trimPeriod = trimPeriod;
501      }
502  
503  
504      /**
505       *  Sets the leftPositive attribute of the IbNumberFormat object
506       *
507       * @param s The new leftPositive value
508       */
509      public void setLeftPositive(String s) {
510        leftPositive = s;
511      }
512  
513  
514      /**
515       *  Sets the leftNegative attribute of the IbNumberFormat object
516       *
517       * @param s The new leftNegative value
518       */
519      public void setLeftNegative(String s) {
520        leftNegative = s;
521      }
522  
523  
524      /**
525       *  Sets the rightPositive attribute of the IbNumberFormat object
526       *
527       * @param s The new rightPositive value
528       */
529      public void setRightPositive(String s) {
530        rightPositive = s;
531      }
532  
533  
534      /**
535       *  Sets the rightNegative attribute of the IbNumberFormat object
536       *
537       * @param s The new rightNegative value
538       */
539      public void setRightNegative(String s) {
540        rightNegative = s;
541      }
542  
543  
544      /**
545       *  Formats a long value.
546       *
547       * @param lval The value to be formated.
548       * @return The formated value.
549       */
550      public String format(long lval) {
551        long tmp = Math.abs(lval);
552        String s = formatter.format(tmp);
553        System.out.println(s);
554        if(rounder != null) {
555          if(trimPeriod) {
556            s = rounder.trimPeriod(s);
557          }
558          else {
559            s = rounder.trim(s);
560          }
561        }
562  
563        if(lval < 0) {
564          return leftNegative + s + rightNegative;
565        }
566        else {
567          return leftPositive + s + rightPositive;
568        }
569      }
570  
571  
572      /**
573       *  Formats a double value.
574       *
575       * @param dval The value to be formated.
576       * @return The formated value.
577       */
578  
579      public String format(double dval) {
580        double tmp = Math.abs(dval);
581        String s = formatter.format(tmp);
582        System.out.println(s);
583        if(rounder != null) {
584          if(trimPeriod) {
585            s = rounder.trimPeriod(s);
586          }
587          else {
588            s = rounder.trim(s);
589          }
590        }
591  
592        if(dval < 0.0) {
593          return leftPositive + s + rightNegative;
594        }
595        else {
596          return leftPositive + s + rightPositive;
597        }
598      }
599    }
600  
601  
602    /**
603     *  Interface establishing special make ups to the basic formated strings
604     */
605    interface MakeUpFormat {
606      /**
607       *  Deletes some figures to the left or right of the basic formated string
608       *  following the context defined by its caller.
609       *
610       * @param s
611       * @return String
612       */
613      public String trim(String s);
614  
615  
616      /**
617       *  Deletes the decimal separator (period) and all figures to the right.
618       *
619       * @param s
620       * @return String
621       */
622      public String trimPeriod(String s);
623    }
624  
625  
626    /**
627     *  Implementation of MakeUpFormat intended for formatting numbers (not SSN
628     *  nor Phone)
629     */
630    class Rounder
631        implements MakeUpFormat {
632      /**
633       */
634      int left;
635      int right;
636      boolean leftTrimmer;
637  
638  
639      /**
640       *  Period is trimmed, no other truncation.
641       */
642      public Rounder() {
643        left = right = 0;
644        leftTrimmer = false;
645      }
646  
647  
648      /**
649       *  Period is not trimmed, only right truncation.
650       *
651       * @param right
652       */
653      public Rounder(int right) {
654        left = 0;
655        this.right = right;
656        leftTrimmer = false;
657      }
658  
659  
660      /**
661       *  Period is not trimmed, there are left and right truncation. The right
662       *  parameter must "include" the thousands separator.
663       *
664       * @param left
665       * @param right
666       */
667      public Rounder(int left, int right) {
668        this.left = left;
669        this.right = right;
670        leftTrimmer = true;
671      }
672  
673  
674      /**
675       * trim
676       *
677       * @param s
678       * @return String
679       */
680      public String trim(String s) {
681        int period = s.indexOf(".");
682        int end = s.length();
683        int start = 0;
684  
685        if(period >= 0) {
686          if(period + right + 1 <= s.length()) {
687            end = period + right + 1;
688          }
689          if(leftTrimmer && period - left >= 0) {
690            start = period - left;
691          }
692        }
693        else if(leftTrimmer && s.length() - left >= 0) {
694          start = s.length() - left;
695        }
696        return s.substring(start, end);
697      }
698  
699  
700      /**
701       *  trimPeriod
702       *
703       * @param s
704       * @return String
705       */
706      public String trimPeriod(String s) {
707        int period = s.indexOf(".");
708        if(period >= 0) {
709          return trim(s.substring(0, period));
710        }
711        else {
712          return trim(s);
713        }
714      }
715    }
716  
717  
718    /**
719     *  Formatter for SSN format.
720     */
721    class SSN_MakeUp extends Rounder {
722      /**
723       *  Constructor for the SSN_MakeUp object
724       */
725      public SSN_MakeUp() {
726        super(9, 0);
727      }
728  
729  
730      /**
731       *  trim
732       *
733       * @param s
734       * @return String
735       */
736      public String trim(String s) {
737        StringBuffer bf = new StringBuffer(super.trim(s));
738        bf.insert(5, '-');
739        bf.insert(3, '-');
740        return bf.toString();
741      }
742    }
743  
744  
745    /**
746     *  Formatter for Phone format.
747     */
748    class Phone_MakeUp extends Rounder {
749      /**
750       *  Constructor for the Phone_MakeUp object
751       */
752      public Phone_MakeUp() {
753        super(10, 0);
754      }
755  
756  
757      /**
758       * trim
759       *
760       * @param s
761       * @return String
762       */
763      public String trim(String s) {
764        StringBuffer bf = new StringBuffer(super.trim(s));
765        bf.insert(6, '-');
766        bf.insert(3, ')');
767        bf.insert(0, '(');
768        return bf.toString();
769      }
770    }
771  }
772  
773