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