EOS 2  1.1.0
Einfache Objektbasierte Sprache
SourceCode.java
gehe zur Dokumentation dieser Datei
1 package de.lathanda.eos.common.gui;
2 
3 import java.io.BufferedReader;
4 import java.io.BufferedWriter;
5 import java.io.File;
6 import java.io.FileInputStream;
7 import java.io.FileOutputStream;
8 import java.io.IOException;
9 import java.io.InputStreamReader;
10 import java.io.OutputStreamWriter;
11 import java.util.LinkedList;
12 import java.util.TreeSet;
13 
14 import javax.swing.JOptionPane;
15 import javax.swing.SwingUtilities;
16 import javax.swing.event.DocumentEvent;
17 import javax.swing.event.DocumentListener;
18 import javax.swing.text.AttributeSet;
19 import javax.swing.text.BadLocationException;
20 import javax.swing.text.DefaultStyledDocument;
21 import javax.swing.text.Element;
22 
23 import de.lathanda.eos.Stop;
24 import de.lathanda.eos.base.event.CleanupListener;
25 import de.lathanda.eos.base.util.ErrorLevel;
26 import de.lathanda.eos.base.util.MessageHandler;
27 import de.lathanda.eos.base.util.MessageHandler.LogListener;
28 import de.lathanda.eos.baseparser.AbstractProgram;
29 import de.lathanda.eos.baseparser.CompilerListener;
30 import de.lathanda.eos.baseparser.Source;
31 import de.lathanda.eos.common.gui.GuiConfiguration.GuiConfigurationListener;
32 import de.lathanda.eos.vm.AbstractMachine;
33 import de.lathanda.eos.vm.DebugInfo;
34 import de.lathanda.eos.vm.DebugListener;
35 import de.lathanda.eos.vm.ErrorInformation;
36 import de.lathanda.eos.vm.Marker;
37 
45 public class SourceCode extends DefaultStyledDocument implements Source, CompilerListener, DebugListener, LogListener,
46  GuiConfigurationListener, CleanupListener, DocumentListener {
47  private static final long serialVersionUID = -7902775704808861534L;
48 
49  private static final Object COMPILE_LOCK = new Object();
50  private final OutputStyle message;
51  private AbstractMachine machine;
52  private AbstractMachine runningMachine;
53  private boolean compileNeeded = false;
54  private boolean sourceDirty = false;
55  private String path = "";
56  private int delay = 0;
57  private AbstractProgram program;
58  private String programText = "";
59  private AutoCompleteHook autoCompleteHook;
60  private CodeColorHook codeColorHook;
61  private TreeSet<Integer> breakpoints = new TreeSet<Integer>();
62  private TreeSet<Integer> errors = new TreeSet<Integer>();
63  private SideInformation sideInformation;
64  private LinkedList<UndoableEdit> undostack = new LinkedList<>();
65  private LinkedList<UndoableEdit> redostack = new LinkedList<>();
66  private MessageHandler msgHandler;
67  public SourceCode() {
68  msgHandler = new MessageHandler();
70  message = new OutputStyle();
71  msgHandler.addLogListener(this);
73  addDocumentListener(this);
74 
75  }
76 
77  public void init(AutoCompleteHook autoCompleteHook, CodeColorHook codeColorHook) {
78  this.codeColorHook = codeColorHook;
79  this.autoCompleteHook = autoCompleteHook;
80  codeColorHook.init(this);
82  }
83 
84  public void loadProgram(File file) throws IOException {
85  BufferedReader br;
86  br = new BufferedReader(new InputStreamReader(new FileInputStream(file), "Utf-8"));
87  StringBuilder src = new StringBuilder();
88  while (br.ready()) {
89  src.append(br.readLine());
90  src.append("\n");
91  }
92  try {
93  path = file.getParent();
94  replace(0, getLength(), src.toString(), null);
95  } catch (BadLocationException ble) {
96  // This shouldn't be possible, but if it happens it's fatal
97  JOptionPane.showMessageDialog(null, ble.getLocalizedMessage(), Messages.getString("InternalError.Title"),
98  JOptionPane.ERROR_MESSAGE);
99  }
100  br.close();
101  sourceDirty = false;
102  }
103 
104  public void saveProgram(File file) throws IOException {
105  FileOutputStream save = null;
106  try {
107  String text = this.getText(0, getLength());
108  save = new FileOutputStream(file);
109  try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(save, "Utf-8"))) {
110  bw.append(text);
111  }
112  path = file.getParent();
113  sourceDirty = false;
114  } catch (BadLocationException ble) {
115  // This shouldn't be possible, but if it happens it's fatal
116  JOptionPane.showMessageDialog(null, Messages.getString("InternalError.Title"), ble.getLocalizedMessage(),
117  JOptionPane.ERROR_MESSAGE);
118  } finally {
119  if (save != null) {
120  save.close();
121  }
122  }
123  }
124 
126  return program;
127  }
128 
129  public boolean isSourceDirty() {
130  return sourceDirty;
131  }
132 
133  public DefaultStyledDocument getOutput() {
134  return message;
135  }
136 
137  public void run() {
138  if (runningMachine != machine) {
139  stop();
140  runningMachine = machine;
141  }
142  if (runningMachine != null) {
143  runningMachine.run();
144  }
145  }
146 
147  public void clear() {
148  try {
149  remove(0, getLength());
150  sourceDirty = false;
151  } catch (BadLocationException ble) {
152  // This shouldn't be possible, but if it happens it's fatal
153  JOptionPane.showMessageDialog(null, ble.getLocalizedMessage(), Messages.getString("InternalError.Title"),
154  JOptionPane.ERROR_MESSAGE);
155  System.exit(1);
156  }
157  }
158 
159  private void handleException(Exception e) {
160  Marker codeRange = machine.getDebugInfo().getCodeRange();
161  errors.add(codeRange.getBeginLine());
162  message.println(Messages.formatError("Error.Report", ((codeRange != null) ? codeRange.getBeginLine() : "?"),
163  e.getLocalizedMessage()));
164  }
165 
166  public void singleStep() {
167  if (runningMachine != machine) {
168  stop();
169  runningMachine = machine;
170  }
171  if (runningMachine != null) {
172  runningMachine.singleStep();
173  }
174  }
175 
176  public void pause() {
177  if (runningMachine != null) {
178  runningMachine.pause();
179  }
180  }
181 
182  public void stop() {
183  if (runningMachine != null) {
184  runningMachine.stop();
185  }
186  codeColorHook.unmarkExecutionPoint();
187  }
188 
189  public void skip() {
190  if (runningMachine != machine) {
191  stop();
192  runningMachine = machine;
193  }
194  if (runningMachine != null) {
195  runningMachine.skip();
196  }
197  }
198 
199  public void setSpeed(int value) {
200  double stepsPerSecond = Math.pow(1.096478196, value);
201  delay = (int) (1000000000 / stepsPerSecond);
202  // 0 = 1 per sec
203  // 100 = 10000 per sec
204  if (machine != null) {
205  machine.setDelay(delay);
206  }
207  }
208 
209  public int getSpeed() {
210  return 1000000000 / delay;
211  }
212 
213  @Override
214  public String getSourceCode() {
215  synchronized (COMPILE_LOCK) {
216  while (!compileNeeded) {
217  try {
218  COMPILE_LOCK.wait();
219  } catch (InterruptedException ex) {
220  }
221  }
222  compileNeeded = false;
223  return programText;
224  }
225  }
226 
227  @Override
228  public void insertString(int pos, String text, AttributeSet attributeSet) throws BadLocationException {
229  try {
230  autoCompleteHook.insertString(pos, text, program);
231  } catch (Exception e) {
232  // Auto completion may never cancel input
233  }
234  storeInsert(pos, text.length(), text);
235  super.insertString(pos, text, attributeSet);
236  }
237 
238  @Override
239  public void remove(int offs, int len) throws BadLocationException {
240  storeRemove(offs, len, programText.substring(offs, offs + len));
241  super.remove(offs, len);
242  }
243 
244  public void changed() {
245  synchronized (COMPILE_LOCK) {
246  compileNeeded = true;
247  try {
248  programText = getText(0, getLength());
249  } catch (BadLocationException ex) {
250  handleException(ex);
251  }
252  sourceDirty = true;
253  COMPILE_LOCK.notifyAll();
254  }
255  }
256 
257  @Override
258  public void compileComplete(LinkedList<ErrorInformation> errors, AbstractProgram program) {
259  SwingUtilities.invokeLater(() -> compileCompleteInteral(errors, program));
260  }
261 
262  private void compileCompleteInteral(LinkedList<ErrorInformation> errors, AbstractProgram program) {
263  pause();
264  this.program = program;
265  this.machine = program.getMachine();
266 
267  machine.removeDebugListener(this);
268  machine.setDelay(delay);
269  machine.addDebugListener(this);
270  this.errors.clear();
271  codeColorHook.doColoring();
272  for (Integer linenumber : breakpoints) {
273  machine.setBreakpoint(linenumber, true);
274  }
275  message.clear();
276  if (errors.size() > 0) {
278  for (ErrorInformation err : errors) {
279  if (errorNr > 0) {
280  errorNr--;
281  message.println(err.getMessage());
282  }
283  }
284  for (ErrorInformation err : errors) {
285  if (err.getCode() != null) {
286  this.errors.add(err.getCode().getBeginLine());
287  codeColorHook.markError(err.getCode());
288  }
289  }
290  } else {
291  message.println(Messages.getError("Source.Ok"));
292  }
293  sideInformation.repaint();
294  }
295 
296  @Override
297  public void debugPointReached(DebugInfo debugInfo) {
298  codeColorHook.markExecutionPoint(debugInfo.getCodeRange());
299  }
300 
301  public class OutputStyle extends DefaultStyledDocument {
302  private static final long serialVersionUID = -8345159320105291891L;
303 
304  public void println(String line) {
305  try {
306  insertString(getLength(), line + "\n", null);
307  } catch (BadLocationException e) {
308  // This should not be possible?
309  }
310  }
311 
312  public void clear() {
313  try {
314  remove(0, getLength());
315  } catch (BadLocationException e) {
316  // This should not be possible?
317  }
318  }
319  }
320 
321  public boolean hasBreakpoint(int linenumber) {
322  return breakpoints.contains(linenumber);
323  }
324 
325  public boolean hasError(int linenumber) {
326  return errors.contains(linenumber);
327  }
328 
329  public void setToggleBreakpoint(int position) {
330  Element root = getDefaultRootElement();
331  int linenumber = root.getElementIndex(position) + 1;
332  if (linenumber < 1)
333  return;
334  if (breakpoints.contains(linenumber)) {
335  breakpoints.remove(linenumber);
336  if (machine != null) {
337  machine.setBreakpoint(linenumber, false);
338  }
339  } else {
340  breakpoints.add(linenumber);
341  if (machine != null) {
342  machine.setBreakpoint(linenumber, true);
343  }
344  }
345  }
346 
347  @Override
348  public String getPath() {
349  return path;
350  }
351 
352  public void setPath(String path) {
353  this.path = path;
354  }
355 
356  public void prettyPrint() {
357  try {
358  this.replace(0, getLength(), this.program.prettyPrint(), null);
359  } catch (BadLocationException e) {
360  // shouldn't happen
361  }
362  }
363 
364  public void setSideInformation(SideInformation sideInformation) {
365  this.sideInformation = sideInformation;
366  }
367 
368  @Override
369  public void message(String msg, ErrorLevel level) {
370  SwingUtilities.invokeLater(() -> messageInteral(msg, level));
371  }
372 
373  private void messageInteral(String msg, ErrorLevel level) {
374  switch (level) {
375  case STATUS:
376  message.println(Messages.getString(msg));
377  break;
378  case INFORMATION:
379  message.println(Messages.getString("Message.Information") + " " + msg);
380  break;
381  case WARNING:
382  message.println(Messages.getString("Message.Warning") + " " + msg);
383  break;
384  case ERROR:
385  message.println(Messages.getString("Message.Error") + " " + msg);
386  break;
387  case FATAL:
388  message.println(Messages.getString("Message.Fatal") + " " + msg);
389  break;
390  }
391  }
392 
393  @Override
394  public void clearMessages() {
395  message.clear();
396  }
397 
398  @Override
399  public void fontsizeChanged(int fontsize) {
400  SwingUtilities.invokeLater(() -> fontsizeChangedInternal(fontsize));
401  }
402 
403  private void fontsizeChangedInternal(int fontsize) {
404  codeColorHook.setFontSize(fontsize);
405  codeColorHook.doColoring();
406  }
407 
408  @Override
409  public void terminate() {
410  if (machine != null) {
411  // machine.stop();
412  machine = null;
413  }
414  }
415 
416  private int linecount = -1;
417 
418  @Override
419  public void changedUpdate(DocumentEvent e) {
420  }
421 
422  @Override
423  public void insertUpdate(DocumentEvent e) {
424  int oldLineCount = linecount;
425  Element root = getDefaultRootElement();
426  linecount = root.getElementIndex(getLength()) + 1;
427  int newLines = linecount - oldLineCount;
428  if (program != null) {
429  int line = program.getLine(e.getOffset());
430  if (line > 0 && newLines > 0) {
431  TreeSet<Integer> newBreakpoints = new TreeSet<Integer>();
432  for (int breakpoint : breakpoints) {
433  int pos = machine.getBreakpointPosition(breakpoint);
434  if (e.getOffset() < pos) {
435  newBreakpoints.add(breakpoint + newLines);
436  } else {
437  newBreakpoints.add(breakpoint);
438  }
439  }
440  breakpoints = newBreakpoints;
441  }
442  }
443  changed();
444  }
445 
446  @Override
447  public void removeUpdate(DocumentEvent e) {
448  int oldLineCount = linecount;
449  linecount = getDefaultRootElement().getElementIndex(getLength()) + 1;
450  int deleteLines = oldLineCount - linecount;
451  if (program != null) {
452  if (deleteLines > 0) {
453  TreeSet<Integer> newBreakpoints = new TreeSet<Integer>();
454  for (int breakpoint : breakpoints) {
455  int pos = machine.getBreakpointPosition(breakpoint);
456  if (e.getOffset() + e.getLength() <= pos) {
457  newBreakpoints.add(breakpoint - deleteLines);
458  } else if (e.getOffset() > pos) {
459  newBreakpoints.add(breakpoint);
460  } else {
461  // breakpoint no longer exists
462  }
463  }
464  breakpoints = newBreakpoints;
465  }
466  }
467  changed();
468  }
469 
470  public void undo() {
471  if (!undostack.isEmpty()) {
472  UndoableEdit ue = undostack.pop();
473  redostack.push(ue);
474  ue.undo();
475  }
476  }
477 
478  public void redo() {
479  if (!redostack.isEmpty()) {
480  UndoableEdit ue = redostack.pop();
481  undostack.push(ue);
482  ue.redo();
483  }
484  }
485 
486  public void discardAllEdits() {
487  undostack.clear();
488  redostack.clear();
489  }
490 
491  private void storeInsert(int pos, int length, String text) {
492  undostack.push(new UndoableEdit(true, pos, length, text));
493  redostack.clear();
494  }
495 
496  private void storeRemove(int pos, int length, String text) {
497  undostack.push(new UndoableEdit(false, pos, length, text));
498  redostack.clear();
499  }
500 
501  private class UndoableEdit {
502  final boolean insert;
503  final int pos;
504  final int length;
505  final String text;
506 
507  public UndoableEdit(boolean insert, int pos, int length, String text) {
508  super();
509  this.insert = insert;
510  this.pos = pos;
511  this.length = length;
512  this.text = text;
513  }
514 
515  public void redo() {
516  try {
517  if (insert) {
518  SourceCode.super.insertString(pos, text, null);
519  } else {
520  SourceCode.super.remove(pos, length);
521  }
522  } catch (BadLocationException e) {
523  }
524  }
525 
526  public void undo() {
527  try {
528  if (insert) {
529  SourceCode.super.remove(pos, length);
530  } else {
531  SourceCode.super.insertString(pos, text, null);
532  }
533  } catch (BadLocationException e) {
534  }
535  }
536  }
537 }
Aufräumklasse.
Definition: Stop.java:15
static void addCleanupListener(CleanupListener cl)
Definition: Stop.java:34
synchronized void addLogListener(LogListener log)
synchronized void addConfigurationListener(GuiConfigurationListener cf)
static String getString(String id)
Definition: Messages.java:29
static String formatError(String id, Object... info)
Definition: Messages.java:25
void compileComplete(LinkedList< ErrorInformation > errors, AbstractProgram program)
void setSideInformation(SideInformation sideInformation)
void removeUpdate(DocumentEvent e)
void insertString(int pos, String text, AttributeSet attributeSet)
DefaultStyledDocument getOutput()
void changedUpdate(DocumentEvent e)
void debugPointReached(DebugInfo debugInfo)
boolean hasError(int linenumber)
boolean hasBreakpoint(int linenumber)
void init(AutoCompleteHook autoCompleteHook, CodeColorHook codeColorHook)
Definition: SourceCode.java:77
void insertUpdate(DocumentEvent e)
void message(String msg, ErrorLevel level)
void insertString(int pos, String text, AbstractProgram program)
void markExecutionPoint(Marker codeRange)
void addDebugListener(DebugListener debugListener)
int getBreakpointPosition(int linenumber)
void removeDebugListener(DebugListener debugListener)
void setBreakpoint(int linenumber, boolean b)
Impressum