2003/10/31 10:57:24
[org.ibex.core.git] / src / org / xwt / Trap.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 import java.util.*;
5 import org.xwt.js.*;
6 import org.xwt.util.*;
7 import java.io.*;
8
9 /**
10  *  This class encapsulates a single trap placed on a given node. The
11  *  traps for a given property name on a given box are maintained as a
12  *  linked list stack, with the most recently placed trap at the head
13  *  of the list.
14  */
15 public class Trap {
16
17     // Static Data //////////////////////////////////////////////////////////////
18
19     /** List of properties that cannot be trapped */
20     private static final Hash PROHIBITED = new Hash(120, 3);
21
22     static {
23         String[] p = new String[] {
24             "shrink", "hshrink", "vshrink", "x", "y",
25             "width", "height", "flex", "colspan", "rowspan", "cols",
26             "rows", "align", "invisible", "absolute", "globalx",
27             "globaly", "minwidth", "minheight", "height", "width",
28             "maxwidth", "maxheight", "numchildren", "hpad", "vpad",
29             "buffered", "cursor", "mousex", "mousey",
30             "mouseinside", "thisbox", "indexof", "path", "font", "fontsize"
31         };
32         for(int i=0; i<p.length; i++) PROHIBITED.put(p[i], Boolean.TRUE);
33     };
34
35
36     // Instance Members ////////////////////////////////////////////////////////
37
38     /** the box on which this trap was placed */
39     private Box trapee = null;
40
41     /** the function for this trap */
42     JS.CompiledFunction f = null;
43
44     /** the next trap down the trap stack */
45     private Trap next = null;
46
47     /** the property that the trap was placed on */
48     private Object name = null;
49
50
51     // Static Methods //////////////////////////////////////////////////////////////////////////
52
53     /**
54      *  adds a trap.
55      *  @param trapee the box to place the trap on
56      *  @param name the name of the property to trap on
57      *  @param f the function to place as a trap
58      */
59     static void addTrap(Box trapee, Object name, JS.CompiledFunction f) {
60
61         // check if this script has already placed a trap on this property
62         for(Trap t = (Trap)trapee.get(name, Trap.class); t != null; t = t.next)
63             if (t.f == f) return;
64         
65         // actually place the trap
66         Trap t = new Trap();
67         t.next = (Trap)trapee.get(name, Trap.class);
68         trapee.put(name, Trap.class, t);
69         t.trapee = trapee;
70         t.name = name;
71         t.f = f;
72     }
73
74
75     /**
76      *  deletes a trap.
77      *  @param trapee the box to remove the trap from
78      *  @param name the name of the property to trap on
79      *  @param f the function to remove
80      */
81     static void delTrap(Box trapee, Object name, JS.CompiledFunction f) {
82         Trap t = (Trap)trapee.get(name, Trap.class);
83         if (t.f == f) {
84             trapee.put(name, Trap.class, t.next);
85             return;
86         }
87         for(; t.next != null; t = t.next)
88             if (t.next.f == f) {
89                 t.next = t.next.next;
90                 return;
91             }
92         Log.logJS("warning: tried to remove a trap that had not been placed");
93     }
94
95
96     // Instance Methods //////////////////////////////////////////////////////////////////////////
97
98     private Trap() { }
99
100     public Object perform() {
101         try {
102             if (f.getNumFormalArgs() > 0) return cascade();
103             JS.Thread.current().setTailCall(f, new TrapArgs(this));
104             return null;
105         } catch (Exception e) {
106             Log.log(this, "Exception thrown from within trap: " + e);
107             return null;
108         }
109     }
110
111     private static JS.CompiledFunction cascadeHelper = null;
112     private static String cascadeHelperText =
113         "return function(q) { var ret = arguments.doTrap;" +
114         "if (ret != false && !arguments.didCascade) arguments.cascade = q; };";
115     static {
116         try {
117             cascadeHelper = JS.parse("cascadeHelper", 1, new StringReader(cascadeHelperText));
118             cascadeHelper = (JS.CompiledFunction)new JS.Thread(cascadeHelper).resume();
119         } catch (Exception e) {
120             Log.log(Trap.class, e);
121         }
122     }
123
124     public void perform(Object val) {
125         try {
126             if (f.getNumFormalArgs() == 0) cascade(val);
127             else JS.Thread.current().setTailCall(cascadeHelper, new TrapArgs(this, val));
128         } catch (Exception e) {
129             Log.log(this, "Exception thrown from within trap: " + e);
130             e.printStackTrace();
131         }
132     }
133     
134     public Object cascade() {
135         if (next != null) { next.perform(); return null; }
136         return trapee.get(name, true);
137     }
138
139     public void cascade(Object val) {
140         if (next != null) next.perform(val);
141         trapee.put(name, val, true);
142     }
143
144     private static class TrapArgs extends JS.Array {
145         private Trap t;
146         public boolean cascadeHappened = false;
147         public TrapArgs(Trap t) { this.t = t; }
148         public TrapArgs(Trap t, Object value) { this.t = t; addElement(value); }
149         
150         public void put(Object key, Object val) {
151             if (key.equals("cascade")) { cascadeHappened = true; t.cascade(val); }
152             else super.put(key, val);
153         }
154
155         public Object get(Object key) {
156             // common case
157             if(!(key instanceof String)) return super.get(key);
158             if (key.equals("trapee")) return t.trapee;
159             if (key.equals("doTrap")) { JS.Thread.current().setTailCall(t.f, this); return null; }
160             if (key.equals("didCascade")) return cascadeHappened ? Boolean.TRUE : Boolean.FALSE;
161             if (key.equals("trapname")) return t.name;
162             if (key.equals("cascade")) return t.cascade();
163             if (key.equals("callee")) return t.f;
164             return super.get(key);
165         }
166     }
167 }
168