KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > nwalsh > saxon > Verbatim


1 // Verbatim.java - Saxon extensions supporting DocBook verbatim environments
2

3 package com.nwalsh.saxon;
4
5 import java.util.Stack JavaDoc;
6 import java.util.StringTokenizer JavaDoc;
7 import org.xml.sax.*;
8 import org.w3c.dom.*;
9 import javax.xml.transform.TransformerException JavaDoc;
10 import com.icl.saxon.Controller;
11 import com.icl.saxon.expr.*;
12 import com.icl.saxon.om.*;
13 import com.icl.saxon.pattern.*;
14 import com.icl.saxon.Context;
15 import com.icl.saxon.tree.*;
16 import com.icl.saxon.functions.Extensions;
17 import com.nwalsh.saxon.NumberLinesEmitter;
18 import com.nwalsh.saxon.CalloutEmitter;
19
20 /**
21  * <p>Saxon extensions supporting DocBook verbatim environments</p>
22  *
23  * <p>$Id: Verbatim.java,v 1.3 2003/08/27 14:24:59 nwalsh Exp $</p>
24  *
25  * <p>Copyright (C) 2000 Norman Walsh.</p>
26  *
27  * <p>This class provides a
28  * <a HREF="http://users.iclway.co.uk/mhkay/saxon/">Saxon</a>
29  * implementation of two features that would be impractical to
30  * implement directly in XSLT: line numbering and callouts.</p>
31  *
32  * <p><b>Line Numbering</b></p>
33  * <p>The <tt>numberLines</tt> method takes a result tree
34  * fragment (assumed to contain the contents of a formatted verbatim
35  * element in DocBook: programlisting, screen, address, literallayout,
36  * or synopsis) and returns a result tree fragment decorated with
37  * line numbers.</p>
38  *
39  * <p><b>Callouts</b></p>
40  * <p>The <tt>insertCallouts</tt> method takes an
41  * <tt>areaspec</tt> and a result tree fragment
42  * (assumed to contain the contents of a formatted verbatim
43  * element in DocBook: programlisting, screen, address, literallayout,
44  * or synopsis) and returns a result tree fragment decorated with
45  * callouts.</p>
46  *
47  * <p><b>Change Log:</b></p>
48  * <dl>
49  * <dt>1.0</dt>
50  * <dd><p>Initial release.</p></dd>
51  * </dl>
52  *
53  * @author Norman Walsh
54  * <a HREF="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
55  *
56  * @version $Id: Verbatim.java,v 1.3 2003/08/27 14:24:59 nwalsh Exp $
57  *
58  */

