source: trunk/src/org/expeditee/actions/Javascript.java@ 1102

Last change on this file since 1102 was 1102, checked in by davidb, 6 years ago

Reworking of the code-base to separate logic from graphics. This version of Expeditee now supports a JFX graphics as an alternative to SWING

File size: 10.8 KB
Line 
1/**
2 * Javascript.java
3 * Copyright (C) 2010 New Zealand Digital Library, http://expeditee.org
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19package org.expeditee.actions;
20
21import java.util.LinkedList;
22import java.util.List;
23
24import org.expeditee.actions.Simple.Status;
25import org.expeditee.agents.Agent;
26import org.expeditee.agents.DefaultAgent;
27import org.expeditee.core.Colour;
28import org.expeditee.gui.DisplayController;
29import org.expeditee.gui.Frame;
30import org.expeditee.gui.FrameIO;
31import org.expeditee.gui.MessageBay;
32import org.expeditee.gui.FrameUtils;
33import org.expeditee.items.Item;
34import org.expeditee.items.Text;
35import org.expeditee.items.Item.HighlightMode;
36import org.expeditee.stats.AgentStats;
37import org.mozilla.javascript.Context;
38import org.mozilla.javascript.EvaluatorException;
39import org.mozilla.javascript.JavaScriptException;
40import org.mozilla.javascript.Scriptable;
41import org.mozilla.javascript.WrappedException;
42
43public class Javascript {
44
45 // Field largely based on Simple.java. Consider sharing code?
46
47
48 /**
49 * Keeps track of how many simple programs are running. Used to check if
50 * simple should read in keyboard input. Or if the keyboard input should be
51 * handled normally by Expeditee.
52 */
53 private static int _programsRunning = 0;
54
55 /**
56 * This flag is set to true if Simple should hijack keyboard input from
57 * Expeditee
58 */
59 private static boolean _consumeKeyboardInput = false;
60
61 public static void ProgramFinished() {
62 _programsRunning--;
63 _stop = false;
64 }
65
66 private static LinkedList<Character> _KeyStrokes = new LinkedList<Character>();
67
68 private static boolean _stop;
69
70 private static Agent _agent = null;
71
72 private static boolean _step;
73
74 private static int _stepPause = -1;
75
76 private static Colour _stepColor;
77
78 private static boolean _nextStatement;
79
80 public static void KeyStroke(char c) {
81 _KeyStrokes.add(c);
82 }
83
84 public static boolean isProgramRunning() {
85 return _programsRunning > 0;
86 }
87
88 public static boolean consumeKeyboardInput() {
89 return _consumeKeyboardInput && _programsRunning > 0;
90 }
91
92 //Have changed parameters, so it takes an Item, not just a Text item.
93 private static void RunJavascriptFrame(Frame frame, Item current,
94 boolean acceptKeyboardInput, boolean step, int pause, Colour color) {
95 try {
96 if (current != null) {
97 /*
98 * Changed the code from the line below because it caused
99 * problems when the "RunFrame" item was on the zero frame
100 */
101 // DisplayIO.addToBack(current.getParent());
102 DisplayController.addToBack(DisplayController.getCurrentFrame());
103 } else {
104 /* TODO we should not have to pass an item just to run a frame! */
105 current = new Text("Dummy");
106 current.setLink(frame.getName());
107 }
108
109 _stepColor = color == null ? Colour.GREEN : color;
110 _stepColor = new Colour(_stepColor.getRed(), _stepColor.getGreen(),
111 _stepColor.getBlue(), Colour.FromComponent255(50));
112 _stepPause = pause;
113 _step = step;
114 _consumeKeyboardInput = acceptKeyboardInput;
115 FrameIO.SaveFrame(frame, true);
116
117 // an item without a link signals to run the current frame
118 if (current != null && current.getLink() == null) {
119 // Make a copy but hide it
120 current = current.copy();
121 current.setLink(frame.getName());
122 }
123
124 _KeyStrokes.clear();
125
126 Thread t = new Thread(current);
127 t.setPriority(Thread.MIN_PRIORITY);
128 t.start();
129 } catch (Exception e) {
130 e.printStackTrace();
131 }
132 }
133
134 public static void RunJavascriptFrame(Frame frame, Text current,
135 boolean acceptKeyboardInput) {
136 RunJavascriptFrame(frame, current, acceptKeyboardInput, false, 0, null);
137 }
138
139 /**
140 * Same as RunJavascriptFrame method above, except that it takes in
141 * any Item, not just a Text item. -kgas1
142 * @param frame - frame to run
143 * @param current - item selected
144 * @param acceptKeyboardInput
145 */
146 public static void RunJavascriptFrame(Frame frame, Item current,
147 boolean acceptKeyboardInput){
148 RunJavascriptFrame(frame, current, acceptKeyboardInput, false, 0, null);
149
150 }
151
152 public static void RunJavascriptFrame(Frame frame, Text current) {
153 RunJavascriptFrame(frame, current, false);
154 }
155
156 /**
157 * Same as RunJavascriptFrame method above, except it takes
158 * any Item as a parameter; not just Text Items. -kgas1
159 * @param frame
160 * @param current
161 */
162 public static void RunJavascriptFrame(Frame frame, Item current){
163 RunJavascriptFrame(frame, current, false);
164 }
165
166 private static void FlagError(Item item) {
167 FrameUtils.DisplayFrame(item.getParent().getName(), true, true);
168 item.setHighlightMode(HighlightMode.Normal);
169 item.setHighlightColor(Colour.CYAN);
170 FrameIO.SaveFrame(item.getParent());
171 }
172
173 /**
174 * Runs a simple code beginning on a frame linked to by the specified item
175 * parameter.
176 *
177 * @param current
178 * the item that is linked to the frame to be run.
179 */
180 public static Status RunFrameAndReportError(Item current, Context context, Scriptable scope)
181 throws Exception {
182 // the item must link to a frame
183 if (current.getLink() == null) {
184 throw new Exception("Could not run unlinked item: "
185 + current.toString());
186 }
187
188 Frame child = FrameIO.LoadFrame(current.getAbsoluteLink());
189
190
191
192 if (_step) {
193 if (child != DisplayController.getCurrentFrame()) {
194 DisplayController.setCurrentFrame(child, true);
195 }
196 DisplayController.addToBack(child);
197 }
198
199 AgentStats.FrameExecuted();
200
201 // if the frame could not be loaded
202 if (child == null) {
203 throw new Exception("Could not load item link: " + current.toString());
204 }
205
206 // loop through non-title, non-name, text items
207 List<Text> body = child.getBodyTextItems(false);
208
209 // if no item was found
210 if (body.size() == 0)
211 throw new Exception("No code to be executed: " + current.toString());
212
213 Status lastItemStatus = Status.OK;
214 for (Text item : body) {
215 AgentStats.ItemExecuted();
216 try {
217 Colour oldColor = item.getBackgroundColor();
218 if (_step) {
219 pause(item);
220 }
221 lastItemStatus = RunItem(item, context, scope, lastItemStatus);
222 if (_step) {
223 if (item.getLink() == null) {
224 item.setBackgroundColor(oldColor);
225 } else {
226 item.setHighlightMode(Item.HighlightMode.None);
227 item.setHighlightColorToDefault();
228 }
229 }
230
231 if (lastItemStatus != Status.OK) {
232 if (lastItemStatus != Status.TrueIf
233 && lastItemStatus != Status.FalseIf) {
234 if (_step) {
235 DisplayController.removeFromBack();
236 }
237 return lastItemStatus;
238 }
239 }
240 } catch (ArrayIndexOutOfBoundsException e) {
241 FlagError(item);
242 throw new IncorrectUseOfStatementException(
243 "Too few parametres: " + item.toString(), item
244 .getStatement());
245 } catch (NullPointerException e) {
246 FlagError(item);
247 throw new Exception("Null pointer exception: "
248 + item.toString());
249 } catch (RuntimeException e) {
250 FlagError(item);
251 throw new IncorrectUseOfStatementException(e.getMessage() + " "
252 + item.toString(), item.getStatement());
253 } catch (Exception e) {
254 throw new Exception(e.getMessage());
255 }
256 }
257
258 if (_step) {
259 DisplayController.removeFromBack();
260 if (DisplayController.getCurrentFrame() != current.getParent())
261 DisplayController.setCurrentFrame(current.getParent(), true);
262 }
263
264 return Status.OK;
265 }
266
267
268 /**
269 * @param item
270 * @param oldColor
271 * @throws Exception
272 * @throws InterruptedException
273 */
274 private static void pause(Text item) throws Exception, InterruptedException {
275 if (!_step)
276 return;
277
278 Colour oldColor = item.getBackgroundColor();
279 item.setBackgroundColor(_stepColor);
280 item.setHighlightMode(Item.HighlightMode.None);
281 item.setHighlightColorToDefault();
282
283 // Make sure we are on the frame with this item
284 Frame parent = item.getParentOrCurrentFrame();
285 if (!parent.equals(DisplayController.getCurrentFrame())) {
286 DisplayController.setCurrentFrame(parent, true);
287 }
288
289 DisplayController.requestRefresh(true);
290
291 int timeRemaining;
292 if (_stepPause < 0)
293 timeRemaining = Integer.MAX_VALUE;
294 else
295 timeRemaining = _stepPause;
296
297 while (timeRemaining > 0 && !_nextStatement) {
298 if (_stop) {
299 item.setBackgroundColor(oldColor);
300 item.setHighlightModeAndColour(HighlightMode.Normal, _stepColor);
301 throw new Exception("Program terminated");
302 }
303 Thread.sleep(DefaultAgent.TIMER_RESOLUTION);
304 timeRemaining -= DefaultAgent.TIMER_RESOLUTION;
305 }
306 _nextStatement = false;
307 // Turn off the highlighting
308 item.setBackgroundColor(oldColor);
309 }
310
311 private static void pause(double time) throws Exception {
312 for (int i = 0; i < time * 10; i++) {
313 if (_stop) {
314 throw new Exception("Program terminated");
315 }
316 Thread.yield();
317 Thread.sleep(100);
318 }
319 }
320
321
322
323 /**
324 * Runs a text item on a frame as a SIMPLE statement. The statement is
325 * parsed and if it is a recognised SIMPLE keyword or procedure the code is
326 * executed.
327 *
328 * @param code
329 * the item containing the code to be executed.
330 * @param context
331 * @return
332 * @throws Exception
333 */
334 private static Status RunItem(Text code, Context context, Scriptable scope, Status lastItemStatus) throws Exception {
335 if (_stop) {
336 throw new Exception("Program terminated");
337 }
338
339 if (code.getLink() != null) {
340 return RunFrameAndReportError(code, context, scope);
341 }
342 else {
343 String statement = code.getText().trim();
344
345 try {
346
347 Object result = context.evaluateString(scope, statement,"<expeditee item>", 0, null);
348 if (result != org.mozilla.javascript.Context.getUndefinedValue()) {
349 System.err.println(org.mozilla.javascript.Context.toString(result));
350 }
351 }
352
353 catch (WrappedException we) {
354 // Some form of exception was caught by JavaScript and
355 // propagated up.
356 System.err.println(we.getWrappedException().toString());
357 we.printStackTrace();
358 throw we;
359 }
360 catch (EvaluatorException ee) {
361 // Some form of JavaScript error.
362 System.err.println("js: " + ee.getMessage());
363 throw ee;
364 }
365 catch (JavaScriptException jse) {
366 // Some form of JavaScript error.
367 System.err.println("js: " + jse.getMessage());
368 throw jse;
369 }
370
371 }
372
373
374 return Status.OK;
375 }
376
377 public static void stop() {
378 _stop = true;
379 if (_agent != null) {
380 _agent.stop();
381 }
382 }
383
384 public static void nextStatement() {
385 _nextStatement = true;
386 }
387
388 public static void ProgramStarted() {
389 _programsRunning++;
390 AgentStats.reset();
391 MessageBay.displayMessage("Running Javascript Program ...", Colour.BLUE);
392 }
393
394
395}
Note: See TracBrowser for help on using the repository browser.