Source code for /engineering/autohit-1998/creator/compiler/SimCompiler.javaOriginal file SimCompiler.java
   1 /**
   2  * .
   3  * Copyright � 1999 Erich P G.
   4  *
   5  */
   6  
   7 package creator.compiler;
   8 
   9 import java.util.Vector;
  10 import java.util.Stack;
  11 import com.sun.xml.tree.XmlDocument;
  12 import com.sun.xml.tree.XmlDocumentBuilder;
  13 import com.sun.xml.tree.ElementNode;
  14 import org.w3c.dom.Element;
  15 import org.w3c.dom.NodeList; 
  16 import org.w3c.dom.Node; 
  17 import org.w3c.dom.CharacterData; 
  18 
  19 import autohit.Sim;
  20 import autohit.vm.*;
  21 
  22 /**
  23  * This is the a Sim compiler.  It will compile xml documents that conform
  24  * to the "%AUTOHIT_ROOT%/lib/sim.dtd" dtd into a Sim object.
  25  * <p>
  26  * WARNING!!!  For the compiler to work, the FIRST element of the java
  27  * CLASSPATH <b>must</b> be the root for the autohit installation.  The compiler
  28  * needs to find the "sim.dtd" file in the "%AUTOHIT_ROOT%/lib" directory.
  29  * <p>
  30  * The private methos translate Token() matches the textual tokens, as defined in the
  31  * dtd to numerics that are used by the rest of the compiler.  Any new tokens
  32  * need to be added to that method.
  33  * <p>
  34  * I can think of several more elegant approaches to making this compiler, but until the
  35  * XML parsers settle a bit, I'm not going to bother...  
  36  * <p>
  37  * IMPORTANT NOTE!!!!  The XML parser WITH DTD ensure instructions are
  38  * coded in the proper order.  ALL of the code below assumes this!!!
  39  * If instructions come out of order or in disallowed places 
  40  * (like a seek outside of a get), I GUARANTEE you'll get a runaway 
  41  * compiler...,
  42  * <p>
  43  * We will look for imbedded variables only in certain locations.
  44  *
  45  * @see autohit.Sim
  46  *
  47  * @author Erich P. Gatejen
  48  * @version 1.0
  49  * <i>Version History</i>
  50  * <code>EPG - Initial - 14Jan99
  51  * EPG - Dumb-bug fix - 8Mar99</code> 
  52  * 
  53  */
  54 public class SimCompiler extends XmlCompiler {
  55 	
  56 	// --- FINAL FIELDS ------------------------------------------------------	
  57 	private final static String dtdName = "sim.dtd";
  58 	
  59 	private final static int    tokNULL         = 0;
  60 	private final static int    tokNAME         = 1;
  61 	private final static int    tokNOTE         = 2;
  62 	private final static int    tokVERSION      = 3;
  63 	private final static int    tokSET          = 4;
  64 	private final static int    tokFOR          = 5;		
  65 	private final static int    tokWHILE        = 6;
  66 	private final static int    tokIF           = 7;		
  67 	private final static int    tokWAIT         = 8;	
  68 	private final static int    tokGET          = 9;
  69 	private final static int    tokHEADER       = 10;
  70 	private final static int    tokNV           = 11;		
  71 	private final static int    tokBLOCK        = 12;	
  72 	private final static int    tokADD          = 13;
  73 	private final static int    tokVERIFY       = 14;	
  74 	private final static int    tokSEEK         = 15;	
  75 	private final static int    tokEXEC         = 16;
  76 		
  77 	// --- FIELDS ------------------------------------------------------------
  78 
  79     /**
  80      *  The work-in-progress Sim object.
  81      */
  82     protected Sim     workingSim;
  83 
  84 	// --- PUBLIC METHODS ----------------------------------------------------	
  85 
  86     /**
  87      *  Constructor.  This is the only and default constructor.
  88      *
  89      *  @throws Exception any exception invalidates the compiler.
  90      */
  91     public SimCompiler() throws Exception {
  92 
  93         super(dtdName);
  94     }    
  95 
  96     /**
  97      *  Compile the xml tree into a Sim object.  This method implements
  98      *  the abstract method in XmlCompiler.  It will be automatically
  99      *  called after the source document is parsed into an xml tree.
 100      *
 101      *  @param xd   A parsed XML document.
 102      *  @return a reference to the target object.
 103      *
 104      *  @see autohit.Sim
 105      *  @see creator.compiler.SimCompiler
 106      */
 107     public Object build(XmlDocument  xd) {
 108         
 109         // Any exception or verification check aborts the compile
 110         try {
 111 
 112             // Ok, build our working Sim object
 113             workingSim = new Sim();
 114             workingSim.init();
 115 
 116             // Get the root element and normalize
 117             ElementNode root = (ElementNode) xd.getDocumentElement();
 118             root.normalize();
 119             
 120             // Peal out the <info> and <code> sub-trees
 121             NodeList rootChildren = root.getChildNodes();            
 122             ElementNode itemTree = (ElementNode) rootChildren.item(0);
 123             ElementNode codeTree = (ElementNode) rootChildren.item(1);
 124             
 125             // Deal with the <info> tree
 126             for (int idx = 0; idx < itemTree.getLength(); idx++) {
 127                 processItem((ElementNode)itemTree.item(idx));                
 128             }
 129 
 130             // Deal with the <code> tree
 131             // Basicall, I'm gonna go wtih recursion.  I don't think it should
 132             // get very deep.   
 133             try {
 134                 processCode(codeTree);
 135                 
 136                 // Put a NOP on the end of the executable
 137                 workingSim.exec.add(new VMINop());
 138                 workingSim.exec.trimToSize();
 139                              
 140             } catch (Exception e) {
 141                 // an otherwise uncaught exception.  A runaway compiler...
 142                 err.error("Runaway compilation errors.  Stopping compile...");                    
 143                 return null;
 144             }   
 145             
 146         } catch (Exception e) {
 147             err.error("Compiler internal error.  Stopping compile...");
 148             return null; // leave the objectCode as null;
 149         }
 150 
 151         return workingSim;     
 152     }    
 153 
 154 
 155 
 156     // == ===============================================================================
 157     // == =                             PRIVATE METHODS                                 =
 158     // == ===============================================================================
 159 
 160     
 161     /**
 162      *  Processes info section tags. 
 163      */    
 164     private void processItem(ElementNode  en) {
 165     
 166         switch( translateToken(en.getNodeName()) ) {
 167 
 168             case tokNAME:
 169                 // Pull the child text element and place it the Sim.name;
 170                 String nameText = getText(en.getFirstChild());
 171                 if (nameText == null) {
 172                     err.error("Empty <name> tag.");        
 173                 } else {
 174                     workingSim.name = nameText;
 175                 }
 176                 break;
 177                 // For now, we are ignoring UIDs                 
 178             
 179             case tokNOTE:
 180                 // Notes will append.  Pull the child text element and paste it to the
 181                 // Sim.note.
 182                 String noteText = getText(en.getFirstChild());
 183                 if (noteText == null) {
 184                     err.error("Empty <note> tag.");        
 185                 } else {
 186                     workingSim.note = new String(workingSim.note + noteText);
 187                 }
 188                 break;                  
 189             
 190             case tokVERSION:
 191                 // For now, ignore VERSION
 192                 break;
 193                 
 194             case tokNULL:
 195             default:
 196                 err.error("Software Detected Fault: creator.compiler.SimCompiler.processItem().  The textual token [" + en.getNodeName() + "] should have NOT reached this code. Check the XML DTD associated with the SimCompiler.");   
 197             
 198         }
 199     }
 200 
 201 
 202     /**
 203      *  Every raw element will enter here.  Bascially, it dispatches it to it's specific
 204      *  handler.  It will do so for every child in the passed node. 
 205      */
 206     private void processCode(ElementNode  en) throws Exception {
 207 
 208         ElementNode  child;
 209 
 210 //DEBUG
 211 //System.out.println("ENTER --- " + en.getNodeName());
 212 
 213         for (int idx = 0; idx < en.getLength(); idx++) {
 214 
 215             child = (ElementNode)en.item(idx); 
 216 //DEBUG
 217 //System.out.println("      --- CHILD " + child.getNodeName());
 218 
 219             switch( translateToken(child.getNodeName()) ) {
 220                 
 221                 case tokSET:    
 222                     handleSet(child);
 223                     break;
 224     
 225                 case tokWAIT:
 226                     handleWait(child);            
 227                     break;
 228                     
 229                 case tokGET:
 230                     handleGet(child);
 231                     break;                
 232     
 233                 case tokHEADER:
 234                     handleHeader(child);
 235                     break; 
 236     
 237                 case tokNV:
 238                     handleNV(child);
 239                     break;
 240 
 241                 case tokFOR:
 242                     handleFor(child);		
 243                     break; 
 244                     
 245                 case tokWHILE:
 246                     handleWhile(child);
 247                     break; 
 248                     
 249                 case tokIF:
 250                     handleIf(child);
 251                     break; 
 252                     
 253                 case tokBLOCK:
 254                     handleBlock(child);
 255                     break; 
 256                     
 257                 case tokADD:
 258                     handleAdd(child);
 259                     break; 
 260                     
 261                 case tokVERIFY:
 262                     handleVerify(child);
 263                     break; 
 264 
 265                 case tokSEEK:
 266                     handleSeek(child);
 267                     break;                 
 268                 
 269                 case tokEXEC:
 270                     handleExec(child);               
 271                     break; 
 272                                     
 273                 case tokNULL:
 274                 default:
 275                     err.error("Software Detected Fault: creator.compiler.SimCompiler.processItem().  The textual token [" + en.getNodeName() + "] should have NOT reached this code. Check the XML DTD associated with the SimCompiler."); 
 276                     throw (new Exception("Software Detected Fault in creator.compiler.SimCompiler.processItem()."));     
 277  
 278             } // end switch
 279     
 280         } // end for
 281     
 282 //DEBUG
 283 //System.out.println("EXIT --- " + en.getNodeName());    
 284         
 285     }    
 286 
 287     // == ===============================================================================
 288     // == =                         TOKEN HANDLERS                                      =
 289     // == ===============================================================================
 290 
 291     /**
 292      *  Emit a set instruction.  Simple enough.
 293      */
 294     private void handleSet(ElementNode en) {
 295  
 296         String name  = en.getAttribute("name");
 297         String value = en.getAttribute("value");
 298         
 299         if (!isValid(name)) {
 300             errBadAttribute(en.toString(), "name", "set");       
 301         } else if (!isValid(value)) {
 302             errBadAttribute(en.toString(), "value", "set");          
 303         } else {
 304                
 305         
 306             VMISet  emitable = new VMISet();
 307             emitable.name  = name;
 308             emitable.value = value;
 309             emitable.orIV(seekIV(name)||seekIV(value));              
 310             
 311             workingSim.exec.add(emitable);
 312         }            
 313     }
 314     
 315 
 316     /**
 317      *  Emit an add instruction.
 318      */
 319     private void handleAdd(ElementNode en) {
 320         
 321         String name  = en.getAttribute("name");
 322         String value = en.getAttribute("value");
 323         if (!isValid(name)) {
 324             errBadAttribute(en.toString(), "name", "add");       
 325         } else if (!isValid(value)) {
 326             errBadAttribute(en.toString(), "value", "add");          
 327         } else {
 328         
 329             VMIAdd  emitable = new VMIAdd();
 330             emitable.name  = name;
 331             emitable.value = value;
 332             emitable.orIV(seekIV(name)||seekIV(value));
 333             
 334             workingSim.exec.add(emitable);
 335         }            
 336     }
 337 
 338     /**
 339      *  Emit a wait instruction.  Simple enough.
 340      */
 341     private void handleWait(ElementNode en) {
 342     
 343         String time  = en.getAttribute("time");
 344         if (isValid(time)) {
 345             
 346             VMIWait  emitable = new VMIWait();
 347             emitable.time = time;
 348             emitable.orIV(seekIV(time));
 349             workingSim.exec.add(emitable);
 350                         
 351         } else {
 352             errBadAttribute(en.toString(), "time", "wait"); 
 353         }
 354     }   
 355 
 356     /**
 357      *  Emit a get instruction.
 358      */
 359     private void handleGet(ElementNode en) {
 360     
 361         String qs  = en.getAttribute("qs");
 362         if (isValid(qs)) {
 363             
 364             // Prepare the instruction
 365             VMIGet  emitable = new VMIGet();
 366             emitable.qs = qs;
 367             emitable.orIV(seekIV(qs));
 368             
 369             // Put out a SCOPE
 370             workingSim.exec.add(new VMIScope());
 371             
 372             // Process inner block and add the get
 373             try {
 374                 processCode(en);
 375                 workingSim.exec.add(emitable);
 376                 
 377             } catch (Exception e) {
 378                 // Stop an error unravelling here....    
 379             }
 380                                 
 381             // Add the RSCOPE
 382             workingSim.exec.add(new VMIRScope());
 383                      
 384         } else {
 385             errBadAttribute(en.toString(), "qs", "get");
 386         }
 387     }   
 388 
 389 
 390     /**
 391      *  Emit a Header instruction.
 392      */
 393     private void handleHeader(ElementNode en) {
 394         
 395         String name  = en.getAttribute("name");
 396         String value = en.getAttribute("value");
 397         if (!isValid(name)) {
 398             errBadAttribute(en.toString(), "name", "header");       
 399         } else if (!isValid(value)) {
 400             errBadAttribute(en.toString(), "value", "header");          
 401         } else {
 402         
 403             VMIHeader  emitable = new VMIHeader();
 404             emitable.name  = name;
 405             emitable.value = value;
 406             emitable.orIV(seekIV(name)||seekIV(value));
 407             workingSim.exec.add(emitable);
 408         }            
 409     }
 410 
 411     /**
 412      *  Emit a NV instruction.
 413      */
 414     private void handleNV(ElementNode en) {
 415         
 416         String name      = en.getAttribute("name");
 417         String valueText = getText(en.getFirstChild());
 418         if (!isValid(name)) {
 419             errBadAttribute(en.toString(), "name", "nv");
 420         } else if (!isValid(valueText)) {
 421             valueText = "\n";          
 422         } else {
 423             VMINV  emitable = new VMINV();
 424             emitable.name  = name;
 425             emitable.value = valueText;
 426             emitable.orIV(seekIV(name)||seekIV(valueText));
 427             workingSim.exec.add(emitable);
 428         }            
 429     }
 430 
 431 
 432     /**
 433      *  handle a block.  easy enough.  just scope it!.
 434      */
 435     private void handleBlock(ElementNode en) {
 436     
 437         // Put out a SCOPE
 438         workingSim.exec.add(new VMIScope());
 439         
 440         // Process inner block and add the get
 441         try {
 442             processCode(en);
 443             
 444         } catch (Exception e) {
 445             // Stop an error unravelling here....    
 446         }
 447                             
 448         // Add the RSCOPE
 449         workingSim.exec.add(new VMIRScope());           
 450     }   
 451 
 452     /**
 453      *  Emit a Verify instruction.
 454      */
 455     private void handleVerify(ElementNode en) {
 456         
 457         String size  = en.getAttribute("size");
 458         String crc   = en.getAttribute("crc");
 459 
 460         VMIVerify emitable = new VMIVerify();
 461         if (isValid(size)) {
 462 
 463             try {
 464                 emitable.size = Integer.parseInt(size); 
 465             } catch (Exception e) {
 466                 errBadAttribute(en.toString(), "size", "verify");
 467                 return;
 468             }                           
 469         }
 470         
 471         // Add a CRC instruction, if the attribute is specified.
 472         if (isValid(crc)) {
 473             
 474             try {
 475 
 476                 int val = Integer.parseInt(crc);
 477                 VMICrc  eCRC = new VMICrc();
 478                 eCRC.expected = val;
 479                 workingSim.exec.add(eCRC); 
 480                
 481             } catch (Exception e) {
 482                 errBadAttribute(en.toString(), "crc", "verify");
 483                 return;  
 484             }
 485                 
 486         }
 487         workingSim.exec.add(emitable);
 488         
 489         // Handle any SEEK or EXEC children
 490         try {
 491             processCode(en);
 492             
 493         } catch (Exception e) {
 494             // Stop an error unravelling here....    
 495         }        
 496     }
 497 
 498     /**
 499      *  Handle a seek.
 500      */
 501     private void handleSeek(ElementNode en) {
 502         
 503         String seekText = en.getAttribute("string");
 504         if (!isValid(seekText)) {
 505             errBadAttribute(en.toString(), "string", "seek");    
 506         } else {
 507             VMISeek  emitable = new VMISeek();
 508             emitable.expected  = seekText;
 509             emitable.orIV(seekIV(seekText));
 510             workingSim.exec.add(emitable);
 511         }
 512     }
 513 
 514     /**
 515      *  Handle an Exec.
 516      */
 517     private void handleExec(ElementNode en) {
 518 
 519         // we don't care if the content is mangled or empty.
 520         
 521         String content = getText(en.getFirstChild());
 522         String invoke = en.getAttribute("invoke");
 523         if (!isValid(invoke)) {
 524             errBadAttribute(en.toString(), "invoke", "exec");
 525         } else {
 526             VMIExec  emitable = new VMIExec();
 527             emitable.invocation = invoke;
 528             emitable.content    = content;
 529             emitable.orIV(seekIV(content)||seekIV(invoke));
 530             workingSim.exec.add(emitable);
 531         }
 532     }
 533 
 534     /**
 535      *  Handle an IF.
 536      *
 537      *  Bounce out any exceptions, cause a screwed IF will spoil a 
 538      *  parent scope.
 539      */
 540     private void handleIf(ElementNode en) throws Exception {
 541         
 542         String expression  = en.getAttribute("e");
 543         String value = en.getAttribute("value");
 544         if (!isValid(expression)) {
 545             errBadAttribute(en.toString(), "e", "if");       
 546         } else if (!isValid(value)) {
 547             errBadAttribute(en.toString(), "value", "if");          
 548         } else {
 549         
 550             VMIIf  emitable = new VMIIf();
 551             emitable.e  = expression;
 552             emitable.value = value;
 553             emitable.orIV(seekIV(expression)||seekIV(value));
 554             workingSim.exec.add(emitable);
 555             // ^^^^ We'll fixup the target in a bit...
 556             
 557             // Put out a SCOPE
 558             workingSim.exec.add(new VMIScope());
 559             
 560             // Process inner block and add the get
 561             try {
 562                 processCode(en);
 563                 
 564             } catch (Exception e) {
 565                 // Stop inner scope unravellings here...    
 566             }
 567                                 
 568             // Add the RSCOPE
 569             workingSim.exec.add(new VMIRScope());
 570             
 571             // Fixup the target
 572             emitable.target =  workingSim.exec.size();  // Points to where the NEXT intruction
 573                                                         // should be put...                      
 574         }            
 575     }
 576 
 577     /**
 578      *  Handle an For
 579      */
 580     private void handleFor(ElementNode en) {
 581         
 582         int    target;
 583         VMISet initSet =  null;
 584         
 585         String count  = en.getAttribute("count");
 586         String init = en.getAttribute("init");
 587         if (!isValid(count)) {
 588             errBadAttribute(en.toString(), "count", "for");       
 589         } else {
 590         
 591             // Prep the FOR
 592             VMIFor  emitable = new VMIFor();
 593             emitable.count  = count;
 594             emitable.orIV(seekIV(count)); 
 595             
 596             // Prep the INIT            
 597             if (init != null) {
 598             
 599                 initSet = new VMISet();
 600                 initSet.value = init; 
 601                 initSet.name  = count;
 602                 initSet.orIV(seekIV(init)||seekIV(count));
 603             }    
 604                     
 605             // Put out a SCOPE
 606             workingSim.exec.add(new VMIScope());             
 607 
 608             // Process inner block and add the get
 609             try {
 610                 
 611                 if (init != null) {
 612                      workingSim.exec.add(initSet);
 613                 }                
 614                 
 615                 // Prepare a jump back and Emit the FOR
 616                 VMIJump emitjump = new VMIJump();
 617                 emitjump.target = workingSim.exec.size();
 618                 workingSim.exec.add(emitable);
 619                             
 620                 // Work the inner scope            
 621                 processCode(en);
 622                 
 623                 // Emit the jump and points the FOR right past it.
 624                 workingSim.exec.add(emitjump);
 625                 emitable.target = workingSim.exec.size();                
 626                 
 627             } catch (Exception e) {
 628                 // Stop inner scope unravellings here...    
 629             }
 630                                 
 631             // Add the RSCOPE
 632             workingSim.exec.add(new VMIRScope());
 633         }
 634     }
 635 
 636 
 637     /**
 638      *  Handle an WHILE.
 639      *
 640      *  Bounce out any exceptions, cause a screwed IF will spoil a 
 641      *  parent scope.
 642      */
 643     private void handleWhile(ElementNode en) throws Exception {
 644         
 645         String expression  = en.getAttribute("e");
 646         String value = en.getAttribute("value");
 647         if (!isValid(expression)) {
 648             errBadAttribute(en.toString(), "e", "while");       
 649         } else if (!isValid(value)) {
 650             errBadAttribute(en.toString(), "value", "while");          
 651         } else {
 652 
 653             // Prep the while
 654             VMIWhile  emitable = new VMIWhile();
 655             emitable.e  = expression;
 656             emitable.value = value;
 657             emitable.orIV(seekIV(expression)||seekIV(value));
 658        
 659             // Emit a SCOPE
 660             workingSim.exec.add(new VMIScope());
 661             
 662             // Emit a NOP and point the while at it
 663             emitable.target =  workingSim.exec.size();
 664             workingSim.exec.add(new VMINop()); 
 665             
 666             // Process inner block
 667             try {
 668                 processCode(en);
 669                 
 670             } catch (Exception e) {
 671                 // Stop inner scope unravellings here...    
 672             }
 673                                 
 674             // Emit the WHILE and RSCOPE
 675             workingSim.exec.add(emitable);            
 676             workingSim.exec.add(new VMIRScope());
 677                      
 678         }            
 679     }
 680 
 681     // == ===============================================================================
 682     // == =                             HELPERS                                         =
 683     // == ===============================================================================
 684         
 685     /**
 686      *  See if a string contains an imbedded variable.
 687      * 
 688      *  It uses VMInstruction.IVToken as the delimiter.
 689      *
 690      *  @param s the string to check.
 691      *  @return true if yes, otherwise false
 692      */
 693     private boolean seekIV(String  s) {
 694         
 695         int sl = s.length() - 1;
 696         for (int idx = 0; idx < sl; idx++) {
 697             if ((s.charAt(idx) == VMInstruction.IVToken)&&
 698                 (s.charAt(idx+1) != VMInstruction.IVToken)) 
 699                 return true;           
 700         } 
 701         return false;   
 702     }
 703     
 704     /**
 705      *  Valid string.
 706      * 
 707      *  Checks if the reference is not null and the srring contains characters.
 708      *
 709      *  @param s the string to check.
 710      *  @return true if valid, otherwise false
 711      */
 712     private boolean isValid(String  s) {        
 713         if ((s == null)||(s.length() < 1)) return false;
 714         else return true;   
 715     }    
 716     
 717   
 718     /**
 719      *  Translate a textual token into a numeric token.
 720      *
 721      *  @param text token text.
 722      *  @return token numeric.
 723      */  
 724     private int translateToken(String  text) {
 725 
 726         // I'm going to let the parser try and optimize this... 
 727 //DEBUG
 728 //System.out.println("XLATE token:  [" + text + "]");
 729         try {       
 730             switch(text.charAt(0)) {
 731                 
 732                 case    'n': switch(text.charAt(1)) {
 733                                 case    'a': return tokNAME;
 734                                 case    'o': return tokNOTE;
 735                                 case    'v': return tokNV;
 736                                 default:     return tokNULL;
 737                              }
 738                 
 739                 case    'v': switch(text.charAt(3)) {
 740                                 case    'i': return tokVERIFY;
 741                                 case    's': return tokVERSION;
 742                                 default:     return tokNULL;
 743                              }
 744                              
 745                 case    'w': switch(text.charAt(1)) {
 746                                 case    'h': return tokWHILE;
 747                                 case    'a': return tokWAIT;
 748                                 default:     return tokNULL;
 749                              }
 750                 
 751                 case    'i': return  tokIF;            
 752                 case    's': switch(text.charAt(2)) {
 753                                 case    't': return tokSET;
 754                                 case    'e': return tokSEEK;
 755                                 default:     return tokNULL;
 756                              }
 757                 
 758                 case    'f': return  tokFOR;   
 759                 case    'g': return  tokGET;
 760                 case    'h': return  tokHEADER;                    
 761                 case    'b': return  tokBLOCK;
 762                 case    'a': return  tokADD;    
 763                 case    'e': return  tokEXEC;
 764                                                
 765                 default: return tokNULL;                            
 766             }
 767         } catch (Exception e) { }
 768         return tokNULL;      
 769     }
 770     
 771     /**
 772      *  Get the text out of an XML node.
 773      *
 774      *  @param cdn XML node.
 775      *  @return the text.
 776      */  
 777     private String getText(Node    cdn) {
 778 
 779         try {
 780             if ((cdn.getNodeType() == Node.TEXT_NODE)||(cdn.getNodeType() != Node. CDATA_SECTION_NODE)) {
 781                 CharacterData   cdnc = (CharacterData)cdn;
 782                 return cdnc.getData();    
 783             }
 784         } catch (Exception e) { } // ignore.  re are returning empty anyway
 785         return null;
 786     }
 787     
 788     /**
 789      *  Get the text out of an XML node.
 790      *
 791      *  @param where where it was bad.
 792      *  @param which which attribute was bad.
 793      *  @param tag  which tag.
 794      *  @return the text.
 795      */  
 796     private void errBadAttribute(String where, String which, String tag) {
 797         err.error("Invalid '" + which + "' attribute for <" + tag + ">.  @" + where);
 798     }    
 799     
 800 	// --- INTERNAL CLASSES ---------------------------------------------------	
 801 
 802 	// --- DEBUG METHODS ------------------------------------------------------
 803 
 804 }