Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: com/nwalsh/saxon/Verbatim.java


1   // Verbatim.java - Saxon extensions supporting DocBook verbatim environments
2   
3   package com.nwalsh.saxon;
4   
5   import java.util.Stack;
6   import java.util.StringTokenizer;
7   import org.xml.sax.*;
8   import org.w3c.dom.*;
9   import javax.xml.transform.TransformerException;
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   *
24   * <p>Copyright (C) 2000 Norman Walsh.</p>
25   *
26   * <p>This class provides a
27   * <a href="http://users.iclway.co.uk/mhkay/saxon/">Saxon</a>
28   * implementation of two features that would be impractical to
29   * implement directly in XSLT: line numbering and callouts.</p>
30   *
31   * <p><b>Line Numbering</b></p>
32   * <p>The <tt>numberLines</tt> method takes a result tree
33   * fragment (assumed to contain the contents of a formatted verbatim
34   * element in DocBook: programlisting, screen, address, literallayout,
35   * or synopsis) and returns a result tree fragment decorated with
36   * line numbers.</p>
37   *
38   * <p><b>Callouts</b></p>
39   * <p>The <tt>insertCallouts</tt> method takes an
40   * <tt>areaspec</tt> and a result tree fragment
41   * (assumed to contain the contents of a formatted verbatim
42   * element in DocBook: programlisting, screen, address, literallayout,
43   * or synopsis) and returns a result tree fragment decorated with
44   * callouts.</p>
45   *
46   * <p><b>Change Log:</b></p>
47   * <dl>
48   * <dt>1.0</dt>
49   * <dd><p>Initial release.</p></dd>
50   * </dl>
51   *
52   * @author Norman Walsh
53   * <a href="mailto:ndw@nwalsh.com">ndw@nwalsh.com</a>
54   *
55   *
56   */
57  public class Verbatim {
58    /** True if the stylesheet is producing formatting objects */
59    private static boolean foStylesheet = false;
60    /** The modulus for line numbering (every 'modulus' line is numbered). */
61    private static int modulus = 0;
62    /** The width (in characters) of line numbers (for padding). */
63    private static int width = 0;
64    /** The separator between the line number and the verbatim text. */
65    private static String separator = "";
66  
67    /** True if callouts have been setup */
68    private static boolean calloutsSetup = false;
69    /** The default column for callouts that have only a line or line range */
70    private static int defaultColumn = 60;
71    /** The path to use for graphical callout decorations. */
72    private static String graphicsPath = null;
73    /** The extension to use for graphical callout decorations. */
74    private static String graphicsExt = null;
75    /** The largest callout number that can be represented graphically. */
76    private static int graphicsMax = 10;
77  
78    /** The FormatCallout object to use for formatting callouts. */
79    private static FormatCallout fCallout = null;
80  
81    /**
82     * <p>Constructor for Verbatim</p>
83     *
84     * <p>All of the methods are static, so the constructor does nothing.</p>
85     */
86    public Verbatim() {
87    }
88  
89    /**
90     * <p>Find the string value of a stylesheet variable or parameter</p>
91     *
92     * <p>Returns the string value of <code>varName</code> in the current
93     * <code>context</code>. Returns the empty string if the variable is
94     * not defined.</p>
95     *
96     * @param context The current stylesheet context
97     * @param varName The name of the variable (without the dollar sign)
98     *
99     * @return The string value of the variable
100    */
101   protected static String getVariable(Context context, String varName) {
102     Value variable = null;
103     String varString = null;
104 
105     try {
106       variable = Extensions.evaluate(context, "$" + varName);
107       varString = variable.asString();
108       return varString;
109     } catch (TransformerException te) {
110       System.out.println("Undefined variable: " + varName);
111       return "";
112     } catch (IllegalArgumentException iae) {
113       System.out.println("Undefined variable: " + varName);
114       return "";
115     }
116   }
117 
118   /**
119    * <p>Setup the parameters associated with line numbering</p>
120    *
121    * <p>This method queries the stylesheet for the variables
122    * associated with line numbering. It is called automatically before
123    * lines are numbered. The context is used to retrieve the values,
124    * this allows templates to redefine these variables.</p>
125    *
126    * <p>The following variables are queried. If the variables do not
127    * exist, builtin defaults will be used (but you may also get a bunch
128    * of messages from the Java interpreter).</p>
129    *
130    * <dl>
131    * <dt><code>linenumbering.everyNth</code></dt>
132    * <dd>Specifies the lines that will be numbered. The first line is
133    * always numbered. (builtin default: 5).</dd>
134    * <dt><code>linenumbering.width</code></dt>
135    * <dd>Specifies the width of the numbers. If the specified width is too
136    * narrow for the largest number needed, it will automatically be made
137    * wider. (builtin default: 3).</dd>
138    * <dt><code>linenumbering.separator</code></dt>
139    * <dd>Specifies the string that separates line numbers from lines
140    * in the program listing. (builtin default: " ").</dd>
141    * <dt><code>stylesheet.result.type</code></dt>
142    * <dd>Specifies the stylesheet result type. The value is either 'fo'
143    * (for XSL Formatting Objects) or it isn't. (builtin default: html).</dd>
144    * </dl>
145    *
146    * @param context The current stylesheet context
147    *
148    */
149   private static void setupLineNumbering(Context context) {
150     // Hardcoded defaults
151     modulus = 5;
152     width = 3;
153     separator = " ";
154     foStylesheet = false;
155 
156     String varString = null;
157 
158     // Get the modulus
159     varString = getVariable(context, "linenumbering.everyNth");
160     try {
161       modulus = Integer.parseInt(varString);
162     } catch (NumberFormatException nfe) {
163       System.out.println("$linenumbering.everyNth is not a number: " + varString);
164     }
165 
166     // Get the width
167     varString = getVariable(context, "linenumbering.width");
168     try {
169       width = Integer.parseInt(varString);
170     } catch (NumberFormatException nfe) {
171       System.out.println("$linenumbering.width is not a number: " + varString);
172     }
173 
174     // Get the separator
175     varString = getVariable(context, "linenumbering.separator");
176     separator = varString;
177 
178     // Get the stylesheet type
179     varString = getVariable(context, "stylesheet.result.type");
180     foStylesheet = (varString.equals("fo"));
181   }
182 
183   /**
184    * <p>Number lines in a verbatim environment</p>
185    *
186    * <p>The extension function expects the following variables to be
187    * available in the calling context: $linenumbering.everyNth,
188    * $linenumbering.width, $linenumbering.separator, and
189    * $stylesheet.result.type.</p>
190    *
191    * <p>This method adds line numbers to a result tree fragment. Each
192    * newline that occurs in a text node is assumed to start a new line.
193    * The first line is always numbered, every subsequent 'everyNth' line
194    * is numbered (so if everyNth=5, lines 1, 5, 10, 15, etc. will be
195    * numbered. If there are fewer than everyNth lines in the environment,
196    * every line is numbered.</p>
197    *
198    * <p>Every line number will be right justified in a string 'width'
199    * characters long. If the line number of the last line in the
200    * environment is too long to fit in the specified width, the width
201    * is automatically increased to the smallest value that can hold the
202    * number of the last line. (In other words, if you specify the value 2
203    * and attempt to enumerate the lines of an environment that is 100 lines
204    * long, the value 3 will automatically be used for every line in the
205    * environment.)</p>
206    *
207    * <p>The 'separator' string is inserted between the line
208    * number and the original program listing. Lines that aren't numbered
209    * are preceded by a 'width' blank string and the separator.</p>
210    *
211    * <p>If inline markup extends across line breaks, markup changes are
212    * required. All the open elements are closed before the line break and
213    * "reopened" afterwards. The reopened elements will have the same
214    * attributes as the originals, except that 'name' and 'id' attributes
215    * are not duplicated if the stylesheet.result.type is "html" and
216    * 'id' attributes will not be duplicated if the result type is "fo".</p>
217    *
218    * @param rtf The result tree fragment of the verbatim environment.
219    *
220    * @return The modified result tree fragment.
221    */
222   public static NodeSetValue numberLines (Context context,
223             NodeSetValue rtf_ns) {
224 
225     FragmentValue rtf = (FragmentValue) rtf_ns;
226 
227     setupLineNumbering(context);
228 
229     try {
230       LineCountEmitter lcEmitter = new LineCountEmitter();
231       rtf.replay(lcEmitter);
232       int numLines = lcEmitter.lineCount();
233 
234       int listingModulus = numLines < modulus ? 1 : modulus;
235 
236       double log10numLines = Math.log(numLines) / Math.log(10);
237 
238       int listingWidth = width < log10numLines+1
239   ? (int) Math.floor(log10numLines + 1)
240   : width;
241 
242       Controller controller = context.getController();
243       NamePool namePool = controller.getNamePool();
244       NumberLinesEmitter nlEmitter = new NumberLinesEmitter(controller,
245                   namePool,
246                   listingModulus,
247                   listingWidth,
248                   separator,
249                   foStylesheet);
250       rtf.replay(nlEmitter);
251       return nlEmitter.getResultTreeFragment();
252     } catch (TransformerException e) {
253       // This "can't" happen.
254       System.out.println("Transformer Exception in numberLines");
255       return rtf;
256     }
257   }
258 
259   /**
260    * <p>Setup the parameters associated with callouts</p>
261    *
262    * <p>This method queries the stylesheet for the variables
263    * associated with line numbering. It is called automatically before
264    * callouts are processed. The context is used to retrieve the values,
265    * this allows templates to redefine these variables.</p>
266    *
267    * <p>The following variables are queried. If the variables do not
268    * exist, builtin defaults will be used (but you may also get a bunch
269    * of messages from the Java interpreter).</p>
270    *
271    * <dl>
272    * <dt><code>callout.graphics</code></dt>
273    * <dd>Are we using callout graphics? A value of 0 or "" is false,
274    * any other value is true. If callout graphics are not used, the
275    * parameters related to graphis are not queried.</dd>
276    * <dt><code>callout.graphics.path</code></dt>
277    * <dd>Specifies the path to callout graphics.</dd>
278    * <dt><code>callout.graphics.extension</code></dt>
279    * <dd>Specifies the extension ot use for callout graphics.</dd>
280    * <dt><code>callout.graphics.number.limit</code></dt>
281    * <dd>Identifies the largest number that can be represented as a
282    * graphic. Larger callout numbers will be represented using text.</dd>
283    * <dt><code>callout.defaultcolumn</code></dt>
284    * <dd>Specifies the default column for callout bullets that do not
285    * specify a column.</dd>
286    * <dt><code>stylesheet.result.type</code></dt>
287    * <dd>Specifies the stylesheet result type. The value is either 'fo'
288    * (for XSL Formatting Objects) or it isn't. (builtin default: html).</dd>
289    * </dl>
290    *
291    * @param context The current stylesheet context
292    *
293    */
294   private static void setupCallouts(Context context) {
295     NamePool namePool = context.getController().getNamePool();
296 
297     boolean useGraphics = false;
298     boolean useUnicode = false;
299 
300     int unicodeStart = 49;
301     int unicodeMax = 0;
302 
303     String unicodeFont = "";
304 
305     // Hardcoded defaults
306     defaultColumn = 60;
307     graphicsPath = null;
308     graphicsExt = null;
309     graphicsMax = 0;
310     foStylesheet = false;
311     calloutsSetup = true;
312 
313     Value variable = null;
314     String varString = null;
315 
316     // Get the stylesheet type
317     varString = getVariable(context, "stylesheet.result.type");
318     foStylesheet = (varString.equals("fo"));
319 
320     // Get the default column
321     varString = getVariable(context, "callout.defaultcolumn");
322     try {
323       defaultColumn = Integer.parseInt(varString);
324     } catch (NumberFormatException nfe) {
325       System.out.println("$callout.defaultcolumn is not a number: "
326        + varString);
327     }
328 
329     // Use graphics at all?
330     varString = getVariable(context, "callout.graphics");
331     useGraphics = !(varString.equals("0") || varString.equals(""));
332 
333     // Use unicode at all?
334     varString = getVariable(context, "callout.unicode");
335     useUnicode = !(varString.equals("0") || varString.equals(""));
336 
337     if (useGraphics) {
338       // Get the graphics path
339       varString = getVariable(context, "callout.graphics.path");
340       graphicsPath = varString;
341 
342       // Get the graphics extension
343       varString = getVariable(context, "callout.graphics.extension");
344       graphicsExt = varString;
345 
346       // Get the number limit
347       varString = getVariable(context, "callout.graphics.number.limit");
348       try {
349   graphicsMax = Integer.parseInt(varString);
350       } catch (NumberFormatException nfe) {
351   System.out.println("$callout.graphics.number.limit is not a number: "
352          + varString);
353   graphicsMax = 0;
354       }
355 
356       fCallout = new FormatGraphicCallout(namePool,
357             graphicsPath,
358             graphicsExt,
359             graphicsMax,
360             foStylesheet);
361     } else if (useUnicode) {
362       // Get the starting character
363       varString = getVariable(context, "callout.unicode.start.character");
364       try {
365   unicodeStart = Integer.parseInt(varString);
366       } catch (NumberFormatException nfe) {
367   System.out.println("$callout.unicode.start.character is not a number: "
368          + varString);
369   unicodeStart = 48;
370       }
371 
372       // Get the number limit
373       varString = getVariable(context, "callout.unicode.number.limit");
374       try {
375   unicodeMax = Integer.parseInt(varString);
376       } catch (NumberFormatException nfe) {
377   System.out.println("$callout.unicode.number.limit is not a number: "
378          + varString);
379   unicodeStart = 0;
380       }
381 
382       // Get the font
383       unicodeFont = getVariable(context, "callout.unicode.font");
384       if (unicodeFont == null) {
385   unicodeFont = "";
386       }
387 
388       fCallout = new FormatUnicodeCallout(namePool,
389             unicodeFont,
390             unicodeStart,
391             unicodeMax,
392             foStylesheet);
393     } else {
394       fCallout = new FormatTextCallout(namePool, foStylesheet);
395     }
396   }
397 
398   /**
399    * <p>Insert text callouts into a verbatim environment.</p>
400    *
401    * <p>This method examines the <tt>areaset</tt> and <tt>area</tt> elements
402    * in the supplied <tt>areaspec</tt> and decorates the supplied
403    * result tree fragment with appropriate callout markers.</p>
404    *
405    * <p>If a <tt>label</tt> attribute is supplied on an <tt>area</tt>,
406    * its content will be used for the label, otherwise the callout
407    * number will be used, surrounded by parenthesis. Callout numbers may
408    * also be represented as graphics. Callouts are
409    * numbered in document order. All of the <tt>area</tt>s in an
410    * <tt>areaset</tt> get the same number.</p>
411    *
412    * <p>Only the <tt>linecolumn</tt> and <tt>linerange</tt> units are
413    * supported. If no unit is specifed, <tt>linecolumn</tt> is assumed.
414    * If only a line is specified, the callout decoration appears in
415    * the defaultColumn. Lines will be padded with blanks to reach the
416    * necessary column, but callouts that are located beyond the last
417    * line of the verbatim environment will be ignored.</p>
418    *
419    * <p>Callouts are inserted before the character at the line/column
420    * where they are to occur.</p>
421    *
422    * <p>If graphical callouts are used, and the callout number is less
423    * than or equal to the $callout.graphics.number.limit, the following image
424    * will be generated for HTML:
425    *
426    * <pre>
427    * &lt;img src="$callout.graphics.path/999$callout.graphics.ext"
428    *         alt="conumber">
429    * </pre>
430    *
431    * If the $stylesheet.result.type is 'fo', the following image will
432    * be generated:
433    *
434    * <pre>
435    * &lt;fo:external-graphic src="$callout.graphics.path/999$callout.graphics.ext"/>
436    * </pre>
437    *
438    * <p>If the callout number exceeds $callout.graphics.number.limit,
439    * the callout will be the callout number surrounded by
440    * parenthesis.</p>
441    *
442    * @param context The stylesheet context.
443    * @param areaspecNodeSet The source node set that contains the areaspec.
444    * @param rtf The result tree fragment of the verbatim environment.
445    *
446    * @return The modified result tree fragment.
447    */
448 
449   public static NodeSetValue insertCallouts (Context context,
450                NodeList areaspecNodeList,
451                NodeSetValue rtf_ns) {
452 
453     FragmentValue rtf = (FragmentValue) rtf_ns;
454 
455     setupCallouts(context);
456 
457     try {
458       Controller controller = context.getController();
459       NamePool namePool = controller.getNamePool();
460       CalloutEmitter cEmitter = new CalloutEmitter(controller,
461                namePool,
462                defaultColumn,
463                foStylesheet,
464                fCallout);
465       cEmitter.setupCallouts(areaspecNodeList);
466       rtf.replay(cEmitter);
467       return cEmitter.getResultTreeFragment();
468     } catch (TransformerException e) {
469       // This "can't" happen.
470       System.out.println("Transformer Exception in insertCallouts");
471       return rtf;
472     }
473   }
474 }