source: trunk/src/org/expeditee/actions/Javascript2.java@ 715

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

Move JS actions to Javascript2 class, handle java ClassNotFound exceptions caused by trying to access an non-existant class from javascript

File size: 8.0 KB
Line 
1package org.expeditee.actions;
2
3import java.awt.Color;
4import java.util.Collection;
5import java.util.LinkedList;
6import java.util.List;
7
8import javax.script.ScriptEngine;
9import javax.script.ScriptEngineManager;
10import javax.script.ScriptException;
11
12import org.expeditee.gui.Frame;
13import org.expeditee.gui.FrameIO;
14import org.expeditee.gui.FrameUtils;
15import org.expeditee.gui.MessageBay;
16import org.expeditee.io.flowlayout.XGroupItem;
17import org.expeditee.items.Item;
18import org.expeditee.items.ItemUtils;
19import org.expeditee.items.Line;
20import org.expeditee.items.Text;
21
22/**
23 * Javascript parser.
24 * Works differently to the other Javascript class in that it
25 * parses a frame as a whole rather than parsing individual text items as separate statements
26 *
27 * @author jts21
28 */
29public class Javascript2 {
30
31 public static final String ERROR_FRAMESET = "JavascriptErrors";
32
33 private static ScriptEngineManager sem = new ScriptEngineManager();
34 private static ScriptEngine se = sem.getEngineByMimeType("application/javascript");
35
36 public static void printJSFrame(Item item) {
37 if(item.getChild() == null) {
38 // if the user clicked on the action without an item on their cursor
39 if(item.hasAction()) {
40 boolean isThis = false;
41 for(String s : item.getAction()) {
42 if(s.equalsIgnoreCase("runJSFrame")) {
43 isThis = true;
44 break;
45 }
46 }
47 if(isThis) {
48 System.out.println(new Javascript2(item.getParentOrCurrentFrame(), true));
49 return;
50 }
51 }
52 MessageBay.warningMessage("Requires either an item with a link to a frame, or no item (will run the current frame)");
53 } else {
54 System.out.println(new Javascript2(item.getChild(), true));
55 }
56 }
57
58 public static void runJSFrame(Item item) throws Exception {
59 if(item.getChild() == null) {
60 // if the user clicked on the action without an item on their cursor
61 if(item.hasAction()) {
62 boolean isThis = false;
63 for(String s : item.getAction()) {
64 if(s.equalsIgnoreCase("runJSFrame")) {
65 isThis = true;
66 break;
67 }
68 }
69 if(isThis) {
70 Javascript2.runFrame(item.getParentOrCurrentFrame(), true);
71 return;
72 }
73 }
74 MessageBay.warningMessage("Requires either an item with a link to a frame, or no item (will run the current frame)");
75 } else {
76 Javascript2.runFrame(item.getChild(), true);
77 }
78 }
79
80 private static synchronized void runFrame(Frame frame, boolean followLinks) throws Exception {
81 Javascript2 js = new Javascript2(frame, followLinks);
82 try {
83 se.eval(js.toString());
84 } catch (ScriptException e) {
85 js.handleError(e.getMessage(), e.getLineNumber());
86 } catch (RuntimeException e) {
87 // there doesn't seem to be a way to safely get the lineNumber on which the error occurred
88 // so as a workaround we just parse the exception
89 String detail = e.getCause().getStackTrace()[1].toString();
90 int lastColon = detail.lastIndexOf(':');
91 int lastBracket = detail.lastIndexOf(')');
92 int lineNumber;
93 if(lastColon == -1 || lastBracket == -1) {
94 lineNumber = -1;
95 } else {
96 lineNumber = Integer.parseInt(detail.substring(lastColon + 1, lastBracket));
97 }
98 js.handleError(e.getMessage(), lineNumber);
99 }
100 }
101
102 private 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
114 private List<Frame> seen = new LinkedList<Frame>();
115 private List<CodeLine> lines = new LinkedList<CodeLine>();
116 private StringBuffer sb = new StringBuffer();
117 private Javascript2(Frame frame, boolean followLinks) {
118 this.parseFrame(frame, followLinks);
119 }
120
121 private void parseFrame(Frame frame, boolean followLinks) {
122 if(frame == null) {
123 return;
124 }
125
126 // make sure we don't get into an infinite loop
127 // TODO: find a smarter way to do this that allows reusing frames but still stops infinite loops?
128 seen.add(frame);
129
130 // get all items on the frame
131 List<Item> y_ordered_items = (List<Item>)frame.getItems();
132 // remove the title item
133 y_ordered_items.remove(frame.getTitleItem());
134
135 XGroupItem toplevel_xgroup = new XGroupItem(frame,y_ordered_items);
136 // ... following on from Steps 1 and 2 in the Constructor in XGroupItem ...
137
138 // Step 3: Reposition any 'out-of-flow' XGroupItems
139 toplevel_xgroup.repositionOutOfFlowGroups(toplevel_xgroup);
140
141 // Step 4: Now add in the remaining (nested) XGroupItems
142 List<XGroupItem> grouped_item_list = toplevel_xgroup.getGroupedItemList();
143 toplevel_xgroup.mapInXGroupItemsRecursive(grouped_item_list);
144
145 // Finally, retrieve linear list of all Items, (ordered, Y by X, allowing for overlap, nested-boxing, and arrow flow)
146 List<Item> overlapping_y_ordered_items = toplevel_xgroup.getYXOverlappingItemList(true);
147
148 // Loop through the items looking for code and links to new frames
149 for(Item i : overlapping_y_ordered_items) {
150 if(followLinks && i.hasLink()) {
151 Frame child = i.getChild();
152 if(child != null && !seen.contains(child)) {
153 this.parseFrame(child, true);
154 }
155 }
156 if(i instanceof Text && !i.isAnnotation()) {
157 String text = ((Text)i).getText();
158 int lineNumber = 0;
159 for(String line : text.trim().split("[\\n|\\r]+")) {
160 sb.append(line).append("\n");
161 lines.add(new CodeLine((Text)i, lineNumber++, line));
162 }
163 }
164 }
165 }
166
167 private void handleError(String message, int lineNumber) throws Exception {
168 // negative line number bad
169 if(lineNumber < 0) {
170 MessageBay.errorMessage("Failed to determine the line on which the error occurred");
171 return;
172 }
173 // if for some reason the error is after the end of the code, assume it should be the last line
174 if(lineNumber >= this.lines.size()) {
175 lineNumber = this.lines.size() - 1;
176 }
177 CodeLine cl = this.lines.get(lineNumber - 1);
178 Frame errorSourceFrame = cl.item.getParent();
179 if(errorSourceFrame == null) {
180 MessageBay.errorMessage("Failed to find frame on which the error occurred");
181 return;
182 }
183 Frame errorFrame;
184 String title = "Error parsing \"" + errorSourceFrame.getTitle() + "\" (" + errorSourceFrame.getName() + ")";
185 if(FrameIO.canAccessFrameset(ERROR_FRAMESET)) {
186 errorFrame = FrameIO.CreateFrame(ERROR_FRAMESET, title, null);
187 } else {
188 errorFrame = FrameIO.CreateFrameset(ERROR_FRAMESET, FrameIO.FRAME_PATH);
189 errorFrame.setTitle(title);
190 }
191 Collection<Item> toAdd = errorSourceFrame.getAllItems();
192 toAdd.remove(errorSourceFrame.getTitleItem());
193 toAdd.remove(cl.item);
194 errorFrame.addAllItems(ItemUtils.CopyItems(toAdd));
195 String errorItemText = cl.item.getText().trim();
196 String[] errorItemLines = errorItemText.split("[\\n|\\r]+");
197 int errorLinePos = 0;
198 int x = cl.item.getX();
199 int y = cl.item.getY();
200 if(cl.line != 0) {
201 for(int i = 0; i < cl.line; i++) {
202 errorLinePos += errorItemLines[i].length();
203 }
204 Text beforeErrorItem = errorFrame.addText(x, y,
205 errorItemText.substring(0, errorLinePos), null);
206 y = beforeErrorItem.getY() + beforeErrorItem.getBoundsHeight();
207 }
208 Text errorItem;
209 errorItem = errorFrame.addText(x, y, errorItemLines[cl.line], null);
210 errorItem.setBackgroundColor(Color.RED);
211 for(String line : message.split("[\\n|\\r]+")) {
212 errorItem.setTooltip("text: " + line);
213 }
214 errorItem.setTooltip("font: " + Text.MONOSPACED_FONT);
215 errorItem.setTooltip("width: " + 80 * 12);
216 errorLinePos += errorItemLines[cl.line].length();
217 if(++errorLinePos < errorItemText.length()) {
218 errorFrame.addText(cl.item.getX(), errorItem.getY() + errorItem.getBoundsHeight(),
219 errorItemText.substring(errorLinePos + 1), null);
220 }
221 errorFrame.change();
222 FrameIO.SaveFrame(errorFrame);
223 MessageBay.displayMessage("Script failed at line " + lineNumber + " - `" + cl.source + "`",
224 errorFrame.getName(), MessageBay.ERROR_COLOR, true, null);
225 FrameUtils.DisplayFrame(errorFrame, true, true);
226 }
227
228 public String toString() {
229 return this.sb.toString();
230 }
231
232}
Note: See TracBrowser for help on using the repository browser.