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 > 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
105
106 final Formatter[] mapComponents;
107
108
109
110
111
112 private static final int UNDETERMINED = -1;
113
114
115
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