View Javadoc

1   package com.sharkysoft.printf;
2   
3   import java.text.CharacterIterator;
4   import java.text.StringCharacterIterator;
5   import java.util.Vector;
6   
7   /***
8    * Precompiled formatting template.
9    *
10   * <p><b>Details:</b>  A <code>PrintfTemplate</code> is a preparsed 
11   * representation of a Printf for Java format string.  The constructor for this 
12   * class accepts a regular <code>String</code> and parses it into a 
13   * representation that is ready for use in {@link Printf}'s formatting 
14   * methods.</p>
15   *
16   * <p>All {@link Printf} calls eventually require an instance of this class in 
17   * order to generate output.  The instance can either be created 
18   * <em>explicitly</em> by the programmer or <em>automatically</em> by Printf for 
19   * Java.  In other words, when the caller passes a format string to 
20   * {@link Printf}, it can be sent as a either a <code>String</code> or a 
21   * <code>PrintfTemplate</code>.  This is demonstrated in the examples 
22   * below:</p>
23   *
24   * <blockquote class="example">
25   *
26   *   <p><b>Declarations:</b></p>
27   *
28      *<blockquote><pre>
29        *String name = "Sharky";
30        *int age = 27;
31        *Object[] data = new Object[] {name, new Integer(age)};
32      *</pre></blockquote>
33   *
34   *   <p><b>Example 1:</b> Sending the <code>printf</code> format string
35   *   as a <code>String</code>:</p>
36   *
37      *<blockquote><pre>
38        *Printf.out("My friend %s is %d years old.\n", data);
39      *</pre></blockquote>
40   *
41   *   <p><b>Example 2:</b> Sending the <code>printf</code> format string
42   *   as a <code>PrintfTemplate</code>:</p>
43   *
44      *<blockquote><pre>
45        *Printf.out
46        *( new PrintfTemplate("My friend %s is %d years old.\n"), 
47        *  data
48        *);
49      *</pre></blockquote>
50   *
51   * </blockquote>
52   *
53   * <p>Examples 1 and 2 produce <em>exactly</em> the same output, and require
54   * approximately the same amount of execution time and system resources.  The
55   * only difference between the two is that the first example requires
56   * {@link Printf} to automatically generate a <code>PrintfTemplate</code> 
57   * <em>during</em> the call, whereas the second example creates one 
58   * <em>prior</em> to the call.  Other factors being equal, the first example is 
59   * preferred simply because it is more readable.</p>
60   *
61   * <p>However, if the call to {@link Printf} is inside a loop, where multiple 
62   * lines of output are produced using exactly the same formatting template, then 
63   * it is probably more efficient to parse the format string just once, prior to 
64   * entering the loop, and then reuse the parsed result.  The only way to 
65   * accomplish this is to explicitely initialize the 
66   * <code>PrintfTemplate</code> outside of the loop.</p>
67   *
68   * <blockquote class="example">
69   *
70   *   <p><b>Example 3:</b> Optimizing a print loop by compiling the format string
71   *   <em>outside</em> the loop:</p>
72   *
73      *<blockquote><pre>
74        *PrintfTemplate format_string = 
75        *  new PrintfTemplate("%d bottles of beer on the wall.\n");
76        *for (int i = 99; i &gt; 0; -- i)
77        *  Printf.out
78        *  ( format_string,
79        *    new Object[]
80        *    { new Integer (i)
81        *    }
82        *  );
83      *</pre></blockquote>
84   *
85   * </blockquote>
86   *
87   * <p>Preliminary measurements have shown that using precompiled format strings
88   * can speed up printf loops by 10-20%, but this of course depends on the 
89   * complexity of the format string.</p>
90   * 
91   * <p>For the complete specification governing the syntax of valid printf format 
92   * strings, see 
93   * <a href="doc-files/specification.htm">Printf for Java Specification 2.0</a>.</p>
94   *
95   * @see Printf
96   * 
97   * @author Sharky
98   * @author Gareth Boden (gareth@tapina.com)
99   */
100 public final class PrintfTemplate
101 {
102 
103   /*
104    * Format string subcomponents.
105    */
106   final Formatter[] mapComponents;
107 
108   /*
109    * Special value for mnArgsRequired, indicating that the true value for
110    * args_required has not yet been determined.
111    */
112   private static final int UNDETERMINED = -1;
113   
114   /*
115    * The length of the argument list required for this format string.
116    */
117   int mnArgsRequired = UNDETERMINED;
118 
119     /***
120      * The types of arguments required for this format string.
121      */
122     Class[] mnArgTypes = null;
123 
124   private final String msAsString;
125   
126   /***
127    * Parses format string.
128    * 
129    * <p><b>Details:</b> This constructor parses the given format string and 
130    * assembles a corresponding formatting engine.  If the format string is 
131    * invalid, a {@link PrintfTemplateException} will be thrown.</p> 
132    * 
133    * @param isFormat the format string
134    * @exception PrintfTemplateException if the format string is invalid
135    */
136   public PrintfTemplate(final String isFormat)
137   {
138     msAsString = isFormat;
139     final Vector vpVector = new Vector();
140     final StringCharacterIterator vpIter = new StringCharacterIterator(isFormat);
141     while (vpIter.current() != CharacterIterator.DONE)
142       vpVector.addElement
143       ( Formatter.create
144         ( new FormatSpecifier(vpIter)
145         )
146       );
147     mapComponents = new Formatter[vpVector.size()];
148     vpVector.copyInto(mapComponents);
149   }
150   
151   /***
152    * Determines data vector length.
153    * 
154    * <p><b>Details:</b> <code>argsRequired</code> determines and returns the 
155    * minimum length of a suitable data vector.</p>
156    * 
157    * @return the minimum length
158    */
159   public int argsRequired()
160   {
161     if (mnArgsRequired == UNDETERMINED)
162     {
163       mnArgsRequired = 0;
164       for (int vnI = 0; vnI < mapComponents.length; ++vnI)
165         mnArgsRequired += mapComponents[vnI].argsRequired();
166     }
167     return mnArgsRequired;
168   }
169 
170     /***
171      * Determines types of arguments required.
172      *
173      * <p><b>Details:</b> <code>argTypes</code> determines and returns the
174      * data types required in a suitable data vector.</p>
175      *
176      * @return the data types
177      */
178     public Class[] argTypes() {
179         if (mnArgTypes == null) {
180             mnArgTypes = new Class[argsRequired()];
181             int vnJ = 0;
182             for (int vnI = 0; vnI < mapComponents.length; ++vnI) {
183                 final Class[] componentArgType = mapComponents[vnI].argTypes();
184                 System.arraycopy(componentArgType, 0, mnArgTypes, vnJ, componentArgType.length);
185                 vnJ += componentArgType.length;
186             }
187         }
188         return mnArgTypes;
189     }
190 
191   /***
192    * Returns original format string.
193    * 
194    * <p><b>Details:</b> <code>toString</code> returns the original format string
195    * used to initialize this instance.</p>
196    * 
197    * @return the format string
198    */
199   public String toString()
200   {
201     return msAsString;
202   }
203   
204 }
205