source: trunk/src/org/expeditee/items/JSItem.java@ 785

Last change on this file since 785 was 785, checked in by jts21, 10 years ago

Tidier thread handling for JSItem (although it does assume that threads should always be restarted, and there is no way of disabling a thread after adding it... TODO: add a flag for disabling a thread, and automatically set that flag if the thread finishes execution successfully)

File size: 6.6 KB
Line 
1package org.expeditee.items;
2
3import java.awt.Graphics2D;
4import java.awt.Polygon;
5import java.util.LinkedList;
6import java.util.List;
7
8import javax.script.Invocable;
9import javax.script.ScriptEngine;
10
11import org.expeditee.actions.Javascript2;
12import org.expeditee.gui.DisplayIO;
13import org.expeditee.items.widgets.InteractiveWidget;
14
15public class JSItem extends XRayable {
16
17 private static final Object global = Javascript2.eval("new Object()");
18
19 // a method to run that will set up and return the root JComponent for this Widget
20 private final String init;
21 // a method to run to populate a List<String> with our state
22 private final String save;
23 // a method to run that will load state from a String[]
24 private final String load;
25 // a method to run that will paint our item using a Graphics2D object
26 private final String paint;
27
28 // we have our own script context since it needs to have some global variables which are specific to each widget
29 private final ScriptEngine scriptEngine;
30 private final Invocable invocable;
31
32 private int _width, _height;
33
34 private static Text getSauce(int width, int height) {
35 Text source = new Text(DisplayIO.getCurrentFrame().getNextItemID(), "@js: " + width + " " + height);
36 source.setParent(DisplayIO.getCurrentFrame());
37 return source;
38 }
39
40 private JSItem(Text source, String init, String save, String load, String paint) throws Exception {
41 super(source);
42 this.init = init;
43 this.save = save;
44 this.load = load;
45 this.paint = paint;
46 this.scriptEngine = Javascript2.scriptEngineManager.getEngineByMimeType("application/javascript");
47 this.invocable = (Invocable) this.scriptEngine;
48 this.scriptEngine.put("global", global);
49 this.scriptEngine.put("invocable", this.invocable);
50 this.scriptEngine.put("item", this);
51 this.scriptEngine.put("source", this._source);
52 this.parseSource();
53 System.out.println(this.init);
54 this.scriptEngine.eval("var init = " + this.init + "\ninit()");
55 this.scriptEngine.eval("save = " + this.save);
56 this.scriptEngine.eval("paint = " + this.paint);
57 this.updateSource();
58 }
59
60 public JSItem(Text source) throws Exception {
61 this(source, source.getData().get(0).replaceAll("\\\\n", "\n"),
62 source.getData().get(1).replaceAll("\\\\n", "\n"),
63 source.getData().get(2).replaceAll("\\\\n", "\n"),
64 source.getData().get(3).replaceAll("\\\\n", "\n"));
65 }
66
67 public JSItem(int width, int height, String init, String save, String load, String paint) throws Exception {
68 this(getSauce(width, height), init, save, load, paint);
69 }
70
71 public JSItem(String init, String save, String load, String paint) throws Exception {
72 this(100, 100, init, save, load, paint);
73 }
74
75 @Override
76 public Item copy() {
77 try {
78 return new JSItem(_source.copy());
79 } catch (Exception e) {
80 e.printStackTrace();
81 }
82 return null;
83 }
84
85 @Override
86 public void paint(Graphics2D g) {
87 try {
88 this.invocable.invokeFunction("paint", (Object)g);
89 } catch (Exception e) {
90 e.printStackTrace();
91 }
92 }
93
94 @Override
95 public void setAnnotation(boolean val) {
96 // TODO Auto-generated method stub
97
98 }
99
100 @Override
101 public void updatePolygon() {
102 _poly = new Polygon();
103 _poly.addPoint(getX(), getY());
104 _poly.addPoint(getX() + _width, getY());
105 _poly.addPoint(getX() + _width, getY() + _height);
106 _poly.addPoint(getX(), getY() + _height);
107 }
108
109 private void parseSource() {
110 String text = _source.getText();
111 text = text.replaceFirst("@js:", "");
112 text = text.replaceAll("\n", "");
113 text = text.trim();
114 int index = text.indexOf(':');
115 String[] values;
116 if(index != -1) {
117 values = text.substring(0, index).split("\\s+");
118 } else {
119 values = text.split("\\s+");
120 }
121 if(values.length >= 2) {
122 _width = Integer.parseInt(values[0]);
123 _height = Integer.parseInt(values[1]);
124 }
125
126 if(index == -1) {
127 return;
128 }
129 String[] args = InteractiveWidget.parseArgs(text.substring(index));
130 try {
131 this.scriptEngine.eval("load = " + this.load);
132 this.invocable.invokeFunction("load", (Object) args);
133 } catch (Exception e) {
134 e.printStackTrace();
135 }
136 }
137
138 private String[] saveArgs() {
139 try {
140 List<String> args = new LinkedList<String>();
141 this.invocable.invokeFunction("save", (Object)args);
142 return args.toArray(new String[0]);
143 } catch (Exception e) {
144 e.printStackTrace();
145 }
146 return null;
147 }
148
149 private void updateSource() {
150 List<String> data = new LinkedList<String>();
151 data.add(this.init.replaceAll("[\n\r]", "\\\\n"));
152 data.add(this.save.replaceAll("[\n\r]", "\\\\n"));
153 data.add(this.load.replaceAll("[\n\r]", "\\\\n"));
154 data.add(this.paint.replaceAll("[\n\r]", "\\\\n"));
155 _source.setData(data);
156
157 StringBuffer newText = new StringBuffer("@js: ");
158 newText.append(_width).append(" ").append(_height);
159
160 String stateArgs = InteractiveWidget.formatArgs(saveArgs());
161 if (stateArgs != null) {
162 newText.append(':');
163 newText.append(stateArgs);
164 }
165
166 _source.setText(newText.toString());
167 }
168
169 @Override
170 public Integer getWidth() {
171 return this._width;
172 }
173
174 @Override
175 public int getHeight() {
176 return this._height;
177 }
178
179 private final class JSThread {
180 private Thread thread;
181 private String code;
182
183 public JSThread(String code) {
184 this.code = code;
185 this.thread = null;
186 }
187
188 public void kill() {
189 if(thread != null) {
190 thread.interrupt();
191 thread = null;
192 }
193 }
194
195 public void start() {
196 thread = new Thread(new Runnable() {
197 @Override
198 public void run() {
199 try {
200 scriptEngine.eval("thread = " + code + "\nthread()");
201 } catch (Exception e) {
202 if(e.getCause() instanceof InterruptedException) {
203 // if the thread was interrupted exit quietly
204 return;
205 }
206 e.printStackTrace();
207 }
208 }
209 });
210 thread.start();
211 }
212 }
213
214 private List<JSThread> threads = new LinkedList<JSThread>();
215
216 public void addThread(String code) {
217 this.threads.add(new JSThread(code));
218 }
219
220 @Override
221 public void onParentStateChanged(ItemParentStateChangedEvent e) {
222 switch (e.getEventType()) {
223 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED:
224 case ItemParentStateChangedEvent.EVENT_TYPE_REMOVED_VIA_OVERLAY:
225 case ItemParentStateChangedEvent.EVENT_TYPE_HIDDEN:
226 for(JSThread t : this.threads) {
227 t.kill();
228 }
229 break;
230
231 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED:
232 case ItemParentStateChangedEvent.EVENT_TYPE_ADDED_VIA_OVERLAY:
233 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN:
234 case ItemParentStateChangedEvent.EVENT_TYPE_SHOWN_VIA_OVERLAY:
235 for(JSThread t : this.threads) {
236 t.start();
237 }
238 break;
239
240 }
241 }
242
243}
Note: See TracBrowser for help on using the repository browser.