Coverage Report - com.ccg.macros.AtMacros
 
Classes in this File Line Coverage Branch Coverage Complexity
AtMacros
67% 
80% 
3.432
 
 1  
 /*----------------------------------------------------------------
 2  
  * $Id: AtMacros.java,v 1.1.1.1 2004/01/26 20:46:18 pkb Exp $
 3  
  * 
 4  
  * (c)2000 - Paul Blankenbaker
 5  
  *
 6  
  * Revision Log
 7  
  * 
 8  
  *  2000-11-14 13:43:20        Paul Blankenbaker 
 9  
  * 
 10  
  *         Created initial version
 11  
  */
 12  
 //----------------------------------------------------------------
 13  
 
 14  
 package com.ccg.macros;
 15  
 
 16  
 import java.io.*;                // needed for I/O operations
 17  
 import java.util.*;                // handy utility classes
 18  
 import java.lang.reflect.*;
 19  
 
 20  
 
 21  
 //----------------------------------------------------------------
 22  
 /** Implementation of a "@macro" {@link Interpreter Interpreter}.
 23  
  * 
 24  
  * <p>This class provides all of the building blocks for a full macro
 25  
  * {@link Interpreter interpreter}. It has all of the tools necessary
 26  
  * to scan ASCII text files looking for "&#064;macros" which it will
 27  
  * then attempt to process.
 28  
  *
 29  
  * <p>A "&#064;macro" entered in the following form will always be
 30  
  * recognized:
 31  
  *
 32  
  * <pre>
 33  
  * &#064;NAME(["ARG0"][,"ARG1"][,"ARG2"]...)
 34  
  * </pre>
 35  
  *
 36  
  * <p>By default, the NAME of the macro must contain only lower case
 37  
  * characters ('a'-'z'), upper case characters ('A'-'Z'), digits
 38  
  * ('0'-'9') and the underscore character ('_').
 39  
  *
 40  
  * <p>For example:
 41  
  *
 42  
  * <pre>
 43  
  * &#064;define("pkb","Paul Blankenbaker")
 44  
  * </pre>
 45  
  *
 46  
  * <p>The following alternative forms can be used as well:
 47  
  *
 48  
  * <dl>
 49  
  *
 50  
  * <dt>&#064;NAME</dt><dd>If the macro takes no arguments, and is
 51  
  * followed by white space, you can use this "short form" notation. It
 52  
  * is a recognized "short form" of &#064;NAME(). For example,
 53  
  * "<b>&#064;pkb - programmer</b>" is equivalent to "<b>&#064;pkb() -
 54  
  * programmer</b>".</dd>
 55  
  *
 56  
  * <dt>&#064;NAME(ARG0[,ARG1])</dt>If your arguments do not contain
 57  
  * any special characters (like spaces, quotes, commas, parenthesis),
 58  
  * you can optionally omit the double quotes. For example,
 59  
  * <b>&#064;define(pkb,Paul)</b> is interpretted the same as
 60  
  * <b>&#064;define("pkb","Paul")</b>.</dd>
 61  
  *
 62  
  * </dl>
 63  
  *
 64  
  * <p>Your best bet is to use the full form (include the parenthesis
 65  
  * and argument quoting) as that form will always be processed as you
 66  
  * expect.
 67  
  *
 68  
  * <p>At times, you may find that you need to escape characters in the
 69  
  * arguments you pass to the macros (in particular the double quote
 70  
  * character). The standard Java escape characters can be used in a
 71  
  * quoted argument. For example, to pass the argument <b>A "very" wet
 72  
  * day!</b> as a argument, one would construct the macro in the
 73  
  * following form:
 74  
  *
 75  
  * <pre>
 76  
  * &#064;define("dayType","A \"very\" wet day!")
 77  
  * <pre>
 78  
  *
 79  
  * <p><b>NOTE:</b>The escape characters are only recognized within
 80  
  * quoted parameters. The following escape sequences are supported:
 81  
  *
 82  
  * <table border=1>
 83  
  *
 84  
  * <tr>
 85  
  * <th align="center"><strong>Escape Seq</strong></th>
 86  
  * <th align="center"><strong>Unicode Value</strong></th>
 87  
  * <th align="center"><strong>Description</strong></th>
 88  
  * </tr>
 89  
  *
 90  
  * <tr>
 91  
  * <td>\b</td><td>&#092;u0008</td><td>Backspace</td>
 92  
  * </tr>
 93  
  *
 94  
  * <tr>
 95  
  * <td>\f</td><td>&#092;u000c</td><td>Form feed</td>
 96  
  * </tr>
 97  
  *
 98  
  * <tr>
 99  
  * <td>\n</td><td>&#092;u000a</td><td>Line feed (new line)</td>
 100  
  * </tr>
 101  
  *
 102  
  * <tr>
 103  
  * <td>\r</td><td>&#092;u000d</td><td>Carriage return</td>
 104  
  * </tr>
 105  
  *
 106  
  * <tr>
 107  
  * <td>\t</td><td>&#092;u0009</td><td>Tab</td>
 108  
  * </tr>
 109  
  *
 110  
  * <tr>
 111  
  * <td>\'</td><td>&#092;u0027</td><td>Single Quote</td>
 112  
  * </tr>
 113  
  *
 114  
  * <tr>
 115  
  * <td>\"</td><td>&#092;u0022</td><td>Double Quote</td>
 116  
  * </tr>
 117  
  *
 118  
  * <tr>
 119  
  * <td>\\</td><td>&#092;u005c</td><td>Backslash</td>
 120  
  * </tr>
 121  
  *
 122  
  * <tr>
 123  
  * <td>\(</td><td>&#092;u0040</td><td>Begin Parenthesis</td>
 124  
  * </tr>
 125  
  *
 126  
  * <tr>
 127  
  * <td>\)</td><td>&#092;u0041</td><td>End Parenthesis</td>
 128  
  * </tr>
 129  
  *
 130  
  * <tr><td>&#092;uXXXX</td><td>&#092;uXXXX</td><td>Any unicode
 131  
  * character - must use 4 hex digits</td></tr>
 132  
  *
 133  
  * </table>
 134  
  *
 135  
  * <p>Using an unrecognized escape sequence in a string will have
 136  
  * unpredictable results. Most likely, the escape character will
 137  
  * simply be ignored (and not appear in the output). You may find
 138  
  * yourself hit with this when quoting DOS style file names. For
 139  
  * example, "c:\t.txt" should be written as "c:\\t.txt" to prevent the
 140  
  * "\t" from coming out as a tab character.
 141  
  *
 142  
  * <p>Also, the escaping of a double quote is only required (it never
 143  
  * hurts to do it), if:</p>
 144  
  *
 145  
  * <ul>
 146  
  * <li>The double quote is outside of parathesis.
 147  
  * <li>AND, the double quote is followed by a comma or end parenthesis.
 148  
  * </ul>
 149  
  *
 150  
  * <p>For example, the following parameters are equal <b>"Hello
 151  
  * \"Paul\"."</b> and <b>"Hello "Paul"."</b>, however the first form
 152  
  * is preferred (support of the latter form was added later as it
 153  
  * seemed reasonable). However, <b>"\"Paul\", B."</b> works where as
 154  
  * <b>""Paul", B."</b> does not (due to the comma following the double
 155  
  * quote).
 156  
  *
 157  
  * <p>There is one issue in regards to including paranthesis in quoted
 158  
  * strings. For example, consider the following:
 159  
  *
 160  
  * <pre>
 161  
  * &#064;define("FullName","&#064;param(0) &#064;param(1)")
 162  
  * &#064;define("PlusPhone","&#064;FullName("&#064;param(0)","&#064;param(1)") (&#064;param(2))")
 163  
  * &#064;define("UnmatchedParen","((--->")
 164  
  * </pre>
 165  
  *
 166  
  * <p>The definition of "PlusPhone" above wants to pass two parameters
 167  
  * to the "&#064;define" macro. The first parameter is not a problem,
 168  
  * but the second parameter contains nested quoted arguments. The
 169  
  * interpreter is "smart" in that it detects the parenthesis following
 170  
  * "&#064;FullName" and ignores all quotes until the last parenthesis
 171  
  * is found. This allows the interpreter to correctly extract the two
 172  
  * arguments in the complex example. However, because of this rule,
 173  
  * the "UnmatchedParen" will not be interpretted properly. When the
 174  
  * two mismatched parathesis in "((--->" string are encountered the
 175  
  * interpreter will ignore the ending quote until it finds two
 176  
  * matching ending parenthesis. If you find yourself in this
 177  
  * situation, you will need to "escape" your parenthesis and write
 178  
  * your macro as follows:
 179  
  *
 180  
  * <pre>
 181  
 &#064;define("FullName","&#064;param(0) &#064;param(1)")
 182  
 &#064;define("PlusPhone","&#064;FullName("&#064;param(0)","&#064;param(1)") (&#064;param(2))")
 183  
 &#064;define("UnmatchedParen","\(\(--->")
 184  
 &#064;define(pkb,"&#064;PlusPhone(Paul,"Blankenbaker","555-7676")")
 185  
 &#064;UnmatchedParen() &#064;pkb
 186  
  * </pre>
 187  
  *
 188  
  * <p>It should be noted, that this class doesn't actually provide any
 189  
  * built in macros itself. This class just provides the parsing
 190  
  * building blocks and methods to make the construction of custom
 191  
  * macro sets possible. Typically other classes make use of the {@link
 192  
  * #addMacro(String,String)} method to define simple text replacement,
 193  
  * the {@link #addMacro(String,MacroHandler)} method to install more
 194  
  * sophisticated macro handlers, and the {@link #addMacros(Object)} to
 195  
  * automatically add methods with a certain signature to the set of
 196  
  * recognized macro handlers.
 197  
  *
 198  
  * <p>The {@link At At} class is a full implementation of an
 199  
  * interpreter which defines several built in macros and provides a
 200  
  * means for additional macros to be defined based on the contents of
 201  
  * the processed file.
 202  
  * 
 203  
  * 
 204  
  * @version $Revision: 1.1.1.1 $
 205  
  * 
 206  
  * @since 1.0
 207  
  * 
 208  
  * @author $Author: pkb $
 209  
  * 
 210  
  * @see At */
 211  
 //----------------------------------------------------------------
 212  
 
 213  
 public class AtMacros implements Interpreter {
 214  
 
 215  
   //----------------------------------------------------------------
 216  
   /** Initializes object to the default parsing rules.
 217  
    * 
 218  
    * <p>This constructor prepares the macro interpreter with the
 219  
    * default parsing rules, but <b>without</b> any definitions.
 220  
    *
 221  
    * <p>After construction, one will typically use the the "add macro"
 222  
    * methods to define macros (see {@link #addMacro(String,String)},
 223  
    * {@link #addMacro(String,MacroHandler)}, and {@link
 224  
    * #addMacros(Object)}).
 225  
    *
 226  
    * <p>Typically one will probably not want to change the parsing
 227  
    * characters, HOWEVER you can with methods like {@link
 228  
    * #setStartMacroChar(char)}, {@link #setEndMacroChars(String)},
 229  
    * etc.
 230  
    * 
 231  
    * @since        1.0 */
 232  
   //----------------------------------------------------------------
 233  
 
 234  26
   public AtMacros() {
 235  26
     _Macros = new Hashtable();
 236  26
     _RecursionDepth = 128;
 237  26
     _StartMacroChar = '@';
 238  26
     _StartQuoteChar = '\"';
 239  26
     _EndQuoteChar = '\"';
 240  26
     _EscapeChar = '\\';
 241  26
     _StartParamChar = '(';
 242  26
     _EndParamChar = ')';
 243  26
     _SepParamChar = ',';
 244  
 
 245  
                                 // array of parameters for invocation
 246  26
     _Params = new Vector[_RecursionDepth];
 247  
 
 248  
                                 // characters which indicate end of a macro
 249  26
     setEndMacroChars("()~!#$%^&*-+=@{}[]\"|\\\';:<>,./?` \t\n\r");
 250  26
   }
 251  
 
 252  
 
 253  
   //----------------------------------------------------------------
 254  
   /** A copy constructor of another {@link AtMacros AtMacros} object.
 255  
    * 
 256  
    * <p>This constructor makes a duplicate of another {@link AtMacros
 257  
    * AtMacros} object. Initially after construction, both objects will
 258  
    * have the same set of definitions - however any new definitions
 259  
    * added to either the new copy or the original will only affect the
 260  
    * object they are added to (each object will have its own
 261  
    * definition rules after the intial construction).
 262  
    * 
 263  
    * @param        from
 264  
    * 
 265  
    *         The {@link AtMacros AtMacros}
 266  
    * 
 267  
    * @since        1.0
 268  
    * 
 269  
    * @see #AtMacros() */
 270  
   //----------------------------------------------------------------
 271  
 
 272  0
   public AtMacros(AtMacros from) {
 273  0
     _Macros = (Hashtable) from._Macros.clone();
 274  0
     _RecursionDepth = from._RecursionDepth;
 275  0
     _StartMacroChar = from._StartMacroChar;
 276  0
     _StartQuoteChar = from._StartQuoteChar;
 277  0
     _EndQuoteChar = from._EndQuoteChar;
 278  0
     _EscapeChar = from._EscapeChar;
 279  0
     _StartParamChar = from._StartParamChar;
 280  0
     _EndParamChar = from._EndParamChar;
 281  0
     _SepParamChar = from._SepParamChar;
 282  
 
 283  0
     _RecursionDepth = from._RecursionDepth;
 284  0
     _Depth = 0;
 285  
                                 // array of parameters for invocation
 286  0
     _Params = new Vector[_RecursionDepth];
 287  0
     _ParamIdx = 0;
 288  0
     _EndMacroChars = (boolean[]) from._EndMacroChars.clone();
 289  0
   }
 290  
 
 291  
 
 292  
   //----------------------------------------------------------------
 293  
   /** Fetch one of the parameters (arguments) passed to a macro.
 294  
    * 
 295  
    * <p>This method is used by macro handlers to retrieve arguments
 296  
    * which are passed to the macro. For example, if one were to write
 297  
    * a macro handler for the "&#064;define(NAME,TEXT)" macro, the
 298  
    * handler would use this method to retrieve the information (see
 299  
    * {@link At#define} for the definition of this handler).
 300  
    *
 301  
    * <p>For example, if the following text were being processed by the
 302  
    * "define" macro handler:
 303  
    *
 304  
    * <pre>
 305  
    * &#064;define("pkb","Paul Blankenbaker")
 306  
    * </pre>
 307  
    *
 308  
    * <p>The "define" macro handler would get the value <b>pkb</b> if
 309  
    * it invoked {@link #getParameter getParameter(0)}, and the value
 310  
    * <b>Paul Blankenbaker</b> if it invoked {@link #getParameter
 311  
    * getParameter(1)}.
 312  
    * 
 313  
    * <p>Requesting a parameter that was not passed returns a zero
 314  
    * length string (not null).
 315  
    *
 316  
    * <p>It should be noted that each parameter returned is also run
 317  
    * through the interpreter if you set the second parameter to
 318  
    * true. This means that if one of the arguments contains macros
 319  
    * itself, they will be expanded in the returned result. Pass false
 320  
    * as the second parameter to get the "raw" string value of the
 321  
    * original parameter.
 322  
    * 
 323  
    * @param        index
 324  
    * 
 325  
    *         The index of the argument you want to request (0 correpsonds
 326  
    *         to the first argument, 1 to the second, and so on).
 327  
    * 
 328  
    * @param        eval
 329  
    * 
 330  
    *         Whether the raw value of the parameter should be evaluated or
 331  
    *         not.
 332  
    * 
 333  
    * @throws        IOException
 334  
    * 
 335  
    *         If a I/O error is encountered when reading from a input
 336  
    *         source, or writing to a output destination.
 337  
    * 
 338  
    * @throws        InterpretException
 339  
    * 
 340  
    *         If the contents of the source being interpretted violates the
 341  
    *         rules of the interpreter. Note, each interpretter is able to
 342  
    *         define its own set of rules as to what is allowed or not.
 343  
    * 
 344  
    * @return
 345  
    * 
 346  
    *         The interpretted results of the parameter passed - never
 347  
    *         returns null, but may return a zero length string (if the
 348  
    *         index specify is out of range or no argument was passed).
 349  
    * 
 350  
    * @since        1.0 */
 351  
   //----------------------------------------------------------------
 352  
 
 353  
   public String getParameter(int i, boolean eval) 
 354  
     throws InterpretException, IOException {
 355  
                                 // if no parameter vector to search,
 356  
                                 // or negative index passed, return
 357  
                                 // empty string
 358  374
     int pi = _ParamIdx - 1;
 359  
 
 360  374
     if ((i < 0) || (pi < 0) || (pi >= _Params.length)) return "";
 361  
 
 362  
                                 // if parameter vector is null or
 363  
                                 // doesn't contain a value for the
 364  
                                 // index specified, return null
 365  374
     Vector params = _Params[pi];
 366  
 
 367  374
     if ((params == null) || (i >= params.size())) return "";
 368  
 
 369  
                                 // if no entry for specified parameter
 370  
                                 // return empty string
 371  352
     Object entry = params.elementAt(i);
 372  352
     if (entry == null) return "";
 373  
 
 374  
                                 // otherwise return interpretted parameter
 375  352
     if (eval) {
 376  
                                 // back off parameter index incase
 377  
                                 // parameter contains references to
 378  
                                 // other parameters
 379  352
       _ParamIdx--;                
 380  352
       String val = interpretCheck(entry.toString());
 381  352
       _Params[pi] = params;        // incase parameters clobbered, restore now
 382  352
       _ParamIdx++;
 383  352
       return val;
 384  
     }
 385  
 
 386  0
     return entry.toString();
 387  
   }
 388  
 
 389  
 
 390  
   //----------------------------------------------------------------
 391  
   /** Fetch one of the parameters (arguments) passed to a macro.
 392  
    * 
 393  
    * <p>This method is used by macro handlers to retrieve arguments
 394  
    * which are passed to the macro. For example, if one were to write
 395  
    * a macro handler for the "&#064;define(NAME,TEXT)" macro, the
 396  
    * handler would use this method to retrieve the information (see
 397  
    * {@link At#define} for the definition of this handler).
 398  
    *
 399  
    * <p>For example, if the following text were being processed by the
 400  
    * "define" macro handler:
 401  
    *
 402  
    * <pre>
 403  
    * &#064;define("pkb","Paul Blankenbaker")
 404  
    * </pre>
 405  
    *
 406  
    * <p>The "define" macro handler would get the value <b>pkb</b> if
 407  
    * it invoked {@link #getParameter getParameter(0)}, and the value
 408  
    * <b>Paul Blankenbaker</b> if it invoked {@link #getParameter
 409  
    * getParameter(1)}.
 410  
    * 
 411  
    * <p>Requesting a parameter that was not passed returns a zero
 412  
    * length string (not null).
 413  
    *
 414  
    * <p>It should be noted that each parameter returned is also run
 415  
    * through the interpreter. This means that if one of the arguments
 416  
    * contains macros itself, they will be expanded in the returned
 417  
    * result.
 418  
    * 
 419  
    * @param        index
 420  
    * 
 421  
    *         The index of the argument you want to request (0 correpsonds
 422  
    *         to the first argument, 1 to the second, and so on).
 423  
    * 
 424  
    * @throws        IOException
 425  
    * 
 426  
    *         If a I/O error is encountered when reading from a input
 427  
    *         source, or writing to a output destination.
 428  
    * 
 429  
    * @throws        InterpretException
 430  
    * 
 431  
    *         If the contents of the source being interpretted violates the
 432  
    *         rules of the interpreter. Note, each interpretter is able to
 433  
    *         define its own set of rules as to what is allowed or not.
 434  
    * 
 435  
    * @return
 436  
    * 
 437  
    *         The interpretted results of the parameter passed - never
 438  
    *         returns null, but may return a zero length string (if the
 439  
    *         index specify is out of range or no argument was passed).
 440  
    * 
 441  
    * @since        1.0 */
 442  
   //----------------------------------------------------------------
 443  
 
 444  
   public String getParameter(int i) throws InterpretException, IOException {
 445  
 
 446  0
     return getParameter(i,true);
 447  
 
 448  
   }
 449  
 
 450  
 
 451  
   //----------------------------------------------------------------
 452  
   /** Add a simple string subsitution macro.
 453  
    * 
 454  
    * <p>This command defines (or replaces an existing definition) a
 455  
    * macro which evaluates to a simple text replacement. Hence, when
 456  
    * the macro is encountered during processing it will be replaced
 457  
    * with the <em>interpretted</em> value assigned. For example:
 458  
    *
 459  
    * <code><pre>
 460  
    * public static void foo(AtMacros am) {
 461  
    *   am.addMacro("first","Paul");
 462  
    *   am.addMacro("last","Blankenbaker");
 463  
    *   am.addMacro("name","&#064;first() &#064;last()");
 464  
    * }
 465  
    * </pre></code>
 466  
    *
 467  
    * <p>After the following is run, when the macro "&#064;first" is
 468  
    * encountered in the text being processed, it will be replaced with
 469  
    * "Paul". The key thing to notice here is that the replaced text is
 470  
    * also interpretted. This means that when the "&#064;name" macro is
 471  
    * encountered in the text being processed, its contained text will
 472  
    * be interpretted as well. This would result in "Paul Blankenbaker"
 473  
    * appearing in the output in this example.
 474  
    * 
 475  
    * @param        mname
 476  
    * 
 477  
    *         The name of the macro to define (if you pass null or a zero
 478  
    *         length string we ignore your request).
 479  
    * 
 480  
    * @param        val
 481  
    * 
 482  
    *         The value to associate with the macro (null is treated like "")
 483  
    * 
 484  
    * @since        1.0
 485  
    * 
 486  
    * @see #addMacros */
 487  
   //----------------------------------------------------------------
 488  
 
 489  
   public void addMacro(String name, String val) {
 490  672
     if ((name != null) && (name.length() > 0)) {
 491  672
       if (val == null) val = "";
 492  
 
 493  672
       _Macros.put(name,val);
 494  
     }
 495  672
   }
 496  
 
 497  
 
 498  
   //----------------------------------------------------------------
 499  
   /** Fetch the object definition for a particular macro.
 500  
    * 
 501  
    * @param        mname
 502  
    * 
 503  
    *         The name of the macro you want to lookup the associated value
 504  
    *         for. You can pass null - and get null back.
 505  
    * 
 506  
    * @return
 507  
    * 
 508  
    *         The {@link java.lang.Object object} associated with the macro
 509  
    *         name, or null if there isn't one associated.
 510  
    * 
 511  
    * @since        1.0 */
 512  
   //----------------------------------------------------------------
 513  
 
 514  
   public Object getMacro(String name) {
 515  86
     return (name != null) ? _Macros.get(name) : null;
 516  
   }
 517  
 
 518  
 
 519  
   //----------------------------------------------------------------
 520  
   /** Fetch the list of all macro names which are currently defined.
 521  
    * 
 522  
    * @return
 523  
    * 
 524  
    *         The {@link Enumeration list} of all macro names which are
 525  
    *         currently defined.
 526  
    * 
 527  
    * @since        1.0 */
 528  
   //----------------------------------------------------------------
 529  
 
 530  
   public Enumeration getMacroNames() {
 531  0
     return _Macros.keys();
 532  
   }
 533  
 
 534  
 
 535  
   //----------------------------------------------------------------
 536  
   /** Associate a {@link MacroHandler MacroHandler} with a macro name.
 537  
    *
 538  
    * <p>This installs any object which implements the {@link
 539  
    * MacroHandler MacroHandler} interface into the defined set of
 540  
    * macros.
 541  
    * 
 542  
    * @param        mname
 543  
    * 
 544  
    *         The name of the macro to define (this must NOT be null!).
 545  
    * 
 546  
    * @param        val
 547  
    * 
 548  
    *         The value to associate with the macro (null is treated like "")
 549  
    * 
 550  
    * @since        1.0 */
 551  
   //----------------------------------------------------------------
 552  
 
 553  
   public void addMacro(String name, MacroHandler mh) {
 554  0
     Object o = mh;
 555  0
     if (o == null) o = "";
 556  
 
 557  0
     _Macros.put(name,o);
 558  0
   }
 559  
 
 560  
 
 561  
   //----------------------------------------------------------------
 562  
   /** Add all "macro handling methods" from a class to our defined set.
 563  
    * 
 564  
    * <p>This method can be used to install a LOT of macro handlers
 565  
    * based upon the method signatures within the object passed. It
 566  
    * uses Java's reflection mechanisms to inspect the methods in the
 567  
    * object passed. For every method which matches the following
 568  
    * signature:
 569  
    *
 570  
    * <pre>
 571  
    * public void mName({@link Output Output} out, {@link Vector Vector} args)
 572  
    *   throws {@link IOException IOException}, {@link InterpretException InterpretException}
 573  
    * </pre>
 574  
    *
 575  
    * <p>A new macro is added such that if the method name in its macro
 576  
    * form ("&#064;mName") is encountered, the associated method in the
 577  
    * object passed will be invoked to carry out the processing.
 578  
    *
 579  
    * <p>This is the easy way to add your own specialized macro
 580  
    * handlers, and what the {@link At} utility relies upon to add all
 581  
    * of its "built-in" functions (like: "&#064;define",
 582  
    * "&#064;include", "&#064;param", etc).
 583  
    * 
 584  
    * @param        o
 585  
    * 
 586  
    *         The object to inspect and look for methods matching the rules
 587  
    *         specified above (must not be null).
 588  
    * 
 589  
    * @since        1.0
 590  
    * 
 591  
    * @see At */
 592  
   //----------------------------------------------------------------
 593  
 
 594  
   public void addMacros(Object macroHandler) {
 595  38
     Method[] methods = macroHandler.getClass().getMethods();
 596  
 
 597  1008
     for (int i = 0; i < methods.length; i++) {
 598  970
       Class[] ptypes = methods[i].getParameterTypes();
 599  970
       if ((ptypes.length == 2) &&
 600  4
           (ptypes[0] == Output.class) &&
 601  
           (ptypes[1] == Vector.class)) {
 602  388
         String mname = methods[i].getName();
 603  388
         _Macros.put(mname,new MacroHandlerMethod(macroHandler,methods[i]));
 604  
       }
 605  
     }
 606  38
   }
 607  
 
 608  
 
 609  
   //----------------------------------------------------------------
 610  
   /** Process a {@link Input input source} and write the results to a {@link Output output destination}.
 611  
    * 
 612  
    * <p>This is one of the methods used to actually do the processing
 613  
    * of data. It reads information from the input, processes any
 614  
    * contained macros and writes the results to the output.
 615  
    *
 616  
    * <p>This is how one typically starts to process macros. This
 617  
    * method simply resets our current recursion depth and then invokes
 618  
    * the {@link #interpretCheck(Input,Output) recursion safe}
 619  
    * implementation to do the actual processing.
 620  
    * 
 621  
    * @param        in
 622  
    * 
 623  
    *         A {@link Input input source} to read and interpret
 624  
    * 
 625  
    * @param        out
 626  
    * 
 627  
    *         A {@link Output output destination} to write the results of
 628  
    *         the interpretation.
 629  
    * 
 630  
    * @throws        InterpretException
 631  
    * 
 632  
    *         If a internal (or macro error) is encountered while processing.
 633  
    * 
 634  
    * @throws        IOException
 635  
    * 
 636  
    *         If a problem reading input or writing output is encountered.
 637  
    * 
 638  
    * @since        1.0
 639  
    * 
 640  
    * @see #interpret(String)
 641  
    * @see #interpretCheck(Input,Output) */
 642  
   //----------------------------------------------------------------
 643  
 
 644  
   public void interpret(Input in, Output out)
 645  
     throws IOException, InterpretException {
 646  
 
 647  38
     _Depth = 0;
 648  38
     _ParamIdx = 0;
 649  38
     interpretCheck(in,out);
 650  
 
 651  38
   }
 652  
 
 653  
 
 654  
   //----------------------------------------------------------------
 655  
   /** Process a source string and return the resulting string.
 656  
    * 
 657  
    * <p>This method evaluates a simple string for any contained
 658  
    * macros. Any contained macros are expanded and the resulting
 659  
    * string is returned.
 660  
    * 
 661  
    * @param        in
 662  
    * 
 663  
    *         A string to interpret - if you pass null you get null back.
 664  
    * 
 665  
    * @return
 666  
    * 
 667  
    *         The results of processing the input string (any contained
 668  
    *         macros are evaluated)
 669  
    * 
 670  
    * @throws        InterpretException
 671  
    * 
 672  
    *         If a internal (or macro error) is encountered while processing.
 673  
    * 
 674  
    * @throws        IOException
 675  
    * 
 676  
    *         If a problem reading input or writing output is encountered.
 677  
    * 
 678  
    * @since        1.0
 679  
    * 
 680  
    * @see #interpret(Input,Output) */
 681  
   //----------------------------------------------------------------
 682  
 
 683  
   public String interpret(String in)
 684  
     throws IOException, InterpretException {
 685  
 
 686  
                                 // if null or no macro start character, just
 687  
                                 // return the string
 688  128
     if ((in == null) || (in.indexOf(getStartMacroChar()) < 0)) return in;
 689  
                                 // otherwise do interpretation
 690  128
     OutputStringBuffer osb = new OutputStringBuffer(in.length()*2);
 691  128
     _Depth = 0;
 692  128
     _ParamIdx = 0;
 693  128
     interpretCheck(new Input(in),osb);
 694  
 
 695  128
     return osb.toString();
 696  
   }
 697  
 
 698  
 
 699  
   //----------------------------------------------------------------
 700  
   /** Process a source string and return the resulting string.
 701  
    * 
 702  
    * <p>This method evaluates a simple string for any contained
 703  
    * macros. Any contained macros are expanded and the resulting
 704  
    * string is returned.
 705  
    *
 706  
    * <p>Since macros often require recursive evaulation, this method
 707  
    * makes sure that the recursion {@link #setDepth depth} is not
 708  
    * exceeded. If the recursion depth is exceeded, a {@link
 709  
    * InterpretException Interpret exception} is thrown indicating as
 710  
    * much.
 711  
    * 
 712  
    * @param        in
 713  
    * 
 714  
    *         A string to interpret - if you pass null you get null back.
 715  
    * 
 716  
    * @return
 717  
    * 
 718  
    *         The results of processing the input string (any contained
 719  
    *         macros are evaluated)
 720  
    * 
 721  
    * @throws        InterpretException
 722  
    * 
 723  
    *         If a internal (or macro error) is encountered while processing.
 724  
    * 
 725  
    * @throws        IOException
 726  
    * 
 727  
    *         If a problem reading input or writing output is encountered.
 728  
    * 
 729  
    * @since        1.0
 730  
    * 
 731  
    * @see #interpretCheck(Input,Output) */
 732  
   //----------------------------------------------------------------
 733  
 
 734  
   public String interpretCheck(String in)
 735  
     throws IOException, InterpretException {
 736  
                                 // if null or no macro start character, just
 737  
                                 // return the string
 738  2396
     if ((in == null) || (in.indexOf(getStartMacroChar()) < 0)) return in;
 739  
                                 // otherwise do interpretation
 740  598
     OutputStringBuffer osb = new OutputStringBuffer(in.length()*2);
 741  598
     interpretCheck(new Input(in),osb);
 742  598
     return osb.toString();
 743  
   }
 744  
 
 745  
 
 746  
   //----------------------------------------------------------------
 747  
   /** Process a {@link Input input source} and write the results to a {@link Output output destination}.
 748  
    * 
 749  
    * <p>This is one of the methods used to actually do the processing
 750  
    * of data. It reads information from the input, processes any
 751  
    * contained macros and writes the results to the output.
 752  
    *
 753  
    * <p>Since macros often require recursive evaulation, this method
 754  
    * makes sure that the recursion {@link #setDepth depth} is not
 755  
    * exceeded. If the recursion depth is exceeded, a {@link
 756  
    * InterpretException Interpret exception} is thrown indicating as
 757  
    * much.
 758  
    * 
 759  
    * @param        in
 760  
    * 
 761  
    *         A {@link Input input source} to read and interpret
 762  
    * 
 763  
    * @param        out
 764  
    * 
 765  
    *         A {@link Output output destination} to write the results of
 766  
    *         the interpretation.
 767  
    * 
 768  
    * @throws        InterpretException
 769  
    * 
 770  
    *         If a internal (or macro error) is encountered while processing.
 771  
    * 
 772  
    * @throws        IOException
 773  
    * 
 774  
    *         If a problem reading input or writing output is encountered.
 775  
    * 
 776  
    * @since        1.0
 777  
    * 
 778  
    * @see #interpret(String)
 779  
    * @see #interpret(Input,Output) */
 780  
   //----------------------------------------------------------------
 781  
 
 782  
   public void interpretCheck(Input in, Output out)
 783  
     throws IOException, InterpretException {
 784  
     
 785  1084
     if (_Depth >= _RecursionDepth) {
 786  0
       throw new InterpretException("recursion depth of "+_Depth+
 787  
                                    " exceeded - giving up");
 788  
     }
 789  
 
 790  1084
     _Depth++;
 791  
 
 792  1084
     char atSymbol = getStartMacroChar();
 793  1084
     char startParamChar = getStartParamChar();
 794  
 
 795  1084
     boolean[] endMacro = _EndMacroChars;
 796  1084
     int endMacroLen = endMacro.length;
 797  
 
 798  
                                 // get info about current buffer
 799  1084
     char[] shiftBuf = in.getBuffer();
 800  1084
     int ofs = in.getOffset();
 801  1084
     int len = in.getLength();
 802  
 
 803  
                                 // while data to process
 804  
     for (;;) {
 805  4178
       if (ofs >= len) {                // if we are out of data in buffer
 806  
                                 // try to get more
 807  1142
         len = in.shiftAndFill(ofs);
 808  1142
         ofs = in.getOffset();        // reset offset after shift/fill
 809  
 
 810  1142
         if (ofs >= len) {        // if no more, then we are done
 811  1084
           _Depth--;                // back off recursion depth count
 812  1084
           return;
 813  
         }
 814  
       }
 815  
 
 816  3094
       int i = ofs;                // look for next "@" symbol
 817  23542
       for (;(i < len) && (shiftBuf[i] != atSymbol); i++);
 818  
 
 819  
                                 // if characters prior to "@" symbol,
 820  
                                 // write them out
 821  3094
       if (i > ofs) {
 822  2170
         out.write(shiftBuf,ofs,i-ofs);
 823  
 
 824  2170
         ofs = i;
 825  
                                 // if no "@" symbol found, then go get
 826  
                                 // more data
 827  2170
         if (ofs >= len) continue;
 828  
       }
 829  
 
 830  
       //----------------------------------------------------------------  
 831  
       // Found the "@" symbol (start of macro)
 832  
       //----------------------------------------------------------------
 833  
 
 834  2790
       ofs++;                        // move past the "@" symbol
 835  2790
       int j = ofs;                // look for character which ends macro
 836  41234
       for (;j < len;j++) { 
 837  21974
         char ch = shiftBuf[j];
 838  21974
         if ((ch < endMacroLen) && endMacro[ch]) break;
 839  
       }
 840  
 
 841  
                                 // if we found a name of a macro
 842  2790
       if ((j < len) || (i == 0)) {
 843  2790
         String mname = new String(shiftBuf,ofs,j-ofs);
 844  2790
         Object macro = _Macros.get(mname);
 845  2790
         if (macro == null) {        // if macro doesn't exist
 846  930
           out.write(shiftBuf,i,j-i);
 847  930
         }
 848  
         else {
 849  1860
           Vector params = null;
 850  1860
           if ((j < len) && (shiftBuf[j] == startParamChar)) {
 851  1820
             j++;
 852  1820
             in.setOffset(j);
 853  1820
             params = parseParams(in);
 854  1820
             j = in.getOffset();
 855  1820
             len = in.getLength();
 856  
           }
 857  
 
 858  1860
           if (macro instanceof MacroHandler) {
 859  1582
             ((MacroHandler)macro).process(out,params);
 860  1582
           }
 861  
           else {
 862  
 
 863  
                                 // evaluate defined parameters prior
 864  
                                 // to processing simple text subsitutions
 865  
             /*
 866  
             if (params != null) {
 867  
               for (int k = 0; k < params.size(); k++) {
 868  
                 StringBuffer pval = (StringBuffer) params.elementAt(k);
 869  
                 if ((pval != null) && (pval.length() > 0)) {
 870  
                   oparam.clear();
 871  
                   interpretCheck(new Input(pval.toString()),oparam);
 872  
                   params.setElementAt(oparam.toString(),k);
 873  
                 }
 874  
               }
 875  
             }
 876  
             */
 877  
 
 878  278
             _Params[_ParamIdx++] = params;
 879  
 
 880  278
             interpretCheck(new Input(macro.toString()),out);
 881  
 
 882  278
             if (_ParamIdx <= 0) {
 883  0
               String pmsg = "Internal error - _ParamIdx invalid ("+
 884  
                 (_ParamIdx-1)+") after evaluating \""+mname+"\".";
 885  0
               throw new InterpretException(pmsg);
 886  
             }
 887  278
             _ParamIdx--;
 888  278
             _Params[_ParamIdx] = null;
 889  
           }
 890  
 
 891  
         }
 892  2790
         ofs = j;                // advance offset
 893  2790
       }
 894  
 
 895  
       //----------------------------------------------------------------
 896  
       // if we didn't find end of macro, see if we can shift buffer
 897  
       // over (maybe we need to fetch more data)
 898  
       //----------------------------------------------------------------
 899  
 
 900  
       else {                        // we didn't find end of macro name
 901  0
         if (i > 0) {                // see if we can shift buffer to left
 902  0
           len = in.shiftAndFill(i);
 903  0
           ofs = in.getOffset();
 904  0
         }
 905  
         else {                        // otherwise, simply write out the character
 906  
                                 // and process as normal
 907  0
           out.write(shiftBuf,i,1);
 908  
         }
 909  
       }
 910  2790
     }
 911  
 
 912  
   }
 913  
 
 914  
 
 915  
   //----------------------------------------------------------------
 916  
   /** Holds the current depth of the recursion as we are processing
 917  
    * 
 918  
    * @serial        
 919  
    * 
 920  
    * @since        1.0  */
 921  
   //----------------------------------------------------------------
 922  
 
 923  
   private int _Depth;
 924  
 
 925  
 
 926  
   //----------------------------------------------------------------
 927  
   /** Set the current depth of the recursion as we are processing
 928  
    * 
 929  
    * @param        val
 930  
    * 
 931  
    *         New int value to assign.
 932  
    * 
 933  
    * @see #getDepth  */
 934  
   //----------------------------------------------------------------
 935  
 
 936  
   public void setDepth(int val) {
 937  0
     _Depth = val;
 938  0
   }
 939  
 
 940  
 
 941  
   //----------------------------------------------------------------
 942  
   /** Get the current depth of the recursion as we are processing
 943  
    * 
 944  
    * @return
 945  
    * 
 946  
    *         Current int value assigned.
 947  
    * 
 948  
    * @see #setDepth  */
 949  
   //----------------------------------------------------------------
 950  
 
 951  
   public int getDepth() {
 952  0
     return _Depth;
 953  
   }
 954  
 
 955  
 
 956  
   //----------------------------------------------------------------
 957  
   /** Holds the number of recursive calls allowed while evalutating
 958  
    * 
 959  
    * @serial        
 960  
    * 
 961  
    * @since        1.0  */
 962  
   //----------------------------------------------------------------
 963  
 
 964  
   private int _RecursionDepth;
 965  
                                 // array of vectors for parameters
 966  
   private Vector[] _Params;
 967  
   private int _ParamIdx;
 968  
 
 969  
 
 970  
   //----------------------------------------------------------------
 971  
   /** Set the number of recursive calls allowed while evalutating
 972  
    *
 973  
    * Typically one doesn't need to worry about setting a recursion
 974  
    * depth limit (the default value should be sufficient in most
 975  
    * cases). However, if one anticipates that they will need to
 976  
    * evalute heavily recursive definitions, you can call this method
 977  
    * to increase how deep we allow the evaluations to proceed before
 978  
    * giving up.
 979  
    *
 980  
    * <p>This value sets the maximum recursive call limit. If the
 981  
    * interpretation of the source input requires a recursion level
 982  
    * greater than this value, the interpreter will give up and throw
 983  
    * an exception. This provides a "safety" from entering a infinite
 984  
    * recursion loop that doesn't terminate until memory is exhausted.
 985  
    * 
 986  
    * @param        val
 987  
    * 
 988  
    *         New int value to assign.
 989  
    * 
 990  
    * @see #getRecursionDepth  */
 991  
   //----------------------------------------------------------------
 992  
 
 993  
   public void setRecursionDepth(int val) {
 994  0
     if (val != _RecursionDepth) {
 995  
                                 // make copy of parameter stack - but
 996  
                                 // with new size limit
 997  0
       Vector[] nparams = new Vector[val];
 998  0
       int n = (val > _RecursionDepth) ? _RecursionDepth : val;
 999  0
       for (int i = 0; i < n; i++) nparams[i] = _Params[i];
 1000  0
       _RecursionDepth = val;
 1001  0
       _Params = nparams;
 1002  
     }
 1003  0
   }
 1004  
 
 1005  
 
 1006  
   //----------------------------------------------------------------
 1007  
   /** Get the number of recursive calls allowed while evalutating
 1008  
    * 
 1009  
    * @return
 1010  
    * 
 1011  
    *         Current int value assigned.
 1012  
    * 
 1013  
    * @see #setRecursionDepth  */
 1014  
   //----------------------------------------------------------------
 1015  
 
 1016  
   public int getRecursionDepth() {
 1017  0
     return _RecursionDepth;
 1018  
   }
 1019  
 
 1020  
 
 1021  
   //----------------------------------------------------------------
 1022  
   /** Holds the symbol which indicates the start of a macro
 1023  
    * 
 1024  
    * @serial        
 1025  
    * 
 1026  
    * @since        1.0  */
 1027  
   //----------------------------------------------------------------
 1028  
 
 1029  
   private char _StartMacroChar;
 1030  
 
 1031  
 
 1032  
   //----------------------------------------------------------------
 1033  
   /** Set the symbol which indicates the start of a macro
 1034  
    * 
 1035  
    * @param        val
 1036  
    * 
 1037  
    *         New char value to assign.
 1038  
    * 
 1039  
    * @see #getStartMacroChar  */
 1040  
   //----------------------------------------------------------------
 1041  
 
 1042  
   public void setStartMacroChar(char val) {
 1043  0
     _StartMacroChar = val;
 1044  0
   }
 1045  
 
 1046  
 
 1047  
   //----------------------------------------------------------------
 1048  
   /** Get the symbol which indicates the start of a macro
 1049  
    * 
 1050  
    * @return
 1051  
    * 
 1052  
    *         Current char value assigned.
 1053  
    * 
 1054  
    * @see #setStartMacroChar  */
 1055  
   //----------------------------------------------------------------
 1056  
 
 1057  
   public char getStartMacroChar() {
 1058  3608
     return _StartMacroChar;
 1059  
   }
 1060  
 
 1061  
 
 1062  
   //----------------------------------------------------------------
 1063  
   /** Set the symbol(s) which indicate the end of a macro name
 1064  
    * 
 1065  
    * @param        val
 1066  
    * 
 1067  
    *         String containing characters which mark the end of a token. */
 1068  
   //----------------------------------------------------------------
 1069  
 
 1070  
   public boolean setEndMacroChars(String val) {
 1071  26
     boolean rc = (val != null) && (val.length() > 0);
 1072  
 
 1073  26
     if (rc) {
 1074  26
       if (_EndMacroChars == null) _EndMacroChars = new boolean[128];
 1075  
 
 1076  3354
       for (int i = 0; i < _EndMacroChars.length; i++ ) {
 1077  3328
         _EndMacroChars[i] = false;
 1078  
       }
 1079  
 
 1080  26
       int vlen = val.length();
 1081  936
       for (int i = 0; i < vlen; i++) {
 1082  910
         int pos = val.charAt(i);
 1083  910
         if ((pos >= 0) && (pos < _EndMacroChars.length)) {
 1084  910
           _EndMacroChars[pos] = true;
 1085  910
         }
 1086  0
         else rc = false;
 1087  
       }
 1088  
     }
 1089  
 
 1090  26
     return rc;                        // true if ALL values specified accepted
 1091  
   }
 1092  
 
 1093  
 
 1094  
 
 1095  
   //----------------------------------------------------------------
 1096  
   /** Set/clear individual character which indicates end of macro name.
 1097  
    * 
 1098  
    * <p>This method can be used to individually add/remove characters
 1099  
    * which indicate the end of a macro name.
 1100  
    * 
 1101  
    * @param        c
 1102  
    * 
 1103  
    *         The character which we are trying to match against
 1104  
    * 
 1105  
    * @param        indicates_end
 1106  
    * 
 1107  
    *         Set to true if you want the character to indicate the end of a
 1108  
    *         macro, or false if you don't want it to indicate the end.
 1109  
    * 
 1110  
    * @return
 1111  
    * 
 1112  
    *         true if we allowed the addition, false if not
 1113  
    * 
 1114  
    * @since        1.0
 1115  
    * 
 1116  
    * @see #setEndMacroChars */
 1117  
   //----------------------------------------------------------------
 1118  
 
 1119  
   public boolean setEndMacroChar(char match, boolean indicates_end) {
 1120  0
     if ((match >= 0) && (match < _EndMacroChars.length)) {
 1121  0
       _EndMacroChars[match] = indicates_end;
 1122  0
       return true;
 1123  
     }
 1124  0
     return false;
 1125  
   }
 1126  
 
 1127  
 
 1128  
   //----------------------------------------------------------------
 1129  
   /** Holds the symbol which starts a "quoted" string
 1130  
    * 
 1131  
    * @serial        
 1132  
    * 
 1133  
    * @since        1.0  */
 1134  
   //----------------------------------------------------------------
 1135  
 
 1136  
   private char _StartQuoteChar;
 1137  
 
 1138  
 
 1139  
   //----------------------------------------------------------------
 1140  
   /** Set the symbol which starts a "quoted" string
 1141  
    * 
 1142  
    * @param        val
 1143  
    * 
 1144  
    *         New char value to assign.
 1145  
    * 
 1146  
    * @see #getStartQuoteChar  */
 1147  
   //----------------------------------------------------------------
 1148  
 
 1149  
   public void setStartQuoteChar(char val) {
 1150  0
     _StartQuoteChar = val;
 1151  0
   }
 1152  
 
 1153  
 
 1154  
   //----------------------------------------------------------------
 1155  
   /** Get the symbol which starts a "quoted" string
 1156  
    * 
 1157  
    * @return
 1158  
    * 
 1159  
    *         Current char value assigned.
 1160  
    * 
 1161  
    * @see #setStartQuoteChar  */
 1162  
   //----------------------------------------------------------------
 1163  
 
 1164  
   public char getStartQuoteChar() {
 1165  1820
     return _StartQuoteChar;
 1166  
   }
 1167  
 
 1168  
 
 1169  
   //----------------------------------------------------------------
 1170  
   /** Holds the symbol which ends a "quoted" string
 1171  
    * 
 1172  
    * @serial        
 1173  
    * 
 1174  
    * @since        1.0  */
 1175  
   //----------------------------------------------------------------
 1176  
 
 1177  
   private char _EndQuoteChar;
 1178  
 
 1179  
 
 1180  
   //----------------------------------------------------------------
 1181  
   /** Set the symbol which ends a "quoted" string
 1182  
    * 
 1183  
    * @param        val
 1184  
    * 
 1185  
    *         New char value to assign.
 1186  
    * 
 1187  
    * @see #getEndQuoteChar  */
 1188  
   //----------------------------------------------------------------
 1189  
 
 1190  
   public void setEndQuoteChar(char val) {
 1191  0
     _EndQuoteChar = val;
 1192  0
   }
 1193  
 
 1194  
 
 1195  
   //----------------------------------------------------------------
 1196  
   /** Get the symbol which ends a "quoted" string
 1197  
    * 
 1198  
    * @return
 1199  
    * 
 1200  
    *         Current char value assigned.
 1201  
    * 
 1202  
    * @see #setEndQuoteChar  */
 1203  
   //----------------------------------------------------------------
 1204  
 
 1205  
   public char getEndQuoteChar() {
 1206  1820
     return _EndQuoteChar;
 1207  
   }
 1208  
 
 1209  
 
 1210  
   //----------------------------------------------------------------
 1211  
   /** Holds the "escape" character which can appear in quoted strings
 1212  
    * 
 1213  
    * @serial        
 1214  
    * 
 1215  
    * @since        1.0  */
 1216  
   //----------------------------------------------------------------
 1217  
 
 1218  
   private char _EscapeChar;
 1219  
 
 1220  
 
 1221  
   //----------------------------------------------------------------
 1222  
   /** Set the "escape" character which can appear in quoted strings
 1223  
    * 
 1224  
    * @param        val
 1225  
    * 
 1226  
    *         New char value to assign.
 1227  
    * 
 1228  
    * @see #getEscapeChar  */
 1229  
   //----------------------------------------------------------------
 1230  
 
 1231  
   public void setEscapeChar(char val) {
 1232  0
     _EscapeChar = val;
 1233  0
   }
 1234  
 
 1235  
 
 1236  
   //----------------------------------------------------------------
 1237  
   /** Get the "escape" character which can appear in quoted strings
 1238  
    * 
 1239  
    * @return
 1240  
    * 
 1241  
    *         Current char value assigned.
 1242  
    * 
 1243  
    * @see #setEscapeChar  */
 1244  
   //----------------------------------------------------------------
 1245  
 
 1246  
   public char getEscapeChar() {
 1247  1820
     return _EscapeChar;
 1248  
   }
 1249  
 
 1250  
 
 1251  
   //----------------------------------------------------------------
 1252  
   /** Holds the symbol which indicates the start of the parameter list
 1253  
    * 
 1254  
    * @serial        
 1255  
    * 
 1256  
    * @since        1.0  */
 1257  
   //----------------------------------------------------------------
 1258  
 
 1259  
   private char _StartParamChar;
 1260  
 
 1261  
 
 1262  
   //----------------------------------------------------------------
 1263  
   /** Set the symbol which indicates the start of the parameter list
 1264  
    * 
 1265  
    * @param        val
 1266  
    * 
 1267  
    *         New char value to assign.
 1268  
    * 
 1269  
    * @see #getStartParamChar  */
 1270  
   //----------------------------------------------------------------
 1271  
 
 1272  
   public void setStartParamChar(char val) {
 1273  0
     if (val != _StartParamChar) {
 1274  0
       setEndMacroChar(_StartParamChar,false);
 1275  0
       setEndMacroChar(val,true);
 1276  0
       _StartParamChar = val;
 1277  
     }
 1278  0
   }
 1279  
 
 1280  
 
 1281  
   //----------------------------------------------------------------
 1282  
   /** Get the symbol which indicates the start of the parameter list
 1283  
    * 
 1284  
    * @return
 1285  
    * 
 1286  
    *         Current char value assigned.
 1287  
    * 
 1288  
    * @see #setStartParamChar  */
 1289  
   //----------------------------------------------------------------
 1290  
 
 1291  
   public char getStartParamChar() {
 1292  2904
     return _StartParamChar;
 1293  
   }
 1294  
 
 1295  
 
 1296  
   //----------------------------------------------------------------
 1297  
   /** Holds the symbol which indicates the end of the parameter list
 1298  
    * 
 1299  
    * @serial        
 1300  
    * 
 1301  
    * @since        1.0  */
 1302  
   //----------------------------------------------------------------
 1303  
 
 1304  
   private char _EndParamChar;
 1305  
 
 1306  
 
 1307  
   //----------------------------------------------------------------
 1308  
   /** Set the symbol which indicates the end of the parameter list
 1309  
    * 
 1310  
    * @param        val
 1311  
    * 
 1312  
    *         New char value to assign.
 1313  
    * 
 1314  
    * @see #getEndParamChar  */
 1315  
   //----------------------------------------------------------------
 1316  
 
 1317  
   public void setEndParamChar(char val) {
 1318  0
     _EndParamChar = val;
 1319  0
   }
 1320  
 
 1321  
 
 1322  
   //----------------------------------------------------------------
 1323  
   /** Get the symbol which indicates the end of the parameter list
 1324  
    * 
 1325  
    * @return
 1326  
    * 
 1327  
    *         Current char value assigned.
 1328  
    * 
 1329  
    * @see #setEndParamChar  */
 1330  
   //----------------------------------------------------------------
 1331  
 
 1332  
   public char getEndParamChar() {
 1333  1820
     return _EndParamChar;
 1334  
   }
 1335  
 
 1336  
 
 1337  
   //----------------------------------------------------------------
 1338  
   /** Holds the character used to separate parameters in a parameter list
 1339  
    * 
 1340  
    * @serial        
 1341  
    * 
 1342  
    * @since        1.0  */
 1343  
   //----------------------------------------------------------------
 1344  
 
 1345  
   private char _SepParamChar;
 1346  
 
 1347  
 
 1348  
   //----------------------------------------------------------------
 1349  
   /** Set the character used to separate parameters in a parameter list
 1350  
    * 
 1351  
    * @param        val
 1352  
    * 
 1353  
    *         New char value to assign.
 1354  
    * 
 1355  
    * @see #getSepParamChar  */
 1356  
   //----------------------------------------------------------------
 1357  
 
 1358  
   public void setSepParamChar(char val) {
 1359  0
     _SepParamChar = val;
 1360  0
   }
 1361  
 
 1362  
 
 1363  
   //----------------------------------------------------------------
 1364  
   /** Get the character used to separate parameters in a parameter list
 1365  
    * 
 1366  
    * @return
 1367  
    * 
 1368  
    *         Current char value assigned.
 1369  
    * 
 1370  
    * @see #setSepParamChar  */
 1371  
   //----------------------------------------------------------------
 1372  
 
 1373  
   public char getSepParamChar() {
 1374  1820
     return _SepParamChar;
 1375  
   }
 1376  
 
 1377  
 
 1378  
   //----------------------------------------------------------------  
 1379  
   // Private data
 1380  
   //----------------------------------------------------------------
 1381  
 
 1382  
                                 // used to lookup macro definitions
 1383  
   private Hashtable _Macros;
 1384  
                                 // map of characters which mark end of
 1385  
                                 // a macro name
 1386  
   private boolean[] _EndMacroChars;
 1387  
 
 1388  
   protected Vector parseParams(Input in) 
 1389  
     throws IOException, InterpretException {
 1390  
 
 1391  1820
     Vector params = new Vector();
 1392  
 
 1393  1820
     char startQuoteChar = getStartQuoteChar();
 1394  1820
     char endQuoteChar = getEndQuoteChar();
 1395  1820
     char escChar = getEscapeChar();
 1396  1820
     char sepParamChar = getSepParamChar();
 1397  1820
     char startParamChar = getStartParamChar();
 1398  1820
     char endParamChar = getEndParamChar();
 1399  
 
 1400  1820
     char[] buf = in.getBuffer();
 1401  1820
     int ofs = in.getOffset();
 1402  1820
     int len = in.getLength();
 1403  
 
 1404  
                                 // while looking for more parameters
 1405  
     for (;;) {
 1406  3476
       if (ofs >= len) {
 1407  0
         len = in.shiftAndFill(ofs);
 1408  0
         ofs = in.getOffset();
 1409  0
         if (ofs >= len) {
 1410  0
           throw new InterpretException();
 1411  
         }
 1412  
       }
 1413  
 
 1414  3476
       StringBuffer pval = new StringBuffer();
 1415  3476
       char c = buf[ofs++];
 1416  
         
 1417  
                                 // if end of parameters, then success
 1418  3476
       if (c == endParamChar) {
 1419  78
         if (pval.length() > 0) params.addElement(pval);
 1420  
 
 1421  
                                 // update position in output
 1422  78
         in.setOffset(ofs);
 1423  78
         return params;
 1424  
       }
 1425  
 
 1426  
                                 // if zero length parameter
 1427  3398
       if (c == sepParamChar) {
 1428  
                         // skip separator, add param, and look for next
 1429  20
         params.addElement(pval);
 1430  20
         continue;
 1431  
       }
 1432  
 
 1433  
       //----------------------------------------------------------------  
 1434  
       // Parse quoted parameter
 1435  
       //----------------------------------------------------------------
 1436  
 
 1437  3378
       if (c == startQuoteChar) {
 1438  
                                 // was character escaped?
 1439  2792
         boolean escaped = false;
 1440  2792
         int unicodeCnt = 0;
 1441  2792
         char unicodeChar = 0;
 1442  2792
         int depth=0;                // used to count nested macro calls
 1443  
 
 1444  
         for (;;) {
 1445  
                                 // get next character
 1446  
                                 // see if we need to shift in more data
 1447  61576
           if (ofs >= len) {
 1448  12
             len = in.shiftAndFill(ofs);
 1449  12
             ofs = in.getOffset();
 1450  12
             if (ofs >= len) {
 1451  0
               StringBuffer sb = new StringBuffer(128);
 1452  0
               sb.append(startQuoteChar);
 1453  0
               if (pval.length() > 12) {
 1454  0
                 pval.setLength(12);
 1455  0
                 pval.append("...");
 1456  
               }
 1457  0
               sb.append(" missing ending quote (");
 1458  0
               sb.append(endQuoteChar);
 1459  0
               sb.append(").");
 1460  0
               throw new InterpretException(sb.toString());
 1461  
             }
 1462  
           }
 1463  61576
           c = buf[ofs++];
 1464  
 
 1465  
                                 // if character had been escaped
 1466  
                                 // (preceded by a "\" character
 1467  
                                 // typically) then handle special
 1468  61576
           if (escaped) {
 1469  
                                 // if we are building a unicode escape
 1470  
                                 // character, then join 4 hex digits
 1471  230
             if (unicodeCnt > 0) {
 1472  
                                 // get next hex digit
 1473  0
               int val = Character.digit(c,16);
 1474  0
               if (val == -1) {
 1475  0
                 throw new InterpretException("invalid \"\\uXXXX\" unicode "+
 1476  
                                              "escape sequence");
 1477  
               }
 1478  
                                 // add digit to current character being built
 1479  0
               unicodeChar <<= 4;
 1480  0
               unicodeChar += val;
 1481  0
               unicodeCnt--;
 1482  
                                 // if this was the last digit, then done
 1483  0
               if (unicodeCnt == 0) {
 1484  0
                 pval.append(unicodeChar);
 1485  0
                 escaped = false;
 1486  
               }
 1487  0
             }
 1488  
                                 // see if start of unicode escape sequence
 1489  230
             else if (c == 'u') {
 1490  0
               unicodeChar = 0;
 1491  0
               unicodeCnt = 4;
 1492  0
             }
 1493  
                                 // see if standard escape character
 1494  
             else {
 1495  230
               String escChars = "btnfr\"\'\\";
 1496  230
               char[] escSub = { 
 1497  
                 '\b', '\t', '\n', '\f', '\r', '\"', '\'', '\\'
 1498  
               };
 1499  230
               int pos = escChars.indexOf(c);
 1500  
                                 // if valid escape character, then append
 1501  230
               if (pos >= 0) pval.append(escSub[pos]);
 1502  
               else {                // otherwise, just quote next character
 1503  0
                 pval.append(c);
 1504  
               }
 1505  
                                 // no longer part of a escape sequence
 1506  230
               escaped = false;
 1507  
             }
 1508  230
             continue;
 1509  
           }
 1510  
                                 // if end of parameter list, decrement
 1511  
                                 // depth count
 1512  61346
           else if ((c == endParamChar) && (depth > 0)) {
 1513  2064
             depth--;
 1514  2064
             pval.append(c);
 1515  2064
           }
 1516  
 
 1517  
                                 // if start of another parameter list,
 1518  
                                 // then increment depth (so we pop out
 1519  
                                 // at the proper time)
 1520  59282
           else if (c == startParamChar) {
 1521  2064
             depth++;
 1522  2064
             pval.append(c);
 1523  2064
           }
 1524  
 
 1525  
                                 // if inside nested parenthesis,
 1526  
                                 // simply copy until we break free
 1527  57218
           else if (depth > 0) {
 1528  24888
             pval.append(c);
 1529  24888
           }
 1530  
                                 // if end of quote outside of nested
 1531  
                                 // macro calls. Handles following cases:
 1532  
                                 // 
 1533  
                                 // @f1("@f2("text")")
 1534  
                                 // 
 1535  
                                 // The "f1" macro would receive a
 1536  
                                 // parameter @f2("text"), which is
 1537  
                                 // what we want for nested processing
 1538  32330
           else if ((c == endQuoteChar) && (depth == 0)) {
 1539  
 
 1540  2808
             if (ofs >= len) {        // might need to load more data
 1541  0
               len = in.shiftAndFill(ofs);
 1542  0
               ofs = in.getOffset();
 1543  0
               if (ofs >= len) {
 1544  0
                 throw new InterpretException();
 1545  
               }
 1546  
             }
 1547  
 
 1548  2808
             c = buf[ofs++];
 1549  2808
             if (c == sepParamChar) {
 1550  
                                 // add parameter
 1551  1496
               params.addElement(pval);
 1552  1496
               break;
 1553  
             }
 1554  1312
             else if (c == endParamChar) {
 1555  
                                 // add parameter
 1556  1296
               params.addElement(pval);
 1557  1296
               in.setOffset(ofs);
 1558  1296
               return params;
 1559  
             }
 1560  
             else {
 1561  
                                 // if not using strict quoting rules
 1562  16
               ofs--;
 1563  
             /* uncomment below for strict quote rules
 1564  
 
 1565  
                @bold("My name is "Paul".") 
 1566  
                   Will cause an error with strict rules on - it needs to be:
 1567  
                @bold("My name is \"Paul\".")
 1568  
 
 1569  
               StringBuffer sb = new StringBuffer(128);
 1570  
               sb.append(startQuoteChar);
 1571  
               if (pval.length() > 12) {
 1572  
                 pval.setLength(12);
 1573  
                 pval.append("...");
 1574  
               }
 1575  
               pval.append(endQuoteChar);
 1576  
               sb.append(" not followed by \"");
 1577  
               sb.append(endParamChar);
 1578  
               sb.append("\" or \"");
 1579  
               sb.append(sepParamChar);
 1580  
               sb.append("\".");
 1581  
               throw new InterpretException(sb.toString());
 1582  
             */
 1583  
             }
 1584  16
           }
 1585  
                                 // see if we are entering the "escape"
 1586  
                                 // next character sequence
 1587  29522
           else if (c == escChar) {
 1588  230
             escaped = true;
 1589  230
           }
 1590  
                                 // otherwise, just append a normal
 1591  
                                 // character
 1592  29292
           else pval.append(c);
 1593  29292
         }
 1594  1496
       }
 1595  
 
 1596  
       //----------------------------------------------------------------  
 1597  
       // Parse non-quoted parameter
 1598  
       //----------------------------------------------------------------
 1599  
 
 1600  
       else for (;;) {
 1601  
                                 // if end of all parameters for macro
 1602  1396
         if (c == endParamChar) {
 1603  446
           params.addElement(pval);
 1604  446
           in.setOffset(ofs);
 1605  446
           return params;
 1606  
         }
 1607  
                                 // end of this parameter?
 1608  950
         else if (c == sepParamChar) {
 1609  140
           params.addElement(pval);
 1610  140
           break;
 1611  
         }
 1612  
                                 // normal character to append
 1613  810
         else pval.append(c);
 1614  
 
 1615  
                                 // get next character
 1616  
                                 // see if we need to shift in more data
 1617  810
         if (ofs >= len) {
 1618  0
           len = in.shiftAndFill(ofs);
 1619  0
           ofs = in.getOffset();
 1620  0
           if (ofs >= len) {
 1621  0
             throw new InterpretException();
 1622  
           }
 1623  
         }
 1624  810
         c = buf[ofs++];
 1625  810
       }
 1626  1636
     }
 1627  
 
 1628  
     //----------------------------------------------------------------  
 1629  
     // This point is never reached!
 1630  
     //----------------------------------------------------------------
 1631  
 
 1632  
     }
 1633  
 
 1634  
 }
 1635  
 
 1636  
 //----------------------------------------------------------------  
 1637  
 // Simple macro handler to invoke a specific method of a object
 1638  
 // when a macro match is made-
 1639  
 //----------------------------------------------------------------
 1640  
 
 1641  
 class MacroHandlerMethod implements MacroHandler {
 1642  
 
 1643  
   MacroHandlerMethod(Object o, Method m) {
 1644  
     _m = m;
 1645  
     _o = o;
 1646  
     _args = new Object[2];
 1647  
   }
 1648  
 
 1649  
   public void process(Output out, Vector args)
 1650  
     throws IOException, InterpretException {
 1651  
 
 1652  
     _args[0] = out;
 1653  
     _args[1] = args;
 1654  
 
 1655  
     try {
 1656  
       _m.invoke(_o,_args);
 1657  
     } catch (Exception e) {
 1658  
       e.printStackTrace();
 1659  
       if (e instanceof IOException) throw (IOException) e;
 1660  
       if (e instanceof InterpretException) throw (InterpretException)e;
 1661  
       else throw new InterpretException(e.getMessage());
 1662  
     }
 1663  
   }
 1664  
 
 1665  
   public String toString() {
 1666  
     return _m.toString();
 1667  
   }
 1668  
 
 1669  
   private Object[] _args;
 1670  
   private Method _m;                // method to invoke in object
 1671  
   private Object _o;                // object to invoke method on
 1672  
 }
 1673  
 
 1674