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