Coverage Report - com.ccg.macros.At
 
Classes in this File Line Coverage Branch Coverage Complexity
At
0% 
0% 
5.053
 
 1  
 /*----------------------------------------------------------------
 2  
  * $Id: At.java,v 1.1.1.1 2005/06/08 00:33:07 pkb Exp $
 3  
  * 
 4  
  * (c)2000 - Paul Blankenbaker
 5  
  *
 6  
  * Revision Log
 7  
  * 
 8  
  *  2000-11-16 13:10:56        Paul Blankenbaker 
 9  
  * 
 10  
  *         Created initial version
 11  
  */
 12  
 //----------------------------------------------------------------
 13  
 
 14  
 package com.ccg.macros;
 15  
 
 16  
 import com.ccg.util.*;
 17  
 import com.ccg.io.*;
 18  
 
 19  
 import java.io.*;                // needed for I/O operations
 20  
 import java.util.*;                // handy utility classes
 21  
 
 22  
 
 23  
 //----------------------------------------------------------------
 24  
 /** A command line implementation of "@macros".
 25  
  * 
 26  
  * <p>This is a full working "filter" type of program which is capable
 27  
  * of processing macros. It comes with a built in set of macros (see
 28  
  * the method defintions for the built in macros.
 29  
  *
 30  
  * <p>This program works as a standard Unix filter. It reads in a
 31  
  * ASCII text file, interprets (processes) any contained macros and
 32  
  * outputs the resulting text file. As an example, try feeding in the
 33  
  * following contents to a invocation of "java com.ccg.macros.At":
 34  
  *
 35  
  * <pre>
 36  
 &#064;define(pkb_email,"&#064;quote(paul&#064;mekwin.com)")
 37  
 &#064;define(pkb,"Paul Blankenbaker (&#064;pkb_email)")
 38  
 
 39  
 Hello &#064;pkb, looks like your
 40  
 email is "&#064;pkb_email". Today is &#064;now.
 41  
 
 42  
 &#064;define(pkb_email,"&#064;quote(paul_blankenbaker&#064;yahoo.com)")
 43  
 Did the email address in the &#064;quote(&#064;pkb) macro change?
 44  
 &#064;quote(&#064;pkb)=&#064;pkb
 45  
 
 46  
 Currently, the &#064;quote(&#064;pkb) macro is defined as
 47  
 "&#064;definition(pkb)". The built-in &#064;quote(&#064;now) macro
 48  
 is defined as a Java method invocation which looks like:
 49  
 "&#064;definition(now)".
 50  
 
 51  
 Here is a list of ALL available definitions.
 52  
 
 53  
 &#064;dumpDefinitions
 54  
  * </pre>
 55  
  *
 56  
  * <p>You can use many of the standard command line options which the
 57  
  * {@link CommandLineUtility CommandLineUtility} class recognizes. In
 58  
  * addtion, the following command line options are recognized:
 59  
  *
 60  
  * <dl>
 61  
  *
 62  
  * <dt><b>-Dname=val</b></dt><dd>This allows one to define macros on
 63  
  * the command line. You may include zero or more definitions. For
 64  
  * example "-Dfirst=Paul -Dlast=Blankenbaker" on the command line is
 65  
  * equivalent to
 66  
  * '@define("first","Paul")@define("last","Blankenbaker")' in a source
 67  
  * file. This allows one to pass/define values outside of the source
 68  
  * files.</dd>
 69  
  *
 70  
  * <dt><b>-nodefaults [true|false]</b></dt><dd>Specify this option to
 71  
  * turn off the loading of the user's default macro definitions. By
 72  
  * default, this application first tries to load the file
 73  
  * "$HOME/.etc/atmacros/default.atmacros" (discarding all output) to
 74  
  * allow one to load a set of custom definitions (if this file isn't
 75  
  * found, it isn't loaded). Use the "-nodefaults" option to disable
 76  
  * this feature.</dd>
 77  
  *
 78  
  * </dl>
 79  
  * 
 80  
  * @version $Revision: 1.1.1.1 $
 81  
  * 
 82  
  * @since 1.0
 83  
  * 
 84  
  * @author $Author: pkb $
 85  
  * 
 86  
  * @see #run */
 87  
 //----------------------------------------------------------------
 88  
 
 89  
 public class At extends CommandLineUtility {
 90  
 
 91  
   //----------------------------------------------------------------
 92  
   /** Initializes object with an array of command line arguments.
 93  
    * 
 94  
    * <p>This constructor initializes the object with an array of
 95  
    * command line arguments. Once constructed one, can use the {@link
 96  
    * #run} method to cause the interpreter to run.
 97  
    *
 98  
    * @param        args
 99  
    *
 100  
    *        Array of command line arguments (typically the what the {@link
 101  
    *        #main} method would be passed at startup).
 102  
    * 
 103  
    * @since        1.0
 104  
    * 
 105  
    * @see #run */
 106  
   //----------------------------------------------------------------
 107  
 
 108  
   public At(String[] args) {
 109  0
     super(args);
 110  
 
 111  0
     setLineSeparator(null);
 112  
 
 113  0
     AtMacros am = new AtMacros();
 114  
 
 115  
     //----------------------------------------------------------------  
 116  
     // See if any -Dmacro=val values on command line
 117  
     //----------------------------------------------------------------
 118  
 
 119  0
     for (int i = 0; i < args.length; i++) {
 120  0
       String a = args[i];
 121  0
       if ((a.length() > 2) && a.startsWith("-D")) {
 122  0
         int epos = a.indexOf('=');
 123  0
         if (epos < 0) {
 124  0
           am.addMacro(a.substring(2),"");
 125  0
         }
 126  
         else {
 127  0
           String mname = a.substring(2,epos);
 128  0
           String mval = a.substring(epos+1);
 129  0
           am.addMacro(mname,mval);
 130  
         }
 131  
       }
 132  
     }
 133  
 
 134  0
     setAtMacros(am);
 135  0
   }
 136  
 
 137  
 
 138  
   //----------------------------------------------------------------
 139  
   /** Holds the string used to separate lines on the current platform.
 140  
    * 
 141  
    * @serial        
 142  
    * 
 143  
    * @since        1.0  */
 144  
   //----------------------------------------------------------------
 145  
 
 146  
   private String _LineSeparator;
 147  
 
 148  
 
 149  
   //----------------------------------------------------------------
 150  
   /** Set the string used to separate lines on the current platform.
 151  
    * 
 152  
    * @param        val
 153  
    * 
 154  
    *         New String value to assign - note, if you pass null, we will
 155  
    *         attempt to reset it based on the "line.separator" system
 156  
    *         property.
 157  
    * 
 158  
    * @see #getLineSeparator  */
 159  
   //----------------------------------------------------------------
 160  
 
 161  
   public void setLineSeparator(String val) {
 162  0
     if (val == null) {
 163  0
       val = System.getProperty("line.separator");
 164  0
       if (val == null) val = "\n";
 165  
     }
 166  0
     _LineSeparator = val;
 167  0
   }
 168  
 
 169  
 
 170  
   //----------------------------------------------------------------
 171  
   /** Get the string used to separate lines on the current platform.
 172  
    * 
 173  
    * @return
 174  
    * 
 175  
    *         Current String value assigned.
 176  
    * 
 177  
    * @see #setLineSeparator  */
 178  
   //----------------------------------------------------------------
 179  
 
 180  
   public String getLineSeparator() {
 181  0
     return _LineSeparator;
 182  
   }
 183  
 
 184  
 
 185  
   //----------------------------------------------------------------
 186  
   /** Holds the "&#064;macro" interpreter to use.
 187  
    * 
 188  
    * @serial        
 189  
    * 
 190  
    * @since        1.0  */
 191  
   //----------------------------------------------------------------
 192  
 
 193  
   private AtMacros _AtMacros;
 194  
 
 195  
 
 196  
   //----------------------------------------------------------------
 197  
   /** Process a set of well known user files.
 198  
    * 
 199  
    * <p>This method will attempt to load definitions from file(s) in
 200  
    * some well known (or expected locations). All files are optional
 201  
    * (if not found nothing is done). Also, no output is produced while
 202  
    * interpretting these files (they are purely for the purpose of
 203  
    * adding standard definitions).
 204  
    *
 205  
    * <p>Refer to the {@link ConfigSource} class and in particular the
 206  
    * {@link ConfigSource#getFile} method to see how the path to the
 207  
    * default file is determined.
 208  
    * 
 209  
    * @since        1.0 */
 210  
   //----------------------------------------------------------------
 211  
 
 212  
   public void loadDefaultDefinitions() {
 213  0
     File ifile = null;
 214  0
     InputReader in = null;
 215  
 
 216  
     try {
 217  0
       ConfigSource cs = new ConfigSource(At.class);
 218  0
       ifile = cs.getFile();
 219  
 
 220  0
       Interpreter amacs = getAtMacros();
 221  
 
 222  0
       if (ifile.exists() && (amacs != null)) {
 223  0
         in = new InputReader(ifile);
 224  0
         amacs.interpret(new InputReader(ifile),new OutputNull());
 225  0
         System.err.println("loaded file:"+ifile);
 226  0
       }
 227  0
       else System.err.println("didn't find file:"+ifile);
 228  0
     } catch (Exception e) {
 229  0
       Log.error("problem processing \""+in+"\"",e);
 230  0
     }
 231  
 
 232  0
     if (in != null) try { in.close(); } catch (IOException ioe) { }
 233  
 
 234  0
   }
 235  
 
 236  
 
 237  
   //----------------------------------------------------------------
 238  
   /** Set the "&#064;macro" interpreter to use.
 239  
    * 
 240  
    * @param        val
 241  
    * 
 242  
    *         New AtMacros value to assign.
 243  
    * 
 244  
    * @see #getAtMacros  */
 245  
   //----------------------------------------------------------------
 246  
 
 247  
   public void setAtMacros(AtMacros val) {
 248  0
     if (val != null) {
 249  0
       _AtMacros = val;
 250  0
       _AtMacros.addMacros(this);
 251  
     }
 252  0
   }
 253  
 
 254  
 
 255  
   //----------------------------------------------------------------
 256  
   /** Get the "&#064;macro" interpreter to use.
 257  
    * 
 258  
    * @return
 259  
    * 
 260  
    *         Current AtMacros value assigned.
 261  
    * 
 262  
    * @see #setAtMacros  */
 263  
   //----------------------------------------------------------------
 264  
 
 265  
   public AtMacros getAtMacros() {
 266  0
     return _AtMacros;
 267  
   }
 268  
 
 269  
 
 270  
   //----------------------------------------------------------------
 271  
   /** Runs the interpreter reading from the input source and writing
 272  
    * the output.
 273  
    * 
 274  
    * This method interprets the contents of the input file and
 275  
    * produces the resulting ASCII output file. By default, the
 276  
    * standard input will be used for input, and the standard output
 277  
    * will be used for output. However, one can adjust these via
 278  
    * command line arguments or the methods of the {@link
 279  
    * CommandLineUtility parent} class.
 280  
    * 
 281  
    * @since        1.0 */
 282  
   //----------------------------------------------------------------
 283  
 
 284  
   public void run() {
 285  
 
 286  0
     OutputWriter o = null;
 287  
 
 288  
     try {
 289  0
       AtMacros interpreter = getAtMacros();
 290  
       //      interpreter.addMacro("pkb_email","paul@mekwin.com");
 291  
       //      interpreter.addMacro("pkb","Paul Blankenbaker "+
 292  
       //                           "(@email(@pkb_email))");
 293  
 
 294  0
       o = new OutputWriter(getOutputStream());
 295  
       //      Input isb = new Input("@define(paul,@pkb)Hello \"@paul\"!\n\n");
 296  
       //      interpreter.interpret(isb,o);
 297  
 
 298  0
       if (!getBoolean("nodefaults",Boolean.FALSE).booleanValue()) {
 299  0
         loadDefaultDefinitions();
 300  
       }
 301  
 
 302  0
       interpreter.interpret(new InputReader(getInputStream()),o);
 303  
 
 304  0
     } catch (Exception e) {
 305  0
       e.printStackTrace();
 306  
     } finally {
 307  0
       if (o != null) try { o.close(); } catch (Throwable t) { }
 308  0
     }
 309  0
   }
 310  
 
 311  
 
 312  
   //----------------------------------------------------------------
 313  
   /** &#064;param(N) macro - get parameter value.
 314  
    * 
 315  
    * This macro is typically used in the definition of run time macros
 316  
    * (in the VAL parameter of "&#064;define(NAME,VAL)". It allows a user
 317  
    * defined macro (run time defined) to reference the parameter
 318  
    * passed to its invocation. Take a look at the following:
 319  
    *
 320  
    * <pre>
 321  
    * &#064;define(email,"&lt;a href=\"mailto:&#064;param(0)\"&gt;&#064;param(0)&lt;/a&gt;")
 322  
    * &#064;email("&#064;("paul&#064;mekwin.com")")
 323  
    * </pre>
 324  
    *
 325  
    * <p>The above defines a new run time macro "&#064;email", the new
 326  
    * "&#064;email" macro allows the user to pass an argument which is
 327  
    * subsitutes in two places. This provides a means to make some
 328  
    * pretty powerful run time macros.
 329  
    * 
 330  
    * @param        out
 331  
    * 
 332  
    *         The {@link Output output device} to write the results of
 333  
    *         processing the argument to.
 334  
    * 
 335  
    * @param        args
 336  
    * 
 337  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 338  
    * 
 339  
    * @throws        IOException
 340  
    * 
 341  
    *         If there is a problem writing to the {@link Output output device}
 342  
    * 
 343  
    * @throws        InterpretException
 344  
    * 
 345  
    *         If a problem is encountered interpretting the arguments passed.
 346  
    * 
 347  
    * @since        1.0 */
 348  
   //----------------------------------------------------------------
 349  
 
 350  
   public void param(Output out, Vector args)
 351  
     throws IOException, InterpretException {
 352  
 
 353  0
     if ((args != null) && (args.size() >= 1)) {
 354  0
       Object o = args.elementAt(0);
 355  0
       int idx = -1;
 356  
       try {
 357  0
         idx = Integer.parseInt(_AtMacros.interpretCheck(o.toString()));
 358  0
       } catch (Throwable t) {
 359  0
         throw new InterpretException("@param("+o+") - "+t.getMessage());
 360  0
       }
 361  
       
 362  0
       out.write(_AtMacros.getParameter(idx));
 363  
     }
 364  0
   }
 365  
 
 366  
 
 367  
   //----------------------------------------------------------------
 368  
   /** &#064;quote(TEXT) macro - output text verbatim.
 369  
    * 
 370  
    * This macro is invoked in the form "&#064;quote(TEXT)". This macro only
 371  
    * looks at the first argument (additional arguments are
 372  
    * ignored). The text of the first argument is written verbatim to
 373  
    * the output device. This allows one to output macro names. For
 374  
    * example: <b>&#064;quote("&#064;quote(TEXT)")</b> would result in
 375  
    * <b>&#064;quote(TEXT)</b> in the output stream.
 376  
    * 
 377  
    * @param        out
 378  
    * 
 379  
    *         The {@link Output output device} to write the results of
 380  
    *         processing the argument to.
 381  
    * 
 382  
    * @param        args
 383  
    * 
 384  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 385  
    * 
 386  
    * @throws        IOException
 387  
    * 
 388  
    *         If there is a problem writing to the {@link Output output device}
 389  
    * 
 390  
    * @throws        InterpretException
 391  
    * 
 392  
    *         If a problem is encountered interpretting the arguments passed.
 393  
    * 
 394  
    * @since        1.0 */
 395  
   //----------------------------------------------------------------
 396  
 
 397  
   public void quote(Output out, Vector args)
 398  
     throws IOException, InterpretException {
 399  
 
 400  0
     if ((args != null) && (args.size() >= 1)) {
 401  0
       Object o = args.elementAt(0);
 402  0
       if (o != null) out.write(o.toString());
 403  
     }
 404  0
   }
 405  
 
 406  
 
 407  
   //----------------------------------------------------------------
 408  
   /** &#064;now() macro - dumps current time.
 409  
    * 
 410  
    * This macro is invoked in the form "&#064;now()". This macro has no
 411  
    * arguments (currently) and simply dumps the current time in the
 412  
    * default date format of the JVM.
 413  
    * 
 414  
    * @param        out
 415  
    * 
 416  
    *         The {@link Output output device} to write the results of
 417  
    *         processing the argument to.
 418  
    * 
 419  
    * @param        args
 420  
    * 
 421  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 422  
    * 
 423  
    * @throws        IOException
 424  
    * 
 425  
    *         If there is a problem writing to the {@link Output output device}
 426  
    * 
 427  
    * @throws        InterpretException
 428  
    * 
 429  
    *         If a problem is encountered interpretting the arguments passed.
 430  
    * 
 431  
    * @since        1.0 */
 432  
   //----------------------------------------------------------------
 433  
 
 434  
   public void now(Output out, Vector args) 
 435  
     throws IOException, InterpretException {
 436  
 
 437  0
     out.write(getDateFormat().format(new Date()));
 438  0
   }
 439  
 
 440  
 
 441  
   //----------------------------------------------------------------
 442  
   /** &#064;define(NAME,VAL[,OPT]) macro - define a new macro.
 443  
    * 
 444  
    * This macro is invoked in the form
 445  
    * "&#064;define(NAME,VAL[,OPT])". This macro is used to define new
 446  
    * text replacement macros. The first argument NAME is the name of
 447  
    * the new macro to define (do not include the leading "&#064;"
 448  
    * sign). The second argument VAL is the text which is to be
 449  
    * subsituted. The third arguments allows for some specialized
 450  
    * options during the definition. For example:
 451  
    *
 452  
    * <pre>
 453  
    * &#064;define(pkb_email,"&#064;quote("paul&#064;mekwin.com")")
 454  
    * </pre>
 455  
    *
 456  
    * <p>After interpretting the above, one could then use
 457  
    * "&#064;pkb_email()" in the document and have "paul&#064;mekwin.com"
 458  
    * appear. It should be noted, that the "VAL" area can contain
 459  
    * references to other macros. For example:
 460  
    *
 461  
    * <pre>
 462  
    * &#064;define(pkb,"Paul Blankenbaker (&#064;pkb_email)")
 463  
    * </pre>
 464  
    *
 465  
    * <p>There are two important concepts to understand with the
 466  
    * above. One is that the &#064;pkb macro just defined contains a
 467  
    * reference to another macro. This is handled properly and the
 468  
    * contained &#064;pkb_email reference will be expanded each time the
 469  
    * &#064;pkb macro is used. The second concept is that the expansion
 470  
    * occurs at the time the &#064;pkb macro is used, not at the time it is
 471  
    * defined. In other words, if someone changed the definition of the
 472  
    * &#064;pkb_email macro, then it would affect subsequent invocations of
 473  
    * the &#064;pkb macro.
 474  
    *
 475  
    * <p>The third parameter to this macro is optional, and can be
 476  
    * extremely useful in some circumstances. The third parameter can
 477  
    * be one of the following:
 478  
    *
 479  
    * <dl>
 480  
    *
 481  
    * <dt><b>ifnew</b></dt><dd>The "ifnew" option means that the
 482  
    * definition should only be accepted if a definition does not
 483  
    * already exist. This can be a very handy way to provide default
 484  
    * values.</dd>
 485  
    *
 486  
    * <dt><b>now</b></dt><dd>The "now" option means that the value
 487  
    * should be interpretted immediately (right now). This can be more
 488  
    * efficient and prevents future changes from having affects.</dd>
 489  
    *
 490  
    * </dl>
 491  
    *
 492  
    * <p>To get an idea of how these behave, try running the the
 493  
    * following through to see what comes out:
 494  
    *
 495  
    * <pre>
 496  
    * &#064;define("name","Joe Schmoe")
 497  
    * &#064;define("phone","333-333-3333")
 498  
    * &#064;define("pname","&#064;name &#064;phone")
 499  
    * &#064;define("nname","&#064;name &#064;phone","now")
 500  
    * &#064;define("phone","444-444-4444")
 501  
    * &#064;define("phone","555-555-5555","ifnew")
 502  
    * &#064;quote("&#064;pname=")&#064;pname
 503  
    * &#064;quote("&#064;nname=")&#064;nname
 504  
    * </pre>
 505  
    * 
 506  
    * @param        out
 507  
    * 
 508  
    *         The {@link Output output device} to write the results of
 509  
    *         processing the argument to.
 510  
    * 
 511  
    * @param        args
 512  
    * 
 513  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 514  
    * 
 515  
    * @throws        IOException
 516  
    * 
 517  
    *         If there is a problem writing to the {@link Output output device}
 518  
    * 
 519  
    * @throws        InterpretException
 520  
    * 
 521  
    *         If a problem is encountered interpretting the arguments passed.
 522  
    * 
 523  
    * @since        1.0 */
 524  
   //----------------------------------------------------------------
 525  
 
 526  
   public void define(Output out, Vector args) 
 527  
     throws IOException, InterpretException {
 528  
 
 529  0
     if ((args != null) && (args.size() >= 2)) {
 530  0
       Object n = args.elementAt(0);
 531  0
       Object v = args.elementAt(1);
 532  0
       if ((n != null) && (v != null)) {
 533  0
         String name = n.toString();
 534  0
         String val = v.toString();
 535  
 
 536  
                                 // see if any special options specified
 537  0
         if (args.size() > 2) {
 538  0
           Object op = args.elementAt(2);
 539  0
           String opStr = null;
 540  0
           if (op != null) opStr = op.toString();
 541  
 
 542  0
           if (opStr != null) {
 543  
                                 // "now" option requires us to evaluate now
 544  0
             if (opStr.equals("now")) { val = _AtMacros.interpret(val); }
 545  
                                 // "ifnew" option means to skip
 546  
                                 // definition if already defined
 547  0
             else if (opStr.equals("ifnew")) {
 548  0
               if (_AtMacros.getMacro(name) != null) return;
 549  
             }
 550  
           }
 551  
         }
 552  0
         _AtMacros.addMacro(name,val);
 553  
       }
 554  
     }
 555  0
   }
 556  
 
 557  
 
 558  
   //----------------------------------------------------------------
 559  
   /** &#064;definition(NAME) macro - fetch definition of a macro.
 560  
    * 
 561  
    * This macro is mainly used for debugging purposes, it returns the
 562  
    * current definition of a macro. You pass the NAME of the macro
 563  
    * (without the leading "&#064;" symbol) which you want to retrieve the
 564  
    * definition for. The current definition of the macro is returned
 565  
    * as a text string. If the macro was created with the
 566  
    * "&#064;define(NAME,VAL)" macro, then the returned text will be
 567  
    * "VAL". If the macro is a built-in macro (such as
 568  
    * "&#064;definition(NAME)", then the returned value will be the name of
 569  
    * the Java class/method used to handle the macro.
 570  
    * 
 571  
    * @param        out
 572  
    * 
 573  
    *         The {@link Output output device} to write the results of
 574  
    *         processing the argument to.
 575  
    * 
 576  
    * @param        args
 577  
    * 
 578  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 579  
    * 
 580  
    * @throws        IOException
 581  
    * 
 582  
    *         If there is a problem writing to the {@link Output output device}
 583  
    * 
 584  
    * @throws        InterpretException
 585  
    * 
 586  
    *         If a problem is encountered interpretting the arguments passed.
 587  
    * 
 588  
    * @since        1.0 */
 589  
   //----------------------------------------------------------------
 590  
 
 591  
   public void definition(Output out, Vector args) 
 592  
     throws IOException, InterpretException {
 593  
 
 594  0
     if ((args != null) && (args.size() >= 1)) {
 595  0
       Object n = args.elementAt(0);
 596  0
       if (n != null) {
 597  0
         Object o = _AtMacros.getMacro(n.toString());
 598  0
         out.write(o);
 599  
       }
 600  
     }
 601  0
   }
 602  
 
 603  
 
 604  
   //----------------------------------------------------------------
 605  
   /** &#064;htmlEscape(TEXT) - escape special HTML characters '<', '>' and '&'.
 606  
    * 
 607  
    * <p>This should probably be relocated to a special HTML version of
 608  
    * the {@link At At} class. However, I needed something that would
 609  
    * translate '<' into "&lt;", '>' into "&gt;" and '&' into "&amp;"
 610  
    * so that HTML examples would appear as the expected when viewed by
 611  
    * a browser.
 612  
    *
 613  
    * <p>This macro simply replaces HTML special characters, with the
 614  
    * HTML escape codes.
 615  
    * 
 616  
    * @param        out
 617  
    * 
 618  
    *         The {@link Output output device} to write the results of
 619  
    *         processing the argument to.
 620  
    * 
 621  
    * @param        args
 622  
    * 
 623  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 624  
    * 
 625  
    * @throws        IOException
 626  
    * 
 627  
    *         If there is a problem writing to the {@link Output output device}
 628  
    * 
 629  
    * @throws        InterpretException
 630  
    * 
 631  
    *         If a problem is encountered interpretting the arguments passed.
 632  
    * 
 633  
    * @since        1.0 */
 634  
   //----------------------------------------------------------------
 635  
 
 636  
   public void htmlEscape(Output out, Vector args)
 637  
     throws IOException, InterpretException {
 638  
 
 639  
                                 // exit if nothing to do
 640  0
     if ((args == null) || (args.size() != 1)) return;
 641  
 
 642  
                                 // see if there is anything to do
 643  0
     Object f = args.elementAt(0);
 644  0
     if (f == null) return;
 645  
 
 646  0
     String text = _AtMacros.interpretCheck(f.toString());
 647  
 
 648  0
     if ((text == null) || (text.length() == 0)) return;
 649  
 
 650  0
     int tlen = text.length();
 651  
 
 652  
                                 // now, replace special HTML
 653  
                                 // characters with their escape codes
 654  0
     StringBuffer sb = new StringBuffer(tlen*2);
 655  
 
 656  0
     for (int i = 0; i < tlen; i++) {
 657  0
       char c = text.charAt(i);
 658  0
       if (c == '<') sb.append("&lt;");
 659  0
       else if (c == '&') sb.append("&amp;");
 660  0
       else sb.append(c);
 661  
     }
 662  
 
 663  0
     out.write(sb);
 664  
 
 665  0
   }
 666  
 
 667  
 
 668  
   //----------------------------------------------------------------
 669  
   /** &#064;dumpDefinitions() macro - dump definition of all macros.
 670  
    * 
 671  
    * <p>This macro is mainly used for debugging purposes. It dumps the
 672  
    * definition of ALL defined macros.
 673  
    *
 674  
    * <p>NOTE: You may optionally pass a single argument to this
 675  
    * method. If passed, the argument will be expanded for each
 676  
    * definition in our macro dictionary. The following macros will be
 677  
    * available:
 678  
    *
 679  
    * <dl> 
 680  
    *
 681  
    * <dt>&#064;dumpDefName()</dt><dd>The name of the macro the
 682  
    * definition refers to.
 683  
    *
 684  
    * <dt>&#064;dumpDefValue()</dt><dd>The string value of the macro
 685  
    * (this may be the name of the Java method used to evaluate
 686  
    * it.</dd>
 687  
    *
 688  
    * </dl>
 689  
    *
 690  
    * <p>Here is an example which produces the same results as the
 691  
    * default output:
 692  
    *
 693  
    * <pre>
 694  
    * &#064;dumpDefinitions("&#064;dumpDefName()=&#064;dumpDefValue()
 695  
    * ")
 696  
    * </pre>
 697  
    *
 698  
    * <p>Here's an example which provides a little fancier output in
 699  
    * HTML:
 700  
    *
 701  
    * <pre>
 702  
    * &#064;dumpDefinitions("&lt;h2&gt;&#064;quote(&#064;)&#064;dumpDefName()&lt;/h2&gt;
 703  
    * &lt;pre&gt;
 704  
    * &#064;htmlEscape("&#064;dumpDefValue()")
 705  
    * &lt;/pre&gt;
 706  
    * ")
 707  
    * </pre>
 708  
    * 
 709  
    * @param        out
 710  
    * 
 711  
    *         The {@link Output output device} to write the results of
 712  
    *         processing the argument to.
 713  
    * 
 714  
    * @param        args
 715  
    * 
 716  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 717  
    * 
 718  
    * @throws        IOException
 719  
    * 
 720  
    *         If there is a problem writing to the {@link Output output device}
 721  
    * 
 722  
    * @throws        InterpretException
 723  
    * 
 724  
    *         If a problem is encountered interpretting the arguments passed.
 725  
    * 
 726  
    * @since        1.0 */
 727  
   //----------------------------------------------------------------
 728  
 
 729  
   public void dumpDefinitions(Output out, Vector args) 
 730  
     throws IOException, InterpretException {
 731  
 
 732  
                                 // default dump ouput
 733  0
     String format = "@dumpDefName()=@dumpDefValue()"+_LineSeparator;
 734  
                                 // see if user overrode format
 735  0
     if ((args != null) && (args.size() > 0)) {
 736  0
       Object o = args.elementAt(0);
 737  0
       if (o != null) format = o.toString();
 738  
     }
 739  
                                 // get and sort macro names that are defined
 740  0
     Vector allNames = new Vector(500);
 741  0
     Enumeration keys = _AtMacros.getMacroNames();
 742  0
     while (keys.hasMoreElements()) {
 743  0
       String name = keys.nextElement().toString();
 744  0
       allNames.addElement(name);
 745  0
     }
 746  
 
 747  0
     String[] mnames = new String[allNames.size()];
 748  0
     for (int i = 0; i < mnames.length; i++) {
 749  0
       mnames[i] = (String) allNames.elementAt(i);
 750  
     }
 751  0
     Arrays.sort(mnames,new com.ccg.util.CompareStrings());
 752  
 
 753  0
     class Verbatim implements MacroHandler {
 754  
       String _Text;
 755  
       public void process(Output ov, Vector av)
 756  
         throws IOException, InterpretException {
 757  0
         ov.write(_Text);
 758  0
       }
 759  
     }
 760  
 
 761  0
     Verbatim v = new Verbatim();
 762  0
     _AtMacros.addMacro("dumpDefValue",v);
 763  
 
 764  
                                 // now, go process each macro definition
 765  0
     for (int i = 0; i < mnames.length; i++) {
 766  0
       _AtMacros.addMacro("dumpDefName",mnames[i]);
 767  0
       v._Text = _AtMacros.getMacro(mnames[i]).toString();
 768  0
       out.write(_AtMacros.interpretCheck(format));
 769  
     }
 770  
 
 771  
                                 // reset to blank definitions - not
 772  
                                 // sure if we need to do this
 773  
     //    _AtMacros.addMacro("dumpDefName",null);
 774  
     //    _AtMacros.addMacro("dumpDefValue",null);
 775  
     /*
 776  
     String beforeAll = null;
 777  
     //    String beforeEach = ""+_AtMacros.getStartMacroChar()+"define(\"";
 778  
     //    String betweenEach = "\",\"";
 779  
     //    String afterEach = "\")\n";
 780  
     String beforeEach = null;
 781  
     String betweenEach = "=";
 782  
     String afterEach = "\n";
 783  
     String afterAll = null;
 784  
 
 785  
     Enumeration keys = _AtMacros.getMacroNames();
 786  
     while (keys.hasMoreElements()) {
 787  
       String name = keys.nextElement().toString();
 788  
       Object o = _AtMacros.getMacro(name);
 789  
 
 790  
       if (beforeEach != null) out.write(beforeEach);
 791  
       out.write(name);
 792  
       out.write(betweenEach);
 793  
       out.write(o);
 794  
       out.write(afterEach);
 795  
     }
 796  
     */
 797  0
   }
 798  
 
 799  
 
 800  
   //----------------------------------------------------------------
 801  
   /** &#064;loadTSV(NAME,SOURCE) load a tab separated table.
 802  
    * 
 803  
    * <p>This macro is used to load the definition of a tab separated
 804  
    * table. It doesn't actually read the contents of the database
 805  
    * immediately, instead it defines a new macro named "NAME" and
 806  
    * associates a SOURCE (typically a file or URL) where we should
 807  
    * fetch the data from.
 808  
    *
 809  
    * <p>When you want to insert the data of the table into your
 810  
    * output, you then use the "@NAME(FORMAT)" macro to read each line
 811  
    * from the table and FORMAT the output of the table.
 812  
    *
 813  
    * <p>For example, if one created a table named {@link
 814  
    * <a href=doc-files/phone.tsv>phone.tsv</a>} like the following:
 815  
    *
 816  
 <pre>
 817  
 Last        First        Phone
 818  
 Blankenbaker        Paul        555-555-5555
 819  
 Brown        Megan        555-555-5555
 820  
 </pre>
 821  
    *
 822  
    * <p>And you used the following macros:
 823  
    *
 824  
 <pre>
 825  
 &#064;loadTSV("phoneTable","{@link <a href=doc-files/phone.tsv>phone.tsv</a>}")
 826  
 &#064;phoneTable("@param(1),@param(0),@param(2)\n")
 827  
 </pre>
 828  
    *
 829  
    * <p>This will result in the output of one blank line followed by a
 830  
    * comma seaparated list of just the data fields. This is shown below:
 831  
    *
 832  
 <pre>
 833  
 
 834  
 Paul,Blankenbaker,555-555-5555
 835  
 Megan,Brown,555-555-5555
 836  
 </pre>
 837  
    *
 838  
    * <p>If you are producing HTML, you can use the table formatting
 839  
    * tags to nicely format your table data.
 840  
    * 
 841  
    * @param        out
 842  
    * 
 843  
    *         The {@link Output output device} to write the results of
 844  
    *         processing the argument to.
 845  
    * 
 846  
    * @param        args
 847  
    * 
 848  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 849  
    * 
 850  
    * @throws        IOException
 851  
    * 
 852  
    *         If there is a problem writing to the {@link Output output device}
 853  
    * 
 854  
    * @throws        InterpretException
 855  
    * 
 856  
    *         If a problem is encountered interpretting the arguments passed.
 857  
    * 
 858  
    * @since        1.0 */
 859  
   //----------------------------------------------------------------
 860  
 
 861  
   public void loadTSV(Output out, Vector args) 
 862  
     throws IOException, InterpretException {
 863  
 
 864  0
     if (args.size() < 2) {
 865  0
       throw new InterpretException("@loadTSV(NAME,SOURCE) requires "+
 866  
                                    "two arguments.");
 867  
     }
 868  
 
 869  0
     String name = args.elementAt(0).toString();
 870  0
     String source = args.elementAt(1).toString();
 871  
 
 872  0
     if ((name == null) || (source == null) ||
 873  
         (name.length() == 0) || (source.length() == 0)) {
 874  0
       throw new InterpretException("null argument passed to "+
 875  
                                    "@loadTSV(NAME,SOURCE)");
 876  
     }
 877  
 
 878  0
     _AtMacros.addMacro(name,new TableInsert(_AtMacros,name,source));
 879  0
   }
 880  
 
 881  
 
 882  
   //----------------------------------------------------------------
 883  
   /** &#064;include(SOURCE,[IGNERR],[NOOUT]) insert and process other file(s).
 884  
    * 
 885  
    * <p>This macro is used to load and process the contents of another
 886  
    * file. Currently the full or relative path to the source must be
 887  
    * specified (in the future, path searching may be added).
 888  
    *
 889  
    * <p>For example, to include the file
 890  
    * "/opt/include/atmacros/companies.atmacros", one could use the
 891  
    * following command:
 892  
    *
 893  
 <pre>
 894  
 &#064;include("/etc/hosts")
 895  
 </pre>
 896  
    *
 897  
    * <p>The second parameter IGNERR is optional. It may be set to
 898  
    * either "true" or "false" (only first letter is checked). If
 899  
    * omitted, it defaults to a value of "false". When set to "true",
 900  
    * then no error will be thrown if the file is not found. If set to
 901  
    * "false" (or omitted), then an error will be thrown if the desired
 902  
    * SOURCE could not be found.
 903  
    *
 904  
    * <p>The third parameter DISOUT is optional. It may be set to
 905  
    * either "true" or "false" (only first letter is checked). If
 906  
    * omitted, it defaults to a value of "false". When set to "true",
 907  
    * then no output will be produced while interpretting the file
 908  
    * included (this allows one to load definition files). When set to
 909  
    * "false" (or omitted), any output produced as a result of
 910  
    * interpretting the file will be written to the current output
 911  
    * device.
 912  
    *
 913  
    * <p>When using this directive to include definition files, it is
 914  
    * often handy to set the DISOUT value to "true" to disable any
 915  
    * output as a result of interpretting the definition file (this
 916  
    * allows one to enter comments in their definition files).
 917  
    * 
 918  
    * @param        out
 919  
    * 
 920  
    *         The {@link Output output device} to write the results of
 921  
    *         processing the argument to.
 922  
    * 
 923  
    * @param        args
 924  
    * 
 925  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 926  
    * 
 927  
    * @throws        IOException
 928  
    * 
 929  
    *         If there is a problem writing to the {@link Output output device}
 930  
    * 
 931  
    * @throws        InterpretException
 932  
    * 
 933  
    *         If a problem is encountered interpretting the arguments passed.
 934  
    * 
 935  
    * @since        1.0 */
 936  
   //----------------------------------------------------------------
 937  
 
 938  
   public void include(Output out, Vector args) 
 939  
     throws IOException, InterpretException {
 940  
 
 941  0
     int argc = args.size();
 942  0
     if (argc < 1) return;
 943  
 
 944  0
     String source = _AtMacros.interpretCheck(args.elementAt(0).toString());
 945  
 
 946  
     try {
 947  
                                 // get source file to process
 948  0
       Reader in = com.ccg.io.Utility.getReader(source);
 949  
 
 950  0
       if (argc > 2) {                // turn off output if necessary
 951  0
         String disout = _AtMacros.interpretCheck(args.elementAt(2).toString());
 952  0
         if (disout.length() > 0 &&
 953  
             (Character.toLowerCase(disout.charAt(0)) == 't')) {
 954  0
           out = new OutputNull();
 955  
         }
 956  
       }
 957  
 
 958  
                                 // go interpret the contents of the file
 959  0
       _AtMacros.interpretCheck(new InputReader(in),out);
 960  
 
 961  0
     } catch (IOException ioe) {
 962  0
       if (argc > 1) {
 963  0
         String cont = _AtMacros.interpretCheck(args.elementAt(1).toString());
 964  0
         if (cont.length() > 0 && 
 965  
             Character.toLowerCase(cont.charAt(0)) == 't') {
 966  0
           Log.warning("Warning: source \""+source+
 967  
                       "\" skipped:"+ioe.toString());
 968  0
           ioe = null;                // OK to continue
 969  
         }
 970  
       }
 971  0
       if (ioe != null) throw ioe;
 972  0
     }
 973  
 
 974  0
   }
 975  
 
 976  
 
 977  
   //----------------------------------------------------------------
 978  
   /** &#064;copyToDir(MATCH0,MATCH1,&02e;&02e;&02e;,DIR) copy one or
 979  
    * more files to directory.
 980  
    * 
 981  
    * <p>This macro is used to copy 0 or more existing files to a
 982  
    * output directory. If the directory doesn't exist, it will be
 983  
    * created. You may use the "*" as a wildcard in each of the file
 984  
    * name strings to match more than one file.
 985  
    *
 986  
    * <p>For example, to copy the ".at" and "*.properties" files from
 987  
    * the "/etc/macros" directory to the "/home/user/etc/macros"
 988  
    * directory, one could use the following:
 989  
    *
 990  
 <pre>
 991  
 &#064;defnow("sdir","/etc/macros")
 992  
 &#064;copyToDir("&#064;sdir()/*.at","&#064;sdir()/*.properties","/home/user/etc/macros")
 993  
 </pre>
 994  
    *
 995  
    * @param        out
 996  
    * 
 997  
    *         The {@link Output output device} to write the results of
 998  
    *         processing the argument to.
 999  
    * 
 1000  
    * @param        args
 1001  
    * 
 1002  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 1003  
    * 
 1004  
    * @throws        IOException
 1005  
    * 
 1006  
    *         If there is a problem writing to the {@link Output output device}
 1007  
    * 
 1008  
    * @throws        InterpretException
 1009  
    * 
 1010  
    *         If a problem is encountered interpretting the arguments passed.
 1011  
    * 
 1012  
    * @since        1.0 */
 1013  
   //----------------------------------------------------------------
 1014  
 
 1015  
   public void copyToDir(Output out, Vector args) 
 1016  
     throws IOException, InterpretException {
 1017  
 
 1018  0
     int argc = args.size();
 1019  0
     if (argc < 2) return;        // must have at least two arguments
 1020  
 
 1021  0
     argc--;                        // last argument is the output directory
 1022  
     
 1023  
                                 // get output directory
 1024  0
     String dstStr = _AtMacros.interpretCheck(args.elementAt(argc).toString());
 1025  
 
 1026  0
     File dst = new File(FileName.fixSlashes(dstStr));
 1027  
                                 // try to create directory - if it
 1028  
                                 // doesn't exist
 1029  0
     if (!dst.isDirectory()) {
 1030  0
       dst.mkdirs();
 1031  0
       if (!dst.isDirectory()) {
 1032  0
         throw new IOException("unable to create directory \""+dst+"\"");
 1033  
       }
 1034  
     }
 1035  
 
 1036  0
     PrintWriterString pws = PrintWriterString.create();
 1037  
 
 1038  
                                 // now, process each of the wild-card
 1039  
                                 // file name strings
 1040  0
     for (int i = 0; i < argc; i++) {
 1041  0
       String match = _AtMacros.interpretCheck(args.elementAt(i).toString());
 1042  0
       String[] list = FilenameFilterWild.list(FileName.fixSlashes(match));
 1043  0
       if (list != null) for (int j = 0; j < list.length; j++) {
 1044  0
         File src = new File(list[j]);
 1045  
                                 // only copy existing normal files
 1046  0
         if (src.exists() && src.isFile()) {
 1047  0
           File ofile = new File(dst,src.getName());
 1048  
 
 1049  0
           pws.print("Copy:");
 1050  0
           pws.print(src);
 1051  0
           pws.print("  to:");
 1052  0
           pws.println(dst);
 1053  0
           Copy.fileToFile(src,ofile);
 1054  0
         }
 1055  0
         else if (Debug.ENABLED && Debug.isEnabledFor(3)) {
 1056  0
           pws.print("Skip:");
 1057  0
           pws.println(src);
 1058  
         }
 1059  
       }
 1060  
     }
 1061  
 
 1062  0
     out.write(pws);
 1063  0
   }
 1064  
 
 1065  
 
 1066  
   //----------------------------------------------------------------
 1067  
   /** &#064;atCopyToDir(MATCH0,MATCH1,&02e;&02e;&02e;,DIR) copy AND
 1068  
    * interpret one or more files to directory.
 1069  
    * 
 1070  
    * <p>This macro is used to copy 0 or more existing files to a
 1071  
    * output directory. If the directory doesn't exist, it will be
 1072  
    * created. You may use the "*" as a wildcard in each of the file
 1073  
    * name strings to match more than one file.
 1074  
    *
 1075  
    * <p>There is a HUGE difference between this command and the
 1076  
    * "&#064;copyToDir". This command interprets each file that it
 1077  
    * copies and evaluates any "&#064macros" which it contains. This
 1078  
    * can be a VERY useful way to install HTML documents to their final
 1079  
    * location and do some subsitutions during the installation process.
 1080  
    *
 1081  
    * <p>It should be noted that any definitions (or redefinitions)
 1082  
    * made by the files processed WILL AFFECT all subsequent files
 1083  
    * processed (I may add a version of this macro which saves/restores
 1084  
    * the macros for each file processed).
 1085  
    *
 1086  
    * <p>To see the power of this function, take a look at the {@link
 1087  
    * <a href=doc-files/atcopy.atmacros>atcopy.atmacros</a>}
 1088  
    * "script". It does the following:
 1089  
    *
 1090  
    * <ul>
 1091  
    *
 1092  
    * <li>Makes some of its own definitions.</li>
 1093  
    *
 1094  
    * <li>Loads a set of definitions ({@link
 1095  
    * <a href=doc-files/htmlmemo.atmacros>htmlmemo.atmacros</a>}).</li>
 1096  
    *
 1097  
    * <li>Then copies a set of HTML documents (both {@link
 1098  
    * <a href=doc-files/memo1.html>memo1.html</a>} and  {@link
 1099  
    * <a href=doc-files/memo2.html>memo2.html</a>}) using the defined macros.
 1100  
    *
 1101  
    * </ul>
 1102  
    *
 1103  
    * <p>If you have the source code installed such that the "ccg" top
 1104  
    * level directory can be found under the "$COMHOME" environment
 1105  
    * variable, you should be able to try this out via:
 1106  
    *
 1107  
    * <pre>
 1108  
    * java {@link At com.ccg.macros.At} -Dcomhome=$COMHOME -Dhtmlroot=$HOME/public_html \
 1109  
    *   -in {@link <a href=doc-files/atcopy.atmacros>$COMHOME/ccg/macros/doc-files/atcopy.atmacros</a>}
 1110  
    * </pre>
 1111  
    *
 1112  
    * @param        out
 1113  
    * 
 1114  
    *         The {@link Output output device} to write the results of
 1115  
    *         processing the argument to.
 1116  
    * 
 1117  
    * @param        args
 1118  
    * 
 1119  
    *         A {@link Vector vector} of arguments which were passed to the macro.
 1120  
    * 
 1121  
    * @throws        IOException
 1122  
    * 
 1123  
    *         If there is a problem writing to the {@link Output output device}
 1124  
    * 
 1125  
    * @throws        InterpretException
 1126  
    * 
 1127  
    *         If a problem is encountered interpretting the arguments passed.
 1128  
    * 
 1129  
    * @since        1.0 */
 1130  
   //----------------------------------------------------------------
 1131  
 
 1132  
   public void atCopyToDir(Output out, Vector args) 
 1133  
     throws IOException, InterpretException {
 1134  
 
 1135  0
     int argc = args.size();
 1136  0
     if (argc < 2) return;        // must have at least two arguments
 1137  
 
 1138  0
     argc--;                        // last argument is the output directory
 1139  
     
 1140  
                                 // get output directory
 1141  0
     String dstStr = _AtMacros.interpretCheck(args.elementAt(argc).toString());
 1142  
 
 1143  0
     File dst = new File(FileName.fixSlashes(dstStr));
 1144  
                                 // try to create directory - if it
 1145  
                                 // doesn't exist
 1146  0
     if (!dst.isDirectory()) {
 1147  0
       dst.mkdirs();
 1148  0
       if (!dst.isDirectory()) {
 1149  0
         throw new IOException("unable to create directory \""+dst+"\"");
 1150  
       }
 1151  
     }
 1152  
 
 1153  0
     PrintWriterString pws = PrintWriterString.create();
 1154  
 
 1155  
                                 // now, process each of the wild-card
 1156  
                                 // file name strings
 1157  0
     for (int i = 0; i < argc; i++) {
 1158  0
       String match = _AtMacros.interpretCheck(args.elementAt(i).toString());
 1159  0
       String[] list = FilenameFilterWild.list(FileName.fixSlashes(match));
 1160  0
       if (list != null) for (int j = 0; j < list.length; j++) {
 1161  0
         File src = new File(list[j]);
 1162  
                                 // only copy existing normal files
 1163  0
         if (src.exists() && src.isFile()) {
 1164  0
           File ofile = new File(dst,src.getName());
 1165  
 
 1166  0
           pws.print("Interpret:");
 1167  0
           pws.print(src);
 1168  0
           pws.print("  to:");
 1169  0
           pws.println(dst);
 1170  
 
 1171  0
           OutputWriter ow = new OutputWriter(ofile);
 1172  0
           InputReader ir = new InputReader(src);
 1173  
 
 1174  
                                 // and copy/interpret the files
 1175  0
           getAtMacros().interpret(ir,ow);
 1176  0
           ow.close();                // don't forget to close (to flush out data)
 1177  0
         }
 1178  0
         else if (Debug.ENABLED && Debug.isEnabledFor(3)) {
 1179  0
           pws.print("Skip:");
 1180  0
           pws.println(src);
 1181  
         }
 1182  
       }
 1183  
     }
 1184  
 
 1185  0
     out.write(pws);
 1186  0
   }
 1187  
 
 1188  
 }
 1189  
 
 1190