d14e4ad992f4c51e50c6b1fc24c440293cdfc11b
[jalview.git] / Format.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer ($$Version-Rel$$)
3  * Copyright (C) $$Year-Rel$$ The Jalview Authors
4  * 
5  * This file is part of Jalview.
6  * 
7  * Jalview is free software: you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License 
9  * as published by the Free Software Foundation, either version 3
10  * of the License, or (at your option) any later version.
11  *  
12  * Jalview is distributed in the hope that it will be useful, but 
13  * WITHOUT ANY WARRANTY; without even the implied warranty 
14  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
15  * PURPOSE.  See the GNU General Public License for more details.
16  * 
17  * You should have received a copy of the GNU General Public License
18  * along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
19  * The Jalview Authors are detailed in the 'AUTHORS' file.
20  */
21 /**
22  * A class for formatting numbers that follows printf conventions.
23  * Also implements C-like atoi and atof functions
24  * @version 1.03 25 Oct 1997
25  * @author Cay Horstmann
26  */
27 package jalview.util;
28
29 /**
30  * DOCUMENT ME!
31  * 
32  * @author $author$
33  * @version $Revision$
34  */
35 public class Format
36 {
37   private int width;
38
39   private int precision;
40
41   private String pre;
42
43   private String post;
44
45   private boolean leading_zeroes;
46
47   private boolean show_plus;
48
49   private boolean alternate;
50
51   private boolean show_space;
52
53   private boolean left_align;
54
55   private char fmt; // one of cdeEfgGiosxXos
56
57   private final String formatString;
58
59   /**
60    * Creates a new Format object.
61    * 
62    * @param s
63    *          DOCUMENT ME!
64    */
65   public Format(String s)
66   {
67     formatString = s;
68     width = 0;
69     precision = -1;
70     pre = "";
71     post = "";
72     leading_zeroes = false;
73     show_plus = false;
74     alternate = false;
75     show_space = false;
76     left_align = false;
77     fmt = ' ';
78
79     int length = s.length();
80     int parse_state = 0;
81
82     // 0 = prefix, 1 = flags, 2 = width, 3 = precision,
83     // 4 = format, 5 = end
84     int i = 0;
85
86     while (parse_state == 0)
87     {
88       if (i >= length)
89       {
90         parse_state = 5;
91       }
92       else if (s.charAt(i) == '%')
93       {
94         if (i < (length - 1))
95         {
96           if (s.charAt(i + 1) == '%')
97           {
98             pre = pre + '%';
99             i++;
100           }
101           else
102           {
103             parse_state = 1;
104           }
105         }
106         else
107         {
108           throw new java.lang.IllegalArgumentException();
109         }
110       }
111       else
112       {
113         pre = pre + s.charAt(i);
114       }
115
116       i++;
117     }
118
119     while (parse_state == 1)
120     {
121       if (i >= length)
122       {
123         parse_state = 5;
124       }
125       else if (s.charAt(i) == ' ')
126       {
127         show_space = true;
128       }
129       else if (s.charAt(i) == '-')
130       {
131         left_align = true;
132       }
133       else if (s.charAt(i) == '+')
134       {
135         show_plus = true;
136       }
137       else if (s.charAt(i) == '0')
138       {
139         leading_zeroes = true;
140       }
141       else if (s.charAt(i) == '#')
142       {
143         alternate = true;
144       }
145       else
146       {
147         parse_state = 2;
148         i--;
149       }
150
151       i++;
152     }
153
154     while (parse_state == 2)
155     {
156       if (i >= length)
157       {
158         parse_state = 5;
159       }
160       else if (('0' <= s.charAt(i)) && (s.charAt(i) <= '9'))
161       {
162         width = ((width * 10) + s.charAt(i)) - '0';
163         i++;
164       }
165       else if (s.charAt(i) == '.')
166       {
167         parse_state = 3;
168         precision = 0;
169         i++;
170       }
171       else
172       {
173         parse_state = 4;
174       }
175     }
176
177     while (parse_state == 3)
178     {
179       if (i >= length)
180       {
181         parse_state = 5;
182       }
183       else if (('0' <= s.charAt(i)) && (s.charAt(i) <= '9'))
184       {
185         precision = ((precision * 10) + s.charAt(i)) - '0';
186         i++;
187       }
188       else
189       {
190         parse_state = 4;
191       }
192     }
193
194     if (parse_state == 4)
195     {
196       if (i >= length)
197       {
198         parse_state = 5;
199       }
200       else
201       {
202         fmt = s.charAt(i);
203       }
204
205       i++;
206     }
207
208     if (i < length)
209     {
210       post = s.substring(i, length);
211     }
212   }
213
214   /**
215    * Formats the number following printf conventions. Main limitation: Can only
216    * handle one format parameter at a time Use multiple Format objects to format
217    * more than one number
218    * 
219    * @param s
220    *          the format string following printf conventions The string has a
221    *          prefix, a format code and a suffix. The prefix and suffix become
222    *          part of the formatted output. The format code directs the
223    *          formatting of the (single) parameter to be formatted. The code has
224    *          the following structure
225    *          <ul>
226    *          <li>a % (required)
227    *          <li>a modifier (optional)
228    *          <dl>
229    *          <dt>+
230    *          <dd>forces display of + for positive numbers
231    *          <dt>0
232    *          <dd>show leading zeroes
233    *          <dt>-
234    *          <dd>align left in the field
235    *          <dt>space
236    *          <dd>prepend a space in front of positive numbers
237    *          <dt>#
238    *          <dd>use "alternate" format. Add 0 or 0x for octal or hexadecimal
239    *          numbers. Don't suppress trailing zeroes in general floating point
240    *          format.
241    *          </dl>
242    *          <li>an integer denoting field width (optional)
243    *          <li>a period followed by an integer denoting precision (optional)
244    *          <li>a format descriptor (required)
245    *          <dl>
246    *          <dt>f
247    *          <dd>floating point number in fixed format
248    *          <dt>e, E
249    *          <dd>floating point number in exponential notation (scientific
250    *          format). The E format results in an uppercase E for the exponent
251    *          (1.14130E+003), the e format in a lowercase e.
252    *          <dt>g, G
253    *          <dd>floating point number in general format (fixed format for
254    *          small numbers, exponential format for large numbers). Trailing
255    *          zeroes are suppressed. The G format results in an uppercase E for
256    *          the exponent (if any), the g format in a lowercase e.
257    *          <dt>d, i
258    *          <dd>integer in decimal
259    *          <dt>x
260    *          <dd>integer in hexadecimal
261    *          <dt>o
262    *          <dd>integer in octal
263    *          <dt>s
264    *          <dd>string
265    *          <dt>c
266    *          <dd>character
267    *          </dl>
268    *          </ul>
269    * @exception IllegalArgumentException
270    *              if bad format
271    * 
272    */
273   public static String getHexString(java.awt.Color color)
274   {
275     String r;
276     String g;
277     String b;
278     r = Integer.toHexString(color.getRed());
279
280     if (r.length() < 2)
281     {
282       r = "0" + r;
283     }
284
285     g = Integer.toHexString(color.getGreen());
286
287     if (g.length() < 2)
288     {
289       g = "0" + g;
290     }
291
292     b = Integer.toHexString(color.getBlue());
293
294     if (b.length() < 2)
295     {
296       b = "0" + b;
297     }
298
299     return r + g + b;
300   }
301
302   /**
303    * prints a formatted number following printf conventions
304    * 
305    * @param s
306    *          a PrintStream
307    * @param fmt
308    *          the format string
309    * @param x
310    *          the double to print
311    */
312   public static void print(java.io.PrintStream s, String fmt, double x)
313   {
314     s.print(new Format(fmt).form(x));
315   }
316
317   /**
318    * prints a formatted number following printf conventions
319    * 
320    * @param s
321    *          a PrintStream
322    * @param fmt
323    *          the format string
324    * @param x
325    *          the long to print
326    */
327   public static void print(java.io.PrintStream s, String fmt, long x)
328   {
329     s.print(new Format(fmt).form(x));
330   }
331
332   /**
333    * prints a formatted number following printf conventions
334    * 
335    * @param s
336    *          a PrintStream
337    * @param fmt
338    *          the format string
339    * @param x
340    *          the character to
341    */
342   public static void print(java.io.PrintStream s, String fmt, char x)
343   {
344     s.print(new Format(fmt).form(x));
345   }
346
347   /**
348    * prints a formatted number following printf conventions
349    * 
350    * @param s
351    *          a PrintStream, fmt the format string
352    * @param x
353    *          a string that represents the digits to print
354    */
355   public static void print(java.io.PrintStream s, String fmt, String x)
356   {
357     s.print(new Format(fmt).form(x));
358   }
359
360   /**
361    * Converts a string of digits (decimal, octal or hex) to an integer
362    * 
363    * @param s
364    *          a string
365    * @return the numeric value of the prefix of s representing a base 10 integer
366    */
367   public static int atoi(String s)
368   {
369     return (int) atol(s);
370   }
371
372   /**
373    * Converts a string of digits (decimal, octal or hex) to a long integer
374    * 
375    * @param s
376    *          a string
377    * @return the numeric value of the prefix of s representing a base 10 integer
378    */
379   public static long atol(String s)
380   {
381     int i = 0;
382
383     while ((i < s.length()) && Character.isWhitespace(s.charAt(i)))
384     {
385       i++;
386     }
387
388     if ((i < s.length()) && (s.charAt(i) == '0'))
389     {
390       if (((i + 1) < s.length())
391               && ((s.charAt(i + 1) == 'x') || (s.charAt(i + 1) == 'X')))
392       {
393         return parseLong(s.substring(i + 2), 16);
394       }
395       else
396       {
397         return parseLong(s, 8);
398       }
399     }
400     else
401     {
402       return parseLong(s, 10);
403     }
404   }
405
406   /**
407    * DOCUMENT ME!
408    * 
409    * @param s
410    *          DOCUMENT ME!
411    * @param base
412    *          DOCUMENT ME!
413    * 
414    * @return DOCUMENT ME!
415    */
416   private static long parseLong(String s, int base)
417   {
418     int i = 0;
419     int sign = 1;
420     long r = 0;
421
422     while ((i < s.length()) && Character.isWhitespace(s.charAt(i)))
423     {
424       i++;
425     }
426
427     if ((i < s.length()) && (s.charAt(i) == '-'))
428     {
429       sign = -1;
430       i++;
431     }
432     else if ((i < s.length()) && (s.charAt(i) == '+'))
433     {
434       i++;
435     }
436
437     while (i < s.length())
438     {
439       char ch = s.charAt(i);
440
441       if (('0' <= ch) && (ch < ('0' + base)))
442       {
443         r = ((r * base) + ch) - '0';
444       }
445       else if (('A' <= ch) && (ch < (('A' + base) - 10)))
446       {
447         r = ((r * base) + ch) - 'A' + 10;
448       }
449       else if (('a' <= ch) && (ch < (('a' + base) - 10)))
450       {
451         r = ((r * base) + ch) - 'a' + 10;
452       }
453       else
454       {
455         return r * sign;
456       }
457
458       i++;
459     }
460
461     return r * sign;
462   }
463
464   /**
465    * Converts a string of digits to an double
466    * 
467    * @param s
468    *          a string
469    */
470   public static double atof(String s)
471   {
472     int i = 0;
473     int sign = 1;
474     double r = 0; // integer part
475     // double f = 0; // fractional part
476     double p = 1; // exponent of fractional part
477     int state = 0; // 0 = int part, 1 = frac part
478
479     while ((i < s.length()) && Character.isWhitespace(s.charAt(i)))
480     {
481       i++;
482     }
483
484     if ((i < s.length()) && (s.charAt(i) == '-'))
485     {
486       sign = -1;
487       i++;
488     }
489     else if ((i < s.length()) && (s.charAt(i) == '+'))
490     {
491       i++;
492     }
493
494     while (i < s.length())
495     {
496       char ch = s.charAt(i);
497
498       if (('0' <= ch) && (ch <= '9'))
499       {
500         if (state == 0)
501         {
502           r = ((r * 10) + ch) - '0';
503         }
504         else if (state == 1)
505         {
506           p = p / 10;
507           r = r + (p * (ch - '0'));
508         }
509       }
510       else if (ch == '.')
511       {
512         if (state == 0)
513         {
514           state = 1;
515         }
516         else
517         {
518           return sign * r;
519         }
520       }
521       else if ((ch == 'e') || (ch == 'E'))
522       {
523         long e = (int) parseLong(s.substring(i + 1), 10);
524
525         return sign * r * Math.pow(10, e);
526       }
527       else
528       {
529         return sign * r;
530       }
531
532       i++;
533     }
534
535     return sign * r;
536   }
537
538   /**
539    * Formats a double into a string (like sprintf in C)
540    * 
541    * @param x
542    *          the number to format
543    * @return the formatted string
544    * @exception IllegalArgumentException
545    *              if bad argument
546    */
547   public String form(double x)
548   {
549     String r;
550
551     if (precision < 0)
552     {
553       precision = 6;
554     }
555
556     int s = 1;
557
558     if (x < 0)
559     {
560       x = -x;
561       s = -1;
562     }
563
564     if (fmt == 'f')
565     {
566       r = fixed_format(x);
567     }
568     else if ((fmt == 'e') || (fmt == 'E') || (fmt == 'g') || (fmt == 'G'))
569     {
570       r = exp_format(x);
571     }
572     else
573     {
574       throw new java.lang.IllegalArgumentException();
575     }
576
577     return pad(sign(s, r));
578   }
579
580   /**
581    * Formats a long integer into a string (like sprintf in C)
582    * 
583    * @param x
584    *          the number to format
585    * @return the formatted string
586    */
587   public String form(long x)
588   {
589     String r;
590     int s = 0;
591
592     if ((fmt == 'd') || (fmt == 'i'))
593     {
594       if (x < 0)
595       {
596         r = ("" + x).substring(1);
597         s = -1;
598       }
599       else
600       {
601         r = "" + x;
602         s = 1;
603       }
604     }
605     else if (fmt == 'o')
606     {
607       r = convert(x, 3, 7, "01234567");
608     }
609     else if (fmt == 'x')
610     {
611       r = convert(x, 4, 15, "0123456789abcdef");
612     }
613     else if (fmt == 'X')
614     {
615       r = convert(x, 4, 15, "0123456789ABCDEF");
616     }
617     else
618     {
619       throw new java.lang.IllegalArgumentException();
620     }
621
622     return pad(sign(s, r));
623   }
624
625   /**
626    * Formats a character into a string (like sprintf in C)
627    * 
628    * @param debounceTrap
629    *          the value to format
630    * @return the formatted string
631    */
632   public String form(char c)
633   {
634     if (fmt != 'c')
635     {
636       throw new java.lang.IllegalArgumentException();
637     }
638
639     String r = "" + c;
640
641     return pad(r);
642   }
643
644   /**
645    * Formats a string into a larger string (like sprintf in C)
646    * 
647    * @param debounceTrap
648    *          the value to format
649    * @return the formatted string
650    */
651   public String form(String s)
652   {
653     if (fmt != 's')
654     {
655       throw new java.lang.IllegalArgumentException();
656     }
657
658     if (precision >= 0)
659     {
660       s = s.substring(0, precision);
661     }
662
663     return pad(s);
664   }
665
666   /**
667    * DOCUMENT ME!
668    * 
669    * @param c
670    *          DOCUMENT ME!
671    * @param n
672    *          DOCUMENT ME!
673    * 
674    * @return DOCUMENT ME!
675    */
676   private static String repeat(char c, int n)
677   {
678     if (n <= 0)
679     {
680       return "";
681     }
682
683     StringBuffer s = new StringBuffer(n);
684
685     for (int i = 0; i < n; i++)
686     {
687       s.append(c);
688     }
689
690     return s.toString();
691   }
692
693   /**
694    * DOCUMENT ME!
695    * 
696    * @param x
697    *          DOCUMENT ME!
698    * @param n
699    *          DOCUMENT ME!
700    * @param m
701    *          DOCUMENT ME!
702    * @param d
703    *          DOCUMENT ME!
704    * 
705    * @return DOCUMENT ME!
706    */
707   private static String convert(long x, int n, int m, String d)
708   {
709     if (x == 0)
710     {
711       return "0";
712     }
713
714     String r = "";
715
716     while (x != 0)
717     {
718       r = d.charAt((int) (x & m)) + r;
719       x = x >>> n;
720     }
721
722     return r;
723   }
724
725   /**
726    * DOCUMENT ME!
727    * 
728    * @param r
729    *          DOCUMENT ME!
730    * 
731    * @return DOCUMENT ME!
732    */
733   private String pad(String r)
734   {
735     String p = repeat(' ', width - r.length());
736
737     if (left_align)
738     {
739       return pre + r + p + post;
740     }
741     else
742     {
743       return pre + p + r + post;
744     }
745   }
746
747   /**
748    * DOCUMENT ME!
749    * 
750    * @param s
751    *          DOCUMENT ME!
752    * @param r
753    *          DOCUMENT ME!
754    * 
755    * @return DOCUMENT ME!
756    */
757   private String sign(int s, String r)
758   {
759     String p = "";
760
761     if (s < 0)
762     {
763       p = "-";
764     }
765     else if (s > 0)
766     {
767       if (show_plus)
768       {
769         p = "+";
770       }
771       else if (show_space)
772       {
773         p = " ";
774       }
775     }
776     else
777     {
778       if ((fmt == 'o') && alternate && (r.length() > 0)
779               && (r.charAt(0) != '0'))
780       {
781         p = "0";
782       }
783       else if ((fmt == 'x') && alternate)
784       {
785         p = "0x";
786       }
787       else if ((fmt == 'X') && alternate)
788       {
789         p = "0X";
790       }
791     }
792
793     int w = 0;
794
795     if (leading_zeroes)
796     {
797       w = width;
798     }
799     else if (((fmt == 'd') || (fmt == 'i') || (fmt == 'x') || (fmt == 'X') || (fmt == 'o'))
800             && (precision > 0))
801     {
802       w = precision;
803     }
804
805     return p + repeat('0', w - p.length() - r.length()) + r;
806   }
807
808   /**
809    * DOCUMENT ME!
810    * 
811    * @param d
812    *          DOCUMENT ME!
813    * 
814    * @return DOCUMENT ME!
815    */
816   private String fixed_format(double d)
817   {
818     boolean removeTrailing = ((fmt == 'G') || (fmt == 'g')) && !alternate;
819
820     // remove trailing zeroes and decimal point
821     if (d > 0x7FFFFFFFFFFFFFFFL)
822     {
823       return exp_format(d);
824     }
825
826     if (precision == 0)
827     {
828       return (long) (d + 0.5) + (removeTrailing ? "" : ".");
829     }
830
831     long whole = (long) d;
832     double fr = d - whole; // fractional part
833
834     if ((fr >= 1) || (fr < 0))
835     {
836       return exp_format(d);
837     }
838
839     double factor = 1;
840     String leading_zeroes = "";
841
842     for (int i = 1; (i <= precision) && (factor <= 0x7FFFFFFFFFFFFFFFL); i++)
843     {
844       factor *= 10;
845       leading_zeroes = leading_zeroes + "0";
846     }
847
848     long l = (long) ((factor * fr) + 0.5);
849
850     if (l >= factor)
851     {
852       l = 0;
853       whole++;
854     }
855
856     // CSH 10-25-97
857     String z = leading_zeroes + l;
858     z = "." + z.substring(z.length() - precision, z.length());
859
860     if (removeTrailing)
861     {
862       int t = z.length() - 1;
863
864       while ((t >= 0) && (z.charAt(t) == '0'))
865       {
866         t--;
867       }
868
869       if ((t >= 0) && (z.charAt(t) == '.'))
870       {
871         t--;
872       }
873
874       z = z.substring(0, t + 1);
875     }
876
877     return whole + z;
878   }
879
880   /**
881    * DOCUMENT ME!
882    * 
883    * @param d
884    *          DOCUMENT ME!
885    * 
886    * @return DOCUMENT ME!
887    */
888   private String exp_format(double d)
889   {
890     String f = "";
891     int e = 0;
892     double dd = d;
893     double factor = 1;
894
895     if (d != 0)
896     {
897       while (dd > 10)
898       {
899         e++;
900         factor /= 10;
901         dd = dd / 10;
902       }
903
904       while (dd < 1)
905       {
906         e--;
907         factor *= 10;
908         dd = dd * 10;
909       }
910     }
911
912     if (((fmt == 'g') || (fmt == 'G')) && (e >= -4) && (e < precision))
913     {
914       return fixed_format(d);
915     }
916
917     d = d * factor;
918     f = f + fixed_format(d);
919
920     if ((fmt == 'e') || (fmt == 'g'))
921     {
922       f = f + "e";
923     }
924     else
925     {
926       f = f + "E";
927     }
928
929     String p = "000";
930
931     if (e >= 0)
932     {
933       f = f + "+";
934       p = p + e;
935     }
936     else
937     {
938       f = f + "-";
939       p = p + (-e);
940     }
941
942     return f + p.substring(p.length() - 3, p.length());
943   }
944
945   @Override
946   public String toString()
947   {
948     return formatString;
949   }
950 }