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