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  
154