JAL-1432 updated copyright notices
[jalview.git] / src / jalview / util / Format.java
1 /*
2  * Jalview - A Sequence Alignment Editor and Viewer (Version 2.8.0b1)
3  * Copyright (C) 2014 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 of the License, or (at your option) any later version.
10  *  
11  * Jalview is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty 
13  * of MERCHANTABILITY or FITNESS FOR A PARTICULAR 
14  * PURPOSE.  See the GNU General Public License for more details.
15  * 
16  * You should have received a copy of the GNU General Public License along with Jalview.  If not, see <http://www.gnu.org/licenses/>.
17  * The Jalview Authors are detailed in the 'AUTHORS' file.
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 has a
216    *          prefix, a format code and a suffix. The prefix and suffix become
217    *          part of the formatted output. The format code directs the
218    *          formatting of the (single) parameter to be formatted. The code has
219    *          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 hexadecimal
234    *          numbers. Don't suppress trailing zeroes in general floating point
235    *          format.
236    *          </dl>
237    *          <li>an integer denoting field width (optional)
238    *          <li>a period followed by an integer denoting precision (optional)
239    *          <li>a format descriptor (required)
240    *          <dl>
241    *          <dt>f
242    *          <dd>floating point number in fixed format
243    *          <dt>e, E
244    *          <dd>floating point number in exponential notation (scientific
245    *          format). The E format results in an uppercase E for the exponent
246    *          (1.14130E+003), the e format in a lowercase e.
247    *          <dt>g, G
248    *          <dd>floating point number in general format (fixed format for
249    *          small numbers, exponential format for large numbers). Trailing
250    *          zeroes are suppressed. The G format results in an uppercase E for
251    *          the exponent (if any), the g format in a lowercase e.
252    *          <dt>d, i
253    *          <dd>integer in decimal
254    *          <dt>x
255    *          <dd>integer in hexadecimal
256    *          <dt>o
257    *          <dd>integer in octal
258    *          <dt>s
259    *          <dd>string
260    *          <dt>c
261    *          <dd>character
262    *          </dl>
263    *          </ul>
264    * @exception IllegalArgumentException
265    *              if bad format
266    * 
267    */
268   public static String getHexString(java.awt.Color color)
269   {
270     String r;
271     String g;
272     String b;
273     r = Integer.toHexString(color.getRed());
274
275     if (r.length() < 2)
276     {
277       r = "0" + r;
278     }
279
280     g = Integer.toHexString(color.getGreen());
281
282     if (g.length() < 2)
283     {
284       g = "0" + g;
285     }
286
287     b = Integer.toHexString(color.getBlue());
288
289     if (b.length() < 2)
290     {
291       b = "0" + b;
292     }
293
294     return r + g + b;
295   }
296
297   /**
298    * prints a formatted number following printf conventions
299    * 
300    * @param s
301    *          a PrintStream
302    * @param fmt
303    *          the format string
304    * @param x
305    *          the double to print
306    */
307   public static void print(java.io.PrintStream s, String fmt, double x)
308   {
309     s.print(new Format(fmt).form(x));
310   }
311
312   /**
313    * prints a formatted number following printf conventions
314    * 
315    * @param s
316    *          a PrintStream
317    * @param fmt
318    *          the format string
319    * @param x
320    *          the long to print
321    */
322   public static void print(java.io.PrintStream s, String fmt, long x)
323   {
324     s.print(new Format(fmt).form(x));
325   }
326
327   /**
328    * prints a formatted number following printf conventions
329    * 
330    * @param s
331    *          a PrintStream
332    * @param fmt
333    *          the format string
334    * @param x
335    *          the character to
336    */
337   public static void print(java.io.PrintStream s, String fmt, char x)
338   {
339     s.print(new Format(fmt).form(x));
340   }
341
342   /**
343    * prints a formatted number following printf conventions
344    * 
345    * @param s
346    *          a PrintStream, fmt the format string
347    * @param x
348    *          a string that represents the digits to print
349    */
350   public static void print(java.io.PrintStream s, String fmt, String x)
351   {
352     s.print(new Format(fmt).form(x));
353   }
354
355   /**
356    * Converts a string of digits (decimal, octal or hex) to an integer
357    * 
358    * @param s
359    *          a string
360    * @return the numeric value of the prefix of s representing a base 10 integer
361    */
362   public static int atoi(String s)
363   {
364     return (int) atol(s);
365   }
366
367   /**
368    * Converts a string of digits (decimal, octal or hex) to a long integer
369    * 
370    * @param s
371    *          a string
372    * @return the numeric value of the prefix of s representing a base 10 integer
373    */
374   public static long atol(String s)
375   {
376     int i = 0;
377
378     while ((i < s.length()) && Character.isWhitespace(s.charAt(i)))
379     {
380       i++;
381     }
382
383     if ((i < s.length()) && (s.charAt(i) == '0'))
384     {
385       if (((i + 1) < s.length())
386               && ((s.charAt(i + 1) == 'x') || (s.charAt(i + 1) == 'X')))
387       {
388         return parseLong(s.substring(i + 2), 16);
389       }
390       else
391       {
392         return parseLong(s, 8);
393       }
394     }
395     else
396     {
397       return parseLong(s, 10);
398     }
399   }
400
401   /**
402    * DOCUMENT ME!
403    * 
404    * @param s
405    *          DOCUMENT ME!
406    * @param base
407    *          DOCUMENT ME!
408    * 
409    * @return DOCUMENT ME!
410    */
411   private static long parseLong(String s, int base)
412   {
413     int i = 0;
414     int sign = 1;
415     long r = 0;
416
417     while ((i < s.length()) && Character.isWhitespace(s.charAt(i)))
418     {
419       i++;
420     }
421
422     if ((i < s.length()) && (s.charAt(i) == '-'))
423     {
424       sign = -1;
425       i++;
426     }
427     else if ((i < s.length()) && (s.charAt(i) == '+'))
428     {
429       i++;
430     }
431
432     while (i < s.length())
433     {
434       char ch = s.charAt(i);
435
436       if (('0' <= ch) && (ch < ('0' + base)))
437       {
438         r = ((r * base) + ch) - '0';
439       }
440       else if (('A' <= ch) && (ch < (('A' + base) - 10)))
441       {
442         r = ((r * base) + ch) - 'A' + 10;
443       }
444       else if (('a' <= ch) && (ch < (('a' + base) - 10)))
445       {
446         r = ((r * base) + ch) - 'a' + 10;
447       }
448       else
449       {
450         return r * sign;
451       }
452
453       i++;
454     }
455
456     return r * sign;
457   }
458
459   /**
460    * Converts a string of digits to an double
461    * 
462    * @param s
463    *          a string
464    */
465   public static double atof(String s)
466   {
467     int i = 0;
468     int sign = 1;
469     double r = 0; // integer part
470     // double f = 0; // fractional part
471     double p = 1; // exponent of fractional part
472     int state = 0; // 0 = int part, 1 = frac part
473
474     while ((i < s.length()) && Character.isWhitespace(s.charAt(i)))
475     {
476       i++;
477     }
478
479     if ((i < s.length()) && (s.charAt(i) == '-'))
480     {
481       sign = -1;
482       i++;
483     }
484     else if ((i < s.length()) && (s.charAt(i) == '+'))
485     {
486       i++;
487     }
488
489     while (i < s.length())
490     {
491       char ch = s.charAt(i);
492
493       if (('0' <= ch) && (ch <= '9'))
494       {
495         if (state == 0)
496         {
497           r = ((r * 10) + ch) - '0';
498         }
499         else if (state == 1)
500         {
501           p = p / 10;
502           r = r + (p * (ch - '0'));
503         }
504       }
505       else if (ch == '.')
506       {
507         if (state == 0)
508         {
509           state = 1;
510         }
511         else
512         {
513           return sign * r;
514         }
515       }
516       else if ((ch == 'e') || (ch == 'E'))
517       {
518         long e = (int) parseLong(s.substring(i + 1), 10);
519
520         return sign * r * Math.pow(10, e);
521       }
522       else
523       {
524         return sign * r;
525       }
526
527       i++;
528     }
529
530     return sign * r;
531   }
532
533   /**
534    * Formats a double into a string (like sprintf in C)
535    * 
536    * @param x
537    *          the number to format
538    * @return the formatted string
539    * @exception IllegalArgumentException
540    *              if bad argument
541    */
542   public String form(double x)
543   {
544     String r;
545
546     if (precision < 0)
547     {
548       precision = 6;
549     }
550
551     int s = 1;
552
553     if (x < 0)
554     {
555       x = -x;
556       s = -1;
557     }
558
559     if (fmt == 'f')
560     {
561       r = fixed_format(x);
562     }
563     else if ((fmt == 'e') || (fmt == 'E') || (fmt == 'g') || (fmt == 'G'))
564     {
565       r = exp_format(x);
566     }
567     else
568     {
569       throw new java.lang.IllegalArgumentException();
570     }
571
572     return pad(sign(s, r));
573   }
574
575   /**
576    * Formats a long integer into a string (like sprintf in C)
577    * 
578    * @param x
579    *          the number to format
580    * @return the formatted string
581    */
582   public String form(long x)
583   {
584     String r;
585     int s = 0;
586
587     if ((fmt == 'd') || (fmt == 'i'))
588     {
589       if (x < 0)
590       {
591         r = ("" + x).substring(1);
592         s = -1;
593       }
594       else
595       {
596         r = "" + x;
597         s = 1;
598       }
599     }
600     else if (fmt == 'o')
601     {
602       r = convert(x, 3, 7, "01234567");
603     }
604     else if (fmt == 'x')
605     {
606       r = convert(x, 4, 15, "0123456789abcdef");
607     }
608     else if (fmt == 'X')
609     {
610       r = convert(x, 4, 15, "0123456789ABCDEF");
611     }
612     else
613     {
614       throw new java.lang.IllegalArgumentException();
615     }
616
617     return pad(sign(s, r));
618   }
619
620   /**
621    * Formats a character into a string (like sprintf in C)
622    * 
623    * @param x
624    *          the value to format
625    * @return the formatted string
626    */
627   public String form(char c)
628   {
629     if (fmt != 'c')
630     {
631       throw new java.lang.IllegalArgumentException();
632     }
633
634     String r = "" + c;
635
636     return pad(r);
637   }
638
639   /**
640    * Formats a string into a larger string (like sprintf in C)
641    * 
642    * @param x
643    *          the value to format
644    * @return the formatted string
645    */
646   public String form(String s)
647   {
648     if (fmt != 's')
649     {
650       throw new java.lang.IllegalArgumentException();
651     }
652
653     if (precision >= 0)
654     {
655       s = s.substring(0, precision);
656     }
657
658     return pad(s);
659   }
660
661   /**
662    * DOCUMENT ME!
663    * 
664    * @param c
665    *          DOCUMENT ME!
666    * @param n
667    *          DOCUMENT ME!
668    * 
669    * @return DOCUMENT ME!
670    */
671   private static String repeat(char c, int n)
672   {
673     if (n <= 0)
674     {
675       return "";
676     }
677
678     StringBuffer s = new StringBuffer(n);
679
680     for (int i = 0; i < n; i++)
681     {
682       s.append(c);
683     }
684
685     return s.toString();
686   }
687
688   /**
689    * DOCUMENT ME!
690    * 
691    * @param x
692    *          DOCUMENT ME!
693    * @param n
694    *          DOCUMENT ME!
695    * @param m
696    *          DOCUMENT ME!
697    * @param d
698    *          DOCUMENT ME!
699    * 
700    * @return DOCUMENT ME!
701    */
702   private static String convert(long x, int n, int m, String d)
703   {
704     if (x == 0)
705     {
706       return "0";
707     }
708
709     String r = "";
710
711     while (x != 0)
712     {
713       r = d.charAt((int) (x & m)) + r;
714       x = x >>> n;
715     }
716
717     return r;
718   }
719
720   /**
721    * DOCUMENT ME!
722    * 
723    * @param r
724    *          DOCUMENT ME!
725    * 
726    * @return DOCUMENT ME!
727    */
728   private String pad(String r)
729   {
730     String p = repeat(' ', width - r.length());
731
732     if (left_align)
733     {
734       return pre + r + p + post;
735     }
736     else
737     {
738       return pre + p + r + post;
739     }
740   }
741
742   /**
743    * DOCUMENT ME!
744    * 
745    * @param s
746    *          DOCUMENT ME!
747    * @param r
748    *          DOCUMENT ME!
749    * 
750    * @return DOCUMENT ME!
751    */
752   private String sign(int s, String r)
753   {
754     String p = "";
755
756     if (s < 0)
757     {
758       p = "-";
759     }
760     else if (s > 0)
761     {
762       if (show_plus)
763       {
764         p = "+";
765       }
766       else if (show_space)
767       {
768         p = " ";
769       }
770     }
771     else
772     {
773       if ((fmt == 'o') && alternate && (r.length() > 0)
774               && (r.charAt(0) != '0'))
775       {
776         p = "0";
777       }
778       else if ((fmt == 'x') && alternate)
779       {
780         p = "0x";
781       }
782       else if ((fmt == 'X') && alternate)
783       {
784         p = "0X";
785       }
786     }
787
788     int w = 0;
789
790     if (leading_zeroes)
791     {
792       w = width;
793     }
794     else if (((fmt == 'd') || (fmt == 'i') || (fmt == 'x') || (fmt == 'X') || (fmt == 'o'))
795             && (precision > 0))
796     {
797       w = precision;
798     }
799
800     return p + repeat('0', w - p.length() - r.length()) + r;
801   }
802
803   /**
804    * DOCUMENT ME!
805    * 
806    * @param d
807    *          DOCUMENT ME!
808    * 
809    * @return DOCUMENT ME!
810    */
811   private String fixed_format(double d)
812   {
813     boolean removeTrailing = ((fmt == 'G') || (fmt == 'g')) && !alternate;
814
815     // remove trailing zeroes and decimal point
816     if (d > 0x7FFFFFFFFFFFFFFFL)
817     {
818       return exp_format(d);
819     }
820
821     if (precision == 0)
822     {
823       return (long) (d + 0.5) + (removeTrailing ? "" : ".");
824     }
825
826     long whole = (long) d;
827     double fr = d - whole; // fractional part
828
829     if ((fr >= 1) || (fr < 0))
830     {
831       return exp_format(d);
832     }
833
834     double factor = 1;
835     String leading_zeroes = "";
836
837     for (int i = 1; (i <= precision) && (factor <= 0x7FFFFFFFFFFFFFFFL); i++)
838     {
839       factor *= 10;
840       leading_zeroes = leading_zeroes + "0";
841     }
842
843     long l = (long) ((factor * fr) + 0.5);
844
845     if (l >= factor)
846     {
847       l = 0;
848       whole++;
849     }
850
851     // CSH 10-25-97
852     String z = leading_zeroes + l;
853     z = "." + z.substring(z.length() - precision, z.length());
854
855     if (removeTrailing)
856     {
857       int t = z.length() - 1;
858
859       while ((t >= 0) && (z.charAt(t) == '0'))
860       {
861         t--;
862       }
863
864       if ((t >= 0) && (z.charAt(t) == '.'))
865       {
866         t--;
867       }
868
869       z = z.substring(0, t + 1);
870     }
871
872     return whole + z;
873   }
874
875   /**
876    * DOCUMENT ME!
877    * 
878    * @param d
879    *          DOCUMENT ME!
880    * 
881    * @return DOCUMENT ME!
882    */
883   private String exp_format(double d)
884   {
885     String f = "";
886     int e = 0;
887     double dd = d;
888     double factor = 1;
889
890     if (d != 0)
891     {
892       while (dd > 10)
893       {
894         e++;
895         factor /= 10;
896         dd = dd / 10;
897       }
898
899       while (dd < 1)
900       {
901         e--;
902         factor *= 10;
903         dd = dd * 10;
904       }
905     }
906
907     if (((fmt == 'g') || (fmt == 'G')) && (e >= -4) && (e < precision))
908     {
909       return fixed_format(d);
910     }
911
912     d = d * factor;
913     f = f + fixed_format(d);
914
915     if ((fmt == 'e') || (fmt == 'g'))
916     {
917       f = f + "e";
918     }
919     else
920     {
921       f = f + "E";
922     }
923
924     String p = "000";
925
926     if (e >= 0)
927     {
928       f = f + "+";
929       p = p + e;
930     }
931     else
932     {
933       f = f + "-";
934       p = p + (-e);
935     }
936
937     return f + p.substring(p.length() - 3, p.length());
938   }
939 }