added more flexible handling of Repeats -- now you can specify what they get tagged...
[sbp.git] / src / edu / berkeley / sbp / misc / ReflectiveGrammar.java
1 package edu.berkeley.sbp.misc;
2 import java.io.*;
3 import java.util.*;
4 import java.lang.reflect.*;
5 import edu.berkeley.sbp.*;
6 import edu.berkeley.sbp.misc.*;
7 import edu.berkeley.sbp.tib.*;
8 import edu.berkeley.sbp.chr.*;
9 import edu.berkeley.sbp.util.*;
10
11 public class ReflectiveGrammar extends MetaGrammar {
12
13     final Class baseClass;
14     public ReflectiveGrammar(Class baseClass) { this.baseClass = baseClass; }
15
16     public Object convertLabel(String label) { return new ClassMark(label); }
17     public Object repeatTag() { return arrayMark; }
18
19     private static class ArrayMark { }
20     private static final ArrayMark arrayMark = new ArrayMark();
21
22     private static class ClassMark {
23         public final String clazz;
24         public ClassMark(String clazz) { this.clazz = clazz; }
25         public String toString() { return clazz+"$"; }
26     }
27
28     public String stringify(Tree<Object> t) throws Exception {
29         StringBuffer ret = new StringBuffer();
30         for(int i=0; i<t.numChildren(); i++) {
31             Tree<Object> child = t.child(i);
32             Object head = child.numChildren() > 0 ? buildHead(child, String.class) : child.head();
33             if (head!=null) ret.append(head);
34         }
35         return ret.toString();
36     }
37
38     public Object build(Tree<Object> t) throws Exception { return buildHead(t, null); }
39     public Object buildHead(Tree<Object> t, Class c) throws Exception {
40         //System.out.println("buildHead " + (c==null?null:c.getName()) + " " + t);
41         Object h = t.head();
42
43         if (h != null && h instanceof ClassMark) {
44             String clazz = ReflectiveWalker.mangle(((ClassMark)h).clazz);
45             Class c2 = (c!=null && c.getName().endsWith("$"+clazz)) ? c : Reflection.forNameOrNull(baseClass.getName()+"$"+clazz);
46             if (c2!=null) return buildBody(t, c2);
47             for(Method m : baseClass.getMethods()) {
48                 if (!m.getName().equals(clazz)) continue;
49                 Object[] o = new Object[m.getParameterTypes().length];
50                 for(int i=0; i<o.length; i++)
51                     o[i] = i>=t.numChildren() ? null : buildHead(t.child(i), m.getParameterTypes()[i]);
52                 return m.invoke(null, o);
53             }
54             throw new RuntimeException("couldn't figure out what to invoke for ClassMark " + clazz);
55         } else if (h==arrayMark && c==null) {
56             c = Object[].class;            
57         }
58         if (c==null) System.out.println(h + " -- " + t.toPrettyString());
59         if (c.isArray()) {
60             Object[] ret = new Object[t.numChildren()];
61             for(int i=0; i<ret.length; i++)
62                 ret[i] = buildHead(t.child(i), c.getComponentType());
63             return Reflection.lub(ret);
64         }
65         if (c==String.class) return stringify(t);
66         if (c==int.class)    { String s = stringify(t); if (s==null||"".equals(s)) return new Integer(0); return new Integer(s); }
67         if (h==null)         return buildBody(t, c);
68
69         if (t.numChildren() > 0)
70             throw new RuntimeException("can't buildHead() on a tree with children when the head is of type " +
71                                        h.getClass().getName() + "(c=="+(c==null?"null":c.getName())+")");
72         return h;
73     }
74
75     public Object buildBody(Tree<Object> t, Class c) throws Exception {
76         System.out.println("buildBody " + (c==null?null:c.getName()) + " " + t);
77         c = resolveClass(t, c);
78         if (c==null) return buildHead(t, null);
79         Object o = c.newInstance();
80         Field[] f = c.getFields();
81         OUTER: for(int i=0; i<t.numChildren(); i++) {
82             Object label = t.label(i);
83             Field field = null;
84             if (label!=null) field = Reflection.getField(c, label+"");
85             if (field==null && label != null)
86                 for(Method m : c.getMethods())
87                     if (m.getName().equals(label)) {
88                         m.invoke(o, new Object[] { buildHead(t.child(i), m.getParameterTypes()[0]) });
89                         continue OUTER;
90                     }
91             if (field==null && label==null) {
92                 field = Reflection.getField(c, 0);
93             }
94             if (field==null) {
95                 System.err.println("warning: skipping field " + label + " ("+i+") on class " + c.getName());
96                 System.err.println("   tree: " + t);
97             }
98             else {
99                 Object tgt = Reflection.rebuild(buildHead(t.child(i), field.getType()), field.getType());
100                 //if (tgt instanceof Object[]) tgt = Reflection.lub(tgt);
101                 System.err.println("setting field " + field.getName() + " on " + c.getName() + " to " + tgt);
102                 try {
103                     field.set(o, tgt);
104                 } catch (Exception e) {
105                     e.printStackTrace();
106                 }
107             }
108         }
109         return o;
110     }
111
112     public Class resolveClass(Tree<Object> t, Class c) throws Exception {
113         if (c==null) return null;
114         if (c==int.class) return c;
115         if (c==String.class) return c;
116         System.out.println("resolving " + c.getName());
117         if (Reflection.isConcrete(c)) return c;
118         Class ret = null;
119         Class[] subs = (Class[])c.getField("subclasses").get(null);
120         OUTER: for(int i=0; i<subs.length; i++) {
121             System.err.println("trying " + subs[i].getName());
122             for(int j=0; j<t.numChildren(); j++)
123                 if (Reflection.getField(subs[i], t.label(j)+"")==null) {
124                     System.err.println("skipping due to " + t.label(j));
125                     continue OUTER;
126                 }
127             if (ret != null)
128                 throw new RuntimeException("couldn't decide between two classes:\n  " + subs[i].getName() + "\n  " + ret.getName());
129             ret = subs[i];
130         }
131         if (t.head()==null) return null;
132         if (ret==null) throw new RuntimeException("couldn't find a class to match tree: " + t);
133         return ret;
134     }
135
136 }