(no commit message)
[oweals/gnunet.git] / src / monkey / seaspider / org / gnunet / seaspider / parser / LineNumberInfo.java
1 package org.gnunet.seaspider.parser;
2
3 import java.lang.reflect.Field;
4 import java.util.Enumeration;
5 import java.util.WeakHashMap;
6
7 /**
8  * Obtain line number information for any JTB node.  Note that
9  * "any" really means any -- we use reflection to overcome the
10  * problem that we do not know the full name of the AST root
11  * (in particular, there maybe multiple AST hierarchies in the
12  * project with multiple root "Node" classes).<p>
13  * 
14  * Essentially, pass a JTB node to the static "get" method
15  * and (if it is a JTB node and there is source corresponding
16  * to it), a LineNumberInfo object with the scope of the 
17  * subtree is returned.  Otherwise null.<p>
18  * 
19  *
20  * Minimal example:
21  * <code>
22  * void semanticError(String message, Node n) {
23  *   throw new Error(message + " at " + LineNumberInfo.get(n));
24  * }
25  * </code>
26  *<p>
27  *
28  * This code is dual licensed under both BSD and LGPL.
29  * 
30  * @author Christian Grothoff
31  */
32 public class LineNumberInfo {
33
34     private static final Object NULL = new Object();
35     private static final Object SELF = new Object();
36     private static final WeakHashMap<Object, Object> cacheF_ = new WeakHashMap<Object,Object>();
37     private static final WeakHashMap<Object, Object> cacheL_ = new WeakHashMap<Object,Object>();
38     
39     public final int lineStart;
40     public final int lineEnd;
41     public final int colStart;
42     public final int colEnd;
43     
44     public LineNumberInfo(int s, int e, int cs, int ce) {
45         lineStart = s;
46         lineEnd = e;
47         colStart = cs;
48         colEnd = ce;
49     }
50     
51     public String toString() {
52         return "["+lineStart+":"+colStart+","+lineEnd+":"+colEnd+"]";
53     }
54
55     /**
56      * Compute line number information for the given JTB node.
57      * 
58      * @param o any JTB node (the type should be node, but since this
59      *  code handles any JTB AST we cannot hardwire the 
60      *  type ("Node" -- but which package?)
61      * @return
62      */
63     public static LineNumberInfo get(Object o) {
64         if (o == null) 
65             return null; // fail!        
66         Object p = firstMatch(o);        
67         Object q = lastMatch(o);
68         try {
69             int lstart = ((Integer)p.getClass().getField("beginLine").get(p)).intValue();
70             int cstart = ((Integer)p.getClass().getField("beginColumn").get(p)).intValue();
71             int lend = ((Integer)p.getClass().getField("endLine").get(q)).intValue();
72             int cend = ((Integer)p.getClass().getField("endColumn").get(q)).intValue();
73             return new LineNumberInfo(lstart, lend, cstart, cend);
74         } catch (Throwable t) {
75             return null; // failed
76         }
77     }
78
79     private static Object firstMatch(Object o) {
80         synchronized(cacheF_) {
81             Object r = cacheF_.get(o);
82             if (r != null)
83                 return (r == SELF) ? o : (r == NULL) ? null : r;
84         }
85         Object r = firstMatch_(o);
86         synchronized(cacheF_) {
87             cacheF_.put(o, r == o ? SELF : r == null ? NULL : r);
88         }
89         return r;
90     }
91
92    
93     private static Object lastMatch(Object o) {
94         synchronized(cacheL_) {
95             Object r = cacheL_.get(o);
96             if (r != null)
97                 return (r == SELF) ? o : (r == NULL) ? null : r;
98         }
99         Object r = lastMatch_(o);
100         synchronized(cacheL_) {
101             cacheL_.put(o, r == o ? SELF : r == null ? NULL : r);
102         }
103         return r;
104     }
105
106     private static Object firstMatch_(Object o) {        
107         if (o == null)
108             return null;
109         Class c = o.getClass();
110         if  (c.getName().endsWith("NodeToken"))
111             return o;        
112         try {
113             int i=0;            
114             while (true) {
115                 Field f = null;                           
116                 try {
117                     f = c.getField("f" + i);
118                 } catch (Throwable t) {                    
119                 }
120                 if ( (f == null) && (i == 0) && (c.getName().endsWith("NodeChoice")) ) {
121                     f = c.getField("choice");
122                 } else if ( (f == null) && (i == 0) && (c.getName().endsWith("NodeOptional")) ) {
123                     f = c.getField("node");
124                 } else if ( (f == null) && (i == 0) ) {                        
125                     // special cases: node sequence, etc.
126                     Enumeration e = (Enumeration) c.getMethod("elements").invoke(o);
127                     while (e.hasMoreElements()) {
128                         Object x = firstMatch(e.nextElement());
129                         if (x != null)
130                             return x;
131                     }
132                 } 
133                 if (f != null) {
134                     Object r = firstMatch(f.get(o));
135                     if (r != null)
136                         return r;
137                 } else {
138                     return null;
139                 }
140                 i++;
141             }
142         } catch (Throwable t) {
143         }
144         return null;
145     }
146
147     private static Object lastMatch_(Object o) {
148         if (o == null)
149             return null;
150         Class c = o.getClass();
151         if  (c.getName().endsWith("NodeToken"))
152             return o;
153
154         Object ret = null;
155         try {
156             int i=0;            
157             while (true) {
158                 Field f = null;                           
159                 try {
160                     f = c.getField("f" + i);
161                 } catch (Throwable t) {                    
162                 }
163                 if ( (f == null) && (i == 0) && (c.getName().endsWith("NodeChoice")) ) {
164                     f = c.getField("choice");
165                 } else if ( (f == null) && (i == 0) && (c.getName().endsWith("NodeOptional")) ) {
166                     f = c.getField("node");
167                 } else if ( (f == null) && (i == 0) ) {                        
168                     // special cases: node sequence, etc.
169                     Enumeration e = (Enumeration) o.getClass().getMethod("elements").invoke(o);
170                     while (e.hasMoreElements()) {
171                         Object x = lastMatch(e.nextElement());
172                         if (x != null)
173                             ret = x;
174                     }                    
175                     return ret;
176                 }
177                 if (f != null) {
178                     Object r = lastMatch(f.get(o));
179                     if (r != null)
180                         ret = r;
181                 } else {
182                     return ret;
183                 }
184                 i++;
185             }            
186         } catch (Throwable t) {
187         }        
188         return ret;
189     }
190     
191 }
192