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

Quick Search    Search Deep

Source code: com/voytechs/jnetstream/codec/Linker.java


1   /*
2    * File: Linker.java
3    * Auth: Mark Bednarczyk
4    * Date: DATE
5    *   Id: $Id: Linker.java,v 1.1.1.1 2003/09/22 16:32:15 voytechs Exp $
6    ********************************************
7    Copyright (C) 2003  Mark Bednarczyk
8   
9    This program is free software; you can redistribute it and/or
10   modify it under the terms of the GNU General Public License
11   as published by the Free Software Foundation; either version 2
12   of the License, or (at your option) any later version.
13  
14   This program is distributed in the hope that it will be useful,
15   but WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   GNU General Public License for more details.
18  
19   You should have received a copy of the GNU General Public License
20   along with this program; if not, write to the Free Software
21   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22   ********************************************
23   * $Log: Linker.java,v $
24   * Revision 1.1.1.1  2003/09/22 16:32:15  voytechs
25   * Initial import.
26   *
27   */
28  package com.voytechs.jnetstream.codec;
29  
30  import com.voytechs.jnetstream.primitive.*;
31  import com.voytechs.jnetstream.codec.Packet;
32  import com.voytechs.jnetstream.npl.*;
33  
34  import java.lang.*;
35  import java.util.*;
36  import java.io.*;
37  
38  /**
39   * 
40   */
41  public class Linker 
42    extends Visitor {
43  
44    /* Internal attributes */
45    private static final boolean debug = false;
46  
47    private static final int F_RECORD_SYMTABLE = 1;
48    private static final int F_LINK_TYPES = 2;
49    private static final int F_VARIABLE_REFERENCES = 3;
50    private static final int F_LINK_HEADERS = 4;
51    private static final int F_LINK_HEADERS_STAGE1 = 4;
52    private static final int F_LINK_HEADERS_STAGE2 = 5;
53  
54    private SymTable global;
55    private SymTable packet;
56    private SymTable types; // Special offline symtable for types.
57    private int stage = 0;
58    private StatementContainer defaultCandidate = null;
59  
60  
61    private Hashtable symTables = new Hashtable();
62  
63  
64  
65  
66  
67    /**
68     *
69     * @param
70     * @exception
71     */
72    public Linker(SymTable sym) {
73      this.global = sym;
74  
75      packet = new SymTable("packet", global);
76      types = new SymTable("types");
77    }
78  
79    public void link(NodeList block) 
80      throws NodeException {
81  
82      recordDefinitions(block);
83      loadTypePrimitives();
84      resolveTypes(block);
85      resolveReferences(block);
86      linkHeaders(block);
87    }
88  
89    public void resolveTypes(NodeList block) throws NodeException { stage = F_LINK_TYPES; visit(block, null, null); }
90    public void resolveReferences(NodeList block) throws NodeException { stage = F_VARIABLE_REFERENCES; visit(block, packet, null); }
91    public void linkHeaders(NodeList block) throws NodeException { 
92      stage = F_LINK_HEADERS_STAGE1; 
93      visit(block, null, null); 
94  
95      stage = F_LINK_HEADERS_STAGE2; 
96      visit(block, null, null); 
97  
98    }
99  
100   protected void exportProperty(String name, SymTable sym) {
101 
102     if(debug) 
103       System.out.println("Exporting " + name + " to " + sym.getName());
104 
105     MutableReferenceNode ref = new MutableReferenceNode(name);
106     ref.setSymTable(sym);
107     sym.addSymbol(name ,ref);
108   }
109 
110 
111   /**
112    * traverses the top level nodes and records their
113    * definitions in the SymTable. Only statement are
114    * expected at this level. If any non-statement nodes
115    * ie. expression are found, an error message is reported.
116    */
117   public void recordDefinitions(NodeList block)
118     throws NodeException {
119 
120     stage = F_RECORD_SYMTABLE;
121 
122     exportProperty(Packet.PACKET_LENGTH, packet);
123     exportProperty(Packet.PACKET_SNAPLEN, packet);
124     exportProperty(Packet.PACKET_START, packet);
125     exportProperty(Packet.PACKET_END, packet);
126     exportProperty(Packet.PACKET_REMAINING, packet);
127     exportProperty(Packet.PACKET_REMAINING, packet);
128     exportProperty(Packet.CAPTURE_TIMESTAMP, packet);
129 
130     exportProperty(Packet.CAPTURE_DEVICE_IP, global);
131     exportProperty(Packet.CAPTURE_DEVICE_ARCH, global);
132     exportProperty(Packet.CAPTURE_DEVICE_FILENAME, global);
133     exportProperty(Packet.CAPTURE_DEVICE_OS, global);
134     exportProperty(Packet.FIRST_HEADER, global);
135 
136     /**
137      * Traverse top level statements.
138      *
139      * Top level statements are added to the "global.packet" symtable.
140      */
141     for(int i = 0; i < block.size(); i ++) {
142       Node node = block.get(i);
143       if( (node instanceof StatementNode) == false) 
144         throw new NodeException("Expected a statement here. Only statements are allowed top-level.", node);
145 
146       visit(node, packet, null); // Implemented in Visitor class
147     }
148 
149   }
150 
151   /**
152    *
153    *
154    *
155    *
156    *
157    */
158   public void loadTypePrimitives() 
159     throws NodeException {
160     
161     Enumeration keys = types.getSymbols();
162     while(keys.hasMoreElements()) {
163       String k = (String)keys.nextElement();
164       SymTable.Symbol s = types.lookupSymbol(k);
165       if(s.getType() != SymTable.Symbol.PRIMITIVE) 
166         continue;
167 
168       if(k.equals("int")) 
169         s.setImplementation(new IntPrimitive());
170 
171       else if(k.equals("short")) 
172         s.setImplementation(new ShortPrimitive());
173 
174       else if(k.equals("byte")) 
175         s.setImplementation(new BytePrimitive());
176 
177       else if(k.equals("long")) 
178         s.setImplementation(new LongPrimitive());
179 
180       else if(k.equals("address")) 
181         s.setImplementation(new AddressPrimitive());
182 
183       else if(k.equals("ipaddress")) 
184         s.setImplementation(new IpAddressPrimitive());
185 
186       else if(k.equals("ipnetmask")) 
187         s.setImplementation(new IpNetmaskPrimitive());
188 
189       else if(k.equals("macaddress")) 
190         s.setImplementation(new MacAddressPrimitive());
191 
192       else if(k.equals("string")) 
193         s.setImplementation(new StringPrimitive());
194 
195       else if(k.equals("hexdump")) 
196         s.setImplementation(new HexdumpPrimitive());
197 
198       else if(k.equals("time")) 
199         s.setImplementation(new TimePrimitive());
200 
201       else if(k.equals(DNSNamePrimitive.NAME)) 
202         s.setImplementation(new DNSNamePrimitive());
203 
204       else {
205         throw new NodeException("Unknown type " + k + ". External primtive loader not implemented yet.", s.getDefinition());
206       }
207     }
208   }
209 
210 
211 
212 
213   /**
214    *
215    *
216    *
217    *
218    *
219    */
220   public boolean traverse(FamilyStatement stat, Object user1, Object user2)
221     throws NodeException  {
222 
223     if(debug)
224       System.out.println("Visiting Family");
225 
226     if(stage == F_LINK_HEADERS_STAGE1 || stage == F_LINK_HEADERS_STAGE2) {
227         visit(stat.getCode(), stat, null);
228     }
229     
230     if(stage == F_RECORD_SYMTABLE) {
231       SymTable table = (SymTable)user1;
232       
233       /**
234        * Record the statement in our parents symtable.
235        */
236       table.addSymbol(stat.getName(), stat);
237 
238       /**
239        * If we have code, which every header must have,
240        * then allocate a sub symtable so our children can
241        * record in it.
242        */
243       if(stat.getCode() != null) {
244         stat.setSymTable(new SymTable(stat.getName(), table));
245 
246         /**
247          * Now pass on the table to our children.
248          */
249         visit(stat.getCode(), stat.getSymTable(), user2);
250       }
251     }
252 
253     return true;
254   }
255 
256 
257 
258   /**
259    *
260    *
261    *
262    *
263    *
264    */
265   public boolean traverse(HeaderStatement stat, Object user1, Object user2)
266     throws NodeException  {
267 
268 
269     if(debug)
270       System.out.println("Visiting Header");
271 
272     if(stage == F_RECORD_SYMTABLE) {
273       SymTable table = (SymTable)user1;
274 
275       if(debug)
276         System.out.println("Recording header " + stat.getName() + " into SymTable(" + table.getName() + ")");
277       
278       /**
279        * Record the statement in our parents symtable.
280        */
281       table.addSymbol(stat.getName(), stat);
282 
283       /**
284        * If we have code, which every header must have,
285        * then allocate a sub symtable so our children can
286        * record in it.
287        */
288       if(stat.getCode() != null) {
289         stat.setSymTable(new SymTable(stat.getName(), table));
290 
291         /**
292          * Now pass on the table to our children.
293          */
294         visit(stat.getCode(), stat.getSymTable(), user2);
295       }
296       if(stat.getArrayDimension() != null) {
297         visit(stat.getArrayDimension(), user1, user2);
298       }
299     }
300       
301     else if(stage == F_LINK_TYPES) {
302       if(stat.getCode() != null) {
303         visit(stat.getCode(), user1, user2);
304       }
305       if(stat.getArrayDimension() != null) {
306         visit(stat.getArrayDimension(), user1, user2);
307       }
308     }
309 
310 
311     else if(stage == F_VARIABLE_REFERENCES) {
312       if(stat.getCode() != null) {
313         visit(stat.getCode(), stat.getSymTable(), stat);
314       }
315       else {
316         throw new NodeException("Empty header " + stat.getName(), stat);
317       }
318       if(stat.getArrayDimension() != null) {
319         visit(stat.getArrayDimension(), user1, user2);
320       }
321     }
322 
323     else if(stage == F_LINK_HEADERS_STAGE1) {
324         if(stat.isDefaultCandidate()) {
325           if(defaultCandidate != null)
326             throw new NodeException("Only 1 default link candidate can be declared", stat);
327 
328           defaultCandidate = stat;
329         }
330         visit(stat.getCode(), stat, null);
331     }
332     else if( stage == F_LINK_HEADERS_STAGE2) {
333         visit(stat.getCode(), stat, null);
334 
335         /**
336          * Now set the default candidate in every header.
337          * With the exception of the default header its self. This would
338          * create a loop. Loop terminates because default candidate usually
339          * suck all of the available data so Snaplen Exception is thrown when
340          * end of packet is reached to break the loop. But we want to do this cleanly.
341          */
342         if(defaultCandidate != null && stat != defaultCandidate)
343           stat.setDefaultCandidate(defaultCandidate);
344     }
345 
346     else {
347         throw new NodeException("Unimplemented stage " + stage + " in header. Not a good idea", stat);
348     }
349     return true;
350   }
351 
352 
353 
354 
355 
356   /**
357    *
358    *
359    *
360    *
361    *
362    */
363   public boolean traverse(FieldStatement stat, Object user1, Object user2)
364     throws NodeException  {
365 
366       if(debug)
367         System.out.println("Visiting Field stage=" + stage);
368 
369     if(stage == F_VARIABLE_REFERENCES) {
370       SymTable table = (SymTable)user1;
371 
372       if(debug)
373         System.out.println("Linking Field References");
374 
375 
376       if(stat.hasArray()) {
377 
378 //        MutableArrayReferenceNode ref = new MutableArrayReferenceNode(stat.getName(), stat.getArrayDimension());
379 //        ref.setSymTable(table);
380 //        table.addSymbol(stat.getName(), SymTable.Symbol.REFERENCE, ref);
381       }
382 
383       else {
384 
385         MutableReferenceNode ref = new MutableReferenceNode(stat.getName());
386         ref.setSymTable(table);
387         table.addSymbol(stat.getName(), SymTable.Symbol.REFERENCE, ref);
388       }
389 
390       if(stat.getCode() != null) {
391         visit(stat.getCode(), stat.getSymTable(), stat);
392       }
393 
394       if(stat.getSizeCode() != null) {
395         visit(stat.getSizeCode(), user1, user2);
396       }
397 
398       if(stat.getSkipCode() != null) {
399         visit(stat.getSkipCode(), user1, user2);
400       }
401       if(stat.getArrayDimension() != null) {
402         visit(stat.getArrayDimension(), user1, user2);
403       }
404     }
405       /**
406        * Make sure that the field type is recorded
407        * in the SymTable so the field can be linked
408        * later.
409        */
410 
411     else if(stage == F_RECORD_SYMTABLE) {
412 
413 
414       SymTable table = (SymTable)user1;
415 
416       if(debug)
417         System.out.println("Recording symtable Field References");
418 
419       /**
420        * First record the type to a primitive. Types are recorded in the
421        * special symtable.
422        */
423       if(types.contains(stat.getType()) == false)
424         types.addSymbol(stat.getType(), SymTable.Symbol.PRIMITIVE, null);
425 
426       /**
427        * Now check for code.
428        * Allocate new symtable for subfields.
429        */
430       SymTable subFieldSymTable = new SymTable(stat.getName(), table);
431       stat.setSymTable(subFieldSymTable);
432 
433       /**
434        * Export builtin properties
435        */
436       exportProperty(FieldStatement.SIZE, subFieldSymTable);
437       exportProperty(Field.VALUE_NAME, subFieldSymTable);
438 
439       if(stat.getCode() != null) {
440         visit(stat.getCode(), stat.getSymTable(), user2);
441       }
442 
443       if(stat.getSizeCode() != null) {
444         visit(stat.getSizeCode(), user1, user2);
445       }
446 
447       if(stat.getSkipCode() != null) {
448         visit(stat.getSkipCode(), user1, user2);
449       }
450 
451       if(stat.getArrayDimension() != null) {
452         visit(stat.getArrayDimension(), user1, user2);
453       }
454     }
455 
456     else if(stage == F_LINK_TYPES) {
457 
458       if(debug)
459         System.out.println("Linking types in Field");
460 
461       SymTable.Symbol s = types.lookupSymbol(stat.getType());
462       if(s == null || s.getImplementation() == null) 
463         throw new NodeException("Unresolved link " + stat.getType(), stat);
464 
465       stat.setTypePrimitive((ProtocolPrimitiveFactory)s.getImplementation());
466 
467       if(stat.getCode() != null) {
468         visit(stat.getCode(), user1, user2);
469       }
470 
471       if(stat.getSizeCode() != null) {
472         visit(stat.getSizeCode(), user1, user2);
473       }
474 
475       if(stat.getSkipCode() != null) {
476         visit(stat.getSkipCode(), user1, user2);
477       }
478 
479       if(stat.getArrayDimension() != null) {
480         visit(stat.getArrayDimension(), user1, user2);
481       }
482     }
483     else if(stage == F_LINK_HEADERS_STAGE1) {
484       if(stat.isLinkable()) {
485         HeaderStatement header = (HeaderStatement)user1;
486         SymTable.Symbol s = header.getSymTable().lookupSymbol(stat.getName());
487         header.setLinkVariable((ReferenceNode)s.getDefinition());
488 
489         if(debug)
490           System.out.println("Recording link variable " + stat.getName() + " in header(" + header.getName() + ")");
491       }
492     }
493 
494     return true;
495   }
496 
497 
498 
499 
500 
501   /**
502    *
503    *
504    *
505    *
506    *
507    */
508   public boolean traverse(AssertStatement stat, Object user1, Object user2)
509     throws NodeException  {
510 
511     if(debug)
512       System.out.println("Visiting Assert");
513 
514     if(stat.getCode() != null) {
515       visit(stat.getCode(), user1, user2);
516     }
517     return true;
518   }
519 
520 
521 
522 
523   /**
524    *
525    *
526    *
527    *
528    *
529    */
530   public boolean traverse(LinkStatement stat, Object user1, Object user2)
531     throws NodeException  {
532 
533     if(debug)
534       System.out.println("Visiting Link");
535 
536     if(stage == F_VARIABLE_REFERENCES) {
537 
538       if(debug)
539         System.out.println("Linking link References");
540 
541       /**
542        * For an Link statement variable references need to be
543        * resolved in the target header not the current header.
544        * We therefore have to find and substitute the target's 
545        * symtable.
546        */
547 
548       SymTable.Symbol s = packet.lookupSymbol(stat.getName());
549 
550       if(s == null || (s.getDefinition() instanceof StatementContainer) == false) 
551         throw new NodeException("Link target header [" + stat.getName() + "] not found", stat);
552 
553       StatementContainer target = (StatementContainer)s.getDefinition();
554 
555       if(stat.getCode() != null) {
556         visit(stat.getCode(), target.getSymTable(), user2);
557       }
558     }
559 
560 
561     else if(stage == F_LINK_HEADERS_STAGE2) {
562       StatementContainer header = (StatementContainer)user1;
563       String headerToLinkTo = stat.getName();
564       SymTable.Symbol s = packet.lookupSymbol(headerToLinkTo);
565       if(s == null || (s.getDefinition() instanceof StatementContainer) == false) 
566         throw new NodeException("Link target header [" + headerToLinkTo + "] not found", stat);
567 
568       StatementContainer target = (StatementContainer)s.getDefinition();
569 
570       Node code = stat.getCode(); // Code can be null
571 
572       if(debug)
573         System.out.println("Linking links: " + header.getName() + " to " + headerToLinkTo);
574 
575       if(code == null || code instanceof OpNode) {
576         target.addLinkAssertion((OpNode)code, header);
577       }
578       else if(code instanceof IntNode) {
579         if(target.getLinkVariable() == null)
580           throw new NodeException("Link target does not have a linked variable set.", code);
581 
582         target.setLinkAssertion(((IntNode)code).getInt(), header);
583       }
584 
585       if(debug)
586         System.out.println("Resolving link: " + header.getName() + " to " + headerToLinkTo);
587 
588     }
589     return true;
590   }
591 
592 
593 
594 
595   /**
596    *
597    *
598    *
599    *
600    *
601    */
602   public boolean traverse(MutableReferenceNode  node, Object user1, Object user2)
603     throws NodeException  {
604 
605     if(debug)
606       System.out.println("Visiting MutableReferenceNode");
607 
608     /**
609      * Replace Reference node found in the tree with reference from the SymTable.
610      */
611     if(stage == F_VARIABLE_REFERENCES) {
612       SymTable table = (SymTable)user1;
613 
614       SymTable.Symbol s = table.lookupSymbol(node.getName());
615       Node refNode;
616       if(s == null) {
617         throw new NodeException("Undefined variable reference \"" + node.getName() + "\" in sym table " + table.getName(), node);
618       }
619 
620 
621       refNode = s.getDefinition();
622 
623       if( user2 instanceof OpNode) {
624         OpNode op = (OpNode)user2;
625 
626         if(op.getLeft() == node) {
627           op.setLeft(refNode);
628         }
629         else if(op.getRight() == node) {
630           op.setRight(refNode);
631         }
632         else {
633           throw new NodeException("Can not dereference node " + node.getName() + ". Invalid parent.", node);
634         }
635       }
636       else {
637         node.setReference((ReferenceNode)refNode);
638 //        node.setSymTable(((MutableReferenceNode)refNode).getSymTable());
639 //        throw new NodeException("Can not dereference node " + node.getName() + ". Parent unknown.", node);
640   }
641     }
642     return true;
643   }
644 
645   /**
646    *
647    *
648    *
649    *
650    *
651    */
652   public boolean traverse(VariableStatement stat, Object user1, Object user2)
653     throws NodeException  {
654 
655     if(debug)
656       System.out.println("Visiting VariableStatement " + stat.getName() + " stage=" + stage);
657 
658     if(stage == F_LINK_TYPES) {
659       SymTable table = (SymTable)user1;
660 
661       SymTable.Symbol s = types.lookupSymbol(stat.getType());
662       if(s == null || s.getImplementation() == null) 
663         throw new NodeException("Unresolved link " + stat.getType(), stat);
664 
665       stat.setTypePrimitive((ProtocolPrimitiveFactory)s.getImplementation());
666     }
667 
668 
669     if(stage == F_RECORD_SYMTABLE) {
670       /**
671        * First record the type to a primitive. Types are recorded in the
672        * special symtable.
673        */
674       if(types.contains(stat.getType()) == false)
675         types.addSymbol(stat.getType(), SymTable.Symbol.PRIMITIVE, null);
676     }
677 
678     if(stage == F_VARIABLE_REFERENCES) {
679       SymTable table = (SymTable)user1;
680       MutableReferenceNode ref = new MutableReferenceNode(stat.getName());
681       ref.setSymTable(table);
682       table.addSymbol(stat.getName(), SymTable.Symbol.REFERENCE, ref);
683     }
684     
685 
686     return true;
687   }
688 
689 
690   public boolean traverse(ConstReferenceNode node, Object user1, Object user2)
691     throws NodeException  {
692 
693     if(debug)
694       System.out.println("Visiting ConstReferenceNode " + node.getName() + " stage=" + stage);
695 
696     if(stage == F_VARIABLE_REFERENCES) {
697       SymTable table = (SymTable)user1;
698       table.addSymbol(node.getName(), SymTable.Symbol.REFERENCE, node);
699     }
700     
701 
702     return true;
703   }
704 
705 
706   public boolean traverse(EnumReferenceNode node, Object user1, Object user2)
707     throws NodeException  {
708 
709     if(debug)
710       System.out.println("Visiting EnumReferenceNode " + node.getName() + " stage=" + stage);
711 
712     if(stage == F_VARIABLE_REFERENCES) {
713       SymTable table = (SymTable)user1;
714       table.addSymbol(node.getName(), SymTable.Symbol.REFERENCE, node);
715 
716       if( (user2 instanceof EnumStatement) == false)
717         throw new NodeException("Enum properties can only be declared within 'enum' statement", node);
718 
719 
720       EnumStatement enum = (EnumStatement)user2;
721       enum.setEnumValue(node.getPrimitive(), node.getString());
722       
723     }
724     
725 
726     return true;
727   }
728 
729 
730 
731 
732 
733   /**
734    * Special OpNode traversal routine that records the last node visited.
735    *
736    * @param op OpNode to be traversed.
737    * @param user1 Depends at what stage of linking. Usually SymTable.
738    * @param user2 Parent node that called this.
739    */
740   public boolean traverse(OpNode op, Object user1, Object user2) 
741     throws NodeException { 
742 
743     if(stage == F_VARIABLE_REFERENCES) {
744       if(op.getLeft() != null)
745         visit(op.getLeft(), user1, op);
746 
747       if(op.getRight() != null)
748         visit(op.getRight(), user1, op);
749     }
750     
751     return true;
752   }
753 
754 
755   /**
756    *
757    *
758    *
759    *
760    *
761    */
762   public boolean traverse(IfStatement stat, Object user1, Object user2)
763     throws NodeException  {
764 
765       if(debug)
766         System.out.println("Visiting If stage=" + stage);
767 
768 
769       if(debug)
770         System.out.println("Linking If References");
771 
772       if(stat.getCondition() != null) {
773         visit((Node)stat.getCondition(), user1, user2);
774       }
775 
776       if(stat.getCode() != null) {
777         visit(stat.getCode(), user1, user2);
778       }
779 
780       if(stat.getElseCode() != null) {
781         visit(stat.getElseCode(), user1, user2);
782       }
783     return true;
784   }
785 
786   /**
787    *
788    *
789    *
790    *
791    *
792    */
793   public boolean traverse(ForStatement stat, Object user1, Object user2)
794     throws NodeException  {
795 
796       if(debug)
797         System.out.println("Visiting For stage=" + stage);
798 
799 
800       if(stat.getInitCode() != null) {
801         visit(stat.getInitCode(), user1, user2);
802       }
803 
804       if(stat.getCondition() != null) {
805         visit((Node)stat.getCondition(), user1, user2);
806       }
807 
808       if(stat.getForCode() != null) {
809         visit(stat.getForCode(), user1, user2);
810       }
811 
812       if(stat.getCode() != null) {
813         visit(stat.getCode(), user1, user2);
814       }
815     return true;
816   }
817 
818   public boolean traverse(WhileStatement stat, Object user1, Object user2)
819     throws NodeException  {
820 
821     if(debug)
822       System.out.println("Visiting For stage=" + stage);
823 
824 
825     if(stat.getCondition() != null) {
826       visit((Node)stat.getCondition(), user1, user2);
827     }
828 
829     if(stat.getCode() != null) {
830       visit(stat.getCode(), user1, user2);
831     }
832 
833     return true;
834   }
835 
836   public boolean traverse(PrintStatement stat, Object user1, Object user2)
837     throws NodeException  {
838 
839     if(debug)
840       System.out.println("Visiting Print stage=" + stage);
841 
842 
843     if(stat.getStringExpression() != null) {
844       visit((Node)stat.getStringExpression(), user1, user2);
845     }
846 
847     return true;
848   }
849 
850   public boolean traverse(EnumStatement stat, Object user1, Object user2)
851     throws NodeException  {
852 
853     if(debug)
854       System.out.println("Visiting Print stage=" + stage);
855 
856 
857 
858     if(stage == F_VARIABLE_REFERENCES) {
859       visit(stat.getEnumCode(), user1, stat); // records enum values in stat.
860 
861       if( (user2 instanceof FieldStatement) == false)
862         throw new NodeException("'enum' statement can only be associated with a field.", stat);
863 
864       FieldStatement field = (FieldStatement)user2;
865       field.setEnumTable(stat);
866     }
867     else if(stat.getEnumCode() != null) {
868       visit(stat.getEnumCode(), user1, user2);
869     }
870 
871     return true;
872   }
873 
874   public boolean traverse(PropertyStatement stat, Object user1, Object user2)
875     throws NodeException  {
876 
877     if(debug)
878       System.out.println("Visiting Property stage=" + stage);
879 
880 
881     visit(stat.getExpressionCode(), user1, stat);
882 
883     if(stage == F_VARIABLE_REFERENCES) {
884       String n = stat.getName();
885       SymTable sym = (SymTable)user1;
886       SymTable targetSym = sym.lookupSymTable(stat.getName(), true);
887 
888       if( targetSym != null && targetSym != sym) {
889         if(stat.isTargetPermTable())
890           throw new NodeException("Not allowed to set \"perm\" property \"" + stat.getName() + "\" in non-local context", stat);
891 
892         if(debug)
893           System.out.println(stat.getName() + " targetSym = " + targetSym.getName());
894 
895         sym = targetSym;
896 
897         String[] path = n.split(SymTable.DELIMITER);
898         n = path[path.length -1];
899         
900       }
901 
902       stat.setSymTable(sym);
903 
904       if(stat.isTargetPermTable() == false) {
905 
906         if(stat.getName().equals(FieldStatement.SIZE) && (user2 instanceof FieldStatement) == false)
907           throw new NodeException("\"" + FieldStatement.SIZE + "\" property can only be applied to 'field' statements.", stat);
908 
909         MutableReferenceNode ref = new MutableReferenceNode(n);
910         ref.setSymTable(sym);
911 
912         sym.addSymbol(n, ref);
913         return true;
914       }
915 
916       /**
917        * Verify that properties are being applied in proper context.
918        */
919       if(stat.getName().equals(FieldStatement.SIZE))
920         throw new NodeException("\"" + FieldStatement.SIZE + "\" property can only be applied in 'local' context.", stat);
921 
922 
923       if( (user2 instanceof HeaderStatement) || (user2 instanceof FieldStatement) ) {
924         StatementNode node = (StatementNode)user2;
925         Hashtable perm = node.getPermTable();
926         StringNode exp = (StringNode)stat.getExpressionCode();
927         String e = exp.getString();
928 
929         /**
930          * First set the property in the PERM table in the statement. Any Headers created will
931          * have a reference to the same table.
932          */
933         StringPrimitive p = new StringPrimitive();
934         p.setValue(e);
935         perm.put(n, p);
936 
937         /**
938          * Next we add the variable to the sym table as a primitive so it can be referenced
939          * from within NPL.
940          */
941 
942         ConstReferenceNode ref = new ConstReferenceNode(n, p);
943         ref.setSymTable(sym);
944 
945         sym.addSymbol(n, ref);
946 
947       }
948       else 
949         throw new NodeException("property statement only allowed inside a header or a field. (" + user2.getClass().getName() + ")", stat);
950     }
951 
952     return true;
953   }
954 
955 
956   /**
957    * Test function for Linker
958    * @param args command line arguments
959    */
960   public static void main(String [] args) {
961 
962     ExpTokenizer tokens = null;
963 
964     if(args.length == 1) {
965       tokens = new ExpTokenizer(new StringReader(args[0]));
966     }
967 
968     else if(args.length == 2) {
969       try {tokens = new ExpTokenizer(new FileReader(args[1]));} catch(FileNotFoundException f) { f.printStackTrace(); return;}
970     }
971     else 
972       tokens = new ExpTokenizer();
973 
974 
975 
976     StatementParser parser;
977     parser = new StatementParser();
978 
979     NodeList block = new NodeList();
980     Node node;
981 
982     try {
983         while(tokens.hasMoreTokens()) {
984           block.add(node = parser.parseInstruction(tokens));
985           System.out.println(node);
986         }
987 
988         Linker linker = new Linker(new SymTable("test"));
989         linker.recordDefinitions(block);
990         linker.loadTypePrimitives();
991         linker.resolveTypes(block);
992 
993         System.out.println("SYMTABLE=======================================");
994         System.out.println(linker.global);
995     }
996     catch(SyntaxError se) {
997       se.printStackTrace();
998     }
999     catch(NodeException ne) {
1000      ne.printStackTrace();
1001    }
1002
1003
1004
1005  }
1006
1007} /* END OF: Linker */