59 public class Verbatim {
60   /** True if the stylesheet is producing formatting objects */
61   private static boolean foStylesheet = false;
62   /** The modulus for line numbering (every 'modulus' line is numbered). */
63   private static int modulus = 0;
64   /** The width (in characters) of line numbers (for padding). */
65   private static int width = 0;
66   /** The starting line number. */
67   private static int startinglinenumber = 1;
68   /** The separator between the line number and the verbatim text. */
69   private static String JavaDoc separator = "";
70
71   /** True if callouts have been setup */
72   private static boolean calloutsSetup = false;
73   /** The default column for callouts that have only a line or line range */
74   private static int defaultColumn = 60;
75   /** The path to use for graphical callout decorations. */
76   private static String JavaDoc graphicsPath = null;
77   /** The extension to use for graphical callout decorations. */
78   private static String JavaDoc graphicsExt = null;
79   /** The largest callout number that can be represented graphically. */
80   private static int graphicsMax = 10;
81
82   /** The FormatCallout object to use for formatting callouts. */
83   private static FormatCallout fCallout = null;
84
85   /**
86    * <p>Constructor for Verbatim</p>
87    *
88    * <p>All of the methods are static, so the constructor does nothing.</p>
89    */

90   public Verbatim() {
91   }
92
93   /**
94    * <p>Find the string value of a stylesheet variable or parameter</p>
95    *
96    * <p>Returns the string value of <code>varName</code> in the current
97    * <code>context</code>. Returns the empty string if the variable is
98    * not defined.</p>
99    *
100    * @param context The current stylesheet context
101    * @param varName The name of the variable (without the dollar sign)
102    *
103    * @return The string value of the variable
104    */

105   protected static String JavaDoc getVariable(Context context, String JavaDoc varName) {
106     Value variable = null;
107     String JavaDoc varString = null;
108
109     try {
110       variable = Extensions.evaluate(context, "$" + varName);
111       varString = variable.asString();
112       return varString;
113     } catch (TransformerException JavaDoc te) {
114       System.out.println("Undefined variable: " + varName);
115       return "";
116     } catch (IllegalArgumentException JavaDoc iae) {
117       System.out.println("Undefined variable: " + varName);
118       return "";
119     }
120   }
121
122   /**
123    * <p>Setup the parameters associated with line numbering</p>
124    *
125    * <p>This method queries the stylesheet for the variables
126    * associated with line numbering. It is called automatically before
127    * lines are numbered. The context is used to retrieve the values,
128    * this allows templates to redefine these variables.</p>
129    *
130    * <p>The following variables are queried. If the variables do not
131    * exist, builtin defaults will be used (but you may also get a bunch
132    * of messages from the Java interpreter).</p>
133    *
134    * <dl>
135    * <dt><code>linenumbering.everyNth</code></dt>
136    * <dd>Specifies the lines that will be numbered. The first line is
137    * always numbered. (builtin default: 5).</dd>
138    * <dt><code>linenumbering.width</code></dt>
139    * <dd>Specifies the width of the numbers. If the specified width is too
140    * narrow for the largest number needed, it will automatically be made
141    * wider. (builtin default: 3).</dd>
142    * <dt><code>linenumbering.separator</code></dt>
143    * <dd>Specifies the string that separates line numbers from lines
144    * in the program listing. (builtin default: " ").</dd>
145    * <dt><code>linenumbering.startinglinenumber</code></dt>
146    * <dd>Specifies the initial line number
147    * in the program listing. (builtin default: "1").</dd>
148    * <dt><code>stylesheet.result.type</code></dt>
149    * <dd>Specifies the stylesheet result type. The value is either 'fo'
150    * (for XSL Formatting Objects) or it isn't. (builtin default: html).</dd>
151    * </dl>
152    *
153    * @param context The current stylesheet context
154    *
155    */

156   private static void setupLineNumbering(Context context) {
157     // Hardcoded defaults
158
modulus = 5;
159     width = 3;
160     startinglinenumber = 1;
161     separator = " ";
162     foStylesheet = false;
163
164     String JavaDoc varString = null;
165
166     // Get the modulus
167
varString = getVariable(context, "linenumbering.everyNth");
168     try {
169       modulus = Integer.parseInt(varString);
170     } catch (NumberFormatException JavaDoc nfe) {
171       System.out.println("$linenumbering.everyNth is not a number: " + varString);
172     }
173
174     // Get the width
175
varString = getVariable(context, "linenumbering.width");
176     try {
177       width = Integer.parseInt(varString);
178     } catch (NumberFormatException JavaDoc nfe) {
179       System.out.println("$linenumbering.width is not a number: " + varString);
180     }
181
182     // Get the startinglinenumber
183
varString = getVariable(context, "linenumbering.startinglinenumber");
184     try {
185       startinglinenumber = Integer.parseInt(varString);
186     } catch (NumberFormatException JavaDoc nfe) {
187       System.out.println("$linenumbering.startinglinenumber is not a number: " + varString);
188     }
189
190     // Get the separator
191
varString = getVariable(context, "linenumbering.separator");
192     separator = varString;
193
194     // Get the stylesheet type
195
varString = getVariable(context, "stylesheet.result.type");
196     foStylesheet = (varString.equals("fo"));
197   }
198
199   /**
200    * <p>Number lines in a verbatim environment</p>
201    *
202    * <p>The extension function expects the following variables to be
203    * available in the calling context: $linenumbering.everyNth,
204    * $linenumbering.width, $linenumbering.separator, and
205    * $stylesheet.result.type.</p>
206    *
207    * <p>This method adds line numbers to a result tree fragment. Each
208    * newline that occurs in a text node is assumed to start a new line.
209    * The first line is always numbered, every subsequent 'everyNth' line
210    * is numbered (so if everyNth=5, lines 1, 5, 10, 15, etc. will be
211    * numbered. If there are fewer than everyNth lines in the environment,
212    * every line is numbered.</p>
213    *
214    * <p>Every line number will be right justified in a string 'width'
215    * characters long. If the line number of the last line in the
216    * environment is too long to fit in the specified width, the width
217    * is automatically increased to the smallest value that can hold the
218    * number of the last line. (In other words, if you specify the value 2
219    * and attempt to enumerate the lines of an environment that is 100 lines
220    * long, the value 3 will automatically be used for every line in the
221    * environment.)</p>
222    *
223    * <p>The 'separator' string is inserted between the line
224    * number and the original program listing. Lines that aren't numbered
225    * are preceded by a 'width' blank string and the separator.</p>
226    *
227    * <p>If inline markup extends across line breaks, markup changes are
228    * required. All the open elements are closed before the line break and
229    * "reopened" afterwards. The reopened elements will have the same
230    * attributes as the originals, except that 'name' and 'id' attributes
231    * are not duplicated if the stylesheet.result.type is "html" and
232    * 'id' attributes will not be duplicated if the result type is "fo".</p>
233    *
234    * @param rtf The result tree fragment of the verbatim environment.
235    *
236    * @return The modified result tree fragment.
237    */

238   public static NodeSetValue numberLines (Context context,
239                       NodeSetValue rtf_ns) {
240
241     FragmentValue rtf = (FragmentValue) rtf_ns;
242
243     setupLineNumbering(context);
244
245     try {
246       LineCountEmitter lcEmitter = new LineCountEmitter();
247       rtf.replay(lcEmitter);
248       int numLines = lcEmitter.lineCount();
249
250       int listingModulus = numLines < modulus ? 1 : modulus;
251
252       double log10numLines = Math.log(numLines) / Math.log(10);
253
254       int listingWidth = width < log10numLines+1
255     ? (int) Math.floor(log10numLines + 1)
256     : width;
257
258       Controller controller = context.getController();
259       NamePool namePool = controller.getNamePool();
260       NumberLinesEmitter nlEmitter = new NumberLinesEmitter(controller,
261                                 namePool,
262                                 startinglinenumber,
263                                 listingModulus,
264                                 listingWidth,
265                                 separator,
266                                 foStylesheet);
267       rtf.replay(nlEmitter);
268       return nlEmitter.getResultTreeFragment();
269     } catch (TransformerException JavaDoc e) {
270       // This "can't" happen.
271
System.out.println("Transformer Exception in numberLines");
272       return rtf;
273     }
274   }
275
276   /**
277    * <p>Setup the parameters associated with callouts</p>
278    *
279    * <p>This method queries the stylesheet for the variables
280    * associated with line numbering. It is called automatically before
281    * callouts are processed. The context is used to retrieve the values,
282    * this allows templates to redefine these variables.</p>
283    *
284    * <p>The following variables are queried. If the variables do not
285    * exist, builtin defaults will be used (but you may also get a bunch
286    * of messages from the Java interpreter).</p>
287    *
288    * <dl>
289    * <dt><code>callout.graphics</code></dt>
290    * <dd>Are we using callout graphics? A value of 0 or "" is false,
291    * any other value is true. If callout graphics are not used, the
292    * parameters related to graphis are not queried.</dd>
293    * <dt><code>callout.graphics.path</code></dt>
294    * <dd>Specifies the path to callout graphics.</dd>
295    * <dt><code>callout.graphics.extension</code></dt>
296    * <dd>Specifies the extension ot use for callout graphics.</dd>
297    * <dt><code>callout.graphics.number.limit</code></dt>
298    * <dd>Identifies the largest number that can be represented as a
299    * graphic. Larger callout numbers will be represented using text.</dd>
300    * <dt><code>callout.defaultcolumn</code></dt>
301    * <dd>Specifies the default column for callout bullets that do not
302    * specify a column.</dd>
303    * <dt><code>stylesheet.result.type</code></dt>
304    * <dd>Specifies the stylesheet result type. The value is either 'fo'
305    * (for XSL Formatting Objects) or it isn't. (builtin default: html).</dd>
306    * </dl>
307    *
308    * @param context The current stylesheet context
309    *
310    */

311   private static void setupCallouts(Context context) {
312     NamePool namePool = context.getController().getNamePool();
313
314     boolean useGraphics = false;
315     boolean useUnicode = false;
316
317     int unicodeStart = 49;
318     int unicodeMax = 0;
319
320     String JavaDoc unicodeFont = "";
321
322     // Hardcoded defaults
323
defaultColumn = 60;
324     graphicsPath = null;
325     graphicsExt = null;
326     graphicsMax = 0;
327     foStylesheet = false;
328     calloutsSetup = true;
329
330     Value variable = null;
331     String JavaDoc varString = null;
332
333     // Get the stylesheet type
334
varString = getVariable(context, "stylesheet.result.type");
335     foStylesheet = (varString.equals("fo"));
336
337     // Get the default column
338
varString = getVariable(context, "callout.defaultcolumn");
339     try {
340       defaultColumn = Integer.parseInt(varString);
341     } catch (NumberFormatException JavaDoc nfe) {
342       System.out.println("$callout.defaultcolumn is not a number: "
343              + varString);
344     }
345
346     // Use graphics at all?
347
varString = getVariable(context, "callout.graphics");
348     useGraphics = !(varString.equals("0") || varString.equals(""));
349
350     // Use unicode at all?
351
varString = getVariable(context, "callout.unicode");
352     useUnicode = !(varString.equals("0") || varString.equals(""));
353
354     if (useGraphics) {
355       // Get the graphics path
356
varString = getVariable(context, "callout.graphics.path");
357       graphicsPath = varString;
358
359       // Get the graphics extension
360
varString = getVariable(context, "callout.graphics.extension");
361       graphicsExt = varString;
362
363       // Get the number limit
364
varString = getVariable(context, "callout.graphics.number.limit");
365       try {
366     graphicsMax = Integer.parseInt(varString);
367       } catch (NumberFormatException JavaDoc nfe) {
368     System.out.println("$callout.graphics.number.limit is not a number: "
369                + varString);
370     graphicsMax = 0;
371       }
372
373       fCallout = new FormatGraphicCallout(namePool,
374                       graphicsPath,
375                       graphicsExt,
376                       graphicsMax,
377                       foStylesheet);
378     } else if (useUnicode) {
379       // Get the starting character
380
varString = getVariable(context, "callout.unicode.start.character");
381       try {
382     unicodeStart = Integer.parseInt(varString);
383       } catch (NumberFormatException JavaDoc nfe) {
384     System.out.println("$callout.unicode.start.character is not a number: "
385                + varString);
386     unicodeStart = 48;
387       }
388
389       // Get the number limit
390
varString = getVariable(context, "callout.unicode.number.limit");
391       try {
392     unicodeMax = Integer.parseInt(varString);
393       } catch (NumberFormatException JavaDoc nfe) {
394     System.out.println("$callout.unicode.number.limit is not a number: "
395                + varString);
396     unicodeStart = 0;
397       }
398
399       // Get the font
400
unicodeFont = getVariable(context, "callout.unicode.font");
401       if (unicodeFont == null) {
402     unicodeFont = "";
403       }
404
405       fCallout = new FormatUnicodeCallout(namePool,
406                       unicodeFont,
407                       unicodeStart,
408                       unicodeMax,
409                       foStylesheet);
410     } else {
411       fCallout = new FormatTextCallout(namePool, foStylesheet);
412     }
413   }
414
415   /**
416    * <p>Insert text callouts into a verbatim environment.</p>
417    *
418    * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
419    * in the supplied <tt>areaspec</tt> and decorates the supplied
420    * result tree fragment with appropriate callout markers.</p>
421    *
422    * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
423    * its content will be used for the label, otherwise the callout
424    * number will be used, surrounded by parenthesis. Callout numbers may
425    * also be represented as graphics. Callouts are
426    * numbered in document order. All of the <tt>area</tt>s in an
427    * <tt>areaset</tt> get the same number.</p>
428    *
429    * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
430    * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
431    * If only a line is specified, the callout decoration appears in
432    * the defaultColumn. Lines will be padded with blanks to reach the
433    * necessary column, but callouts that are located beyond the last
434    * line of the verbatim environment will be ignored.</p>
435    *
436    * <p>Callouts are inserted before the character at the line/column
437    * where they are to occur.</p>
438    *
439    * <p>If graphical callouts are used, and the callout number is less
440    * than or equal to the $callout.graphics.number.limit, the following image
441    * will be generated for HTML:
442    *
443    * <pre>
444    * &lt;img SRC="$callout.graphics.path/999$callout.graphics.ext"
445    * alt="conumber">
446    * </pre>
447    *
448    * If the $stylesheet.result.type is 'fo', the following image will
449    * be generated:
450    *
451    * <pre>
452    * &lt;fo:external-graphic SRC="$callout.graphics.path/999$callout.graphics.ext"/>
453    * </pre>
454    *
455    * <p>If the callout number exceeds $callout.graphics.number.limit,
456    * the callout will be the callout number surrounded by
457    * parenthesis.</p>
458    *
459    * @param context The stylesheet context.
460    * @param areaspecNodeSet The source node set that contains the areaspec.
461    * @param rtf The result tree fragment of the verbatim environment.
462    *
463    * @return The modified result tree fragment.
464    */

465
466   public static NodeSetValue insertCallouts (Context context,
467                          NodeList areaspecNodeList,
468                          NodeSetValue rtf_ns) {
469
470     FragmentValue rtf = (FragmentValue) rtf_ns;
471
472     setupCallouts(context);
473
474     try {
475       Controller controller = context.getController();
476       NamePool namePool = controller.getNamePool();
477       CalloutEmitter cEmitter = new CalloutEmitter(controller,
478                            namePool,
479                            defaultColumn,
480                            foStylesheet,
481                            fCallout);
482       cEmitter.setupCallouts(areaspecNodeList);
483       rtf.replay(cEmitter);
484       return cEmitter.getResultTreeFragment();
485     } catch (TransformerException JavaDoc e) {
486       // This "can't" happen.
487
System.out.println("Transformer Exception in insertCallouts");
488       return rtf;
489     }
490   }
491 }
492
Popular Tags