source: trunk/src/org/expeditee/actions/ScriptBase.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: 8.7 KB
Line 
1/**
2 * ScriptBase.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.Collection;
22import java.util.LinkedList;
23import java.util.List;
24
25import javax.script.Bindings;
26import javax.script.Invocable;
27import javax.script.ScriptContext;
28import javax.script.ScriptEngine;
29import javax.script.ScriptEngineManager;
30import javax.script.ScriptException;
31import javax.script.SimpleScriptContext;
32
33import org.expeditee.core.Colour;
34import org.expeditee.gui.Frame;
35import org.expeditee.gui.FrameIO;
36import org.expeditee.gui.FrameUtils;
37import org.expeditee.gui.MessageBay;
38import org.expeditee.io.flowlayout.XGroupItem;
39import org.expeditee.items.Item;
40import org.expeditee.items.ItemUtils;
41import org.expeditee.items.Text;
42
43/**
44 * Javascript parser.
45 * Works differently to the other Javascript class in that it
46 * parses a frame as a whole rather than parsing individual text items as separate statements
47 *
48 * @author jts21
49 * @author davidb
50 */
51public abstract class ScriptBase {
52
53 protected static ScriptEngineManager scriptEngineManager;
54 protected String ERROR_FRAMESET;
55 protected ScriptEngine scriptEngine = null;
56
57 static {
58 scriptEngineManager = new ScriptEngineManager();
59 }
60
61 protected abstract void init();
62
63 public Object eval(String code) {
64 try {
65 return scriptEngine.eval(code);
66 } catch (ScriptException e) {
67 e.printStackTrace();
68 }
69 return null;
70 }
71
72 protected synchronized void runFrame(Frame frame, boolean followLinks) throws Exception {
73 try {
74 try {
75 scriptEngine.eval(this.toString());
76 } catch (ScriptException e) {
77 this.handleError(e.getMessage(), e.getLineNumber());
78 } catch (RuntimeException e) {
79 // there doesn't seem to be a way to safely get the lineNumber on which the error occurred
80 // so as a workaround we just parse the exception
81 if(e.getCause() == null) {
82 throw e;
83 }
84 String detail = e.getCause().getStackTrace()[1].toString();
85 int lastColon = detail.lastIndexOf(':');
86 int lastBracket = detail.lastIndexOf(')');
87 int lineNumber;
88 if(lastColon == -1 || lastBracket == -1) {
89 lineNumber = -1;
90 } else {
91 lineNumber = Integer.parseInt(detail.substring(lastColon + 1, lastBracket));
92 }
93 this.handleError(e.getMessage(), lineNumber);
94 }
95 } catch(Exception e) {
96 this.handleError(null, -1);
97 System.out.println(this.toString());
98 e.printStackTrace();
99 }
100 }
101
102 protected static final class CodeLine {
103 public Text item;
104 public int line;
105 public String source;
106
107 public CodeLine(Text item, int line, String source) {
108 this.item = item;
109 this.line = line;
110 this.source = source;
111 }
112
113 public String toString() {
114 return line + ": " + source;
115 }
116 }
117
118 protected List<Frame> seen = new LinkedList<Frame>();
119 protected List<CodeLine> lines = new LinkedList<CodeLine>();
120 protected StringBuffer sb = new StringBuffer();
121
122 protected ScriptBase(Frame frame, boolean followLinks) {
123 init();
124 this.parseFrame(frame, followLinks);
125 }
126
127 protected void parseFrame(Frame frame, boolean followLinks, int depth) {
128 if(frame == null) {
129 return;
130 }
131
132 // make sure we don't get into an infinite loop
133 // TODO: find a smarter way to do this that allows reusing frames but still stops infinite loops?
134 seen.add(frame);
135
136 // get all items on the frame
137 List<Item> y_ordered_items = (List<Item>)frame.getItems();
138 // remove the title item
139 y_ordered_items.remove(frame.getTitleItem());
140
141 XGroupItem toplevel_xgroup = new XGroupItem(frame,y_ordered_items);
142 // ... following on from Steps 1 and 2 in the Constructor in XGroupItem ...
143
144 // Step 3: Reposition any 'out-of-flow' XGroupItems
145 toplevel_xgroup.repositionOutOfFlowGroups(toplevel_xgroup);
146
147 // Step 4: Now add in the remaining (nested) XGroupItems
148 List<XGroupItem> grouped_item_list = toplevel_xgroup.getGroupedItemList();
149 toplevel_xgroup.mapInXGroupItemsRecursive(grouped_item_list);
150
151 // Finally, retrieve linear list of all Items, (ordered, Y by X, allowing for overlap, nested-boxing, and arrow flow)
152 List<Item> overlapping_y_ordered_items = toplevel_xgroup.getYXOverlappingItemList(true);
153
154 processItems(overlapping_y_ordered_items, followLinks, depth);
155 }
156
157 protected void parseFrame(Frame frame, boolean followLinks) {
158 parseFrame(frame,followLinks,0);
159 }
160
161 protected void processItems(List<Item> overlapping_y_ordered_items, boolean followLinks, int depth) {
162
163 // Base version suits syntax that uses curly braces { ... } for block structured code
164 // Override in cases where scriptable language does not do this
165
166 // Loop through the items looking for code and links to new frames
167 for(Item i : overlapping_y_ordered_items) {
168 if(followLinks && i.hasLink()) {
169 Frame child = i.getChild();
170 if(child != null && !seen.contains(child)) {
171 this.parseFrame(child, true, depth);
172 }
173 }
174 if(i instanceof Text && !i.isAnnotation()) {
175 String text = ((Text)i).getText();
176 if (i == org.expeditee.io.flowlayout.XGroupItem.GROUPSEP_START) {
177 text = "{";
178 depth++;
179 }
180 else if (i == org.expeditee.io.flowlayout.XGroupItem.GROUPSEP_END) {
181 text = "}";
182 depth--;
183 }
184
185 int lineNumber = 0;
186 for(String line : text.trim().split("[\\n\\r]+")) {
187 sb.append(line).append("\n");
188 lines.add(new CodeLine((Text)i, lineNumber++, line));
189 }
190 }
191 }
192 }
193
194 private void handleError(String message, int lineNumber) throws Exception {
195 // negative line number bad
196 if(lineNumber < 0) {
197 MessageBay.errorMessage("Failed to determine the line on which the error occurred");
198 return;
199 }
200 // if for some reason the error is after the end of the code, assume it should be the last line
201 if(lineNumber > this.lines.size()) {
202 lineNumber = this.lines.size();
203 }
204 CodeLine cl = this.lines.get(lineNumber - 1);
205 Frame errorSourceFrame = cl.item.getParent();
206 if(errorSourceFrame == null) {
207 MessageBay.errorMessage("Failed to find frame on which the error occurred");
208 return;
209 }
210 Frame errorFrame;
211 String title = "Error parsing \"" + errorSourceFrame.getTitle() + "\" (" + errorSourceFrame.getName() + ")";
212 if(FrameIO.canAccessFrameset(ERROR_FRAMESET)) {
213 errorFrame = FrameIO.CreateFrame(ERROR_FRAMESET, title, null);
214 } else {
215 errorFrame = FrameIO.CreateFrameset(ERROR_FRAMESET, FrameIO.FRAME_PATH);
216 errorFrame.setTitle(title);
217 }
218 Collection<Item> toAdd = errorSourceFrame.getAllItems();
219 toAdd.remove(errorSourceFrame.getTitleItem());
220 toAdd.remove(cl.item);
221 errorFrame.addAllItems(ItemUtils.CopyItems(toAdd));
222 String errorItemText = cl.item.getText().trim();
223 String[] errorItemLines = errorItemText.split("[\\n\\r]+");
224 int errorLinePos = 0;
225 int x = cl.item.getX();
226 int y = cl.item.getY();
227 if(cl.line != 0) {
228 for(int i = 0; i < cl.line; i++) {
229 errorLinePos += errorItemLines[i].length();
230 }
231 Text beforeErrorItem = errorFrame.addText(x, y,
232 errorItemText.substring(0, errorLinePos), null);
233 y = beforeErrorItem.getY() + beforeErrorItem.getBoundsHeight();
234 }
235 Text errorItem;
236 errorItem = errorFrame.addText(x, y, errorItemLines[cl.line], null);
237 errorItem.setBackgroundColor(Colour.RED);
238 for(String line : message.split("[\\n\\r]+")) {
239 errorItem.setTooltip("text: " + line);
240 }
241 errorItem.setTooltip("font: " + Text.MONOSPACED_FONT);
242 errorItem.setTooltip("width: " + 80 * 12);
243 errorLinePos += errorItemLines[cl.line].length();
244 if(++errorLinePos < errorItemText.length()) {
245 errorFrame.addText(cl.item.getX(), errorItem.getY() + errorItem.getBoundsHeight(),
246 errorItemText.substring(errorLinePos + 1), null);
247 }
248 errorFrame.change();
249 FrameIO.SaveFrame(errorFrame);
250 MessageBay.displayMessage("Script failed at line " + lineNumber + " - `" + cl.source + "`",
251 errorFrame.getName(), MessageBay.ERROR_COLOR, true, null);
252 FrameUtils.DisplayFrame(errorFrame, true, true);
253 }
254
255 public String toString() {
256 return this.sb.toString();
257 }
258
259}
Note: See TracBrowser for help on using the repository browser.